using Azure.Storage.Blobs.Models; using DocumentFormat.OpenXml.Drawing.Charts; using DocumentFormat.OpenXml.Spreadsheet; using HTEX.Lib.ETL.Lesson; using MathNet.Numerics.Distributions; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Logging; using StackExchange.Redis; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Text.Json; using System.Text.RegularExpressions; using System.Xml; using TEAMModelOS.SDK; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK.Helper.Common.FileHelper; using TEAMModelOS.SDK.Models; using TEAMModelOS.SDK.Models.Cosmos.BI; using TEAMModelOS.SDK.Models.Cosmos; using TEAMModelOS.SDK.Models.Cosmos.OpenEntity; using static TEAMModelOS.SDK.Models.Service.SystemService; namespace HTEX.DataETL.Controllers { [ApiController] [Route("lesson-record")] public class LessonRecordController : ControllerBase { private readonly ILogger _logger; private readonly AzureCosmosFactory _azureCosmos; private readonly AzureStorageFactory _azureStorage; private readonly IConfiguration _configuration; private readonly IWebHostEnvironment _webHostEnvironment; private readonly DingDing _dingDing; private readonly AzureRedisFactory _azureRedis; public LessonRecordController(ILogger logger, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage , IConfiguration configuration, IWebHostEnvironment environment,DingDing dingDing,AzureRedisFactory azureRedis ) { _logger = logger; _azureCosmos = azureCosmos; _azureStorage = azureStorage; _configuration = configuration; _webHostEnvironment = environment; _dingDing = dingDing; _azureRedis = azureRedis; } [HttpPost("schools")] public async Task Schools(JsonElement json) { List ids = new List { "cdscxx", "sdsyxq", "cdsxxx", "cdssz", "cdsshx", "ptszx", "cdwwsz", "csdswz", "cdfczx", "sdsy", "qyszgh", "sdsydq", "sslzxq", "cd37z", "cdqysz", "cd11z", "xhlxx", "cdpxjj", "xcfxqy", "csdswx", "thgjxx", "cdwwsx", "cdsjy", "cdsxwy", "cdsxzq", "cdsxmd", "cdsxcf", "cdsxqh", "cdsyxx", "cdsxx", "sxgkxx", "cdqbxx", "qyszfx", "cdpxxq", "cdpxlz", "cdpx", "cdkhxx", "cdhmxx", "cdjsxx", "cdhhxx", "cdhygj", "hygjqb", "cdglxx", "cddpxx", "cddcg", "cdctzm", "cdctxx", "cdchxx", "cdctxq", "sdsyqb", "sslzsx", "sslzjs", "cdjsxc", "cdjsxb", "yfsxwy", "sdyzhy", "yxmdfs", "cdcgxc", "cdcgxa" }; foreach (string id in ids) { var res = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList("select value c from c ", $"Base-{id}"); if (res.list.IsNotEmpty()) { var change= res.list.FindAll(x => x.graduate==1); if (change.IsNotEmpty()) { foreach (var item in change) { item.graduate=0; await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(item, new PartitionKey(item.code)); } } } } return Ok(new { }); } [HttpPost("process-lesson")] public async Task ProcessLesson(JsonElement json) { string scope = ""; string tmdid = ""; string lessonId; string school = null; string tbname = Constant.Teacher; string lcode = string.Empty; string blobname; var client = _azureCosmos.GetCosmosClient(); scope = $"private"; tmdid = $"6712127960"; lessonId = $"648770551606808576"; blobname = $"{tmdid}"; lcode = $"LessonRecord"; tbname = "Teacher"; LessonRecord oldlessonRecord = null; LessonRecord lessonRecord = null; ResponseMessage response = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemStreamAsync(lessonId, new PartitionKey(lcode)); if (response.StatusCode == System.Net.HttpStatusCode.OK) { var doc = JsonDocument.Parse(response.Content); lessonRecord = doc.RootElement.ToObject(); oldlessonRecord = doc.RootElement.ToObject(); } else { lessonRecord = null; } //读取TimeLine.json List PickupMemberIds = new List(); TimeLineData timeLineData = new TimeLineData(); try { BlobDownloadResult timeLineBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/TimeLine.json").DownloadContentAsync(); timeLineData = timeLineBlobDownload.Content.ToObjectFromJson(); lessonRecord.hitaClientCmpCount = timeLineData.events.Where(z => !string.IsNullOrWhiteSpace(z.WrkCmpSrcType) && z.WrkCmpSrcType.Equals("HitaClientCmp")).Count(); //lessonRecord.collateTaskCount = lessonRecord.hitaClientCmpCount; List timeLineEvents = timeLineData.events.FindAll(z => !string.IsNullOrWhiteSpace(z.Event) && z.Event.Equals("PickupResult")); if (timeLineEvents.IsNotEmpty()) { foreach (var timeLineEvent in timeLineEvents) { var memberIds = timeLineEvent.PickupMemberId.ToObject>(); PickupMemberIds.AddRange(memberIds); } } } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},TimeLine.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //读取Task.json ///Event 过滤类型 : 'WrkSpaceLoad', 'WrkCmp' 文件:Task.json 根据clientWorks 中的seatID 匹配base.json 中的 student List taskDatas = new List(); try { BlobDownloadResult taskBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/Task.json").DownloadContentAsync(); taskDatas = taskBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},Task.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //读取互评信息 //Event 过滤类型 'RatingStart' //smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票 //读取IRS.json List smartRatingDatas = new List(); try { BlobDownloadResult smartRatingBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/IRS.json").DownloadContentAsync(); smartRatingDatas = smartRatingBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //读取互动信息 //Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad' //TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答 //读取IRS.json List irsDatas = new List(); try { BlobDownloadResult irsBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/IRS.json").DownloadContentAsync(); irsDatas = irsBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //读取协作信息 ///Event 过滤类型 'CoworkLoad' //TimeLine.json 中找到对应类型,根据Pgid 去 Cowork.json 中找到对应数据 List coworkDatas = new List(); try { BlobDownloadResult irsBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/Cowork.json").DownloadContentAsync(); coworkDatas = irsBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //苏格拉底文件信息 //Sokrates/SokratesRecords.json List sokratesDatas = new List(); try { BlobDownloadResult irsBlobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/Sokrates/SokratesRecords.json").DownloadContentAsync(); sokratesDatas = irsBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},IRS.json转换异常,{ex.Message}{ex.StackTrace},{lessonRecord.id}", GroupNames.成都开发測試群組); } } //读取base.json信息 //如果有更新 则去读取/{lessonId}/IES/base.json List studentLessonDatas = new List(); LessonBase lessonBase = null; try { // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{lessonId} 收到更新", GroupNames.醍摩豆服務運維群組); BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{lessonId}/IES/base.json").DownloadContentAsync(); //attendState string basejson = baseblobDownload.Content.ToString().Replace("\"Uncall\"", "0").Replace("Uncall", "0"); try { lessonBase = basejson.ToObject(); } catch (Exception ex) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},base.json转换异常,{ex.Message}{ex.StackTrace}{basejson},{lessonRecord.id}", GroupNames.成都开发測試群組); //lessonBase = baseblobDownload.Content.ToObjectFromJson(); } //await _dingDing.SendBotMsg($"课例记录文件base.json:{lessonBase.ToJsonString()}", GroupNames.成都开发測試群組); if (lessonBase != null && lessonBase.summary != null) { var baseData = LessonETLService.GetBaseData(lessonBase!); studentLessonDatas= baseData.studentLessonDatas; //lessonRecord.name = lessonBase.summary.activityName; lessonRecord.attendCount = lessonBase.summary.attendCount; lessonRecord.clientCount = lessonBase.summary.clientCount; lessonRecord.attendRate = lessonBase.summary.attendRate; lessonRecord.groupCount = lessonBase.summary.groupCount; lessonRecord.collateTaskCount = lessonBase.summary.collateTaskCount; lessonRecord.hitaClientCmpCount = lessonBase.summary.collateTaskCount + lessonRecord.hitaClientCmpCount; lessonRecord.collateCount = lessonBase.summary.collateCount; lessonRecord.pushCount = lessonBase.summary.pushCount; lessonRecord.totalPoint = lessonBase.summary.totalPoint; lessonRecord.examQuizCount = lessonBase.summary.examQuizCount; lessonRecord.interactionCount = lessonBase.summary.interactionCount; lessonRecord.examPointRate = lessonBase.summary.examPointRate; lessonRecord.clientInteractionCount = lessonBase.summary.clientInteractionCount; lessonRecord.clientInteractionAverge = lessonBase.summary.clientInteractionAverge; lessonRecord.examCount = lessonBase.summary.examCount; lessonRecord.totalInteractPoint = lessonBase.summary.totalInteractPoint; lessonRecord.learningCategory = lessonBase.summary.learningCategory; if (lessonRecord.learningCategory == null) { lessonRecord.learningCategory = new LearningCategory(); } if (lessonRecord.hitaClientCmpCount > 0) { lessonRecord.learningCategory.cooperation = 1; } if (!string.IsNullOrWhiteSpace(lessonRecord.school)) { lessonBase.student.ForEach(x => { if (string.IsNullOrWhiteSpace(x.school)) { x.school = lessonRecord.school; } }); } //计算TP灯 { int T = -1; int P = -1; if (lessonRecord.clientInteractionAverge <= 0) { T = 0; } else if (lessonRecord.clientInteractionAverge > 0 && lessonRecord.clientInteractionAverge < 2) // else if (lessonRecord.clientInteractionAverge >= 1 && lessonRecord.clientInteractionAverge <= 2) { T = 1; } else { T = 2; } //if (lessonRecord.examCount > 0) //{ // //有评测次数大于0则P是直接绿灯 // P = 2; //} //else { //} int a = lessonRecord.hitaClientCmpCount; int b = lessonRecord.pushCount; int c = lessonRecord.examCount; switch (true) { case bool when T == 0: P = 0; break; case bool when T == 1 || T == 2: if (a == 0 && b == 0 && c == 0) { P = 0; } else if ((a > 0 && b > 0) || (a > 0 && c > 0) || (b > 0 && c > 0)) { P = 2; } else { P = 1; } break; } lessonRecord.tLevel = T; lessonRecord.pLevel = P; } //LessonStudentRecord lessonStudentRecord = new LessonStudentRecord //{ // clientSummaryList = lessonBase.report.clientSummaryList, // students = lessonBase.student, // name = lessonRecord.name, // school = lessonRecord.school, // id = lessonRecord.id, // scope = lessonRecord.scope, // tmdid = lessonRecord.tmdid, // code = "LessonStudentRecord", // pk = "LessonStudentRecord", // courseId =lessonRecord.courseId, // groupIds= lessonRecord.groupIds, // periodId = lessonRecord.periodId, // subjectId = lessonRecord.subjectId, //}; //await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,Constant.Student).UpsertItemAsync(lessonStudentRecord, new PartitionKey("LessonStudentRecord")); } //有上传 base.josn. lessonRecord.upload = 1; // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{lessonId} 更新完成", GroupNames.醍摩豆服務運維群組); //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},课堂id:{lessonId} blob刷新完成!", GroupNames.醍摩豆服務運維群組); List examDatas = new List(); if (lessonBase!=null && lessonBase.student!=null) { string owner = lessonRecord.scope.Equals("school") ? lessonRecord.school : lessonRecord.tmdid; examDatas = await LessonETLService.GetExamInfo(lessonRecord, timeLineData, _azureStorage, owner); sokratesDatas= sokratesDatas.IsNotEmpty() ? sokratesDatas : timeLineData!=null ? timeLineData.events : new List(); } LessonLocal lessonLocal = new LessonLocal() { lessonBase=lessonBase, timeLineData= timeLineData, lessonRecord= lessonRecord, taskDatas= taskDatas, smartRatingDatas= smartRatingDatas, irsDatas= irsDatas, coworkDatas= coworkDatas, examDatas=examDatas, sokratesDatas= sokratesDatas, studentLessonDatas=studentLessonDatas }; try { if (lessonRecord.duration>0) { var location = "China"; var studatas = LessonETLService.GenStudentLessonData(lessonLocal, Constant.objectiveTypes); string owner = lessonRecord.scope.Equals("school") ? lessonRecord.school : lessonRecord.tmdid; if (location.Equals("China", StringComparison.OrdinalIgnoreCase)) { List schools = new List(); List studentSemesterRecords = new List(); List overallEducations = new List(); List studentsBase = new List(); List students = new List(); LessonDataAnalysisModel lessonDataAnalysis = null; bool exists = await _azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis-model.json").ExistsAsync(); if (exists) { BlobDownloadResult blobDownload = await _azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis-model.json").DownloadContentAsync(); lessonDataAnalysis = blobDownload.Content.ToObjectFromJson(); string studentSql = $"select value c from c where c.id in ({string.Join(",", lessonLocal.studentLessonDatas.Select(x => $"'{x.id}'"))})"; var studentResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(studentSql, $"Base-{school}"); if (studentResults.list.IsNotEmpty()) { studentsBase.AddRange(studentResults.list); } School schoolBase = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync(school, new PartitionKey("Base")); schools.Add(schoolBase); string? periodId = !string.IsNullOrWhiteSpace(lessonLocal.lessonRecord.periodId) ? lessonLocal.lessonRecord.periodId : schoolBase.period.FirstOrDefault()?.id; var period = schoolBase.period.Find(x => x.id.Equals(periodId)); var semester = SchoolService.GetSemester(period, lessonLocal.lessonRecord.startTime); string code = $"StudentSemesterRecord-{schoolBase.id}"; string studentSemesterRecordSql = $"select value c from c where c.stuid in ({string.Join(",", lessonLocal.studentLessonDatas.Select(x => $"'{x.id}'"))}) and c.semesterId='{semester.currSemester.id}' and c.studyYear={semester.studyYear}"; var studentSemesterRecordResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(studentSemesterRecordSql, code); if (studentSemesterRecordResults.list.IsNotEmpty()) { studentSemesterRecords.AddRange(studentSemesterRecordResults.list); } string overallEducationSql = $"select value c from c where c.studentId in ({string.Join(",", lessonLocal.studentLessonDatas.Select(x => $"'{x.id}'"))}) and c.semesterId='{semester.currSemester.id}' and c.year={semester.studyYear}"; var overallEducationResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(overallEducationSql, $"OverallEducation-{schoolBase.id}"); if (overallEducationResults.list.IsNotEmpty()) { overallEducations.AddRange(overallEducationResults.list); } var studata = LessonETLService.DoStudentLessonDataV2(Constant.objectiveTypes, lessonLocal, location, studentSemesterRecords, overallEducations, lessonDataAnalysis, studentsBase, schools); await Parallel.ForEachAsync(studentSemesterRecords, async (studentSemester, cancellationToken) => { await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(studentSemester, new PartitionKey(studentSemester.code)); }); await Parallel.ForEachAsync(overallEducations, async (overallEducation, cancellationToken) => { await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(overallEducation, partitionKey: new PartitionKey(overallEducation.code)); string key = $"OverallEducation:{overallEducation.schoolCode}:{overallEducation.periodId}:{overallEducation.year}:{overallEducation.semesterId}:{overallEducation?.classId}"; await _azureRedis.GetRedisClient(8).HashSetAsync(key, overallEducation.studentId, overallEducation.ToJsonString()); await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 *24, 0, 0)); }); await _azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studata.studentLessonDatas.ToJsonString(), "records", $"{lessonRecord.id}/student-analysis.json"); XmlDocument xmlDocument = new XmlDocument(); var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); xmlDocument.Load($"{runtimePath}\\summary.xml"); PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties(); List summaryes = new List(); for (int i = 0; i < properties.Length; i++) { string summary = Regex.Replace(LessonETLService.GetPropertySummary(properties[i], xmlDocument), @"\s+", ""); summaryes.Add(summary); } await LessonETLService.ExportToExcelAzureBlob(studata.lessonItems, _azureStorage, owner, $"{lessonRecord.id}/student-analysis.xlsx", xmlDocument, summaryes, properties); } } else { await _azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studatas.studentLessonDatas.ToJsonString(), "records", $"{lessonRecord.id}/student-analysis.json"); } // 使用当前文化设置的日历 CultureInfo cultureInfo = CultureInfo.CurrentCulture; Calendar calendar = cultureInfo.Calendar; //表示如果一年的第一个星期至少有4天在同一年,则该星期被视为第一周,DayOfWeek.Monday和DayOfWeek.Sunday分别表示一周的第一天是星期一或星期日。 var week = calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); string key = $"LessonWeekly:{lessonRecord.scope}:{DateTime.Now.Year}-{week}"; _azureRedis.GetRedisClient(8).HashSet(key, $"{lessonRecord.tmdid}:{lessonRecord.id}", new LessonWeek { week=week, id = lessonRecord.id, cid=lessonRecord.courseId, sid= lessonRecord.subjectId, school=lessonRecord.school, gid= lessonRecord.groupIds?.FirstOrDefault(), //pid= lessonRecord.periodId, scope= lessonRecord.scope, }.ToJsonString() ); await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, TimeSpan.FromDays(10)); } } catch (Exception ex) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}处理学生课中数据发生异常,{lessonId}\n{ex.Message}\n{ex.StackTrace}\n\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組); } } catch (Exception ex) { } return Ok(); } [HttpPost("process-local")] public async Task ProcessLocal(JsonElement json) { List studentLessonDatas = new List(); string? id = json.GetProperty("id").GetString(); if (!string.IsNullOrWhiteSpace(id)) { string? lessonPath = _configuration.GetValue("LessonPath"); string? path = $"{lessonPath}\\locals\\{id}"; var files = FileHelper.ListAllFiles(path); // var sampleJson =System.IO. File.ReadAllTextAsync(path); LessonBase? lessonBase = null; List localStudents = new List(); List taskDatas = new List(); List smartRatingDatas = new List(); List irsDatas = new List(); List coworkDatas = new List(); List examDatas = new List(); TimeLineData? timeLineData = null; foreach (var item in files) { if (item.Contains("IES\\base.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); jsons = jsons.Replace("\"Uncall\"", "0").Replace("Uncall", "0"); lessonBase = jsons.ToObject(); var data = LessonETLService.GetBaseData(lessonBase); localStudents = data.studentLessonDatas; } if (item.Contains("IES\\TimeLine.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); timeLineData = jsons.ToObject(); } if (item.Contains("IES\\Task.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); taskDatas = jsons.ToObject>(); } if (item.Contains("IES\\SmartRating.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); smartRatingDatas = jsons.ToObject>(); } if (item.Contains("IES\\IRS.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); irsDatas = jsons.ToObject>(); } if (item.Contains("IES\\Cowork.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); coworkDatas = jsons.ToObject>(); } try { if (item.Contains($"\\{id}\\Exam\\") && item.EndsWith("Exam.json")) { string examsFile = item; if (examsFile.EndsWith("Exam.json")) { ExamData? examData = null; string jsons = await System.IO.File.ReadAllTextAsync(item); jsons = jsons.Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1"); examData = jsons.ToObject(); if (examData != null && examData.exam.papers.IsNotEmpty()) { string paperId = examData.exam.papers.First().id; string paperPath = $"{path}\\ExamPaper\\{paperId}\\index.json"; string jsonp = await System.IO.File.ReadAllTextAsync(paperPath); LessonPaper lessonPaper = jsonp.ToObject(); examData.paper = lessonPaper; examDatas.Add(examData); } } } } catch (Exception ex) { _logger.LogError(ex, ex.Message); } } if (lessonBase!=null && timeLineData!=null) { studentLessonDatas = localStudents.ToJsonString().ToObject>(); studentLessonDatas = LessonETLService.GetBaseInfo(lessonBase!, studentLessonDatas, id); studentLessonDatas = LessonETLService.GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas, id); studentLessonDatas = LessonETLService.GetCoworkData(lessonBase, timeLineData, coworkDatas, studentLessonDatas, id); studentLessonDatas = LessonETLService.GetExamData(lessonBase, timeLineData, examDatas, studentLessonDatas, Constant.objectiveTypes, id); studentLessonDatas = LessonETLService.GetSmartRatingData(lessonBase, timeLineData, smartRatingDatas, studentLessonDatas, id); studentLessonDatas = LessonETLService.GetTaskData(lessonBase, timeLineData, taskDatas, studentLessonDatas, id); var pickupData = LessonETLService.GetPickupData(lessonBase, timeLineData, studentLessonDatas, id); studentLessonDatas= pickupData.studentLessonDatas; var codeBools= LessonETLService.GetCodeBools(studentLessonDatas); await System.IO.File.WriteAllTextAsync(Path.Combine(path, $"student-analysis.json"), studentLessonDatas.ToJsonString()); string jsons = await System.IO.File.ReadAllTextAsync($"{lessonPath}\\analysis\\analysis-model.json"); LessonDataAnalysisModel lessonDataAnalysis = jsons.ToObject(); var studentLessons = LessonETLService.ProcessStudentDataV2(studentLessonDatas, lessonDataAnalysis,codeBools); XmlDocument xmlDocument = new XmlDocument(); var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); xmlDocument.Load($"{runtimePath}\\summary.xml"); await LessonETLService.ExportToExcelLocal(studentLessons, $"{path}\\analysis.xlsx", xmlDocument); } } return Ok(); } /// /// /// /// /// [HttpPost("process-history-students")] public async Task ProcessHistoryStudents(JsonElement json) { string? lessonBasePath = _configuration.GetValue("LessonPath"); string? pathLessons = $"{lessonBasePath}\\lessons"; string? pathAnalysis = $"{lessonBasePath}\\analysis"; string jsons = await System.IO.File.ReadAllTextAsync($"{pathAnalysis}\\analysis-model.json"); LessonDataAnalysisModel lessonDataAnalysis = jsons.ToObject(); List filesLessons = FileHelper.ListAllFiles(pathLessons, "-local.json"); List filesStudata = FileHelper.ListAllFiles(pathLessons, "-sdata.json"); var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load($"{runtimePath}\\summary.xml"); List schools = new List(); // await Parallel.ForEachAsync(filesLessons, async (fileLesson, _) => List localIds = new List(); foreach (var file in filesLessons) { string lessonId = file.Split("\\").Last().Replace("-local.json", ""); localIds.Add(lessonId); } List lessonLocals = new List(); List lessonRecords = new List(); string recordsPtah = $"{lessonBasePath}\\records\\records.json"; long stime = 0;//2023-09-01 00:00:00 long etime = 0;//2024-11-13 23:59:59 //if (System.IO.File.Exists(recordsPtah)) //{ // string jsonData = await System.IO.File.ReadAllTextAsync(recordsPtah); // lessonRecords= jsonData.ToObject>(); // stime= lessonRecords.Max(x => x.startTime); // etime= lessonRecords.Max(x => x.startTime); //} if (stime==0) { stime = 1693497600000;//2023-09-01 00:00:00 } if (etime==0) { etime = 1731513599000;//2024-11-13 23:59:59 } List locals= await LessonETLService.FixLocalData(localIds, _azureCosmos, _azureStorage, pathLessons, stime, etime); if (locals.IsNotEmpty()) { lessonLocals.AddRange(locals); } int u = 0; Parallel.ForEach(filesLessons, file => { string jsonp = System.IO.File.ReadAllText(file); var lessonLocal = jsonp.ToObject(); if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student.IsNotEmpty()) { lessonLocals.Add(lessonLocal); } else { System.IO.File.Delete(file); System.IO.File.Delete(file.Replace("-local.json", "-count.json")); u++; } }); List ignore = new List() { "PgJump", "PgRcv", "PgAdd" }; if (lessonLocals.IsNotEmpty()) { lessonRecords=lessonLocals.Where(x => x.lessonRecord.scope.Equals("school")).Select(x =>x.lessonRecord).ToList(); } if (lessonRecords.IsNotEmpty()) { await System.IO.File.WriteAllTextAsync(recordsPtah, lessonRecords.ToJsonString()); List studentSemesterRecords= new List(); List overallEducations= new List(); List studentsBase = new List(); string schoolPtah = $"{lessonBasePath}\\schools\\school.json"; if (System.IO.File.Exists(schoolPtah)) { string jsonData = await System.IO.File.ReadAllTextAsync(schoolPtah); schools= jsonData.ToObject>(); } var schoolGroup = lessonLocals.Where(x => !string.IsNullOrWhiteSpace(x.lessonRecord?.school)).GroupBy(x => x.lessonRecord?.school).Select(x => new { key = x.Key, list = x.ToList() }); var newschoolIds = schoolGroup.Select(x=>x.key).ExceptBy(schools.Select(x => x.id), x => x); if (newschoolIds!=null && newschoolIds.Count()>0) { string schoolSql = $"select value c from c where c.id in ({string.Join(",", schoolGroup.Select(x => $"'{x.key}'"))})"; var schoolResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList(schoolSql, "Base"); if (schoolResults.list.IsNotEmpty()) { schools.AddRange(schoolResults.list); } } await System.IO.File.WriteAllTextAsync($"{lessonBasePath}\\schools\\school.json", schools.ToJsonString()); foreach (var group in schoolGroup) { string students_path = $"{lessonBasePath}\\students\\{group.key}\\students.json"; string records_path = $"{lessonBasePath}\\students\\{group.key}\\records.json"; string overall_path = $"{lessonBasePath}\\students\\{group.key}\\overall.json"; if (!Directory.Exists($"{lessonBasePath}\\students\\{group.key}")) { Directory.CreateDirectory($"{lessonBasePath}\\students\\{group.key}"); } var studentIds = group.list.SelectMany(x => x.studentLessonDatas).Where(x=>!string.IsNullOrWhiteSpace(x.id)).Select(x => x.id).Distinct(); List schoolStudent = new List(); if (System.IO.File.Exists(students_path)) { string jsonData = await System.IO.File.ReadAllTextAsync(students_path); schoolStudent= jsonData.ToObject>(); studentsBase.AddRange(schoolStudent); } var newIds= studentIds.ExceptBy(schoolStudent.Where(x=>x.schoolId.Equals(group.key)).Select(x=>x.id),x=>x); if (newIds!=null && newIds.Count()>0) { string studentSql = $"select value c from c where c.id in ({string.Join(",", newIds.Select(x => $"'{x}'"))})"; var studentResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(studentSql, $"Base-{group.key}"); if (studentResults.list.IsNotEmpty()) { schoolStudent.AddRange(studentResults.list); studentsBase.AddRange(studentResults.list); } } await System.IO.File.WriteAllTextAsync(students_path, schoolStudent.ToJsonString()); List schoolStudentSemesterRecords=new List(); if (System.IO.File.Exists(records_path)) { string jsonData = await System.IO.File.ReadAllTextAsync(records_path); schoolStudentSemesterRecords= jsonData.ToObject>(); studentSemesterRecords.AddRange(schoolStudentSemesterRecords); } var newstuIds = studentIds.ExceptBy(studentSemesterRecords.Where(x => x.school.Equals(group.key)).Select(x => x.stuid), x => x); if (newstuIds!=null && newstuIds.Count()>0) { string studentSemesterRecordSql = $"select value c from c where c.stuid in ({string.Join(",", newstuIds.Distinct().Select(x => $"'{x}'"))}) and c.studyYear>=2023"; var studentSemesterRecordResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(studentSemesterRecordSql, $"StudentSemesterRecord-{group.key}"); if (studentSemesterRecordResults.list.IsNotEmpty()) { schoolStudentSemesterRecords.AddRange(studentSemesterRecordResults.list); studentSemesterRecords.AddRange(studentSemesterRecordResults.list); } } await System.IO.File.WriteAllTextAsync(records_path, schoolStudentSemesterRecords.ToJsonString()); List schoolOverallEducations = new List(); if (System.IO.File.Exists(overall_path)) { string jsonData = await System.IO.File.ReadAllTextAsync(overall_path); schoolOverallEducations= jsonData.ToObject>(); overallEducations.AddRange(schoolOverallEducations); } var newstuoIds = studentIds.ExceptBy(schoolOverallEducations.Where(x => x.schoolCode.Equals(group.key)).Select(x => x.studentId), x => x); if (newstuIds!=null && newstuIds.Count()>0) { string overallEducationSql = $"select value c from c where c.studentId in ({string.Join(",", newstuoIds.Select(x => $"'{x}'"))}) and c.year>=2023"; var overallEducationResults = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList(overallEducationSql, $"OverallEducation-{group.key}"); if (overallEducationResults.list.IsNotEmpty()) { schoolOverallEducations.AddRange(overallEducationResults.list); overallEducations.AddRange(overallEducationResults.list); } } await System.IO.File.WriteAllTextAsync(overall_path, schoolOverallEducations.ToJsonString()); } //List<(string id,string owner, List studentLessonData)> studentLessonDatas= new List<(string id, string owner, List)>(); ConcurrentQueue lessonItems = new ConcurrentQueue(); int v = 0; Parallel.ForEach(filesStudata, file => { string jsonp = System.IO.File.ReadAllText(file); var studentResult = jsonp.ToObject(); lessonItems.Enqueue(studentResult); v++; }); int n = 0; lessonLocals= lessonLocals.ExceptBy(lessonItems.Select(x => x.id), y => y.lessonRecord.id).ToList(); Parallel.ForEach(lessonLocals, (item, con) => { try { var studata = LessonETLService.DoStudentLessonDataV2(Constant.objectiveTypes, item, "China", studentSemesterRecords, overallEducations, lessonDataAnalysis, studentsBase, schools); if (studata.codeBools.FindAll(x => x.value).IsNotEmpty()) { string owner = item.lessonRecord.scope.Equals("school") ? item.lessonRecord.school : item.lessonRecord.tmdid; //studentLessonDatas.Add((item.lessonRecord.id, owner, studata.studentLessonDatas)); LessonStudentResult result = new LessonStudentResult { id= item.lessonRecord.id, owner= owner, studentLessons= studata.lessonItems, codeBools= studata.codeBools, lessonDatas= studata.studentLessonDatas }; string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(item.lessonRecord.startTime).ToString("yyyyMM"); System.IO.File.WriteAllText($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-sdata.json", item.ToJsonString()); lessonItems.Enqueue(result); } } catch (Exception ex) { Console.WriteLine($"{ex.Message},{ex.StackTrace}"); throw new Exception(ex.Message, ex); } n++; }); foreach (var group in schoolGroup) { string students_path = $"{lessonBasePath}\\students\\{group.key}\\students.json"; string records_path = $"{lessonBasePath}\\students\\{group.key}\\records.json"; string overall_path = $"{lessonBasePath}\\students\\{group.key}\\overall.json"; if (!Directory.Exists($"{lessonBasePath}\\students\\{group.key}")) { Directory.CreateDirectory($"{lessonBasePath}\\students\\{group.key}"); } var schoolStudent = studentsBase.FindAll(x => x.schoolId.Equals(group.key)); var schoolStudentSemesterRecords= studentSemesterRecords.FindAll(x => x.school.Equals(group.key)); var schoolOverallEducations = overallEducations.FindAll(x => x.schoolCode.Equals(group.key)); await System.IO.File.WriteAllTextAsync(students_path, schoolStudent.ToJsonString()); await System.IO.File.WriteAllTextAsync(records_path, schoolStudentSemesterRecords.ToJsonString()); await System.IO.File.WriteAllTextAsync(overall_path, schoolOverallEducations.ToJsonString()); } int m = 0; var grpdata = studentSemesterRecords.GroupBy(x => x.code).Select(x => new { key = x.Key, list = x.ToList() }); foreach (var group in grpdata) { var pages = group.list.Page(100); foreach (var page in pages) { List>> list = new(); foreach (var studentSemester in page) { list.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(studentSemester, new PartitionKey(studentSemester.code))) ; m++; } await Task.WhenAll(list); } } int k = 0; var gpover = overallEducations.GroupBy(x => x.code).Select(x => new { key = x.Key, list = x.ToList() }); foreach (var item in gpover) { var pages = item.list.Page(100); foreach (var page in pages) { List>> list = new(); foreach (var overallEducation in page) { list.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(overallEducation, partitionKey: new PartitionKey(overallEducation.code))); //string key = $"OverallEducation:{overallEducation.schoolCode}:{overallEducation.periodId}:{overallEducation.year}:{overallEducation.semesterId}:{overallEducation?.classId}"; //await _azureRedis.GetRedisClient(8).HashSetAsync(key, overallEducation.studentId, overallEducation.ToJsonString()); //await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 *24, 0, 0)); } await Task.WhenAll(list); } } int p = 0; // 获取类的属性 PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties(); List summaryes= new List(); for (int i = 0; i < properties.Length; i++) { string summary = Regex.Replace(LessonETLService.GetPropertySummary(properties[i], xmlDocument), @"\s+", ""); summaryes.Add(summary); } await Parallel.ForEachAsync(lessonItems, async (lessonItem, _) => { await _azureStorage.GetBlobContainerClient(lessonItem.owner).UploadFileByContainer(lessonItem.lessonDatas.ToJsonString(), "records", $"{lessonItem.id}/student-analysis.json"); await LessonETLService.ExportToExcelAzureBlob(lessonItem.studentLessons, _azureStorage, lessonItem.owner, $"{lessonItem.id}/student-analysis.xlsx", xmlDocument, summaryes, properties); p++; }); return Ok(new { p, m, k,n,u}); } return Ok(new {}); } /// /// 课例数据ETL处理过程 /// /// /// [HttpPost("process-history")] public async Task ProcessHistory(JsonElement json) { Stopwatch stopwatch= new Stopwatch(); List localIds = new List(); string? lessonBasePath = _configuration.GetValue("LessonPath"); string? pathLessons = $"{lessonBasePath}\\lessons"; string? pathAnalysis = $"{lessonBasePath}\\analysis"; var filesLessons = FileHelper.ListAllFiles(pathLessons); foreach (var file in filesLessons) { if (file.EndsWith("-local.json")) { string lessonId = file.Split("\\").Last().Replace("-local.json", ""); localIds.Add(lessonId); } } bool loadLocal = true; var filesAnalysis = FileHelper.ListAllFiles(pathAnalysis); long stime = 1693497600000;//2023-09-01 00:00:00 bool force = false; if ((json.TryGetProperty("force", out JsonElement _force)&& _force.ValueKind.Equals(JsonValueKind.True))) { force= _force.GetBoolean(); } if (force) { List lessonDataAnalysisMonths = new List(); foreach (var file in filesAnalysis) { //读取每月的数据 if (file.EndsWith("-m-analysis.json")) { string jsons = await System.IO.File.ReadAllTextAsync(file); LessonDataAnalysisMonth lessonDataAnalysis = jsons.ToObject(); lessonDataAnalysisMonths.Add(lessonDataAnalysis); } } if (lessonDataAnalysisMonths.IsNotEmpty()) { var maxUpdateTime = lessonDataAnalysisMonths.Max(x => x.updateTime); if (maxUpdateTime>0) { //更新周期是一周 if (DateTimeOffset.Now.ToUnixTimeMilliseconds()- maxUpdateTime>604800000) { stime=maxUpdateTime; loadLocal =true; } else { stime=maxUpdateTime; loadLocal=false; } } } } // if (loadLocal ||force) { List ignore = new List() { "PgJump", "PgRcv", "PgAdd" }; await LessonETLService.FixLocalData(localIds, _azureCosmos, _azureStorage, pathLessons, stime, DateTimeOffset.Now.ToUnixTimeMilliseconds()); List techCounts = new List(); filesLessons = FileHelper.ListAllFiles(pathLessons, "-local.json"); List lessonLocals = new List(); int u = 0; if (force) { stopwatch.Start(); Parallel.ForEach(filesLessons, file => { string jsonp = System.IO.File.ReadAllText(file); var lessonLocal = jsonp.ToObject(); if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student.IsNotEmpty()) { lessonLocals.Add(lessonLocal); } else { System.IO.File.Delete(file); System.IO.File.Delete(file.Replace("-local.json", "-count.json")); u++; } }); stopwatch.Stop(); } _logger.LogInformation($"Loaded {lessonLocals.Count} lessons in {stopwatch.Elapsed.TotalSeconds} seconds"); int index = 0; await Parallel.ForEachAsync(filesLessons, async (item, _) => { TechCount techCount= await LessonETLService.GetTeachCount(_azureCosmos, item, pathLessons, ignore, Constant.objectiveTypes, _azureStorage, force, lessonLocals); if (techCount != null) { techCounts.Add(techCount); } index++; }); long newest = lessonLocals.Max(x=>x.lessonRecord.startTime); await LessonETLService.GenAnalysisData(pathAnalysis, newest, techCounts,_azureStorage); } return Ok(new { }); } } }