package sync import ( "encoding/json" "fmt" "log" "os" "sync" "time" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" ) // DeepReportPath stores the path to the report generated during full recursive import. var DeepReportPath = "full_import_report.json" // oneDeepSync ensure there is no more than one running deep sync. var oneDeepSync sync.Mutex var oneThemeDeepSync sync.Mutex // DeepSyncProgress expose the progression of the depp synchronization (0 = 0%, 255 = 100%). var DeepSyncProgress uint8 // avoidImporterSync checks if Sync should be called or not. func avoidImporterSync() bool { return DeepSyncProgress > 1 && DeepSyncProgress < 255 } // SpeedySyncDeep performs a recursive synchronisation without importing files. func SpeedySyncDeep(i Importer) (errs map[string][]string) { oneDeepSync.Lock() defer func() { oneDeepSync.Unlock() if DeepSyncProgress != 255 { log.Printf("Speedy synchronization terminated at step %d/255", DeepSyncProgress) } }() DeepSyncProgress = 1 errs = map[string][]string{} startTime := time.Now() if err := i.Sync(); err != nil { errs["_sync"] = []string{err.Error()} if _id := i.Id(); _id != nil { errs["_sync"] = append(errs["_sync"], *_id) } } errs["_date"] = []string{fmt.Sprintf("%v", startTime)} errs["_themes"] = SyncThemes(i) if themes, err := fic.GetThemes(); err == nil { DeepSyncProgress = 2 var themeStep uint8 = uint8(250) / uint8(len(themes)) for tid, theme := range themes { DeepSyncProgress = 3 + uint8(tid)*themeStep errs[theme.Name] = SyncExercices(i, theme) if exercices, err := theme.GetExercices(); err == nil { if len(exercices) == 0 { continue } var exerciceStep uint8 = themeStep / uint8(len(exercices)) for eid, exercice := range exercices { log.Printf("Speedy synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path) DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep flagsBindings, ferrs := SyncExerciceFlags(i, exercice) errs[theme.Name] = append(errs[theme.Name], ferrs...) DeepSyncProgress += exerciceStep / 2 _, herrs := SyncExerciceHints(i, exercice, flagsBindings) errs[theme.Name] = append(errs[theme.Name], herrs...) } } } } DeepSyncProgress = 254 errs["_date"] = append(errs["_date"], fmt.Sprintf("%v", time.Now())) DeepSyncProgress = 255 log.Println("Speedy synchronization done in", time.Since(startTime)) return } // SyncDeep performs a recursive synchronisation: from themes to challenge items. func SyncDeep(i Importer) (errs map[string][]string) { oneDeepSync.Lock() defer func() { oneDeepSync.Unlock() if DeepSyncProgress != 255 { log.Printf("Full synchronization terminated at step %d/255", DeepSyncProgress) } }() DeepSyncProgress = 1 errs = map[string][]string{} startTime := time.Now() if err := i.Sync(); err != nil { errs["_sync"] = []string{err.Error()} } errs["_date"] = []string{fmt.Sprintf("%v", startTime)} errs["_themes"] = SyncThemes(i) if themes, err := fic.GetThemes(); err == nil { DeepSyncProgress = 2 var themeStep uint8 = uint8(250) / uint8(len(themes)) for tid, theme := range themes { errs[theme.Name] = SyncThemeDeep(i, theme, tid, themeStep) } } DeepSyncProgress = 254 EditDeepReport(errs, true) if err := settings.ForceRegeneration(); err != nil { errs["_regeneration"] = append(errs["_regeneration"], err.Error()) } DeepSyncProgress = 255 log.Println("Full synchronization done in", time.Since(startTime)) return } func readDeepReport() (ret map[string][]string, err error) { if fdfrom, err := os.Open(DeepReportPath); err == nil { defer fdfrom.Close() jdec := json.NewDecoder(fdfrom) if err := jdec.Decode(&ret); err != nil { return nil, err } } else { return nil, err } return } func EditDeepReport(errs map[string][]string, erase bool) { errs["_regeneration"] = []string{} if !erase { if in, err := readDeepReport(); err != nil { errs["_regeneration"] = append(errs["_regeneration"], err.Error()) log.Println(err) } else { for k, v := range errs { in[k] = v } errs = in } } if _, ok := errs["_date"]; ok { errs["_date"] = append(errs["_date"], fmt.Sprintf("%v", time.Now())) } if fdto, err := os.Create(DeepReportPath); err == nil { defer fdto.Close() if out, err := json.Marshal(errs); err == nil { fdto.Write(out) } else { errs["_regeneration"] = append(errs["_regeneration"], err.Error()) log.Println(err) } } else { errs["_regeneration"] = append(errs["_regeneration"], err.Error()) log.Println(err) } } // SyncThemeDeep performs a recursive synchronisation: from challenges to challenge items. func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8) (errs []string) { oneThemeDeepSync.Lock() defer oneThemeDeepSync.Unlock() if !avoidImporterSync() { if err := i.Sync(); err != nil { errs = append(errs, err.Error()) } } DeepSyncProgress = 3 + uint8(tid)*themeStep errs = SyncExercices(i, theme) if exercices, err := theme.GetExercices(); err == nil && len(exercices) > 0 { var exerciceStep uint8 = themeStep / uint8(len(exercices)) for eid, exercice := range exercices { log.Printf("Deep synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path) DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep errs = append(errs, SyncExerciceFiles(i, exercice)...) DeepSyncProgress += exerciceStep / 3 flagsBindings, ferrs := SyncExerciceFlags(i, exercice) errs = append(errs, ferrs...) DeepSyncProgress += exerciceStep / 3 _, herrs := SyncExerciceHints(i, exercice, flagsBindings) errs = append(errs, herrs...) } } return }