Skip to content

Commit 09fafa7

Browse files
committed
fix: changed migration number to 00063 and merged with main
2 parents 2288fe2 + fb97c24 commit 09fafa7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1159
-166
lines changed

backend/migrations/00062_add_audit_columns_to_all_tables.sql

Lines changed: 411 additions & 0 deletions
Large diffs are not rendered by default.
File renamed without changes.

backend/seeder/main.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
const (
2525
DaysInPast = 90
2626
MonthsInFuture = 6
27+
SystemBatchID = 99999
28+
SystemBatchUID = "system_batch"
2729
)
2830

2931
var (
@@ -50,6 +52,13 @@ func main() {
5052
func seedTestData(db *gorm.DB) {
5153
// isTesting is false because this needs to seed real users w/ kratos
5254
testServer := handlers.NewServer(false, context.Background())
55+
var systemUser models.User
56+
systemUserID := uint(SystemBatchID)
57+
if err := db.Where("username = ?", SystemBatchUID).First(&systemUser).Error; err == nil {
58+
systemUserID = systemUser.ID
59+
}
60+
db = db.WithContext(context.WithValue(context.Background(), models.UserIDKey, systemUserID))
61+
5362
facilities := []models.Facility{
5463
{
5564
Name: "BCF",
@@ -157,13 +166,8 @@ func seedTestData(db *gorm.DB) {
157166
outcomes := []string{"college_credit", "grade", "certificate", "pathway_completion"}
158167
milestoneTypes := []models.MilestoneType{models.DiscussionPost, models.AssignmentSubmission, models.QuizSubmission, models.GradeReceived}
159168

160-
var adminUser models.User
161-
if db.Where("role = ?", models.SystemAdmin).Limit(1).Find(&adminUser).Error != nil {
162-
log.Fatalf("Failed to get users from db")
163-
return
164-
}
165169
classes := []models.ProgramClass{}
166-
classes, err = createFacilityPrograms(db, adminUser.ID)
170+
classes, err = createFacilityPrograms(db)
167171
if err != nil {
168172
log.Printf("Failed to create facility programs: %v", err)
169173
}
@@ -536,7 +540,7 @@ func generateFakeVideos(providerID uint, count int) []models.Video {
536540
}
537541
return videos
538542
}
539-
func createFacilityPrograms(db *gorm.DB, adminID uint) ([]models.ProgramClass, error) {
543+
func createFacilityPrograms(db *gorm.DB) ([]models.ProgramClass, error) {
540544
facilities := []models.Facility{}
541545
fundingTypes := [6]models.FundingType{models.EduGrants, models.FederalGrants, models.InmateWelfare, models.NonProfitOrgs, models.Other, models.StateGrants}
542546
creditTypes := [4]models.CreditType{models.Completion, models.EarnedTime, models.Education, models.Participation}
@@ -579,9 +583,8 @@ func createFacilityPrograms(db *gorm.DB, adminID uint) ([]models.ProgramClass, e
579583
programs := make([]models.Program, 0, 7)
580584
for range 7 {
581585
programs = append(programs, models.Program{
582-
Name: getRandomProgram(programMap),
583-
FundingType: fundingTypes[rand.Intn(len(fundingTypes))],
584-
CreateUserID: adminID,
586+
Name: getRandomProgram(programMap),
587+
FundingType: fundingTypes[rand.Intn(len(fundingTypes))],
585588
})
586589
}
587590
for i := range programs {
@@ -626,7 +629,6 @@ func createFacilityPrograms(db *gorm.DB, adminID uint) ([]models.ProgramClass, e
626629
EndDt: &endDates[rand.Intn(len(endDates))],
627630
FacilityID: facilities[idx].ID,
628631
ProgramID: programs[i].ID,
629-
CreateUserID: adminID,
630632
}
631633
if err := db.Create(&class).Error; err != nil {
632634
log.Printf("Failed to create program class: %v", err)

backend/src/database/DB.go

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package database
22

33
import (
44
"UnlockEdv2/src/models"
5+
"context"
56
"database/sql"
67
"encoding/json"
78
"fmt"
89
"os"
910
"sync"
11+
"time"
1012
"unicode"
1113

1214
"github.com/glebarez/sqlite"
@@ -51,7 +53,8 @@ func InitDB(isTesting bool) *DB {
5153
var gormDb *gorm.DB
5254
var err error
5355
if isTesting {
54-
gormDb, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{
56+
dbName := fmt.Sprintf("file:memdb_%d?mode=memory&cache=shared", time.Now().UnixNano())
57+
gormDb, err = gorm.Open(sqlite.Open(dbName), &gorm.Config{
5558
DisableForeignKeyConstraintWhenMigrating: true,
5659
})
5760
if err != nil {
@@ -163,43 +166,77 @@ func (db *DB) SeedDefaultData(isTesting bool) {
163166
}
164167
}
165168
}
166-
defaultFacility := models.Facility{
167-
Name: "Default",
168-
Timezone: "America/Chicago",
169-
}
170-
logrus.Printf("Creating facility: %v", defaultFacility)
171-
if err := db.Create(&defaultFacility).Error; err != nil {
172-
logrus.Fatalf("Failed to create user: %v", err)
169+
170+
// Get system_batch user for audit field references
171+
var systemBatchUser models.User
172+
systemBatchUserID := uint(99999)
173+
if err := db.Where("username = ?", "system_batch").First(&systemBatchUser).Error; err != nil {
174+
logrus.Printf("Warning: system_batch user not found, this might indicate migration hasn't run yet: %v", err)
175+
} else {
176+
systemBatchUserID = systemBatchUser.ID
173177
}
178+
dbWithCtx := db.WithContext(context.WithValue(context.Background(), models.UserIDKey, systemBatchUserID))
179+
174180
user := models.User{
175181
Username: "SuperAdmin",
176182
NameFirst: "Super",
177183
NameLast: "Admin",
178184
179185
Role: models.SystemAdmin,
180-
FacilityID: 1,
186+
FacilityID: 0, // will update after facility creation
187+
DatabaseFields: models.DatabaseFields{
188+
CreateUserID: &systemBatchUserID, // using system_batch user ID
189+
},
181190
}
182191
logrus.Printf("Creating user: %v", user)
183192
logrus.Println("Make sure to sync the Kratos instance if you are freshly migrating")
184-
if err := db.Create(&user).Error; err != nil {
193+
if err := dbWithCtx.Create(&user).Error; err != nil {
185194
logrus.Fatalf("Failed to create user: %v", err)
186195
}
187196

197+
// Step 2: Create facility referencing the user we just created
198+
defaultFacility := models.Facility{
199+
Name: "Default",
200+
Timezone: "America/Chicago",
201+
DatabaseFields: models.DatabaseFields{
202+
CreateUserID: &user.ID,
203+
},
204+
}
205+
logrus.Printf("Creating facility: %v", defaultFacility)
206+
if err := dbWithCtx.Create(&defaultFacility).Error; err != nil {
207+
logrus.Fatalf("Failed to create facility: %v", err)
208+
209+
}
210+
211+
if err := db.Model(&user).Update("facility_id", defaultFacility.ID).Error; err != nil {
212+
logrus.Fatalf("Failed to update user facility_id: %v", err)
213+
}
214+
188215
links := []models.HelpfulLink{}
189216
if err := json.Unmarshal([]byte(defaultLeftMenuLinks), &links); err != nil {
190217
logrus.Fatalf("Failed to unmarshal default left menu links: %v", err)
191218
}
192-
if err := db.Create(&links).Error; err != nil {
219+
if err := dbWithCtx.Create(&links).Error; err != nil {
193220
logrus.Fatalf("Failed to create left menu links: %v", err)
194221
}
195222
for idx := range defaultOpenContentProviders {
196-
if err := db.Create(&defaultOpenContentProviders[idx]).Error; err != nil {
223+
if err := dbWithCtx.Create(&defaultOpenContentProviders[idx]).Error; err != nil {
197224
logrus.Fatalf("Failed to create default open content providers: %v", err)
198225
}
199226
}
200227
}
201228
}
202229

230+
func (db *DB) softDeleteMap() map[string]any {
231+
updates := map[string]any{
232+
"deleted_at": time.Now(),
233+
}
234+
if userID, ok := db.Statement.Context.Value(models.UserIDKey).(uint); ok {
235+
updates["update_user_id"] = userID
236+
}
237+
return updates
238+
}
239+
203240
const (
204241
defaultLeftMenuLinks = `[{
205242
"title": "Unlocked Labs",

backend/src/database/class_enrollments.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package database
22

33
import (
44
"UnlockEdv2/src/models"
5-
"context"
65
"fmt"
76
"time"
87
)
@@ -110,10 +109,10 @@ func (db *DB) DeleteProgramClassEnrollments(id int) error {
110109
return nil
111110
}
112111

113-
func (db *DB) GraduateEnrollments(ctx context.Context, adminEmail string, userIds []int, classId int) error {
112+
func (db *DB) GraduateEnrollments(adminEmail string, userIds []int, classId int) error {
114113
enrollment := models.ProgramClassEnrollment{}
115114
// begin transaction
116-
tx := db.WithContext(ctx).Begin()
115+
tx := db.Begin()
117116

118117
// preload necessary relationships
119118
err := tx.Model(&models.ProgramClassEnrollment{}).
@@ -176,6 +175,11 @@ func (db *DB) UpdateProgramClassEnrollments(classId int, userIds []int, status m
176175
if changeReason != nil {
177176
updates["change_reason"] = *changeReason
178177
}
178+
if ctx := db.Statement.Context; ctx != nil {
179+
if userID, ok := ctx.Value(models.UserIDKey).(uint); ok {
180+
updates["update_user_id"] = userID
181+
}
182+
}
179183
if err := db.Model(&models.ProgramClassEnrollment{}).
180184
Where("class_id = ? AND user_id IN (?)", classId, userIds).
181185
Set("class_id", classId).
@@ -185,17 +189,23 @@ func (db *DB) UpdateProgramClassEnrollments(classId int, userIds []int, status m
185189
return nil
186190
}
187191

188-
func (db *DB) UpdateProgramClassEnrollmentDate(ctx context.Context, enrollmentId int, enrolledDate time.Time) error {
189-
if err := db.WithContext(ctx).Model(&models.ProgramClassEnrollment{}).
192+
func (db *DB) UpdateProgramClassEnrollmentDate(enrollmentId int, enrolledDate time.Time) error {
193+
update := map[string]any{"enrolled_at": enrolledDate}
194+
if ctx := db.Statement.Context; ctx != nil {
195+
if userID, ok := ctx.Value(models.UserIDKey).(uint); ok {
196+
update["update_user_id"] = userID
197+
}
198+
}
199+
if err := db.Model(&models.ProgramClassEnrollment{}).
190200
Where("id = ?", enrollmentId).
191-
Update("enrolled_at", enrolledDate).Error; err != nil {
201+
Updates(update).Error; err != nil {
192202
return newUpdateDBError(err, "enrollment date")
193203
}
194204
return nil
195205
}
196206

197-
func (db *DB) UpdateProgramClasses(ctx context.Context, classIDs []int, classMap map[string]any) error {
198-
tx := db.WithContext(ctx).Begin()
207+
func (db *DB) UpdateProgramClasses(classIDs []int, classMap map[string]any) error {
208+
tx := db.Begin()
199209
if tx.Error != nil {
200210
return newUpdateDBError(tx.Error, "begin transaction")
201211
}

backend/src/database/events_attendance.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,27 @@ func (db *DB) GetAttendees(queryParams *models.QueryContext, params url.Values,
3737
}
3838

3939
func (db *DB) LogUserAttendance(attendanceParams []models.ProgramClassEventAttendance, ctx context.Context, adminID *uint, className string) error {
40-
tx := db.Begin().WithContext(ctx)
40+
var updateUserID uint
41+
if ctx := db.Statement.Context; ctx != nil {
42+
if userID, ok := ctx.Value(models.UserIDKey).(uint); ok {
43+
updateUserID = userID
44+
}
45+
}
46+
tx := db.Begin()
4147
if tx.Error != nil {
4248
return NewDBError(tx.Error, "unable to start DB transaction")
4349
}
4450

4551
for _, att := range attendanceParams {
52+
existingRow := updateUserID != 0 && att.ID != 0
53+
if existingRow {
54+
att.UpdateUserID = &updateUserID
55+
}
56+
4657
if err := tx.
4758
Clauses(clause.OnConflict{
4859
Columns: []clause.Column{{Name: "event_id"}, {Name: "user_id"}, {Name: "date"}},
49-
DoUpdates: clause.AssignmentColumns([]string{"attendance_status", "note", "reason_category", "check_in_at", "check_out_at", "minutes_attended", "scheduled_minutes"}),
60+
DoUpdates: clause.AssignmentColumns([]string{"attendance_status", "note", "reason_category", "check_in_at", "check_out_at", "minutes_attended", "scheduled_minutes", "update_user_id"}),
5061
}).
5162
Create(&att).Error; err != nil {
5263
tx.Rollback()
@@ -219,8 +230,8 @@ func (db *DB) GetAttendanceRateForEvent(ctx context.Context, eventID int, classI
219230
end
220231
) * 100.0 /
221232
nullif(
222-
(select count(*) from program_class_enrollments e
223-
where e.class_id = ?
233+
(select count(*) from program_class_enrollments e
234+
where e.class_id = ?
224235
and e.enrolled_at <= ?
225236
and (e.enrollment_ended_at IS NULL OR e.enrollment_ended_at >= ?)),
226237
0
@@ -279,10 +290,10 @@ func (db *DB) GetAttendanceFlagsForClass(classID int, args *models.QueryContext)
279290
}
280291

281292
attendanceQuery := fmt.Sprintf(`select name_first, name_last, doc_id, flag_type from (
282-
select u.name_first, u.name_last,
293+
select u.name_first, u.name_last,
283294
u.doc_id, 'no_attendance' as flag_type %s %s
284295
union all
285-
select u.name_first, u.name_last,
296+
select u.name_first, u.name_last,
286297
u.doc_id, 'multiple_absences' as flag_type %s %s
287298
) AS attendance_flags
288299
ORDER BY name_last, name_first

backend/src/database/facilities.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
log "github.com/sirupsen/logrus"
8+
"gorm.io/gorm"
89
)
910

1011
func (db *DB) GetAllFacilities(page, itemsPerPage int) (int64, []models.Facility, error) {
@@ -43,16 +44,24 @@ func (db *DB) CreateFacility(facility *models.Facility) error {
4344
}
4445

4546
func (db *DB) UpdateFacility(facility *models.Facility, id uint) error {
46-
if err := db.Model(models.Facility{}).Where("id = ?", id).Updates(&facility).Error; err != nil {
47+
facility.ID = id
48+
if err := db.Save(&facility).Error; err != nil {
4749
log.WithField("facility_id", facility.ID).Error("error updating facility name database/UpdateFacility")
4850
return newUpdateDBError(err, "facilities")
4951
}
5052
return nil
5153
}
5254

5355
func (db *DB) DeleteFacility(id int) error {
54-
if err := db.Delete(&models.Facility{}, "id = ?", fmt.Sprintf("%d", id)).Error; err != nil {
55-
return newDeleteDBError(err, "facilities")
56+
updates := db.softDeleteMap()
57+
result := db.Model(&models.Facility{}).
58+
Where("id = ? AND deleted_at IS NULL", fmt.Sprintf("%d", id)).
59+
Updates(updates)
60+
if result.Error != nil {
61+
return newDeleteDBError(result.Error, "facilities")
62+
}
63+
if result.RowsAffected == 0 {
64+
return newDeleteDBError(gorm.ErrRecordNotFound, "facilities")
5665
}
5766
return nil
5867
}

backend/src/database/feature_access.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ func (db *DB) ToggleFeatureAccess(name string) error {
3434
return newCreateDBError(err, "unable to save feature")
3535
}
3636
if len(featureFlag.PageFeatures) > 0 {
37-
if err := db.Model(&models.PageFeatureFlags{}).Where("feature_flag_id = ?", featureFlag.ID).Update("enabled", featureFlag.Enabled).Error; err != nil {
37+
update := map[string]any{"enabled": featureFlag.Enabled}
38+
if ctx := db.Statement.Context; ctx != nil {
39+
if userID, ok := ctx.Value(models.UserIDKey).(uint); ok {
40+
update["update_user_id"] = userID
41+
}
42+
}
43+
if err := db.Model(&models.PageFeatureFlags{}).Where("feature_flag_id = ?", featureFlag.ID).Updates(update).Error; err != nil {
3844
return newCreateDBError(err, "unable to update page features")
3945
}
4046
}

0 commit comments

Comments
 (0)