LessonRecordController.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. using DocumentFormat.OpenXml.Drawing.Charts;
  2. using HTEX.Lib.ETL.Lesson;
  3. using MathNet.Numerics.Distributions;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.Azure.Cosmos;
  6. using StackExchange.Redis;
  7. using System.Text.Json;
  8. using System.Xml;
  9. using TEAMModelOS.SDK;
  10. using TEAMModelOS.SDK.DI;
  11. using TEAMModelOS.SDK.Extension;
  12. using TEAMModelOS.SDK.Helper.Common.FileHelper;
  13. using TEAMModelOS.SDK.Models;
  14. using TEAMModelOS.SDK.Models.Cosmos.Common;
  15. using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
  16. namespace HTEX.DataETL.Controllers
  17. {
  18. [ApiController]
  19. [Route("lesson-record")]
  20. public class LessonRecordController : ControllerBase
  21. {
  22. private readonly ILogger<LessonRecordController> _logger;
  23. private readonly AzureCosmosFactory _azureCosmos;
  24. private readonly AzureStorageFactory _azureStorage;
  25. private readonly IConfiguration _configuration;
  26. private readonly IWebHostEnvironment _webHostEnvironment;
  27. private readonly DingDing _dingDing;
  28. private readonly AzureRedisFactory _azureRedis;
  29. public LessonRecordController(ILogger<LessonRecordController> logger, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage , IConfiguration configuration, IWebHostEnvironment environment,DingDing dingDing,AzureRedisFactory azureRedis )
  30. {
  31. _logger = logger;
  32. _azureCosmos = azureCosmos;
  33. _azureStorage = azureStorage;
  34. _configuration = configuration;
  35. _webHostEnvironment = environment;
  36. _dingDing = dingDing;
  37. _azureRedis = azureRedis;
  38. }
  39. [HttpPost("process-local")]
  40. public async Task<IActionResult> ProcessLocal(JsonElement json)
  41. {
  42. List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
  43. string? id = json.GetProperty("id").GetString();
  44. if (!string.IsNullOrWhiteSpace(id))
  45. {
  46. string? lessonPath = _configuration.GetValue<string>("LessonPath");
  47. string? path = $"{lessonPath}\\locals\\{id}";
  48. var files = FileHelper.ListAllFiles(path);
  49. // var sampleJson =System.IO. File.ReadAllTextAsync(path);
  50. LessonBase? lessonBase = null;
  51. List<LocalStudent> localStudents = new List<LocalStudent>();
  52. List<TaskData> taskDatas = new List<TaskData>();
  53. List<SmartRatingData> smartRatingDatas = new List<SmartRatingData>();
  54. List<IRSData> irsDatas = new List<IRSData>();
  55. List<CoworkData> coworkDatas = new List<CoworkData>();
  56. List<ExamData> examDatas = new List<ExamData>();
  57. TimeLineData? timeLineData = null;
  58. foreach (var item in files)
  59. {
  60. if (item.Contains("IES\\base.json"))
  61. {
  62. string jsons = await System.IO.File.ReadAllTextAsync(item);
  63. jsons = jsons.Replace("\"Uncall\"", "0").Replace("Uncall", "0");
  64. lessonBase = jsons.ToObject<LessonBase>();
  65. var data = LessonETLService.GetBaseData(lessonBase);
  66. localStudents = data.studentLessonDatas;
  67. }
  68. if (item.Contains("IES\\TimeLine.json"))
  69. {
  70. string jsons = await System.IO.File.ReadAllTextAsync(item);
  71. timeLineData = jsons.ToObject<TimeLineData>();
  72. }
  73. if (item.Contains("IES\\Task.json"))
  74. {
  75. string jsons = await System.IO.File.ReadAllTextAsync(item);
  76. taskDatas = jsons.ToObject<List<TaskData>>();
  77. }
  78. if (item.Contains("IES\\SmartRating.json"))
  79. {
  80. string jsons = await System.IO.File.ReadAllTextAsync(item);
  81. smartRatingDatas = jsons.ToObject<List<SmartRatingData>>();
  82. }
  83. if (item.Contains("IES\\IRS.json"))
  84. {
  85. string jsons = await System.IO.File.ReadAllTextAsync(item);
  86. irsDatas = jsons.ToObject<List<IRSData>>();
  87. }
  88. if (item.Contains("IES\\Cowork.json"))
  89. {
  90. string jsons = await System.IO.File.ReadAllTextAsync(item);
  91. coworkDatas = jsons.ToObject<List<CoworkData>>();
  92. }
  93. try
  94. {
  95. if (item.Contains($"\\{id}\\Exam\\") && item.EndsWith("Exam.json"))
  96. {
  97. string examsFile = item;
  98. if (examsFile.EndsWith("Exam.json"))
  99. {
  100. ExamData? examData = null;
  101. string jsons = await System.IO.File.ReadAllTextAsync(item);
  102. jsons = jsons.Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1");
  103. examData = jsons.ToObject<ExamData>();
  104. if (examData != null && examData.exam.papers.IsNotEmpty())
  105. {
  106. string paperId = examData.exam.papers.First().id;
  107. string paperPath = $"{path}\\ExamPaper\\{paperId}\\index.json";
  108. string jsonp = await System.IO.File.ReadAllTextAsync(paperPath);
  109. LessonPaper lessonPaper = jsonp.ToObject<LessonPaper>();
  110. examData.paper = lessonPaper;
  111. examDatas.Add(examData);
  112. }
  113. }
  114. }
  115. }
  116. catch (Exception ex)
  117. {
  118. _logger.LogError(ex, ex.Message);
  119. }
  120. }
  121. if (lessonBase!=null && timeLineData!=null)
  122. {
  123. studentLessonDatas = localStudents.ToJsonString().ToObject<List<StudentLessonData>>();
  124. studentLessonDatas = LessonETLService.GetBaseInfo(lessonBase!, studentLessonDatas, id);
  125. studentLessonDatas = LessonETLService.GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas, id);
  126. studentLessonDatas = LessonETLService.GetCoworkData(lessonBase, timeLineData, coworkDatas, studentLessonDatas, id);
  127. studentLessonDatas = LessonETLService.GetExamData(lessonBase, timeLineData, examDatas, studentLessonDatas, Constant.objectiveTypes, id);
  128. studentLessonDatas = LessonETLService.GetSmartRatingData(lessonBase, timeLineData, smartRatingDatas, studentLessonDatas, id);
  129. studentLessonDatas = LessonETLService.GetTaskData(lessonBase, timeLineData, taskDatas, studentLessonDatas, id);
  130. var pickupData = LessonETLService.GetPickupData(lessonBase, timeLineData, studentLessonDatas, id);
  131. studentLessonDatas= pickupData.studentLessonDatas;
  132. var codeBools= LessonETLService.GetCodeBools(studentLessonDatas);
  133. await System.IO.File.WriteAllTextAsync(Path.Combine(path, $"student-analysis.json"), studentLessonDatas.ToJsonString());
  134. string jsons = await System.IO.File.ReadAllTextAsync($"{lessonPath}\\analysis\\analysis.json");
  135. LessonDataAnalysisCluster lessonDataAnalysis = jsons.ToObject<LessonDataAnalysisCluster>();
  136. var studentLessons = LessonETLService.ProcessStudentDataV2(studentLessonDatas, lessonDataAnalysis,codeBools);
  137. XmlDocument xmlDocument = new XmlDocument();
  138. var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  139. xmlDocument.Load($"{runtimePath}\\summary.xml");
  140. await LessonETLService.ExportToExcelLocal(studentLessons, $"{path}\\analysis.xlsx", xmlDocument);
  141. }
  142. }
  143. return Ok();
  144. }
  145. /// <summary>
  146. ///
  147. /// </summary>
  148. /// <param name="json"></param>
  149. /// <returns></returns>
  150. //[HttpPost("process-fix-history-students")]
  151. //public async Task<IActionResult> ProcessFixHistoryStudents(JsonElement json)
  152. //{
  153. // string? lessonBasePath = _configuration.GetValue<string>("LessonPath");
  154. // string studentsPath = $"{lessonBasePath}\\students";
  155. // List<string> studentsPs = FileHelper.ListAllFiles(studentsPath);
  156. // string? pathLessons = $"{lessonBasePath}\\lessons";
  157. // List<string> filesLessons = FileHelper.ListAllFiles(pathLessons, "-local.json");
  158. // int index = 0;
  159. // foreach (var stu in studentsPs)
  160. // {
  161. // string stujson = await System.IO.File.ReadAllTextAsync(stu);
  162. // var studentSemester = stujson.ToObject<StudentSemesterRecord>();
  163. // await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(studentSemester, new PartitionKey(studentSemester.code));
  164. // index++;
  165. // }
  166. // return Ok();
  167. //}
  168. /// <summary>
  169. ///
  170. /// </summary>
  171. /// <param name="json"></param>
  172. /// <returns></returns>
  173. [HttpPost("process-history-students")]
  174. public async Task<IActionResult> ProcessHistoryStudents(JsonElement json)
  175. {
  176. string? lessonBasePath = _configuration.GetValue<string>("LessonPath");
  177. string? pathLessons = $"{lessonBasePath}\\lessons";
  178. string? pathAnalysis = $"{lessonBasePath}\\analysis";
  179. string jsons = await System.IO.File.ReadAllTextAsync($"{pathAnalysis}\\analysis.json");
  180. LessonDataAnalysisCluster lessonDataAnalysis = jsons.ToObject<LessonDataAnalysisCluster>();
  181. List<string> filesLessons = FileHelper.ListAllFiles(pathLessons, "-local.json");
  182. var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  183. XmlDocument xmlDocument = new XmlDocument();
  184. xmlDocument.Load($"{runtimePath}\\summary.xml");
  185. List<StudentSemesterRecord> students = new List<StudentSemesterRecord>();
  186. List<School> schools = new List<School>();
  187. // await Parallel.ForEachAsync(filesLessons, async (fileLesson, _) =>
  188. List<string> localIds = new List<string>();
  189. foreach (var file in filesLessons)
  190. {
  191. if (file.EndsWith("-local.json"))
  192. {
  193. string lessonId = file.Split("\\").Last().Replace("-local.json", "");
  194. localIds.Add(lessonId);
  195. }
  196. }
  197. long stime = 1690819200000;//2023-08-01 00:00:00
  198. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
  199. .GetList<LessonRecord>($"SELECT value c FROM c where c.startTime>{stime} and c.expire<=0 and c.status<>404 and c.duration>300 and c.pk='LessonRecord' ", $"LessonRecord-ydzt");
  200. List<string> ignore = new List<string>() { "PgJump", "PgRcv", "PgAdd" };
  201. int index = 0;
  202. if (resultSchool.list.IsNotEmpty())
  203. {
  204. List<StudentSemesterRecord> studentSemesterRecords= new List<StudentSemesterRecord>();
  205. List<OverallEducation> overallEducations= new List<OverallEducation>();
  206. List<Student> studentsBase = new List<Student>();
  207. await foreach (var item in LessonETLService.GetLessonLocal(resultSchool.list, localIds, _azureStorage, pathLessons))
  208. {
  209. string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(item.lessonRecord.startTime).ToString("yyyyMM");
  210. TechCount techCount = null;
  211. if (item.lessonBase!=null && item.lessonBase.student!=null)
  212. {
  213. techCount = new TechCount
  214. {
  215. lessonId=item.lessonRecord?.id,
  216. examCount = item.examDatas.Count,
  217. taskCount = item.taskDatas.Count,
  218. irsCount = item.irsDatas.Count,
  219. coworkCount = item.coworkDatas.Count,
  220. smartRatingCount =item.smartRatingDatas.Count,
  221. timeCount=item.sokratesDatas.Where(x => !ignore.Contains(x.Event) && !x.Event.Contains("End", StringComparison.OrdinalIgnoreCase)).GroupBy(x => x.Event).Select(x => new CodeLong() { code=x.Key, value= x.ToList().Count }).ToList()
  222. };
  223. }
  224. else
  225. {
  226. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json");
  227. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-count.json");
  228. }
  229. var studata = await LessonETLService.DoStudentLessonData(Constant.objectiveTypes, _azureStorage, item, _dingDing, _azureCosmos.GetCosmosClient(), "China", _azureRedis, studentSemesterRecords, overallEducations, lessonDataAnalysis, studentsBase, schools);
  230. if (techCount!= null)
  231. {
  232. if (studata.codeBools.FindAll(x => x.value).IsNotEmpty())
  233. {
  234. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord.id}-count.json", techCount.ToJsonString());
  235. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json", item.ToJsonString());
  236. }
  237. else {
  238. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json");
  239. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-count.json");
  240. }
  241. }
  242. index++;
  243. }
  244. await Parallel.ForEachAsync(studentSemesterRecords, async (studentSemester, cancellationToken) =>
  245. {
  246. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(studentSemester, new PartitionKey(studentSemester.code));
  247. });
  248. await Parallel.ForEachAsync(overallEducations, async (overallEducation, cancellationToken) =>
  249. {
  250. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(overallEducation, partitionKey: new PartitionKey(overallEducation.code));
  251. string key = $"OverallEducation:{overallEducation.schoolCode}:{overallEducation.periodId}:{overallEducation.year}:{overallEducation.semesterId}:{overallEducation?.classId}";
  252. await _azureRedis.GetRedisClient(8).HashSetAsync(key, overallEducation.studentId, overallEducation.ToJsonString());
  253. await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 *24, 0, 0));
  254. });
  255. }
  256. return Ok();
  257. }
  258. /// <summary>
  259. /// 课例数据ETL处理过程
  260. /// </summary>
  261. /// <param name="json"></param>
  262. /// <returns></returns>
  263. [HttpPost("process-history")]
  264. public async Task<IActionResult> ProcessHistory(JsonElement json)
  265. {
  266. List<string> localIds = new List<string>();
  267. string? lessonBasePath = _configuration.GetValue<string>("LessonPath");
  268. string? pathLessons = $"{lessonBasePath}\\lessons";
  269. string? pathAnalysis = $"{lessonBasePath}\\analysis";
  270. var filesLessons = FileHelper.ListAllFiles(pathLessons);
  271. foreach (var file in filesLessons)
  272. {
  273. if (file.EndsWith("-local.json"))
  274. {
  275. string lessonId = file.Split("\\").Last().Replace("-local.json", "");
  276. localIds.Add(lessonId);
  277. }
  278. }
  279. bool loadLocal = true;
  280. List<LessonDataAnalysisMonth> lessonDataAnalysisMonths = new List<LessonDataAnalysisMonth>();
  281. var filesAnalysis = FileHelper.ListAllFiles(pathAnalysis);
  282. long stime = 1690819200000;//2023-08-01 00:00:00
  283. foreach (var file in filesAnalysis)
  284. {
  285. //读取每月的数据
  286. if (file.EndsWith("-m-analysis.json"))
  287. {
  288. string jsons = await System.IO.File.ReadAllTextAsync(file);
  289. LessonDataAnalysisMonth lessonDataAnalysis = jsons.ToObject<LessonDataAnalysisMonth>();
  290. lessonDataAnalysisMonths.Add(lessonDataAnalysis);
  291. }
  292. }
  293. if (lessonDataAnalysisMonths.IsNotEmpty())
  294. {
  295. var maxUpdateTime = lessonDataAnalysisMonths.Max(x => x.updateTime);
  296. if (maxUpdateTime>0)
  297. {
  298. //更新周期是一周
  299. if (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()- maxUpdateTime>604800000)
  300. {
  301. stime=maxUpdateTime;
  302. loadLocal =true;
  303. }
  304. else
  305. {
  306. stime=maxUpdateTime;
  307. loadLocal=false;
  308. }
  309. }
  310. }
  311. HashSet<string> yearMonth = new HashSet<string>();
  312. long newest = 0;
  313. bool force = false;
  314. if ((json.TryGetProperty("force", out JsonElement _force)&& _force.ValueKind.Equals(JsonValueKind.True)))
  315. {
  316. force= _force.GetBoolean();
  317. }
  318. // if (loadLocal ||force)
  319. {
  320. List<LessonRecord> lessonRecords = new List<LessonRecord>();
  321. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
  322. .GetList<LessonRecord>($"SELECT value c FROM c where c.startTime>={stime} and c.expire<=0 and c.status<>404 and c.duration>300 and c.pk='LessonRecord' ", null);
  323. if (resultSchool.list.IsNotEmpty())
  324. {
  325. newest= resultSchool.list.Max(x => x.startTime);
  326. lessonRecords.AddRange(resultSchool.list);
  327. }
  328. else
  329. {
  330. newest=stime;
  331. }
  332. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  333. .GetList<LessonRecord>($"SELECT value c FROM c where c.startTime>={stime} and c.expire<=0 and c.status<>404 and c.duration>300 and c.pk='LessonRecord' ", null);
  334. if (resultTeacher.list.IsNotEmpty())
  335. {
  336. long max = resultTeacher.list.Max(x => x.startTime);
  337. if (max<newest)
  338. {
  339. newest=max;
  340. }
  341. lessonRecords.AddRange(resultTeacher.list);
  342. }
  343. List<string> ignore = new List<string>() { "PgJump", "PgRcv", "PgAdd" };
  344. if (lessonRecords.IsNotEmpty())
  345. {
  346. await foreach (var item in LessonETLService.GetLessonLocal(lessonRecords, localIds, _azureStorage, pathLessons))
  347. {
  348. string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(item.lessonRecord.startTime).ToString("yyyyMM");
  349. if (item.lessonBase!=null && item.lessonBase.student!=null)
  350. {
  351. TechCount techCount = new TechCount
  352. {
  353. lessonId=item.lessonRecord?.id,
  354. examCount = item.examDatas.Count,
  355. taskCount = item.taskDatas.Count,
  356. irsCount = item.irsDatas.Count,
  357. coworkCount = item.coworkDatas.Count,
  358. smartRatingCount =item.smartRatingDatas.Count,
  359. timeCount=item.sokratesDatas.Where(x => !ignore.Contains(x.Event) && !x.Event.Contains("End", StringComparison.OrdinalIgnoreCase)).GroupBy(x => x.Event).Select(x => new CodeLong() { code=x.Key, value= x.ToList().Count }).ToList()
  360. };
  361. if (techCount.examCount>0 || techCount.taskCount>0 ||techCount.irsCount>0 || techCount.coworkCount>0 || techCount.smartRatingCount>0)
  362. {
  363. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord.id}-count.json", techCount.ToJsonString());
  364. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json", item.ToJsonString());
  365. }
  366. }
  367. else
  368. {
  369. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json");
  370. System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-count.json");
  371. }
  372. }
  373. }
  374. List<TechCount> techCounts = new List<TechCount>();
  375. filesLessons = FileHelper.ListAllFiles(pathLessons, "-local.json");
  376. await foreach (var item in LessonETLService.GetTeachCount(lessonRecords, filesLessons, pathLessons, ignore, Constant.objectiveTypes, _azureStorage, force))
  377. {
  378. techCounts.Add(item);
  379. }
  380. await LessonETLService.GenAnalysisData(pathAnalysis, newest, techCounts,_azureStorage);
  381. }
  382. return Ok(new { yearMonth });
  383. }
  384. }
  385. }