//Save our timetables to the disk so we don't have to perform the calculation again //Possibly dangerous if someone manages to come up with ~100,000 different subject options of significant size, but I find this unlikely //Otherwise this will fill up 10GB of disk and break whatever it's running on private void saveResult(TimetableOptionsModel model, TimetableBuildResultModel result) { string path = getModelFilePath(model); using (FileStream fileStream = System.IO.File.Open(path, FileMode.CreateNew)) { using (GZipStream compressedStream = new GZipStream(fileStream, CompressionLevel.Optimal)) { using (StreamWriter writer = new StreamWriter(compressedStream)) { JsonSerializerSettings jsonSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; writer.Write(JsonConvert.SerializeObject(result, jsonSettings)); } } } }
public async Task <IActionResult> BuildTimetable([FromBody] TimetableOptionsModel model) { if (model.SubjectCodes.Count > 4) { return(StatusCode(403)); } if (System.IO.File.Exists(getModelFilePath(model))) { return(File(loadResult(model), "application/json; charset=utf-8")); } Stopwatch responseStopwatch = new Stopwatch(); responseStopwatch.Start(); List <Subject> subjects = await subjectsFromSubjectCodes(model.SubjectCodes); IEnumerable <ClassInfo> classInfos = subjects.SelectMany(subject => subject.ClassInfos); IEnumerable <ClassInfo> originalClassInfos = subjects.SelectMany(subject => subject.OriginalClassInfos); //Assign unique ids to each of the 'original' class infos, allowing for compression to work later int id = 0; foreach (var classInfo in originalClassInfos) { classInfo.ID = id++; } //Create a new generator with our sorting options and cancellation token Generator g = new Generator { SortLaterStarts = model.LaterStarts, SortLessDays = model.LessDays, CancellationToken = HttpContext.RequestAborted }; long possiblePermutations = Generator.PossiblePermutationsCount(classInfos); List <Timetable> timetables = new List <Timetable>(); //Check what algorithm to use, if we have over 5M permutations use the expanding algorithm if (possiblePermutations > 5 * 1000 * 1000) { timetables = g.GeneratePermutationsExpanding(classInfos); } else { timetables = g.GenerateTimetablesBruteForce(classInfos); } //Take 25,000 of our timetables and compress them var compressedTimetables = timetables.Take(25000).Select(t => new CompressedTimetable(t)).ToList(); var result = new TimetableBuildResultModel(compressedTimetables, originalClassInfos.ToList()); responseStopwatch.Stop(); //Only save results for meaningfully long requests (10secs) if (responseStopwatch.ElapsedMilliseconds > 10 * 1000) { saveResult(model, result); } return(Json(result)); }
public async Task <IActionResult> BuildTimetable([FromBody] TimetableOptionsModel model) { if (model.SubjectCodes.Count > 5) { return(Json(new TimetableBuildResultModel("Too many subjects selected."))); } if (System.IO.File.Exists(getModelFilePath(model)) && checkCacheDate(model)) { return(File(loadResult(model), "application/json; charset=utf-8")); } List <Subject> subjects = await subjectsFromSubjectCodes(model.SubjectCodes); IEnumerable <ClassInfo> classInfos = subjects.SelectMany(subject => subject.ClassInfos); IEnumerable <ClassInfo> originalClassInfos = subjects.SelectMany(subject => subject.OriginalClassInfos); var allScheduledClasses = classInfos.SelectMany(ci => ci.AllScheduledClasses); //Create a new generator with our sorting options and cancellation token Generator g = new Generator { SortLaterStarts = model.LaterStarts, SortLessDays = model.LessDays, CancellationToken = HttpContext.RequestAborted }; long possiblePermutations = Generator.PossiblePermutationsCount(classInfos); //Trying to generate when there's more than a trillion possibilities is a bad idea if (possiblePermutations > (long)1000 * 1000 * 1000 * 1000) { return(Json(new TimetableBuildResultModel("Can't generate timetables: too many possible timetables."))); } List <Timetable> timetables = new List <Timetable>(); //Check what algorithm to use, if we have over 5M permutations use the expanding algorithm if (possiblePermutations > 5 * 1000 * 1000) { timetables = g.GeneratePermutationsExpanding(classInfos); } else { timetables = g.GenerateTimetablesBruteForce(classInfos); } int numberGenerated = timetables.Count; //Take 25,000 timetables and compress them var result = new TimetableBuildResultModel(timetables.Take(25000).ToList(), numberGenerated, allScheduledClasses.ToList(), originalClassInfos.ToList()); saveResult(model, result); return(Json(result)); }