package fic import ( "database/sql" "path" "time" ) // Claim represents an issue, a bug or a ToDo item. type Claim struct { Id int64 `json:"id"` Subject string `json:"subject"` IdTeam *int64 `json:"id_team"` IdExercice *int64 `json:"id_exercice"` IdAssignee *int64 `json:"id_assignee"` Creation time.Time `json:"creation"` State string `json:"state"` Priority string `json:"priority"` } // GetClaim retrieves the claim with the given identifier. func GetClaim(id int64) (c Claim, err error) { err = DBQueryRow("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_claim = ?", id).Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority) return } // GetClaims returns a list of all Claim registered in the database. func GetClaims() (res []Claim, err error) { var rows *sql.Rows if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims"); err != nil { return } defer rows.Close() for rows.Next() { var c Claim if err = rows.Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority); err != nil { return } res = append(res, c) } err = rows.Err() return } // GetClaim retrieves the claim with the given identifier and registered for the given Team. func (t Team) GetClaim(id int64) (c Claim, err error) { err = DBQueryRow("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_claim = ? AND id_team = ?", id, t.Id).Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority) return } // GetClaims returns a list of all Claim registered for the Team. func (t Team) GetClaims() (res []Claim, err error) { var rows *sql.Rows if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_team = ?", t.Id); err != nil { return nil, err } defer rows.Close() for rows.Next() { var c Claim if err = rows.Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority); err != nil { return } res = append(res, c) } err = rows.Err() return } // GetExercices returns a list of all Claim registered for the Exercice. func (e Exercice) GetClaims() (res []Claim, err error) { var rows *sql.Rows if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_exercice = ?", e.Id); err != nil { return nil, err } defer rows.Close() for rows.Next() { var c Claim if err = rows.Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority); err != nil { return } res = append(res, c) } err = rows.Err() return } // NewClaim creates and fills a new struct Claim and registers it into the database. func NewClaim(subject string, team *Team, exercice *Exercice, assignee *ClaimAssignee, priority string) (Claim, error) { var tid *int64 if team == nil { tid = nil } else { tid = &team.Id } var eid *int64 if exercice == nil { eid = nil } else { eid = &exercice.Id } var aid *int64 if assignee == nil { aid = nil } else { aid = &assignee.Id } if res, err := DBExec("INSERT INTO claims (subject, id_team, id_exercice, id_assignee, creation, state, priority) VALUES (?, ?, ?, ?, ?, ?, ?)", subject, tid, eid, aid, time.Now(), "new", priority); err != nil { return Claim{}, err } else if cid, err := res.LastInsertId(); err != nil { return Claim{}, err } else { return Claim{cid, subject, tid, eid, aid, time.Now(), "new", priority}, nil } } // GetTeam returns the Team linked to the issue, if any. func (c Claim) GetTeam() (*Team, error) { if c.IdTeam == nil { return nil, nil } else if t, err := GetTeam(*c.IdTeam); err != nil { return nil, err } else { return &t, nil } } // SetTeam defines the Team that is linked to this issue. func (c Claim) SetTeam(t Team) { c.IdTeam = &t.Id } // GetExercice returns the Exercice linked to the issue, if any. func (c Claim) GetExercice() (*Exercice, error) { if c.IdExercice == nil { return nil, nil } else if e, err := GetExercice(*c.IdExercice); err != nil { return nil, err } else { return &e, nil } } // SetExercice defines the Exercice that is linked to this issue. func (c Claim) SetExercice(e Exercice) { c.IdExercice = &e.Id } // Update applies modifications back to the database. func (c Claim) Update() (int64, error) { if res, err := DBExec("UPDATE claims SET subject = ?, id_team = ?, id_exercice = ?, id_assignee = ?, creation = ?, state = ?, priority = ? WHERE id_claim = ?", c.Subject, c.IdTeam, c.IdExercice, c.IdAssignee, c.Creation, c.State, c.Priority, c.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // Delete the issue from the database. func (c Claim) Delete() (int64, error) { if res, err := DBExec("DELETE FROM claims WHERE id_claim = ?", c.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // ClearClaims removes all issues from database. func ClearClaims() (int64, error) { if res, err := DBExec("DELETE FROM claims"); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // ClaimDescription represents some text describing an issue. type ClaimDescription struct { Id int64 `json:"id"` // IdAssignee stores the user who handle the claim (or 0 if nobody handles it). IdAssignee int64 `json:"id_assignee"` // Content is the raw description. Content string `json:"content"` // Date is the timestamp when the description was written. Date time.Time `json:"date"` // Publish indicates wether it is shown back to the team. Publish bool `json:"publish"` } // GetLastUpdate returns the date of the latest message written for the given Claim. func (c Claim) GetLastUpdate() (res *time.Time, err error) { err = DBQueryRow("SELECT MAX(date) FROM claim_descriptions WHERE id_claim = ? GROUP BY id_claim", c.Id).Scan(&res) return } // GetDescriptions returns a list of all descriptions stored in the database for the Claim. func (c Claim) GetDescriptions() (res []ClaimDescription, err error) { var rows *sql.Rows if rows, err = DBQuery("SELECT id_description, id_assignee, content, date, publish FROM claim_descriptions WHERE id_claim = ?", c.Id); err != nil { return nil, err } defer rows.Close() for rows.Next() { var d ClaimDescription if err = rows.Scan(&d.Id, &d.IdAssignee, &d.Content, &d.Date, &d.Publish); err != nil { return } res = append(res, d) } err = rows.Err() return } // AddDescription append in the database a new description; then returns the corresponding structure. func (c Claim) AddDescription(content string, assignee ClaimAssignee, publish bool) (ClaimDescription, error) { if res, err := DBExec("INSERT INTO claim_descriptions (id_claim, id_assignee, content, date, publish) VALUES (?, ?, ?, ?, ?)", c.Id, assignee.Id, content, time.Now(), publish); err != nil { return ClaimDescription{}, err } else if did, err := res.LastInsertId(); err != nil { return ClaimDescription{}, err } else { return ClaimDescription{did, assignee.Id, content, time.Now(), publish}, nil } } // GetAssignee retrieves an assignee from its identifier. func (d ClaimDescription) GetAssignee() (a ClaimAssignee, err error) { err = DBQueryRow("SELECT id_assignee, name FROM claim_assignees WHERE id_assignee = ?", d.IdAssignee).Scan(&a.Id, &a.Name) return } // Update applies modifications back to the database func (d ClaimDescription) Update() (int64, error) { if res, err := DBExec("UPDATE claim_descriptions SET id_assignee = ?, content = ?, date = ?, publish = ? WHERE id_description = ?", d.IdAssignee, d.Content, d.Date, d.Publish, d.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // Delete the description in the database. func (d ClaimDescription) Delete() (int64, error) { if res, err := DBExec("DELETE FROM claim_descriptions WHERE id_description = ?", d.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // ClaimAssignee represents a user that can handle claims. type ClaimAssignee struct { Id int64 `json:"id"` Name string `json:"name"` } // GetAssignee retrieves an assignee from its identifier. func GetAssignee(id int64) (a ClaimAssignee, err error) { err = DBQueryRow("SELECT id_assignee, name FROM claim_assignees WHERE id_assignee = ?", id).Scan(&a.Id, &a.Name) return } // GetAssignees returns a list of all assignees found in the database. func GetAssignees() (res []ClaimAssignee, err error) { var rows *sql.Rows if rows, err = DBQuery("SELECT id_assignee, name FROM claim_assignees"); err != nil { return } defer rows.Close() for rows.Next() { var a ClaimAssignee if err = rows.Scan(&a.Id, &a.Name); err != nil { return } res = append(res, a) } err = rows.Err() return } // NewClaimAssignee creates and fills a new struct ClaimAssignee and registers it into the database. func NewClaimAssignee(name string) (ClaimAssignee, error) { if res, err := DBExec("INSERT INTO claim_assignees (name) VALUES (?)", name); err != nil { return ClaimAssignee{}, err } else if aid, err := res.LastInsertId(); err != nil { return ClaimAssignee{}, err } else { return ClaimAssignee{aid, name}, nil } } // Update applies modifications back to the database func (a ClaimAssignee) Update() (int64, error) { if res, err := DBExec("UPDATE claim_assignees SET name = ? WHERE id_assignee = ?", a.Name, a.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // Delete the assignee in the database. func (a ClaimAssignee) Delete() (int64, error) { if res, err := DBExec("DELETE FROM claim_assignees WHERE id_assignee = ?", a.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // ClearAssignees removes all assignees from database. func ClearAssignees() (int64, error) { if res, err := DBExec("DELETE FROM claim_assignees"); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } // GetAssignee returns the assignee assigned to the claim. func (c Claim) GetAssignee() (*ClaimAssignee, error) { if c.IdAssignee == nil { return nil, nil } else if a, err := GetAssignee(*c.IdAssignee); err != nil { return nil, err } else { return &a, nil } } // SetAssignee defines the assignee that'll handle the claim. func (c Claim) SetAssignee(a ClaimAssignee) { c.IdAssignee = &a.Id } type teamIssueText struct { Content string `json:"cnt"` Assignee string `json:"assignee"` Date time.Time `json:"date"` } type teamIssueFile struct { Id int64 `json:"id"` Subject string `json:"subject"` Exercice *string `json:"exercice,omitempty"` ExerciceURL string `json:"url,omitempty"` Assignee *string `json:"assignee,omitempty"` State string `json:"state"` Priority string `json:"priority"` Texts []teamIssueText `json:"texts"` } func (t Team) MyIssueFile() (ret []teamIssueFile, err error) { var claims []Claim if claims, err = t.GetClaims(); err == nil { for _, claim := range claims { var exercice *string = nil var url string if exo, err := claim.GetExercice(); err == nil && exo != nil { exercice = &exo.Title if theme, err := GetTheme(exo.IdTheme); err == nil { url = path.Join(theme.URLId, exo.URLId) } } var assignee *string = nil if a, err := claim.GetAssignee(); err == nil && a != nil { assignee = &a.Name } if descriptions, err := claim.GetDescriptions(); err != nil { return nil, err } else { tif := teamIssueFile{ Id: claim.Id, Subject: claim.Subject, Exercice: exercice, ExerciceURL: url, Assignee: assignee, State: claim.State, Priority: claim.Priority, Texts: []teamIssueText{}, } for _, description := range descriptions { if description.Publish { if people, err := description.GetAssignee(); err != nil { return nil, err } else { tif.Texts = append(tif.Texts, teamIssueText{ Content: description.Content, Assignee: people.Name, Date: description.Date, }) } } } ret = append(ret, tif) } } } return }