← back to kljensen__yale-peer-review

Function bodies 190 total

All specs Real LLM only Function bodies
handlers.handleReviewFormGet function · go · L104-L183 (80 LOC)
internal/handlers/reviews.go
func handleReviewFormGet(app *pocketbase.PocketBase, w http.ResponseWriter, r *http.Request, user *core.Record, projectId string) {
	// Get project
	project, err := db.GetProjectById(app, projectId)
	if err != nil {
		http.Error(w, "Project not found", http.StatusNotFound)
		return
	}

	// Check if project's class is archived
	classId := project.GetString("class")
	class, err := db.GetClassById(app, classId)
	if err != nil {
		http.Error(w, "Class not found", http.StatusNotFound)
		return
	}
	if class.GetBool("archived") {
		http.Error(w, classArchivedError, http.StatusForbidden)
		return
	}

	// Check if project is open (is_open is the sole gatekeeper for submissions;
	// deadline is informational only, allowing faculty to accept late submissions)
	if !project.GetBool("is_open") {
		http.Error(w, "This project is not accepting submissions", http.StatusForbidden)
		return
	}

	// Get teammates
	teammates, err := db.GetTeammatesForUserInProject(app, user.Id, projectId)
	if err != nil {
handlers.handleReviewFormPost function · go · L186-L331 (146 LOC)
internal/handlers/reviews.go
func handleReviewFormPost(app *pocketbase.PocketBase, w http.ResponseWriter, r *http.Request, user *core.Record, projectId string) {
	// Get project and check if open
	project, err := db.GetProjectById(app, projectId)
	if err != nil {
		http.Error(w, "Project not found", http.StatusNotFound)
		return
	}

	// Check if project's class is archived
	classId := project.GetString("class")
	class, err := db.GetClassById(app, classId)
	if err != nil {
		http.Error(w, "Class not found", http.StatusNotFound)
		return
	}
	if class.GetBool("archived") {
		http.Error(w, classArchivedError, http.StatusForbidden)
		return
	}

	// Check if project is open (is_open is the sole gatekeeper for submissions)
	if !project.GetBool("is_open") {
		http.Error(w, "This project is not accepting submissions", http.StatusForbidden)
		return
	}

	// Parse form
	if err := r.ParseForm(); err != nil {
		http.Error(w, "Invalid form data", http.StatusBadRequest)
		return
	}

	// Extract ratings from form
	var total int
	
handlers.ReviewHistoryHandler function · go · L334-L364 (31 LOC)
internal/handlers/reviews.go
func ReviewHistoryHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		if user == nil {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// Get submission history
		submissions, err := db.GetStudentSubmissions(app, user.Id)
		if err != nil {
			http.Error(w, "Failed to load submission history", http.StatusInternalServerError)
			return
		}

		// Build breadcrumbs
		crumbs := []components.BreadcrumbItem{
			components.HomeCrumb(),
			components.ReviewsCrumb(),
			components.CurrentPageCrumb("History"),
		}

		page := components.Page("Submission History",
			components.Container(crumbs, user,
				components.SubmissionHistory(submissions),
			),
		)

		components.RenderComponent(w, page)
	}
}
handlers.TeamRosterHandler function · go · L18-L126 (109 LOC)
internal/handlers/teams.go
func TeamRosterHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project and verify teacher owns the class
		project, class, err := getProjectAndVerifyTeacher(app, projectId, user.Id)
		if err != nil {
			log.Printf("Error getting project: %v", err)
			http.Error(w, "Project not found or unauthorized", http.StatusNotFound)
			return
		}

		if r.Method == "GET" {
			// Check for success/error messages in query params (from redirects)
			successMsg := r.URL.Query().Get("success")
			errorMsg := r.URL.Query().Get("error")
			renderTeamRoster(w, app, project, class, user, successMsg, errorMsg)
			return
		}

		// POST: Batch save team assignments
		if err := r.ParseForm(); err != nil {
			http.Error(w, "Failed to parse form", http.StatusBadRequest)
			return
		}

		// Get current roster state
		roster, err := db.GetProjectRoster(app, projectId)
		if err != ni
handlers.TeamConfirmHandler function · go · L129-L171 (43 LOC)
internal/handlers/teams.go
func TeamConfirmHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		_, _, err := getProjectAndVerifyTeacher(app, projectId, user.Id)
		if err != nil {
			http.Error(w, "Project not found or unauthorized", http.StatusNotFound)
			return
		}

		if err := r.ParseForm(); err != nil {
			http.Error(w, "Failed to parse form", http.StatusBadRequest)
			return
		}

		// Decode changes from form
		changesJSON := r.FormValue("changes")
		if changesJSON == "" {
			http.Redirect(w, r, "/projects/"+projectId+"/teams?error=No+changes+to+apply", http.StatusSeeOther)
			return
		}

		var changes []components.TeamChange
		if err := json.Unmarshal([]byte(changesJSON), &changes); err != nil {
			log.Printf("Failed to decode changes JSON: %v", err)
			http.Redirect(w, r, "/projects/"+projectId+"/teams?error=Invalid+change+data", http.StatusSeeOther)
			return
		}

		// Apply the conf
handlers.TeamAddStudentHandler function · go · L174-L254 (81 LOC)
internal/handlers/teams.go
func TeamAddStudentHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		project, class, err := getProjectAndVerifyTeacher(app, projectId, user.Id)
		if err != nil {
			http.Error(w, "Project not found or unauthorized", http.StatusNotFound)
			return
		}

		if err := r.ParseForm(); err != nil {
			http.Error(w, "Failed to parse form", http.StatusBadRequest)
			return
		}

		rawNetid := strings.TrimSpace(r.FormValue("netid"))
		teamId := r.FormValue("team_id")

		if rawNetid == "" || teamId == "" {
			renderTeamRoster(w, app, project, class, user, "", "NetID and team are required")
			return
		}

		// Validate and normalize netid
		netid, err := db.ValidateNetId(rawNetid)
		if err != nil {
			renderTeamRoster(w, app, project, class, user, "", "Invalid netid: "+err.Error())
			return
		}

		// Find or create user by netid
		userRecord, wasCreated, err := db.GetOrCreat
handlers.TeamCreateHandler function · go · L257-L305 (49 LOC)
internal/handlers/teams.go
func TeamCreateHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		project, class, err := getProjectAndVerifyTeacher(app, projectId, user.Id)
		if err != nil {
			http.Error(w, "Project not found or unauthorized", http.StatusNotFound)
			return
		}

		if err := r.ParseForm(); err != nil {
			http.Error(w, "Failed to parse form", http.StatusBadRequest)
			return
		}

		slug := strings.TrimSpace(r.FormValue("slug"))
		if slug == "" {
			renderTeamRoster(w, app, project, class, user, "", "Team name is required")
			return
		}

		// Check for duplicate slug
		_, err = db.GetTeamByProjectAndSlug(app, projectId, slug)
		if err == nil {
			renderTeamRoster(w, app, project, class, user, "", "A team named '"+slug+"' already exists")
			return
		}

		// Create team
		teamCollection, err := app.FindCollectionByNameOrId("teams")
		if err != nil {
			log.Printf("Failed to find
Want this analysis on your repo? https://repobility.com/scan/
handlers.TeamDeleteHandler function · go · L308-L348 (41 LOC)
internal/handlers/teams.go
func TeamDeleteHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")
		teamId := r.PathValue("teamId")

		project, class, err := getProjectAndVerifyTeacher(app, projectId, user.Id)
		if err != nil {
			http.Error(w, "Project not found or unauthorized", http.StatusNotFound)
			return
		}

		// Verify team exists and belongs to this project
		team, err := db.GetTeamById(app, teamId)
		if err != nil || team.GetString("project") != projectId {
			renderTeamRoster(w, app, project, class, user, "", "Team not found")
			return
		}

		// Check if team has members
		count, err := db.GetTeamMemberCount(app, teamId)
		if err != nil {
			log.Printf("Failed to get member count: %v", err)
			http.Error(w, "Internal server error", http.StatusInternalServerError)
			return
		}
		if count > 0 {
			renderTeamRoster(w, app, project, class, user, "", "Cannot delete team with members. Remo
handlers.getProjectAndVerifyTeacher function · go · L351-L364 (14 LOC)
internal/handlers/teams.go
func getProjectAndVerifyTeacher(app *pocketbase.PocketBase, projectId, teacherId string) (*core.Record, *core.Record, error) {
	project, err := db.GetProjectById(app, projectId)
	if err != nil {
		return nil, nil, err
	}

	classId := project.GetString("class")
	class, err := db.GetClassByIdAndTeacher(app, classId, teacherId)
	if err != nil {
		return nil, nil, err
	}

	return project, class, nil
}
handlers.renderTeamRoster function · go · L367-L406 (40 LOC)
internal/handlers/teams.go
func renderTeamRoster(w http.ResponseWriter, app *pocketbase.PocketBase, project, class *core.Record, user *core.Record, successMsg, errorMsg string) {
	projectId := project.Id

	// Get roster
	roster, err := db.GetProjectRoster(app, projectId)
	if err != nil {
		log.Printf("Failed to get roster: %v", err)
		roster = []db.RosterEntry{}
	}

	// Get teams
	teams, err := db.GetTeamsByProject(app, projectId)
	if err != nil {
		log.Printf("Failed to get teams: %v", err)
		teams = []*core.Record{}
	}

	// Build team member counts
	teamCounts := make(map[string]int)
	for _, entry := range roster {
		teamCounts[entry.TeamId]++
	}

	// Build breadcrumbs
	crumbs := []components.BreadcrumbItem{
		components.HomeCrumb(),
		components.ClassesCrumb(),
		components.ClassCrumb(class),
		components.ProjectCrumb(project),
		components.CurrentPageCrumb("Team Roster"),
	}

	page := components.Page(project.GetString("name")+" - Team Roster",
		components.Container(crumbs, user,
			components.TeamRosterPage
handlers.parseTeamAssignments function · go · L409-L419 (11 LOC)
internal/handlers/teams.go
func parseTeamAssignments(r *http.Request) map[string]string {
	result := make(map[string]string)
	for key, values := range r.Form {
		if strings.HasPrefix(key, "team_") && len(values) > 0 {
			userId := strings.TrimPrefix(key, "team_")
			teamId := values[0]
			result[userId] = teamId
		}
	}
	return result
}
handlers.computeChanges function · go · L422-L462 (41 LOC)
internal/handlers/teams.go
func computeChanges(current, submitted map[string]string, teamSlugs, userNames, userNetIds map[string]string) []components.TeamChange {
	var changes []components.TeamChange

	for userId, newTeamId := range submitted {
		oldTeamId := current[userId]

		if newTeamId == "" && oldTeamId != "" {
			// Remove from team
			changes = append(changes, components.TeamChange{
				UserID:     userId,
				UserName:   userNames[userId],
				UserNetID:  userNetIds[userId],
				ChangeType: "remove",
				FromTeam:   teamSlugs[oldTeamId],
			})
		} else if newTeamId != oldTeamId {
			if oldTeamId == "" {
				// Add to team (user wasn't on any team)
				changes = append(changes, components.TeamChange{
					UserID:     userId,
					UserName:   userNames[userId],
					UserNetID:  userNetIds[userId],
					ChangeType: "add",
					ToTeam:     teamSlugs[newTeamId],
				})
			} else {
				// Move between teams
				changes = append(changes, components.TeamChange{
					UserID:     userId,
					UserName:   userName
handlers.renderTeamConfirmation function · go · L465-L486 (22 LOC)
internal/handlers/teams.go
func renderTeamConfirmation(w http.ResponseWriter, app *pocketbase.PocketBase, project, class *core.Record, user *core.Record, changes []components.TeamChange, hasReviews bool) {
	// Build breadcrumbs
	crumbs := []components.BreadcrumbItem{
		components.HomeCrumb(),
		components.ClassesCrumb(),
		components.ClassCrumb(class),
		components.ProjectCrumb(project),
		{Label: "Team Roster", Href: "/projects/" + project.Id + "/teams"},
		components.CurrentPageCrumb("Confirm Changes"),
	}

	// Encode changes as JSON for the form
	changesJSON := components.EncodeChangesJSON(changes)

	page := components.Page(project.GetString("name")+" - Confirm Team Changes",
		components.Container(crumbs, user,
			components.TeamChangeConfirmation(project.Id, changes, hasReviews, changesJSON),
		),
	)

	components.RenderComponent(w, page)
}
handlers.TeamChangeResult.Summary method · go · L497-L510 (14 LOC)
internal/handlers/teams.go
func (r TeamChangeResult) Summary() string {
	if r.Updated == 0 && r.Removed == 0 {
		return "No changes made"
	}

	parts := []string{}
	if r.Updated > 0 {
		parts = append(parts, pluralize(r.Updated, "assignment", "assignments")+" updated")
	}
	if r.Removed > 0 {
		parts = append(parts, pluralize(r.Removed, "student", "students")+" unassigned")
	}
	return strings.Join(parts, ", ")
}
handlers.pluralize function · go · L512-L517 (6 LOC)
internal/handlers/teams.go
func pluralize(n int, singular, plural string) string {
	if n == 1 {
		return "1 " + singular
	}
	return fmt.Sprintf("%d %s", n, plural)
}
Repobility · severity-and-effort ranking · https://repobility.com
handlers.applyTeamChanges function · go · L520-L573 (54 LOC)
internal/handlers/teams.go
func applyTeamChanges(app *pocketbase.PocketBase, projectId, removedById string, changes []components.TeamChange) TeamChangeResult {
	result := TeamChangeResult{}

	for _, change := range changes {
		switch change.ChangeType {
		case "remove":
			// Soft delete the team member record
			if err := softDeleteTeamMember(app, change.UserID, projectId, removedById); err != nil {
				log.Printf("Failed to remove team member: %v", err)
				result.Errors = append(result.Errors, "Failed to remove "+change.UserNetID)
			} else {
				result.Removed++
			}

		case "move":
			// Soft delete old membership, create new one
			if err := softDeleteTeamMember(app, change.UserID, projectId, removedById); err != nil {
				log.Printf("Failed to remove team member for move: %v", err)
				result.Errors = append(result.Errors, "Failed to move "+change.UserNetID)
				continue
			}
			// Get the team ID from slug
			team, err := db.GetTeamByProjectAndSlug(app, projectId, change.ToTeam)
			if err != nil {
				lo
handlers.softDeleteTeamMember function · go · L576-L582 (7 LOC)
internal/handlers/teams.go
func softDeleteTeamMember(app *pocketbase.PocketBase, userId, projectId, removedById string) error {
	tm, err := db.GetActiveTeamMemberByUserAndProject(app, userId, projectId)
	if err != nil {
		return fmt.Errorf("failed to find active team member: %w", err)
	}
	return db.SoftDeleteTeamMember(app, tm.Id, removedById)
}
handlers.createTeamMember function · go · L585-L595 (11 LOC)
internal/handlers/teams.go
func createTeamMember(app *pocketbase.PocketBase, userId, teamId string) error {
	tmCollection, err := app.FindCollectionByNameOrId("team_members")
	if err != nil {
		return err
	}

	tm := core.NewRecord(tmCollection)
	tm.Set("team", teamId)
	tm.Set("user", userId)
	return app.Save(tm)
}
migrations.init function · go · L10-L74 (65 LOC)
internal/migrations/1_initial_schema.go
func init() {
	m.Register(func(app core.App) error {
		log.Println("Migration: Adding custom fields to users collection...")

		// Get or create users auth collection
		collection, err := app.FindCollectionByNameOrId("users")
		if err != nil {
			log.Printf("Migration: Failed to find users collection: %v", err)
			return err
		}

		// Add netid field if it doesn't exist
		if collection.Fields.GetByName("netid") == nil {
			log.Println("Migration: Adding netid field...")
			netidField := &core.TextField{
				Name:     "netid",
				Required: false, // Make it optional initially to avoid issues with existing users
			}
			collection.Fields.Add(netidField)

			// Add unique index on netid
			collection.AddIndex("idx_netid", true, "netid", "")
		} else {
			log.Println("Migration: netid field already exists")
		}

		// Add is_teacher field if it doesn't exist
		if collection.Fields.GetByName("is_teacher") == nil {
			log.Println("Migration: Adding is_teacher field...")
			isTeacherField := 
migrations.init function · go · L11-L59 (49 LOC)
internal/migrations/2_create_collections.go
func init() {
	m.Register(func(app core.App) error {
		log.Println("Migration: Creating collections for peer review system...")

		// Create classes collection
		if err := createClassesCollection(app); err != nil {
			return err
		}

		// Create projects collection
		if err := createProjectsCollection(app); err != nil {
			return err
		}

		// Create teams collection
		if err := createTeamsCollection(app); err != nil {
			return err
		}

		// Create team_members collection
		if err := createTeamMembersCollection(app); err != nil {
			return err
		}

		// Create reviews collection
		if err := createReviewsCollection(app); err != nil {
			return err
		}

		// Create ratings collection
		if err := createRatingsCollection(app); err != nil {
			return err
		}

		log.Println("Migration: Successfully created all collections")
		return nil
	}, func(app core.App) error {
		// Rollback: delete all collections in reverse order
		collections := []string{"ratings", "reviews", "team_members", "teams
migrations.createClassesCollection function · go · L61-L115 (55 LOC)
internal/migrations/2_create_collections.go
func createClassesCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("classes"); err == nil {
		log.Println("Migration: classes collection already exists")
		return nil
	}

	log.Println("Migration: Creating classes collection...")

	collection := core.NewBaseCollection("classes")

	// Add course_id field (required)
	collection.Fields.Add(&core.TextField{
		Name:     "course_id",
		Required: true,
	})

	// Add instance field (required)
	collection.Fields.Add(&core.TextField{
		Name:     "instance",
		Required: true,
	})

	// Add name field (optional)
	collection.Fields.Add(&core.TextField{
		Name:     "name",
		Required: false,
	})

	// Add teacher relation field (required, single)
	usersCollection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:         "teacher",
		Required:     true,
		MaxSelect:    1,
		CollectionId: usersCollection.Id,
	})

	// Add
migrations.createProjectsCollection function · go · L117-L169 (53 LOC)
internal/migrations/2_create_collections.go
func createProjectsCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("projects"); err == nil {
		log.Println("Migration: projects collection already exists")
		return nil
	}

	log.Println("Migration: Creating projects collection...")

	collection := core.NewBaseCollection("projects")

	// Add class relation field (required, single, cascade delete)
	classesCollection, err := app.FindCollectionByNameOrId("classes")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:          "class",
		Required:      true,
		MaxSelect:     1,
		CollectionId:  classesCollection.Id,
		CascadeDelete: true,
	})

	// Add name field (required)
	collection.Fields.Add(&core.TextField{
		Name:     "name",
		Required: true,
	})

	// Add instructions field (required)
	collection.Fields.Add(&core.TextField{
		Name:     "instructions",
		Required: true,
	})

	// Add deadline field (required)
	collection.Fields.Add(&core.DateField{
migrations.createTeamsCollection function · go · L171-L209 (39 LOC)
internal/migrations/2_create_collections.go
func createTeamsCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("teams"); err == nil {
		log.Println("Migration: teams collection already exists")
		return nil
	}

	log.Println("Migration: Creating teams collection...")

	collection := core.NewBaseCollection("teams")

	// Add project relation field (required, single, cascade delete)
	projectsCollection, err := app.FindCollectionByNameOrId("projects")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:          "project",
		Required:      true,
		MaxSelect:     1,
		CollectionId:  projectsCollection.Id,
		CascadeDelete: true,
	})

	// Add slug field (required)
	collection.Fields.Add(&core.TextField{
		Name:     "slug",
		Required: true,
	})

	// Add unique index on (project, slug)
	collection.AddIndex("idx_teams_project_slug", true, "project, slug", "")

	// Add index on project for lookups
	collection.AddIndex("idx_teams_project", false, "project"
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
migrations.createTeamMembersCollection function · go · L211-L259 (49 LOC)
internal/migrations/2_create_collections.go
func createTeamMembersCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("team_members"); err == nil {
		log.Println("Migration: team_members collection already exists")
		return nil
	}

	log.Println("Migration: Creating team_members collection...")

	collection := core.NewBaseCollection("team_members")

	// Add team relation field (required, single, cascade delete)
	teamsCollection, err := app.FindCollectionByNameOrId("teams")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:          "team",
		Required:      true,
		MaxSelect:     1,
		CollectionId:  teamsCollection.Id,
		CascadeDelete: true,
	})

	// Add user relation field (required, single)
	usersCollection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:         "user",
		Required:     true,
		MaxSelect:    1,
		CollectionId: usersCollection.Id,
	})

	// Add in
migrations.createReviewsCollection function · go · L261-L312 (52 LOC)
internal/migrations/2_create_collections.go
func createReviewsCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("reviews"); err == nil {
		log.Println("Migration: reviews collection already exists")
		return nil
	}

	log.Println("Migration: Creating reviews collection...")

	collection := core.NewBaseCollection("reviews")

	// Add project relation field (required, single, cascade delete)
	projectsCollection, err := app.FindCollectionByNameOrId("projects")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:          "project",
		Required:      true,
		MaxSelect:     1,
		CollectionId:  projectsCollection.Id,
		CascadeDelete: true,
	})

	// Add reviewer relation field (required, single)
	usersCollection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:         "reviewer",
		Required:     true,
		MaxSelect:    1,
		CollectionId: usersCollection.Id,
	})

	// Add sub
migrations.createRatingsCollection function · go · L314-L370 (57 LOC)
internal/migrations/2_create_collections.go
func createRatingsCollection(app core.App) error {
	// Check if already exists
	if _, err := app.FindCollectionByNameOrId("ratings"); err == nil {
		log.Println("Migration: ratings collection already exists")
		return nil
	}

	log.Println("Migration: Creating ratings collection...")

	collection := core.NewBaseCollection("ratings")

	// Add review relation field (required, single, cascade delete)
	reviewsCollection, err := app.FindCollectionByNameOrId("reviews")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:          "review",
		Required:      true,
		MaxSelect:     1,
		CollectionId:  reviewsCollection.Id,
		CascadeDelete: true,
	})

	// Add ratee relation field (required, single)
	usersCollection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return err
	}

	collection.Fields.Add(&core.RelationField{
		Name:         "ratee",
		Required:     true,
		MaxSelect:    1,
		CollectionId: usersCollection.Id,
	})

	// Add percentage fie
migrations.init function · go · L10-L48 (39 LOC)
internal/migrations/3_fix_unique_indexes.go
func init() {
	m.Register(func(app core.App) error {
		log.Println("Migration: Fixing composite unique indexes...")

		if err := updateIndex(app, "classes", "idx_course_instance", true, "course_id, instance", ""); err != nil {
			return err
		}
		if err := updateIndex(app, "teams", "idx_teams_project_slug", true, "project, slug", ""); err != nil {
			return err
		}
		if err := updateIndex(app, "reviews", "idx_reviews_project_reviewer", true, "project, reviewer", ""); err != nil {
			return err
		}
		if err := updateIndex(app, "ratings", "idx_ratings_review_ratee", true, "review, ratee", ""); err != nil {
			return err
		}

		log.Println("Migration: Composite unique indexes updated")
		return nil
	}, func(app core.App) error {
		log.Println("Migration: Reverting composite unique indexes...")

		if err := updateIndex(app, "classes", "idx_course_instance", true, "course_id", "instance"); err != nil {
			return err
		}
		if err := updateIndex(app, "teams", "idx_teams_project_slug", true, "
migrations.updateIndex function · go · L50-L60 (11 LOC)
internal/migrations/3_fix_unique_indexes.go
func updateIndex(app core.App, collectionName, indexName string, unique bool, columnsExpr, whereExpr string) error {
	collection, err := app.FindCollectionByNameOrId(collectionName)
	if err != nil {
		return err
	}

	collection.RemoveIndex(indexName)
	collection.AddIndex(indexName, unique, columnsExpr, whereExpr)

	return app.Save(collection)
}
migrations.init function · go · L10-L69 (60 LOC)
internal/migrations/4_add_team_member_soft_delete.go
func init() {
	m.Register(func(app core.App) error {
		log.Println("Migration: Adding soft delete columns to team_members...")

		collection, err := app.FindCollectionByNameOrId("team_members")
		if err != nil {
			return err
		}

		// Add removed_at field (nullable datetime)
		collection.Fields.Add(&core.DateField{
			Name:     "removed_at",
			Required: false,
		})

		// Add removed_by relation field (nullable, references users)
		usersCollection, err := app.FindCollectionByNameOrId("users")
		if err != nil {
			return err
		}

		collection.Fields.Add(&core.RelationField{
			Name:         "removed_by",
			Required:     false,
			MaxSelect:    1,
			CollectionId: usersCollection.Id,
		})

		// Add index for efficient queries on active members
		collection.AddIndex("idx_team_members_removed", false, "removed_at", "")

		if err := app.Save(collection); err != nil {
			return err
		}

		log.Println("Migration: Soft delete columns added to team_members")
		return nil
	}, func(app core.App
migrations.init function · go · L10-L62 (53 LOC)
internal/migrations/5_add_class_staff.go
func init() {
	m.Register(func(app core.App) error {
		log.Println("Migration: Adding staff field to classes collection...")

		classesCollection, err := app.FindCollectionByNameOrId("classes")
		if err != nil {
			return err
		}

		usersCollection, err := app.FindCollectionByNameOrId("users")
		if err != nil {
			return err
		}

		// Check if field already exists
		if classesCollection.Fields.GetByName("staff") != nil {
			log.Println("Migration: Staff field already exists, skipping...")
			return nil
		}

		// Add staff multi-relation field
		classesCollection.Fields.Add(&core.RelationField{
			Name:         "staff",
			Required:     false,
			MaxSelect:    20,
			CollectionId: usersCollection.Id,
		})

		if err := app.Save(classesCollection); err != nil {
			return err
		}

		log.Println("Migration: Staff field added to classes collection")
		return nil
	}, func(app core.App) error {
		log.Println("Migration: Removing staff field from classes collection...")

		classesCollection, er
seed.createTestUser function · go · L271-L296 (26 LOC)
internal/seed/seed.go
func createTestUser(app *pocketbase.PocketBase, netid, name, email string, isTeacher bool) (*core.Record, error) {
	// Check if user exists
	existing, _ := app.FindFirstRecordByFilter("users", "netid = {:netid}", map[string]any{"netid": netid})
	if existing != nil {
		log.Printf("User %s already exists, skipping", netid)
		return existing, nil
	}

	collection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return nil, err
	}

	record := core.NewRecord(collection)
	record.Set("netid", netid)
	record.Set("name", name)
	record.Set("email", email)
	record.Set("is_teacher", isTeacher)
	record.SetPassword("test1234") // Dummy password

	if err := app.Save(record); err != nil {
		return nil, err
	}

	return record, nil
}
About: code-quality intelligence by Repobility · https://repobility.com
seed.createTestClass function · go · L298-L337 (40 LOC)
internal/seed/seed.go
func createTestClass(app *pocketbase.PocketBase, teacherId, courseId, instance, name string, staffIds []string, archived bool) (*core.Record, error) {
	// Check if class exists
	existing, _ := app.FindFirstRecordByFilter("classes",
		"course_id = {:courseId} && instance = {:instance}",
		map[string]any{"courseId": courseId, "instance": instance})
	if existing != nil {
		// Update staff if provided and different
		if len(staffIds) > 0 {
			existing.Set("staff", staffIds)
			if err := app.Save(existing); err != nil {
				return nil, err
			}
			log.Printf("Class %s %s already exists, updated staff", courseId, instance)
		} else {
			log.Printf("Class %s %s already exists, skipping", courseId, instance)
		}
		return existing, nil
	}

	collection, err := app.FindCollectionByNameOrId("classes")
	if err != nil {
		return nil, err
	}

	record := core.NewRecord(collection)
	record.Set("course_id", courseId)
	record.Set("instance", instance)
	record.Set("name", name)
	record.Set("teacher", teac
seed.createTestProject function · go · L339-L375 (37 LOC)
internal/seed/seed.go
func createTestProject(app *pocketbase.PocketBase, classId, name, instructions string, deadline time.Time, isOpen bool) (*core.Record, error) {
	// Check if project exists
	existing, _ := app.FindFirstRecordByFilter("projects",
		"class = {:classId} && name = {:name}",
		map[string]any{"classId": classId, "name": name})
	if existing != nil {
		// Update is_open to match desired state (allows re-seeding to reset state)
		if existing.GetBool("is_open") != isOpen {
			existing.Set("is_open", isOpen)
			if err := app.Save(existing); err != nil {
				return nil, err
			}
			log.Printf("Project %s already exists, updated is_open to %v", name, isOpen)
		} else {
			log.Printf("Project %s already exists, skipping", name)
		}
		return existing, nil
	}

	collection, err := app.FindCollectionByNameOrId("projects")
	if err != nil {
		return nil, err
	}

	record := core.NewRecord(collection)
	record.Set("class", classId)
	record.Set("name", name)
	record.Set("instructions", instructions)
	record.Se
seed.createTestTeam function · go · L377-L401 (25 LOC)
internal/seed/seed.go
func createTestTeam(app *pocketbase.PocketBase, projectId, slug string) (*core.Record, error) {
	// Check if team exists
	existing, _ := app.FindFirstRecordByFilter("teams",
		"project = {:projectId} && slug = {:slug}",
		map[string]any{"projectId": projectId, "slug": slug})
	if existing != nil {
		log.Printf("Team %s already exists, skipping", slug)
		return existing, nil
	}

	collection, err := app.FindCollectionByNameOrId("teams")
	if err != nil {
		return nil, err
	}

	record := core.NewRecord(collection)
	record.Set("project", projectId)
	record.Set("slug", slug)

	if err := app.Save(record); err != nil {
		return nil, err
	}

	return record, nil
}
seed.addTeamMember function · go · L403-L426 (24 LOC)
internal/seed/seed.go
func addTeamMember(app *pocketbase.PocketBase, teamId, userId string) (*core.Record, error) {
	// Check if team member already exists
	existing, _ := app.FindFirstRecordByFilter("team_members",
		"team = {:teamId} && user = {:userId}",
		map[string]any{"teamId": teamId, "userId": userId})
	if existing != nil {
		return existing, nil // Skip silently - this is expected during re-seeding
	}

	collection, err := app.FindCollectionByNameOrId("team_members")
	if err != nil {
		return nil, err
	}

	record := core.NewRecord(collection)
	record.Set("team", teamId)
	record.Set("user", userId)

	if err := app.Save(record); err != nil {
		return nil, err
	}

	return record, nil
}
timezone.YaleLocation function · go · L13-L20 (8 LOC)
internal/timezone/timezone.go
func YaleLocation() *time.Location {
	loc, err := time.LoadLocation("America/New_York")
	if err != nil {
		// Fallback to UTC if timezone data is missing
		return time.UTC
	}
	return loc
}
timezone.ParseDatabaseTime function · go · L30-L47 (18 LOC)
internal/timezone/timezone.go
func ParseDatabaseTime(s string) time.Time {
	// Try common formats in order of likelihood
	formats := []string{
		"2006-01-02 15:04:05.000Z", // PocketBase SQLite format
		"2006-01-02 15:04:05Z",     // SQLite without milliseconds
		"2006-01-02 15:04:05",      // Plain datetime (assumed UTC)
		time.RFC3339,               // Standard RFC3339
		time.RFC3339Nano,           // RFC3339 with nanoseconds
	}

	for _, format := range formats {
		if t, err := time.Parse(format, s); err == nil {
			return t
		}
	}

	return time.Time{} // Return zero time if all parsing fails
}
updateDisplay function · javascript · L26-L55 (30 LOC)
web/js/review-ratings.js
    function updateDisplay() {
        var total = 0;

        teammates.forEach(function(tm) {
            var val = percentages[tm.id];
            total += val;
            document.getElementById('percent-' + tm.id).textContent = val + '%';
            document.getElementById('input-' + tm.id).value = val;

            var minusBtn = document.getElementById('minus-' + tm.id);
            var plusBtn = document.getElementById('plus-' + tm.id);
            var lockBtn = document.getElementById('lock-' + tm.id);
            var controlDiv = document.getElementById('control-' + tm.id);

            var isLocked = locked[tm.id];
            var unlockedOthersTotal = getUnlockedOthersTotal(tm.id);

            // Disable +/- if locked, or if no unlocked others to redistribute to
            minusBtn.disabled = isLocked || val <= 0 || unlockedOthersTotal <= 0;
            plusBtn.disabled = isLocked || val >= 100 || unlockedOthersTotal <= 0;

            // Update lock icon and styling
  
adjust function · javascript · L62-L108 (47 LOC)
web/js/review-ratings.js
    function adjust(userId, delta) {
        // Can't adjust locked values
        if (locked[userId]) return;

        var oldVal = percentages[userId];
        var newVal = Math.max(0, Math.min(100, oldVal + delta));
        var actualDelta = newVal - oldVal;

        if (actualDelta === 0) return;

        // Only redistribute among UNLOCKED others
        var unlockedOthers = getUnlockedOthers(userId);

        if (unlockedOthers.length === 0) {
            // No one to redistribute to - can't make this change
            return;
        }

        percentages[userId] = newVal;

        var distributeTotal = -actualDelta;
        var othersTotal = unlockedOthers.reduce(function(sum, tm) { return sum + percentages[tm.id]; }, 0);

        if (othersTotal > 0) {
            unlockedOthers.forEach(function(tm) {
                var proportion = percentages[tm.id] / othersTotal;
                var adjustment = Math.round(distributeTotal * proportion);
                percentages[tm.id]
Want this analysis on your repo? https://repobility.com/scan/
checkDirty function · javascript · L13-L20 (8 LOC)
web/js/roster-form.js
    function checkDirty() {
        isDirty = false;
        form.querySelectorAll('.team-select').forEach(function(select) {
            if (select.value !== initialValues[select.name]) {
                isDirty = true;
            }
        });
    }
‹ prevpage 4 / 4