← back to kljensen__yale-peer-review

Function bodies 190 total

All specs Real LLM only Function bodies
db.IsUserOnAnyTeamInProject function · go · L211-L222 (12 LOC)
internal/db/helpers.go
func IsUserOnAnyTeamInProject(app *pocketbase.PocketBase, projectId, userId string) (bool, error) {
	_, err := app.FindFirstRecordByFilter("team_members",
		"user = {:user} && team.project = {:project} && removed_at = ''",
		map[string]any{"user": userId, "project": projectId})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
db.GetReviewsByProject function · go · L227-L234 (8 LOC)
internal/db/helpers.go
func GetReviewsByProject(app *pocketbase.PocketBase, projectId string) ([]*core.Record, error) {
	return app.FindRecordsByFilter("reviews",
		"project = {:project}",
		"",
		0,
		0,
		map[string]any{"project": projectId})
}
db.GetRatingsByReview function · go · L251-L258 (8 LOC)
internal/db/helpers.go
func GetRatingsByReview(app *pocketbase.PocketBase, reviewId string) ([]*core.Record, error) {
	return app.FindRecordsByFilter("ratings",
		"review = {:review}",
		"",
		0,
		0,
		map[string]any{"review": reviewId})
}
db.GetRatingsByRatee function · go · L261-L268 (8 LOC)
internal/db/helpers.go
func GetRatingsByRatee(app *pocketbase.PocketBase, rateeId string) ([]*core.Record, error) {
	return app.FindRecordsByFilter("ratings",
		"ratee = {:ratee}",
		"",
		0,
		0,
		map[string]any{"ratee": rateeId})
}
db.GetTeammatesForUserInProject function · go · L278-L306 (29 LOC)
internal/db/helpers.go
func GetTeammatesForUserInProject(app *pocketbase.PocketBase, userId, projectId string) ([]*core.Record, error) {
	// First find the user's ACTIVE team for this project
	teamMember, err := app.FindFirstRecordByFilter("team_members",
		"user = {:user} && team.project = {:project} && removed_at = ''",
		map[string]any{"user": userId, "project": projectId})
	if err != nil {
		return nil, err
	}

	teamId := teamMember.GetString("team")

	// Get all ACTIVE members of that team (including the user)
	members, err := GetActiveTeamMembersByTeam(app, teamId)
	if err != nil {
		return nil, err
	}

	// Expand to get user records
	var teammates []*core.Record
	for _, member := range members {
		userRecord, err := app.FindRecordById("users", member.GetString("user"))
		if err != nil {
			continue
		}
		teammates = append(teammates, userRecord)
	}

	return teammates, nil
}
db.ValidateNetId function · go · L322-L339 (18 LOC)
internal/db/helpers.go
func ValidateNetId(netid string) (string, error) {
	normalized := strings.ToLower(strings.TrimSpace(netid))

	if len(normalized) < 2 {
		return "", fmt.Errorf("netid too short: %q", netid)
	}
	if len(normalized) > 16 {
		return "", fmt.Errorf("netid too long: %q", netid)
	}
	if strings.Contains(normalized, "@") {
		return "", fmt.Errorf("netid cannot contain @: %q", netid)
	}
	if strings.ContainsAny(normalized, " \t\n\r") {
		return "", fmt.Errorf("netid cannot contain whitespace: %q", netid)
	}

	return normalized, nil
}
db.GetOrCreateUserByNetId function · go · L345-L387 (43 LOC)
internal/db/helpers.go
func GetOrCreateUserByNetId(app *pocketbase.PocketBase, netid string) (*core.Record, bool, error) {
	normalized, err := ValidateNetId(netid)
	if err != nil {
		return nil, false, err
	}

	// Try to find existing user by netid
	record, err := app.FindFirstRecordByFilter("users", "netid = {:netid}", map[string]any{"netid": normalized})
	if err == nil {
		return record, false, nil
	}

	// Only create stub on "not found" error, not on transient DB errors
	if !errors.Is(err, sql.ErrNoRows) {
		return nil, false, fmt.Errorf("failed to lookup user: %w", err)
	}

	// User doesn't exist, create stub
	collection, err := app.FindCollectionByNameOrId("users")
	if err != nil {
		return nil, false, fmt.Errorf("failed to find users collection: %w", err)
	}

	record = core.NewRecord(collection)
	record.Set("netid", normalized)
	record.Set("name", normalized) // Placeholder, will be updated on CAS login
	record.Set("email", normalized+"@yale.edu")
	record.Set("is_teacher", false)

	// Generate random p
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
db.generateRandomPassword function · go · L390-L396 (7 LOC)
internal/db/helpers.go
func generateRandomPassword(length int) (string, error) {
	bytes := make([]byte, length)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes), nil
}
db.GetTeamMemberCount function · go · L399-L405 (7 LOC)
internal/db/helpers.go
func GetTeamMemberCount(app *pocketbase.PocketBase, teamId string) (int, error) {
	members, err := GetActiveTeamMembersByTeam(app, teamId)
	if err != nil {
		return 0, err
	}
	return len(members), nil
}
db.GetActiveTeamMembersByTeam function · go · L410-L417 (8 LOC)
internal/db/helpers.go
func GetActiveTeamMembersByTeam(app *pocketbase.PocketBase, teamId string) ([]*core.Record, error) {
	return app.FindRecordsByFilter("team_members",
		"team = {:team} && removed_at = ''",
		"",
		0,
		0,
		map[string]any{"team": teamId})
}
db.GetActiveTeamMembersByProject function · go · L420-L427 (8 LOC)
internal/db/helpers.go
func GetActiveTeamMembersByProject(app *pocketbase.PocketBase, projectId string) ([]*core.Record, error) {
	return app.FindRecordsByFilter("team_members",
		"team.project = {:project} && removed_at = ''",
		"",
		0,
		0,
		map[string]any{"project": projectId})
}
db.IsUserActiveOnAnyTeamInProject function · go · L437-L448 (12 LOC)
internal/db/helpers.go
func IsUserActiveOnAnyTeamInProject(app *pocketbase.PocketBase, projectId, userId string) (bool, error) {
	_, err := app.FindFirstRecordByFilter("team_members",
		"user = {:user} && team.project = {:project} && removed_at = ''",
		map[string]any{"user": userId, "project": projectId})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}
db.HasProjectReviews function · go · L453-L464 (12 LOC)
internal/db/helpers.go
func HasProjectReviews(app *pocketbase.PocketBase, projectId string) (bool, error) {
	reviews, err := app.FindRecordsByFilter("reviews",
		"project = {:project}",
		"",
		1, // Only need to find one
		0,
		map[string]any{"project": projectId})
	if err != nil {
		return false, err
	}
	return len(reviews) > 0, nil
}
db.GetProjectReviewCount function · go · L467-L473 (7 LOC)
internal/db/helpers.go
func GetProjectReviewCount(app *pocketbase.PocketBase, projectId string) (int, error) {
	reviews, err := GetReviewsByProject(app, projectId)
	if err != nil {
		return 0, err
	}
	return len(reviews), nil
}
db.GetReviewImpactForUser function · go · L486-L528 (43 LOC)
internal/db/helpers.go
func GetReviewImpactForUser(app *pocketbase.PocketBase, userId, projectId string) (*ReviewImpact, error) {
	// Get user info
	user, err := app.FindRecordById("users", userId)
	if err != nil {
		return nil, fmt.Errorf("failed to find user: %w", err)
	}

	impact := &ReviewImpact{
		UserID:    userId,
		UserName:  user.GetString("name"),
		UserNetID: user.GetString("netid"),
	}

	// Check if user has submitted a review
	review, err := GetReviewByProjectAndReviewer(app, projectId, userId)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		return nil, fmt.Errorf("failed to check review: %w", err)
	}
	impact.HasSubmittedReview = review != nil

	// Count ratings given (if they submitted a review)
	if review != nil {
		ratings, err := GetRatingsByReview(app, review.Id)
		if err != nil {
			return nil, fmt.Errorf("failed to count ratings given: %w", err)
		}
		impact.RatingsGivenCount = len(ratings)
	}

	// Count ratings received (from all reviews in this project where ratee = user)
	ratingsR
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
db.GetReviewImpactForUsers function · go · L531-L541 (11 LOC)
internal/db/helpers.go
func GetReviewImpactForUsers(app *pocketbase.PocketBase, userIds []string, projectId string) (map[string]*ReviewImpact, error) {
	impacts := make(map[string]*ReviewImpact)
	for _, userId := range userIds {
		impact, err := GetReviewImpactForUser(app, userId, projectId)
		if err != nil {
			return nil, err
		}
		impacts[userId] = impact
	}
	return impacts, nil
}
db.SoftDeleteTeamMember function · go · L544-L554 (11 LOC)
internal/db/helpers.go
func SoftDeleteTeamMember(app *pocketbase.PocketBase, teamMemberId, removedById string) error {
	record, err := app.FindRecordById("team_members", teamMemberId)
	if err != nil {
		return fmt.Errorf("failed to find team member: %w", err)
	}

	record.Set("removed_at", time.Now().UTC())
	record.Set("removed_by", removedById)

	return app.Save(record)
}
db.GetClassStaffMembers function · go · L559-L581 (23 LOC)
internal/db/helpers.go
func GetClassStaffMembers(app *pocketbase.PocketBase, classId string) ([]*core.Record, error) {
	class, err := app.FindRecordById("classes", classId)
	if err != nil {
		return nil, fmt.Errorf("failed to find class: %w", err)
	}

	staffIds := class.GetStringSlice("staff")
	if len(staffIds) == 0 {
		return []*core.Record{}, nil
	}

	var staffMembers []*core.Record
	for _, staffId := range staffIds {
		user, err := app.FindRecordById("users", staffId)
		if err != nil {
			// Skip users that don't exist (shouldn't happen, but be defensive)
			continue
		}
		staffMembers = append(staffMembers, user)
	}

	return staffMembers, nil
}
db.AddClassStaff function · go · L584-L604 (21 LOC)
internal/db/helpers.go
func AddClassStaff(app *pocketbase.PocketBase, classId, userId string) error {
	class, err := app.FindRecordById("classes", classId)
	if err != nil {
		return fmt.Errorf("failed to find class: %w", err)
	}

	currentStaff := class.GetStringSlice("staff")

	// Check if user is already staff
	for _, id := range currentStaff {
		if id == userId {
			return nil // Already staff, nothing to do
		}
	}

	// Add user to staff list
	currentStaff = append(currentStaff, userId)
	class.Set("staff", currentStaff)

	return app.Save(class)
}
db.RemoveClassStaff function · go · L607-L624 (18 LOC)
internal/db/helpers.go
func RemoveClassStaff(app *pocketbase.PocketBase, classId, userId string) error {
	class, err := app.FindRecordById("classes", classId)
	if err != nil {
		return fmt.Errorf("failed to find class: %w", err)
	}

	currentStaff := class.GetStringSlice("staff")
	newStaff := make([]string, 0, len(currentStaff))

	for _, id := range currentStaff {
		if id != userId {
			newStaff = append(newStaff, id)
		}
	}

	class.Set("staff", newStaff)
	return app.Save(class)
}
db.GetPendingReviews function · go · L81-L89 (9 LOC)
internal/db/queries.go
func GetPendingReviews(app *pocketbase.PocketBase, userId string) ([]PendingReview, error) {
	var results []PendingReview
	err := app.DB().
		NewQuery(pendingReviewsSQL).
		Bind(dbx.Params{"userId": userId}).
		All(&results)

	return results, err
}
db.GetProjectResults function · go · L92-L100 (9 LOC)
internal/db/queries.go
func GetProjectResults(app *pocketbase.PocketBase, projectId string) ([]ProjectResult, error) {
	var results []ProjectResult
	err := app.DB().
		NewQuery(projectResultsSQL).
		Bind(dbx.Params{"projectId": projectId}).
		All(&results)

	return results, err
}
db.GetReviewDetails function · go · L104-L112 (9 LOC)
internal/db/queries.go
func GetReviewDetails(app *pocketbase.PocketBase, rateeId, projectId string) ([]ReviewDetail, error) {
	var results []ReviewDetail
	err := app.DB().
		NewQuery(reviewDetailSQL).
		Bind(dbx.Params{"rateeId": rateeId, "projectId": projectId}).
		All(&results)

	return results, err
}
About: code-quality intelligence by Repobility · https://repobility.com
db.GetStudentSubmissions function · go · L115-L123 (9 LOC)
internal/db/queries.go
func GetStudentSubmissions(app *pocketbase.PocketBase, userId string) ([]StudentSubmission, error) {
	var results []StudentSubmission
	err := app.DB().
		NewQuery(studentSubmissionsSQL).
		Bind(dbx.Params{"userId": userId}).
		All(&results)

	return results, err
}
db.GetProjectRoster function · go · L136-L144 (9 LOC)
internal/db/queries.go
func GetProjectRoster(app *pocketbase.PocketBase, projectId string) ([]RosterEntry, error) {
	var results []RosterEntry
	err := app.DB().
		NewQuery(projectRosterSQL).
		Bind(dbx.Params{"projectId": projectId}).
		All(&results)

	return results, err
}
db.GetProjectProgress function · go · L156-L167 (12 LOC)
internal/db/queries.go
func GetProjectProgress(app *pocketbase.PocketBase, projectId string) (*ProjectProgress, error) {
	var result ProjectProgress
	err := app.DB().
		NewQuery(projectProgressSQL).
		Bind(dbx.Params{"projectId": projectId}).
		One(&result)

	if err != nil {
		return nil, err
	}
	return &result, nil
}
db.GetProjectsProgressByClass function · go · L171-L188 (18 LOC)
internal/db/queries.go
func GetProjectsProgressByClass(app *pocketbase.PocketBase, classId string) (map[string]ProjectProgress, error) {
	var results []ProjectProgress
	err := app.DB().
		NewQuery(projectsProgressSQL).
		Bind(dbx.Params{"classId": classId}).
		All(&results)

	if err != nil {
		return nil, err
	}

	// Convert to map for O(1) lookup by project ID
	progressMap := make(map[string]ProjectProgress)
	for _, p := range results {
		progressMap[p.ProjectId] = p
	}
	return progressMap, nil
}
handlers.ClassListHandler function · go · L15-L52 (38 LOC)
internal/handlers/classes.go
func ClassListHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)

		// Get all teacher's classes (including archived)
		allClasses, err := db.GetClassesByTeacher(app, user.Id, true)
		if err != nil {
			log.Printf("Failed to get classes: %v", err)
			http.Error(w, "Failed to load classes", http.StatusInternalServerError)
			return
		}

		// Split into active and archived classes
		var activeClasses, archivedClasses []*core.Record
		for _, class := range allClasses {
			if class.GetBool("archived") {
				archivedClasses = append(archivedClasses, class)
			} else {
				activeClasses = append(activeClasses, class)
			}
		}

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

		// Render page
		page := components.Page("My Classes",
			components.Container(crumbs, user,
				components.ClassList(activeClasses, archived
handlers.ClassCreateHandler function · go · L55-L112 (58 LOC)
internal/handlers/classes.go
func ClassCreateHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)

		if r.Method == "GET" {
			// Build breadcrumbs
			crumbs := []components.BreadcrumbItem{
				components.HomeCrumb(),
				components.ClassesCrumb(),
				components.CurrentPageCrumb("Create Class"),
			}

			page := components.Page("Create Class",
				components.Container(crumbs, user,
					components.ClassForm(nil),
				),
			)
			components.RenderComponent(w, page)
			return
		}

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

		// Validate required fields
		courseID := r.FormValue("course_id")
		instance := r.FormValue("instance")
		if courseID == "" || instance == "" {
			http.Error(w, "course_id and instance are required", http.StatusBadRequest)
			return
		}

		collection, err := app.FindCollectionByNameOrId("classes")
		if err != n
handlers.ClassDetailHandler function · go · L115-L167 (53 LOC)
internal/handlers/classes.go
func ClassDetailHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		classId := r.PathValue("id")

		// Get class (verify teacher owns it)
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Class not found or unauthorized: %v", err)
			http.Error(w, "Class not found", http.StatusNotFound)
			return
		}

		// Get projects for this class
		projects, err := db.GetProjectsByClass(app, classId)
		if err != nil {
			log.Printf("Failed to get projects: %v", err)
			projects = []*core.Record{}
		}

		// Get progress stats for all projects in this class
		progressMap, err := db.GetProjectsProgressByClass(app, classId)
		if err != nil {
			log.Printf("Failed to get project progress: %v", err)
			progressMap = make(map[string]db.ProjectProgress)
		}

		// Get staff members for this class
		staffMembers, err := db.GetClassStaffMembers(app, classId)
		if err != nil
handlers.ClassArchiveHandler function · go · L171-L203 (33 LOC)
internal/handlers/classes.go
func ClassArchiveHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		classId := r.PathValue("id")

		// Get class (verify user has access - teacher or staff)
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Class not found or unauthorized: %v", err)
			http.Error(w, "Class not found", http.StatusNotFound)
			return
		}

		// Only the class owner can archive/unarchive - staff cannot
		if !db.IsClassOwner(class, user.Id) {
			log.Printf("Staff member %s attempted to archive class %s", user.Id, classId)
			http.Error(w, "Only the class owner can archive or unarchive a class", http.StatusForbidden)
			return
		}

		// Toggle archived status
		class.Set("archived", !class.GetBool("archived"))

		if err := app.Save(class); err != nil {
			log.Printf("Failed to update class: %v", err)
			http.Error(w, "Failed to update class", http.StatusInternalServerErro
Repobility — same analyzer, your code, free for public repos · /scan/
handlers.StaffAddHandler function · go · L207-L275 (69 LOC)
internal/handlers/classes.go
func StaffAddHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		classId := r.PathValue("id")

		// Get class (verify user has access)
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Class not found or unauthorized: %v", err)
			http.Error(w, "Class not found", http.StatusNotFound)
			return
		}

		// Only the class owner can manage staff
		if !db.IsClassOwner(class, user.Id) {
			log.Printf("Non-owner %s attempted to add staff to class %s", user.Id, classId)
			http.Error(w, "Only the class owner can manage staff", http.StatusForbidden)
			return
		}

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

		netid := r.FormValue("netid")
		if netid == "" {
			http.Error(w, "NetID is required", http.StatusBadRequest)
			return
		}

		// Validate and normalize netid
		nor
handlers.StaffRemoveHandler function · go · L279-L311 (33 LOC)
internal/handlers/classes.go
func StaffRemoveHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		classId := r.PathValue("id")
		staffUserId := r.PathValue("userId")

		// Get class (verify user has access)
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Class not found or unauthorized: %v", err)
			http.Error(w, "Class not found", http.StatusNotFound)
			return
		}

		// Only the class owner can manage staff
		if !db.IsClassOwner(class, user.Id) {
			log.Printf("Non-owner %s attempted to remove staff from class %s", user.Id, classId)
			http.Error(w, "Only the class owner can manage staff", http.StatusForbidden)
			return
		}

		// Remove user from class staff
		// Note: We don't unset is_teacher - the user might be staff elsewhere
		if err := db.RemoveClassStaff(app, classId, staffUserId); err != nil {
			log.Printf("Failed to remove staff: %v", err)
			http.Error(w, "Failed 
handlers.HomeHandler function · go · L17-L49 (33 LOC)
internal/handlers/home.go
func HomeHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Manually check authentication since this is a public route
		// Get token from cookie or header
		var token string
		cookie, err := r.Cookie("pb_auth")
		if err == nil && cookie.Value != "" {
			token = cookie.Value
		}

		// Try to get user from token
		var user *core.Record
		if token != "" {
			user, _ = app.FindAuthRecordByToken(token, "auth")
		}

		if user == nil {
			// Not authenticated, show landing page
			page := components.Page("Yale Peer Review",
				LandingPage(),
			)
			components.RenderComponent(w, page)
			return
		}

		// Authenticated - redirect based on role
		if user.GetBool("is_teacher") {
			http.Redirect(w, r, "/classes", http.StatusSeeOther)
		} else {
			http.Redirect(w, r, "/reviews", http.StatusSeeOther)
		}
	}
}
handlers.LandingPage function · go · L52-L59 (8 LOC)
internal/handlers/home.go
func LandingPage() g.Node {
	return Main(Class("container"),
		H1(g.Text("Yale Peer Review System")),
		P(g.Text("A web application for managing peer reviews in Yale courses.")),
		P(g.Text("Please sign in with your Yale NetID to continue.")),
		A(Href("/auth/cas/login"), Role("button"), g.Text("Sign In with Yale CAS")),
	)
}
handlers.checkClassNotArchived function · go · L25-L31 (7 LOC)
internal/handlers/projects.go
func checkClassNotArchived(w http.ResponseWriter, class *core.Record) bool {
	if class.GetBool("archived") {
		http.Error(w, classArchivedError, http.StatusForbidden)
		return true
	}
	return false
}
handlers.ProjectCreateHandler function · go · L34-L103 (70 LOC)
internal/handlers/projects.go
func ProjectCreateHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		classId := r.PathValue("classId")

		// Verify teacher owns the class
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Class not found or unauthorized: %v", err)
			http.Error(w, "Class not found", http.StatusNotFound)
			return
		}

		// Check if class is archived (for POST requests)
		if r.Method == "POST" && checkClassNotArchived(w, class) {
			return
		}

		if r.Method == "GET" {
			// Build breadcrumbs
			crumbs := []components.BreadcrumbItem{
				components.HomeCrumb(),
				components.ClassesCrumb(),
				components.ClassCrumb(class),
				components.CurrentPageCrumb("Create Project"),
			}

			page := components.Page("Create Project",
				components.Container(crumbs, user,
					components.ProjectForm(class, nil),
				),
			)
			components.RenderComponent(w, page)
			return
		
handlers.ProjectDetailHandler function · go · L106-L158 (53 LOC)
internal/handlers/projects.go
func ProjectDetailHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Get class and verify teacher owns it
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Get teams for this project with members
		teams, err := db.GetTeamsWithMembersByProject(app, projectId)
		if err != nil {
			log.Printf("Failed to get teams: %v", err)
			teams = []db.TeamWithMembers{}
		}

		// Get submission progress stats
		progress, err := db.GetProjectProgress(app, projectId)
		if er
handlers.ProjectEditHandler function · go · L161-L232 (72 LOC)
internal/handlers/projects.go
func ProjectEditHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Verify teacher owns the class
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Check if class is archived (for POST requests)
		if r.Method == "POST" && checkClassNotArchived(w, class) {
			return
		}

		if r.Method == "GET" {
			// Build breadcrumbs
			crumbs := []components.BreadcrumbItem{
				components.HomeCrumb(),
				components.ClassesCrumb(),
				components.ClassCrumb(class),
				comp
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
handlers.ProjectToggleHandler function · go · L235-L274 (40 LOC)
internal/handlers/projects.go
func ProjectToggleHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Verify teacher owns the class
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Check if class is archived
		if checkClassNotArchived(w, class) {
			return
		}

		// Toggle is_open status
		project.Set("is_open", !project.GetBool("is_open"))

		if err := app.Save(project); err != nil {
			log.Printf("Failed to update project: %v", err)
			http.Error(w, "Failed to update project", http.Statu
handlers.TeamUploadHandler function · go · L289-L463 (175 LOC)
internal/handlers/projects.go
func TeamUploadHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Verify teacher owns the class
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Check if class is archived
		if checkClassNotArchived(w, class) {
			return
		}

		// Parse CSV file
		file, _, err := r.FormFile("teams_csv")
		if err != nil {
			log.Printf("Failed to read file: %v", err)
			http.Error(w, "Failed to read file", http.StatusBadRequest)
			return
		}
		defer file.Close()

		reader :
handlers.ProjectProgressStreamHandler function · go · L468-L508 (41 LOC)
internal/handlers/projects.go
func ProjectProgressStreamHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Verify teacher owns the project's class (defense in depth)
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}
		classId := project.GetString("class")
		if _, err := db.GetClassByIdAndTeacher(app, classId, user.Id); err != nil {
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		sse := datastar.NewSSE(w, r)

		// Send initial fragment immediately
		var lastStudents, lastTeams int
		if err := sendProgressFragment(app, sse, projectId, &lastStudents, &lastTeams); err != nil {
			return
		}

		// Poll every 5 seconds; only send if data changed
		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-ticker.C:
				if err := sendProgressFrag
handlers.sendProgressFragment function · go · L510-L528 (19 LOC)
internal/handlers/projects.go
func sendProgressFragment(app *pocketbase.PocketBase, sse *datastar.ServerSentEventGenerator, projectId string, lastStudents, lastTeams *int) error {
	progress, err := db.GetProjectProgress(app, projectId)
	if err != nil {
		progress = &db.ProjectProgress{}
	}

	// Skip if nothing changed
	if progress.StudentsSubmitted == *lastStudents && progress.TeamsSubmitted == *lastTeams {
		return nil
	}
	*lastStudents = progress.StudentsSubmitted
	*lastTeams = progress.TeamsSubmitted

	var buf bytes.Buffer
	if err := components.ProjectProgressStats(projectId, progress).Render(&buf); err != nil {
		return err
	}
	return sse.MergeFragments(buf.String())
}
handlers.sanitizeCSVField function · go · L20-L26 (7 LOC)
internal/handlers/results.go
func sanitizeCSVField(s string) string {
	trimmed := strings.TrimLeft(s, " \t\r\n")
	if len(trimmed) > 0 && (trimmed[0] == '=' || trimmed[0] == '+' || trimmed[0] == '-' || trimmed[0] == '@') {
		return "'" + s
	}
	return s
}
handlers.ResultsViewHandler function · go · L29-L76 (48 LOC)
internal/handlers/results.go
func ResultsViewHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Get class and verify teacher owns it
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Get aggregated results
		results, err := db.GetProjectResults(app, projectId)
		if err != nil {
			log.Printf("Failed to get results: %v", err)
			http.Error(w, "Failed to get results", http.StatusInternalServerError)
			return
		}

		// Build breadcrumbs
		crumbs := []components.BreadcrumbItem{
			componen
handlers.ResultDetailHandler function · go · L79-L164 (86 LOC)
internal/handlers/results.go
func ResultDetailHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("projectId")
		userId := r.PathValue("userId")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Get class and verify teacher owns it
		classId := project.GetString("class")
		class, err := db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Get student record
		student, err := app.FindRecordById("users", userId)
		if err != nil {
			log.Printf("Student not found: %v", err)
			http.Error(w, "Student not found", http.StatusNotFound)
			return
		}

		// Get aggregated result for this student
		results, er
handlers.StudentOwnResultsHandler function · go · L168-L275 (108 LOC)
internal/handlers/results.go
func StudentOwnResultsHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Verify student is in this project by checking team membership
		teamMembers, err := db.GetTeamMembersByProject(app, projectId)
		if err != nil {
			log.Printf("Failed to get team members: %v", err)
			http.Error(w, "Failed to get team members", http.StatusInternalServerError)
			return
		}

		inProject := false
		for _, tm := range teamMembers {
			if tm.GetString("user") == user.Id {
				inProject = true
				break
			}
		}

		if !inProject {
			log.Printf("Student not in project")
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Get review details for this stud
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
handlers.ResultsExportHandler function · go · L278-L349 (72 LOC)
internal/handlers/results.go
func ResultsExportHandler(app *pocketbase.PocketBase) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user := auth.GetAuthUser(r)
		projectId := r.PathValue("id")

		// Get project
		project, err := db.GetProjectById(app, projectId)
		if err != nil {
			log.Printf("Project not found: %v", err)
			http.Error(w, "Project not found", http.StatusNotFound)
			return
		}

		// Verify teacher owns the class
		classId := project.GetString("class")
		_, err = db.GetClassByIdAndTeacher(app, classId, user.Id)
		if err != nil {
			log.Printf("Unauthorized access to project: %v", err)
			http.Error(w, "Unauthorized", http.StatusForbidden)
			return
		}

		// Get aggregated results
		results, err := db.GetProjectResults(app, projectId)
		if err != nil {
			log.Printf("Failed to get results: %v", err)
			http.Error(w, "Failed to get results", http.StatusInternalServerError)
			return
		}

		// Set headers for CSV download
		filename := fmt.Sprintf("results-%s.csv", project
handlers.ReviewsListHandler function · go · L22-L80 (59 LOC)
internal/handlers/reviews.go
func ReviewsListHandler(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 pending reviews (not yet submitted)
		pending, err := db.GetPendingReviews(app, user.Id)
		if err != nil {
			http.Error(w, "Failed to load reviews", http.StatusInternalServerError)
			return
		}

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

		// Split submissions into editable vs completed
		var editable, completed []db.StudentSubmission
		now := time.Now()
		for _, sub := range submissions {
			deadline := timezone.ParseDatabaseTime(sub.Deadline)
			if sub.IsOpen && now.Before(deadline) {
				editable = append(editable, sub)
			} else {
				completed = append(completed, sub)
handlers.ReviewSubmitHandler function · go · L83-L101 (19 LOC)
internal/handlers/reviews.go
func ReviewSubmitHandler(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
		}

		projectId := r.PathValue("projectId")

		if r.Method == "GET" {
			handleReviewFormGet(app, w, r, user, projectId)
			return
		}

		// POST: Save review
		handleReviewFormPost(app, w, r, user, projectId)
	}
}
‹ prevpage 3 / 4next ›