Closed irreverentsimplicity closed 4 months ago
type Actor struct {
Id string `json:"actorId"`
TeamId string `json:"teamId"`
Name string `json:"actorName"`
// github handle?
}
Consider adding the user's Gno address as std.Address
. I think the GitHub handle should eventually be derivable from the GitHub oracle realm (from the address). That said, it probably makes sense to keep the addresses optional so not everyone has to be signed up from the get-go.
TeamId implies the user can only be in one team; I would advise against this. Consider instead either having Members []*Actor
in Team
; or using a tree to map Actor ID -> []*Team
/ Team ID -> []*Actor
.
type Team struct {
Id string `json:"teamId"`
Name string `json:"teamName"`
}
Maybe it should have an owner, not sure. Definitely there should be ways to get the team's members, for instance.
type WorkHour struct {
Id string `json:"workHourId"`
ObjectId string `json:"objectId"`
ObjectType string `json:"objectType"` // Task, Project
Amount string `json:"workHourAmount"`
}
Seems weird to track WorkHour
; I'd suggest WorkDuration
; possibly with Duration time.Duration
.
A better way to link to the "Object" may be through an interface: I suggest a Workable
interface implemented by *Task and *Project.
type RewardsPoint struct {
Id string `json:"rewardsPointId"`
ObjectId string `json:"objectId"`
ObjectType string `json:"objectType"`
Amount string `json:"rewardsPointAmount"`
Status string `json:"rewardsPointStatus"` // assigned / released / unused, etc
}
Aside from Object to Workable, Amount
should probably be either std.Coins
or some type where it is easily possible to get the reward as an integer and denomination.
type Resource struct {
Id string `json:"resourceId"`
Name string `json:"resourceName"` // money / fixed expenses etc
}
This one confuses me.
@thehowl Thank you! Will add an Address:std.Address
to the Actor struct. Good points about using an interface instead of an Object mapping. Do you have any pointers to some code of using an interface, just for reference?
I also think adding []Actors
to the Team
object, this may be a frequently used data, so it is less costly to retrieve if we keep it inside the main struct - that's how Project
struct uses []Tasks
in zentasktic_core
.
As for the Resource
type, in my view this is just a generic placeholder for anything that we cannot think about right now. In theory, we can track any project just with time and money, but there might be metrics we cannot think of. Happy to let this out for now.
I also think adding
[]Actors
to theTeam
object, this may be a frequently used data, so it is less costly to retrieve if we keep it inside the main struct - that's howProject
struct uses[]Tasks
inzentasktic_core
.
Just a note, use *Actor
(ie. the pointer is important).
This way you can keep a tree that keeps all the actor (ID -> *Actor
); and then keep the references in a []*Actor
as well. Because we're talking about pointers, loading a []*Actor
is cheaper than loading an []Actor
; and furthermore, any changes done from an *Actor
retrieved from the tree will reflect also on the []*Actor
value in the Team.
Do you have any pointers to some code of using an interface, just for reference?
I wouldn't know where to get it, so here's a reference:
package main
type Workable interface {
// nobody outside your realm will be able to implement this
assertWorkable()
}
type Task struct {
// ...
}
func (*Task) assertWorkable() {}
var _ Workable = &Task{}
type Project struct {
// ...
}
func (*Project) assertWorkable() {}
var _ Workable = &Project{}
With this code, you guarantee that if you have a Workable
type, you'll only be able to assign nil
, *Project
and *Task
to it. (It kind of works like a Union, I guess.)
As for the
Resource
type, in my view this is just a generic placeholder for anything that we cannot think about right now. In theory, we can track any project just with time and money, but there might be metrics we cannot think of. Happy to let this out for now.
I struggle to understand where it would be used in practice. I'd suggest you drop it for now and reconsider it if there is a clear use-case; which might help to suggest also what the data structure should really look like.
Thanks, good point about using pointers, it's a part that I often overlook in Go, as it's not on my daily JS / React toolset. Also got your point about Workable
, will implement it, thanks for the code, The Resource
data type I already took out, all good.
Here's a basic implementation of WorkDuration:
package zentasktic_project
import (
"strconv"
"errors"
"time"
"gno.land/p/demo/avl"
"gno.land/p/demo/zentasktic"
)
type Workable interface {
// restrict implementation of Workable to this realm
assertWorkable()
}
type isWorkable struct {}
type WorkableTask struct {
zentasktic.Task
}
func (wt *WorkableTask) assertWorkable() {}
type WorkableProject struct {
zentasktic.Project
}
func (wp *WorkableProject) assertWorkable() {}
var _ Workable = &WorkableTask{}
var _ Workable = &WorkableProject{}
type WorkDuration struct {
Id string `json:"workHourId"`
ObjectId string `json:"objectId"`
ObjectType string `json:"objectType"` // Task, Project
Duration time.Duration `json:"workDuration"`
Workable Workable `json:"-"`
}
var {
WorkDurations avl.Tree // wd.Id -> wd
}
func (wd WorkDuration) AddWorkDuration() (err errors) {
WorkDurations.Set(wd.Id, wd)
return nil
}
func (wd WorkDuration) RemoveWorkDuration() (err errors) {
// implementation
existingWorkDuration := WorkDuration{}
if WorkDurations.Size() != 0 {
existingWorkDuration, exist := WorkDurations.Get(wd.Id)
if !exist {
return ErrWorkDurationNotFound
}
}
_, removed := WorkDurations.Remove(existingWorkDuration.Id)
if !removed {
return ErrTaskNotRemoved
}
return nil
}
// getters
func GetWorkDurationByOjectId(objectId string, objectType string) (wd WorkDuration, err errors) {
// implementation
workDuration := WorkDuration{}
// Iterate over the WorkDuration AVL tree to see if we have a matching ObjectId.
WorkDurations.Iterate("", "", func(key string, value interface{}) bool {
if workDurationItem, ok := value.(WorkDuration); ok {
if workDurationItem.ObjectId == objectId && workDurationItem.ObjectType == objectType {
workDuration = workDurationItem
}
}
return false // Continue iteration until all nodes have been visited.
})
return workDuration, nil
}
The main point is that I still kept the ObjectType for some fine grain filtering. We may need to assess the WorkDurations for specific types (Tasks / Projects). I don't see how we can do that with just the interface. Am I missing something, is this doable without keeping the ObjectType in the struct?
This is intended as conversation starter for the data types we may use to enrich Decide, types which may be specific only to the Gno ecosystem project management processes. Comments welcome!
Proposed data structures / rich types for a DAO / team
These types are intended to be used in the Decide realm.
Actor
The basic working unit for an individual. Actors can be team members, or individual contributors.
Team
Teams are collective entities, they may or may not have actors assigned.
WorkHour
WorkHours are abstract time measurements assignable to a Task / Projet. They may or may not be combined with Actors or Teams.
RewardsPoints
RewardsPoints are abstract denominations for how much a certain task or project should be worth on completion. They may or may not be assigned to Actors or Teams, but they are releasable once a Task / Project is completed (moving a Task / Project with assigned RewardsPoints from Do to a Collection releases the assigned rewards points from the total existing).
Resources
Resources are abstract denominations for what needs to be spent, e.g. money, electricity, for a specific Task / Project.