Upsert creates or updates a project.
(updates map[string]string)
| 32 | |
| 33 | // Upsert creates or updates a project. |
| 34 | func (pt *ProjectTracker) Upsert(updates map[string]string) bool { |
| 35 | name := strings.TrimSpace(updates["project_name"]) |
| 36 | if name == "" { |
| 37 | name = strings.TrimSpace(updates["name"]) |
| 38 | } |
| 39 | if name == "" { |
| 40 | return false |
| 41 | } |
| 42 | name = strings.ToLower(name) |
| 43 | |
| 44 | pt.mu.Lock() |
| 45 | defer pt.mu.Unlock() |
| 46 | |
| 47 | p, exists := pt.projects[name] |
| 48 | if !exists { |
| 49 | p = &Project{ |
| 50 | Name: name, |
| 51 | Status: "active", |
| 52 | } |
| 53 | pt.projects[name] = p |
| 54 | } |
| 55 | |
| 56 | changed := false |
| 57 | if v := strings.TrimSpace(updates["project_path"]); v != "" && p.Path != v { |
| 58 | p.Path = v |
| 59 | changed = true |
| 60 | } |
| 61 | if v := strings.TrimSpace(updates["project_status"]); v != "" { |
| 62 | normalized := normalizeStatus(v) |
| 63 | if p.Status != normalized { |
| 64 | p.Status = normalized |
| 65 | changed = true |
| 66 | } |
| 67 | } |
| 68 | if v := strings.TrimSpace(updates["project_description"]); v != "" && p.Description != v { |
| 69 | p.Description = v |
| 70 | changed = true |
| 71 | } |
| 72 | if v := strings.TrimSpace(updates["project_technologies"]); v != "" { |
| 73 | techs := parseTechList(v) |
| 74 | if !stringSliceEqual(p.Technologies, techs) { |
| 75 | p.Technologies = mergeTechs(p.Technologies, techs) |
| 76 | changed = true |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | if changed || !exists { |
| 81 | p.LastActive = time.Now() |
| 82 | pt.persist() |
| 83 | } |
| 84 | return changed || !exists |
| 85 | } |
| 86 | |
| 87 | // Touch updates the LastActive timestamp for a project. |
| 88 | func (pt *ProjectTracker) Touch(name string) { |