public static CourseDataUploadRowInfoCollection ToDataUploadRowCollection(this IEnumerable <CsvCourseRow> rows) { var rowInfos = new List <CourseDataUploadRowInfo>(); foreach (var group in CsvCourseRow.GroupRows(rows)) { var courseId = Guid.NewGuid(); foreach (var row in group) { rowInfos.Add(new CourseDataUploadRowInfo(row, rowNumber: rowInfos.Count + 2, courseId)); } } return(new CourseDataUploadRowInfoCollection(rowInfos)); }
public async Task ProcessCourseFile(Guid courseUploadId, Stream stream) { using (var dispatcher = _sqlQueryDispatcherFactory.CreateDispatcher(System.Data.IsolationLevel.ReadCommitted)) { var setProcessingResult = await dispatcher.ExecuteQuery(new SetCourseUploadProcessing() { CourseUploadId = courseUploadId, ProcessingStartedOn = _clock.UtcNow }); if (setProcessingResult != SetCourseUploadProcessingResult.Success) { await DeleteBlob(); return; } await dispatcher.Commit(); } // At this point `stream` should be a CSV that's already known to conform to `CsvCourseRow`'s schema. // We read all the rows upfront because validation needs to group rows into courses. // We also don't expect massive files here so reading everything into memory is ok. List <CsvCourseRow> rows; using (var streamReader = new StreamReader(stream)) using (var csvReader = CreateCsvReader(streamReader)) { rows = await csvReader.GetRecordsAsync <CsvCourseRow>().ToListAsync(); } var rowsCollection = CreateCourseDataUploadRowInfoCollection(); using (var dispatcher = _sqlQueryDispatcherFactory.CreateDispatcher(System.Data.IsolationLevel.ReadCommitted)) { // If CourseName is empty, use the LearnAimRefTitle from LARS var learnAimRefs = rowsCollection.Select(r => r.Data.LearnAimRef).Distinct(); var learningDeliveries = await dispatcher.ExecuteQuery(new GetLearningDeliveries() { LearnAimRefs = learnAimRefs }); foreach (var row in rowsCollection) { if (string.IsNullOrWhiteSpace(row.Data.CourseName)) { row.Data.CourseName = learningDeliveries[row.Data.LearnAimRef].LearnAimRefTitle; } } var venueUpload = await dispatcher.ExecuteQuery(new GetCourseUpload() { CourseUploadId = courseUploadId }); var providerId = venueUpload.ProviderId; await AcquireExclusiveCourseUploadLockForProvider(providerId, dispatcher); await ValidateCourseUploadRows(dispatcher, courseUploadId, providerId, rowsCollection); await dispatcher.Commit(); } await DeleteBlob(); Task DeleteBlob() { var blobName = $"{Constants.CoursesFolder}/{courseUploadId}.csv"; return(_blobContainerClient.DeleteBlobIfExistsAsync(blobName)); } CourseDataUploadRowInfoCollection CreateCourseDataUploadRowInfoCollection() { // N.B. It's important we maintain ordering here; RowNumber needs to match the input var grouped = CsvCourseRow.GroupRows(rows); var groupCourseIds = grouped.Select(g => (CourseId: Guid.NewGuid(), Rows: g)).ToArray(); var rowInfos = new List <CourseDataUploadRowInfo>(rows.Count); foreach (var row in rows) { var courseId = groupCourseIds.Single(g => g.Rows.Contains(row)).CourseId; row.LearnAimRef = NormalizeLearnAimRef(row.LearnAimRef); rowInfos.Add(new CourseDataUploadRowInfo(row, rowNumber: rowInfos.Count + 2, courseId)); } return(new CourseDataUploadRowInfoCollection(rowInfos)); } }