From faf3fc63a1302d0a7641e6785b7c0eb66f4514e2 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Tue, 23 Sep 2025 09:48:26 +0000 Subject: [PATCH] appview/models: move db.Profile* into models Signed-off-by: oppiliappan --- appview/db/profile.go | 210 ++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- appview/db/timeline.go | 2 +- appview/ingester.go | 6 +++--- appview/models/profile.go | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ appview/notify/merged_notifier.go | 2 +- appview/notify/notifier.go | 4 ++-- appview/pages/pages.go | 10 +++++----- appview/posthog/notifier.go | 2 +- appview/state/profile.go | 14 +++++++------- 9 file(s) changed, 217 insertion(s)(+), 210 deletion(s)(-) diff --git a/appview/db/profile.go b/appview/db/profile.go --- a/appview/db/profile.go +++ b/appview/db/profile.go @@ -10,105 +10,14 @@ "strings" "time" "github.com/bluesky-social/indigo/atproto/syntax" - "tangled.org/core/api/tangled" "tangled.org/core/appview/models" ) -type RepoEvent struct { - Repo *models.Repo - Source *models.Repo -} - -type ProfileTimeline struct { - ByMonth []ByMonth -} - -func (p *ProfileTimeline) IsEmpty() bool { - if p == nil { - return true - } - - for _, m := range p.ByMonth { - if !m.IsEmpty() { - return false - } - } - - return true -} - -type ByMonth struct { - RepoEvents []RepoEvent - IssueEvents IssueEvents - PullEvents PullEvents -} - -func (b ByMonth) IsEmpty() bool { - return len(b.RepoEvents) == 0 && - len(b.IssueEvents.Items) == 0 && - len(b.PullEvents.Items) == 0 -} - -type IssueEvents struct { - Items []*models.Issue -} - -type IssueEventStats struct { - Open int - Closed int -} - -func (i IssueEvents) Stats() IssueEventStats { - var open, closed int - for _, issue := range i.Items { - if issue.Open { - open += 1 - } else { - closed += 1 - } - } - - return IssueEventStats{ - Open: open, - Closed: closed, - } -} - -type PullEvents struct { - Items []*models.Pull -} - -func (p PullEvents) Stats() PullEventStats { - var open, merged, closed int - for _, pull := range p.Items { - switch pull.State { - case models.PullOpen: - open += 1 - case models.PullMerged: - merged += 1 - case models.PullClosed: - closed += 1 - } - } - - return PullEventStats{ - Open: open, - Merged: merged, - Closed: closed, - } -} - -type PullEventStats struct { - Closed int - Open int - Merged int -} - const TimeframeMonths = 7 -func MakeProfileTimeline(e Execer, forDid string) (*ProfileTimeline, error) { - timeline := ProfileTimeline{ - ByMonth: make([]ByMonth, TimeframeMonths), +func MakeProfileTimeline(e Execer, forDid string) (*models.ProfileTimeline, error) { + timeline := models.ProfileTimeline{ + ByMonth: make([]models.ByMonth, TimeframeMonths), } currentMonth := time.Now().Month() timeframe := fmt.Sprintf("-%d months", TimeframeMonths) @@ -181,7 +90,7 @@ idx := currentMonth - repoMonth items := &timeline.ByMonth[idx].RepoEvents - *items = append(*items, RepoEvent{ + *items = append(*items, models.RepoEvent{ Repo: &repo, Source: sourceRepo, }) @@ -190,86 +99,7 @@ return &timeline, nil } -type Profile struct { - // ids - ID int - Did string - - // data - Description string - IncludeBluesky bool - Location string - Links [5]string - Stats [2]VanityStat - PinnedRepos [6]syntax.ATURI -} - -func (p Profile) IsLinksEmpty() bool { - for _, l := range p.Links { - if l != "" { - return false - } - } - return true -} - -func (p Profile) IsStatsEmpty() bool { - for _, s := range p.Stats { - if s.Kind != "" { - return false - } - } - return true -} - -func (p Profile) IsPinnedReposEmpty() bool { - for _, r := range p.PinnedRepos { - if r != "" { - return false - } - } - return true -} - -type VanityStatKind string - -const ( - VanityStatMergedPRCount VanityStatKind = "merged-pull-request-count" - VanityStatClosedPRCount VanityStatKind = "closed-pull-request-count" - VanityStatOpenPRCount VanityStatKind = "open-pull-request-count" - VanityStatOpenIssueCount VanityStatKind = "open-issue-count" - VanityStatClosedIssueCount VanityStatKind = "closed-issue-count" - VanityStatRepositoryCount VanityStatKind = "repository-count" -) - -func (v VanityStatKind) String() string { - switch v { - case VanityStatMergedPRCount: - return "Merged PRs" - case VanityStatClosedPRCount: - return "Closed PRs" - case VanityStatOpenPRCount: - return "Open PRs" - case VanityStatOpenIssueCount: - return "Open Issues" - case VanityStatClosedIssueCount: - return "Closed Issues" - case VanityStatRepositoryCount: - return "Repositories" - } - return "" -} - -type VanityStat struct { - Kind VanityStatKind - Value uint64 -} - -func (p *Profile) ProfileAt() syntax.ATURI { - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.Did, tangled.ActorProfileNSID, "self")) -} - -func UpsertProfile(tx *sql.Tx, profile *Profile) error { +func UpsertProfile(tx *sql.Tx, profile *models.Profile) error { defer tx.Rollback() // update links @@ -367,7 +197,7 @@ return tx.Commit() } -func GetProfiles(e Execer, filters ...filter) (map[string]*Profile, error) { +func GetProfiles(e Execer, filters ...filter) (map[string]*models.Profile, error) { var conditions []string var args []any for _, filter := range filters { @@ -397,9 +227,9 @@ if err != nil { return nil, err } - profileMap := make(map[string]*Profile) + profileMap := make(map[string]*models.Profile) for rows.Next() { - var profile Profile + var profile models.Profile var includeBluesky int err = rows.Scan(&profile.ID, &profile.Did, &profile.Description, &includeBluesky, &profile.Location) @@ -470,8 +300,8 @@ return profileMap, nil } -func GetProfile(e Execer, did string) (*Profile, error) { - var profile Profile +func GetProfile(e Execer, did string) (*models.Profile, error) { + var profile models.Profile profile.Did = did includeBluesky := 0 @@ -480,7 +310,7 @@ `select description, include_bluesky, location from profile where did = ?`, did, ).Scan(&profile.Description, &includeBluesky, &profile.Location) if err == sql.ErrNoRows { - profile := Profile{} + profile := models.Profile{} profile.Did = did return &profile, nil } @@ -540,26 +370,26 @@ return &profile, nil } -func GetVanityStat(e Execer, did string, stat VanityStatKind) (uint64, error) { +func GetVanityStat(e Execer, did string, stat models.VanityStatKind) (uint64, error) { query := "" var args []any switch stat { - case VanityStatMergedPRCount: + case models.VanityStatMergedPRCount: query = `select count(id) from pulls where owner_did = ? and state = ?` args = append(args, did, models.PullMerged) - case VanityStatClosedPRCount: + case models.VanityStatClosedPRCount: query = `select count(id) from pulls where owner_did = ? and state = ?` args = append(args, did, models.PullClosed) - case VanityStatOpenPRCount: + case models.VanityStatOpenPRCount: query = `select count(id) from pulls where owner_did = ? and state = ?` args = append(args, did, models.PullOpen) - case VanityStatOpenIssueCount: + case models.VanityStatOpenIssueCount: query = `select count(id) from issues where did = ? and open = 1` args = append(args, did) - case VanityStatClosedIssueCount: + case models.VanityStatClosedIssueCount: query = `select count(id) from issues where did = ? and open = 0` args = append(args, did) - case VanityStatRepositoryCount: + case models.VanityStatRepositoryCount: query = `select count(id) from repos where did = ?` args = append(args, did) } @@ -573,7 +403,7 @@ return result, nil } -func ValidateProfile(e Execer, profile *Profile) error { +func ValidateProfile(e Execer, profile *models.Profile) error { // ensure description is not too long if len(profile.Description) > 256 { return fmt.Errorf("Entered bio is too long.") @@ -621,7 +451,7 @@ return nil } -func validateLinks(profile *Profile) error { +func validateLinks(profile *models.Profile) error { for i, link := range profile.Links { if link == "" { continue diff --git a/appview/db/timeline.go b/appview/db/timeline.go --- a/appview/db/timeline.go +++ b/appview/db/timeline.go @@ -19,7 +19,7 @@ // optional: populate only if Repo is a fork Source *models.Repo // optional: populate only if event is Follow - *Profile + *models.Profile *models.FollowStats *models.FollowStatus diff --git a/appview/ingester.go b/appview/ingester.go --- a/appview/ingester.go +++ b/appview/ingester.go @@ -299,10 +299,10 @@ links[i] = l } } - var stats [2]db.VanityStat + var stats [2]models.VanityStat for i, s := range record.Stats { if i < 2 { - stats[i].Kind = db.VanityStatKind(s) + stats[i].Kind = models.VanityStatKind(s) } } @@ -313,7 +313,7 @@ pinned[i] = syntax.ATURI(r) } } - profile := db.Profile{ + profile := models.Profile{ Did: did, Description: description, IncludeBluesky: includeBluesky, diff --git a/appview/models/profile.go b/appview/models/profile.go new file mode 100644 --- /dev/null +++ b/appview/models/profile.go @@ -0,0 +1,177 @@ +package models + +import ( + "fmt" + + "github.com/bluesky-social/indigo/atproto/syntax" + "tangled.org/core/api/tangled" +) + +type Profile struct { + // ids + ID int + Did string + + // data + Description string + IncludeBluesky bool + Location string + Links [5]string + Stats [2]VanityStat + PinnedRepos [6]syntax.ATURI +} + +func (p Profile) IsLinksEmpty() bool { + for _, l := range p.Links { + if l != "" { + return false + } + } + return true +} + +func (p Profile) IsStatsEmpty() bool { + for _, s := range p.Stats { + if s.Kind != "" { + return false + } + } + return true +} + +func (p Profile) IsPinnedReposEmpty() bool { + for _, r := range p.PinnedRepos { + if r != "" { + return false + } + } + return true +} + +type VanityStatKind string + +const ( + VanityStatMergedPRCount VanityStatKind = "merged-pull-request-count" + VanityStatClosedPRCount VanityStatKind = "closed-pull-request-count" + VanityStatOpenPRCount VanityStatKind = "open-pull-request-count" + VanityStatOpenIssueCount VanityStatKind = "open-issue-count" + VanityStatClosedIssueCount VanityStatKind = "closed-issue-count" + VanityStatRepositoryCount VanityStatKind = "repository-count" +) + +func (v VanityStatKind) String() string { + switch v { + case VanityStatMergedPRCount: + return "Merged PRs" + case VanityStatClosedPRCount: + return "Closed PRs" + case VanityStatOpenPRCount: + return "Open PRs" + case VanityStatOpenIssueCount: + return "Open Issues" + case VanityStatClosedIssueCount: + return "Closed Issues" + case VanityStatRepositoryCount: + return "Repositories" + } + return "" +} + +type VanityStat struct { + Kind VanityStatKind + Value uint64 +} + +func (p *Profile) ProfileAt() syntax.ATURI { + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.Did, tangled.ActorProfileNSID, "self")) +} + +type RepoEvent struct { + Repo *Repo + Source *Repo +} + +type ProfileTimeline struct { + ByMonth []ByMonth +} + +func (p *ProfileTimeline) IsEmpty() bool { + if p == nil { + return true + } + + for _, m := range p.ByMonth { + if !m.IsEmpty() { + return false + } + } + + return true +} + +type ByMonth struct { + RepoEvents []RepoEvent + IssueEvents IssueEvents + PullEvents PullEvents +} + +func (b ByMonth) IsEmpty() bool { + return len(b.RepoEvents) == 0 && + len(b.IssueEvents.Items) == 0 && + len(b.PullEvents.Items) == 0 +} + +type IssueEvents struct { + Items []*Issue +} + +type IssueEventStats struct { + Open int + Closed int +} + +func (i IssueEvents) Stats() IssueEventStats { + var open, closed int + for _, issue := range i.Items { + if issue.Open { + open += 1 + } else { + closed += 1 + } + } + + return IssueEventStats{ + Open: open, + Closed: closed, + } +} + +type PullEvents struct { + Items []*Pull +} + +func (p PullEvents) Stats() PullEventStats { + var open, merged, closed int + for _, pull := range p.Items { + switch pull.State { + case PullOpen: + open += 1 + case PullMerged: + merged += 1 + case PullClosed: + closed += 1 + } + } + + return PullEventStats{ + Open: open, + Merged: merged, + Closed: closed, + } +} + +type PullEventStats struct { + Closed int + Open int + Merged int +} diff --git a/appview/notify/merged_notifier.go b/appview/notify/merged_notifier.go --- a/appview/notify/merged_notifier.go +++ b/appview/notify/merged_notifier.go @@ -62,7 +62,7 @@ notifier.NewPullComment(ctx, comment) } } -func (m *mergedNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) { +func (m *mergedNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) { for _, notifier := range m.notifiers { notifier.UpdateProfile(ctx, profile) } diff --git a/appview/notify/notifier.go b/appview/notify/notifier.go --- a/appview/notify/notifier.go +++ b/appview/notify/notifier.go @@ -21,7 +21,7 @@ NewPull(ctx context.Context, pull *models.Pull) NewPullComment(ctx context.Context, comment *models.PullComment) - UpdateProfile(ctx context.Context, profile *db.Profile) + UpdateProfile(ctx context.Context, profile *models.Profile) NewString(ctx context.Context, s *db.String) EditString(ctx context.Context, s *db.String) @@ -46,7 +46,7 @@ func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {} func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment) {} -func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) {} +func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) {} func (m *BaseNotifier) NewString(ctx context.Context, s *db.String) {} func (m *BaseNotifier) EditString(ctx context.Context, s *db.String) {} diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -413,7 +413,7 @@ UserDid string UserHandle string FollowStatus models.FollowStatus Punchcard *db.Punchcard - Profile *db.Profile + Profile *models.Profile Stats ProfileStats Active string } @@ -441,7 +441,7 @@ type ProfileOverviewParams struct { LoggedInUser *oauth.User Repos []models.Repo CollaboratingRepos []models.Repo - ProfileTimeline *db.ProfileTimeline + ProfileTimeline *models.ProfileTimeline Card *ProfileCard Active string } @@ -492,7 +492,7 @@ UserDid string FollowStatus models.FollowStatus FollowersCount int64 FollowingCount int64 - Profile *db.Profile + Profile *models.Profile } type ProfileFollowersParams struct { @@ -530,7 +530,7 @@ } type EditBioParams struct { LoggedInUser *oauth.User - Profile *db.Profile + Profile *models.Profile } func (p *Pages) EditBioFragment(w io.Writer, params EditBioParams) error { @@ -539,7 +539,7 @@ } type EditPinsParams struct { LoggedInUser *oauth.User - Profile *db.Profile + Profile *models.Profile AllRepos []PinnedRepo } diff --git a/appview/posthog/notifier.go b/appview/posthog/notifier.go --- a/appview/posthog/notifier.go +++ b/appview/posthog/notifier.go @@ -121,7 +121,7 @@ log.Println("failed to enqueue posthog event:", err) } } -func (n *posthogNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) { +func (n *posthogNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) { err := n.client.Enqueue(posthog.Capture{ DistinctId: profile.Did, Event: "edit_profile", diff --git a/appview/state/profile.go b/appview/state/profile.go --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -337,11 +337,11 @@ } else if loggedInUser != nil && loggedInUser.Did == did { followStatus = models.IsSelf } - var profile *db.Profile + var profile *models.Profile if p, exists := profiles[did]; exists { profile = p } else { - profile = &db.Profile{} + profile = &models.Profile{} profile.Did = did } followCards[i] = pages.FollowCard{ @@ -479,7 +479,7 @@ } return nil } -func (s *State) addRepoItems(ctx context.Context, feed *feeds.Feed, repos []db.RepoEvent, author *feeds.Author) error { +func (s *State) addRepoItems(ctx context.Context, feed *feeds.Feed, repos []models.RepoEvent, author *feeds.Author) error { for _, repo := range repos { item, err := s.createRepoItem(ctx, repo, author) if err != nil { @@ -508,7 +508,7 @@ Author: author, } } -func (s *State) createRepoItem(ctx context.Context, repo db.RepoEvent, author *feeds.Author) (*feeds.Item, error) { +func (s *State) createRepoItem(ctx context.Context, repo models.RepoEvent, author *feeds.Author) (*feeds.Item, error) { var title string if repo.Source != nil { sourceOwner, err := s.idResolver.ResolveIdent(ctx, repo.Source.Did) @@ -559,11 +559,11 @@ stat0 := r.FormValue("stat0") stat1 := r.FormValue("stat1") if stat0 != "" { - profile.Stats[0].Kind = db.VanityStatKind(stat0) + profile.Stats[0].Kind = models.VanityStatKind(stat0) } if stat1 != "" { - profile.Stats[1].Kind = db.VanityStatKind(stat1) + profile.Stats[1].Kind = models.VanityStatKind(stat1) } if err := db.ValidateProfile(s.db, profile); err != nil { @@ -614,7 +614,7 @@ s.updateProfile(profile, w, r) } -func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) { +func (s *State) updateProfile(profile *models.Profile, w http.ResponseWriter, r *http.Request) { user := s.oauth.GetUser(r) tx, err := s.db.BeginTx(r.Context(), nil) if err != nil { -- tangled.sh