Replace UUID-looking sync labels in TUI and web with keys like TAS-1, while keeping the remote UUID for task-master matching. Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// backfillTicketKeys assigns project codes and sequential ticket keys for rows
|
|
// that predate migration 005. Safe to run repeatedly (no-ops when already set).
|
|
func (db *DB) backfillTicketKeys() error {
|
|
if err := db.backfillProjectCodes(); err != nil {
|
|
return err
|
|
}
|
|
return db.backfillTicketNumbers()
|
|
}
|
|
|
|
func (db *DB) backfillProjectCodes() error {
|
|
rows, err := db.SQL.Query(`SELECT id, name, code FROM projects`)
|
|
if err != nil {
|
|
return fmt.Errorf("backfill codes: list: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
type row struct {
|
|
id, name, code string
|
|
}
|
|
var all []row
|
|
used := map[string]bool{}
|
|
for rows.Next() {
|
|
var r row
|
|
if err := rows.Scan(&r.id, &r.name, &r.code); err != nil {
|
|
return err
|
|
}
|
|
all = append(all, r)
|
|
if r.code != "" {
|
|
used[r.code] = true
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, r := range all {
|
|
if r.code != "" {
|
|
continue
|
|
}
|
|
code := nextUniqueCode(r.name, used)
|
|
used[code] = true
|
|
if _, err := db.SQL.Exec(`UPDATE projects SET code = ? WHERE id = ?`, code, r.id); err != nil {
|
|
return fmt.Errorf("backfill code %s: %w", r.id, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) backfillTicketNumbers() error {
|
|
type proj struct {
|
|
id, code string
|
|
seq int
|
|
}
|
|
var projects []proj
|
|
{
|
|
projRows, err := db.SQL.Query(`SELECT id, code, ticket_seq FROM projects`)
|
|
if err != nil {
|
|
return fmt.Errorf("backfill tickets: projects: %w", err)
|
|
}
|
|
for projRows.Next() {
|
|
var p proj
|
|
if err := projRows.Scan(&p.id, &p.code, &p.seq); err != nil {
|
|
projRows.Close()
|
|
return err
|
|
}
|
|
projects = append(projects, p)
|
|
}
|
|
err = projRows.Err()
|
|
projRows.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, p := range projects {
|
|
type trow struct {
|
|
id, key string
|
|
number sql.NullInt64
|
|
}
|
|
var tickets []trow
|
|
{
|
|
tRows, err := db.SQL.Query(`
|
|
SELECT id, number, key FROM tickets
|
|
WHERE project_id = ?
|
|
ORDER BY created_at ASC, id ASC`, p.id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for tRows.Next() {
|
|
var tr trow
|
|
if err := tRows.Scan(&tr.id, &tr.number, &tr.key); err != nil {
|
|
tRows.Close()
|
|
return err
|
|
}
|
|
tickets = append(tickets, tr)
|
|
}
|
|
err = tRows.Err()
|
|
tRows.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
seq := p.seq
|
|
for _, tr := range tickets {
|
|
if tr.number.Valid && tr.key != "" {
|
|
if int(tr.number.Int64) > seq {
|
|
seq = int(tr.number.Int64)
|
|
}
|
|
continue
|
|
}
|
|
seq++
|
|
newKey := fmt.Sprintf("%s-%d", p.code, seq)
|
|
if _, err := db.SQL.Exec(`UPDATE tickets SET number = ?, key = ? WHERE id = ?`, seq, newKey, tr.id); err != nil {
|
|
return fmt.Errorf("backfill ticket %s: %w", tr.id, err)
|
|
}
|
|
}
|
|
if seq != p.seq {
|
|
if _, err := db.SQL.Exec(`UPDATE projects SET ticket_seq = ? WHERE id = ?`, seq, p.id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|