LessonRecordController.cs 119 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165
  1. using Azure.Storage.Blobs.Models;
  2. using Microsoft.AspNetCore.Mvc;
  3. using System.Text.Json;
  4. using TEAMModelOS.SDK;
  5. using TEAMModelOS.SDK.DI;
  6. using TEAMModelOS.SDK.Models;
  7. using TEAMModelOS.SDK.Extension;
  8. using StackExchange.Redis;
  9. using System.Text.RegularExpressions;
  10. using System.Linq;
  11. using static TEAMModelOS.SDK.Models.Cosmos.Student.StudentAnalysis;
  12. using static Microsoft.Azure.Amqp.Serialization.SerializableType;
  13. using Microsoft.AspNetCore.DataProtection.KeyManagement;
  14. using System.Drawing;
  15. using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
  16. using System.Diagnostics;
  17. using Microsoft.OData.UriParser;
  18. using HTEX.Test.Service;
  19. using System.IO;
  20. using static Pipelines.Sockets.Unofficial.SocketConnection;
  21. using static HTEX.Test.Controllers.LessonRecordController;
  22. using System.Collections.Generic;
  23. using static System.Formats.Asn1.AsnWriter;
  24. namespace HTEX.Test.Controllers
  25. {
  26. [ApiController]
  27. [Route("lesson-record")]
  28. public class LessonRecordController : ControllerBase
  29. {
  30. private readonly ILogger<LessonRecordController> _logger;
  31. private readonly AzureCosmosFactory _azureCosmos;
  32. private readonly AzureStorageFactory _azureStorage;
  33. private readonly List<string> objectiveTypes=new List<string>(){ "single", "multiple", "sortmultiple" , "judge" };
  34. private readonly double minRank = 1;
  35. private readonly double maxRank = 100;
  36. public LessonRecordController(ILogger<LessonRecordController> logger, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage)
  37. {
  38. _logger = logger;
  39. _azureCosmos = azureCosmos;
  40. _azureStorage = azureStorage;
  41. }
  42. [HttpPost("process-local")]
  43. public async Task<IActionResult> ProcessLocal(JsonElement json)
  44. {
  45. List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
  46. string? id=json.GetProperty("id").GetString();
  47. string path = $"C:\\Users\\CrazyIter\\Downloads\\{id}";
  48. if (!string.IsNullOrWhiteSpace(id))
  49. {
  50. var files = ListAllFiles(path);
  51. // var sampleJson =System.IO. File.ReadAllTextAsync(path);
  52. LessonBase? lessonBase = null;
  53. List<LocalStudent> localStudents = new List<LocalStudent>();
  54. List<TaskData> taskDatas = new List<TaskData>();
  55. List<SmartRatingData> smartRatingDatas = new List<SmartRatingData>();
  56. List<IRSData> irsDatas = new List<IRSData>();
  57. List<CoworkData> coworkDatas = new List<CoworkData>();
  58. List<ExamData> examDatas = new List<ExamData>();
  59. TimeLineData? timeLineData = null;
  60. foreach (var item in files)
  61. {
  62. if (item.Contains("IES\\base.json"))
  63. {
  64. string jsons = await System.IO.File.ReadAllTextAsync(item);
  65. jsons=jsons.Replace("\"Uncall\"", "0").Replace("Uncall", "0");
  66. lessonBase = jsons.ToObject<LessonBase>();
  67. var data = GetBaseData( lessonBase);
  68. localStudents=data.studentLessonDatas;
  69. }
  70. if (item.Contains("IES\\TimeLine.json"))
  71. {
  72. string jsons = await System.IO.File.ReadAllTextAsync(item);
  73. timeLineData = jsons.ToObject<TimeLineData>();
  74. }
  75. if (item.Contains("IES\\Task.json"))
  76. {
  77. string jsons = await System.IO.File.ReadAllTextAsync(item);
  78. taskDatas = jsons.ToObject<List<TaskData>>();
  79. }
  80. if (item.Contains("IES\\SmartRating.json"))
  81. {
  82. string jsons = await System.IO.File.ReadAllTextAsync(item);
  83. smartRatingDatas = jsons.ToObject<List<SmartRatingData>>();
  84. }
  85. if (item.Contains("IES\\IRS.json"))
  86. {
  87. string jsons = await System.IO.File.ReadAllTextAsync(item);
  88. irsDatas = jsons.ToObject<List<IRSData>>();
  89. }
  90. if (item.Contains("IES\\Cowork.json"))
  91. {
  92. string jsons = await System.IO.File.ReadAllTextAsync(item);
  93. coworkDatas = jsons.ToObject<List<CoworkData>>();
  94. }
  95. try
  96. {
  97. if (item.Contains($"\\{id}\\Exam\\") && item.EndsWith("Exam.json"))
  98. {
  99. string examsFile = item;
  100. if (examsFile.EndsWith("Exam.json"))
  101. {
  102. ExamData? examData = null;
  103. string jsons = await System.IO.File.ReadAllTextAsync(item);
  104. jsons= jsons.Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1");
  105. examData = jsons.ToObject<ExamData>();
  106. if (examData!=null && examData.exam.papers.IsNotEmpty())
  107. {
  108. string paperId = examData.exam.papers.First().id;
  109. string paperPath = $"{path}\\ExamPaper\\{paperId}\\index.json";
  110. string jsonp = await System.IO.File.ReadAllTextAsync(paperPath);
  111. LessonPaper lessonPaper = jsonp.ToObject<LessonPaper>();
  112. examData.paper = lessonPaper;
  113. examDatas.Add(examData);
  114. }
  115. }
  116. }
  117. }
  118. catch (Exception ex)
  119. {
  120. _logger.LogError(ex, ex.Message);
  121. }
  122. }
  123. if (lessonBase!=null && timeLineData!=null)
  124. {
  125. studentLessonDatas = localStudents.ToJsonString().ToObject<List<StudentLessonData>>();
  126. studentLessonDatas = GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas, id);
  127. studentLessonDatas = GetCoworkData(lessonBase, timeLineData, coworkDatas, studentLessonDatas);
  128. studentLessonDatas = GetExamData(lessonBase, timeLineData, examDatas, studentLessonDatas);
  129. studentLessonDatas = GetSmartRatingData(lessonBase, timeLineData, smartRatingDatas, studentLessonDatas, id);
  130. studentLessonDatas = GetTaskData(lessonBase, timeLineData, taskDatas, studentLessonDatas);
  131. System.IO.File.WriteAllText(Path.Combine(path, $"{id}-stu.json"), studentLessonDatas.ToJsonString());
  132. string jsons = await System.IO.File.ReadAllTextAsync("analysis.json");
  133. LessonDataAnalysis lessonDataAnalysis = jsons.ToObject<LessonDataAnalysis>();
  134. }
  135. return Ok( new { studentLessonDatas });
  136. }return Ok();
  137. }
  138. [HttpPost("process-preview")]
  139. public async Task<IActionResult> ProcessPreview(JsonElement json)
  140. {
  141. string path = $"F:\\lesson-local";
  142. var files = ListAllFiles(path);
  143. List<TechCount> techCounts = new List<TechCount>();
  144. //var files_count = files.Where(x => x.EndsWith("548724334458441728-count.json")).Take(100).ToList();
  145. /*
  146. "examCount": 0,
  147. "taskCount": 0,
  148. "irsCount": 1,
  149. "coworkCount": 0,
  150. "smartRatingCount": 0,
  151. "timeCount":
  152. */
  153. bool loadLocal = true;
  154. LessonDataAnalysis lessonDataAnalysis = new LessonDataAnalysis();
  155. var files_anlysis = files.Where(x => x.EndsWith("analysis.json")).Take(1).FirstOrDefault();
  156. if (files_anlysis!=null) {
  157. string jsons = await System.IO.File.ReadAllTextAsync(files_anlysis);
  158. lessonDataAnalysis= jsons.ToObject<LessonDataAnalysis>();
  159. loadLocal=false;
  160. }
  161. if (loadLocal) {
  162. foreach (var item in files)
  163. {
  164. if (item.EndsWith("count.json"))
  165. {
  166. string jsons = await System.IO.File.ReadAllTextAsync(item);
  167. TechCount count = jsons.ToObject<TechCount>();
  168. count.interactNormalCount=count.irsCount;
  169. count.lessonId=item.Replace("-count.json", "").Replace(path, "").Replace("\\", "");
  170. string localjson = await System.IO.File.ReadAllTextAsync($"{path}\\{count.lessonId}-local.json");
  171. var lessonLocal = localjson.ToObject<LessonLocal>();
  172. if (lessonLocal?.lessonBase?.summary!=null) {
  173. count.smartRatingCountBase=lessonLocal.lessonBase.summary.smartRatingCount;
  174. count.irsCountBase=lessonLocal.lessonBase.summary.interactionCount;
  175. count.taskCountBase=lessonLocal.lessonBase.summary.collateTaskCount;
  176. count.coworkCountBase=lessonLocal.lessonBase.summary.coworkTaskCount;
  177. count.examCountBase=lessonLocal.lessonBase.summary.examCount;
  178. count.interactNormalCountBase= count.interactNormalCount;
  179. }
  180. if (lessonLocal?.lessonBase?.report?.clientSummaryList!=null)
  181. {
  182. count.pscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x=>x.score>0).Select(x => x.score);
  183. count.gscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.groupScore>0).Select(x => x.groupScore);
  184. count.tscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.interactScore>0).Select(x => x.interactScore);
  185. }
  186. await System.IO.File.WriteAllTextAsync(item, count.ToJsonString());
  187. techCounts.Add(count);
  188. }
  189. }
  190. Console.WriteLine($"techCounts:{techCounts.Count}");
  191. //标准差偏差N倍,视为异常数据
  192. int thresholdMultiplier = 2;
  193. lessonDataAnalysis.pscore = techCounts.SelectMany(x =>x.pscore);
  194. lessonDataAnalysis.pscore= CleanDataBySDThreshold(lessonDataAnalysis.pscore, thresholdMultiplier).OrderBy(x => x);
  195. List<KeyValuePair<double, List<double>>> clustersDataPscore = new();
  196. var clusterPscore = KMeansService.KMeans(lessonDataAnalysis.pscore.Select(x => x).OrderBy(x => x));
  197. foreach (var item in clusterPscore)
  198. {
  199. Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}");
  200. }
  201. Console.WriteLine($"avg: {lessonDataAnalysis.pscore.Average()}");
  202. foreach (var s in clusterPscore.OrderBy(x => x.Key))
  203. {
  204. clustersDataPscore.Add(s);
  205. }
  206. lessonDataAnalysis.clustersPscore=clustersDataPscore;
  207. lessonDataAnalysis.gscore = techCounts.SelectMany(x => x.gscore);
  208. lessonDataAnalysis.gscore= CleanDataBySDThreshold(lessonDataAnalysis.gscore, thresholdMultiplier).OrderBy(x => x);
  209. List<KeyValuePair<double, List<double>>> clustersDataGscore = new();
  210. var clusterGscore = KMeansService.KMeans(lessonDataAnalysis.gscore.Select(x => x).OrderBy(x => x));
  211. foreach (var item in clusterGscore)
  212. {
  213. Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}");
  214. }
  215. Console.WriteLine($"avg: {lessonDataAnalysis.gscore.Average()}");
  216. foreach (var s in clusterGscore.OrderBy(x => x.Key))
  217. {
  218. clustersDataGscore.Add(s);
  219. }
  220. lessonDataAnalysis.clustersGscore=clustersDataGscore;
  221. lessonDataAnalysis.tscore = techCounts.SelectMany(x => x.tscore);
  222. lessonDataAnalysis.tscore= CleanDataBySDThreshold(lessonDataAnalysis.tscore, thresholdMultiplier).OrderBy(x => x);
  223. List<KeyValuePair<double, List<double>>> clustersDataTscore = new();
  224. var clusterTscore = KMeansService.KMeans(lessonDataAnalysis.tscore.Select(x => x).OrderBy(x => x));
  225. foreach (var item in clusterTscore)
  226. {
  227. Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}");
  228. }
  229. Console.WriteLine($"avg: {lessonDataAnalysis.tscore.Average()}");
  230. foreach (var s in clusterTscore.OrderBy(x => x.Key))
  231. {
  232. clustersDataTscore.Add(s);
  233. }
  234. lessonDataAnalysis.clustersTscore=clustersDataTscore;
  235. lessonDataAnalysis.cowork = techCounts.Where(x => x.coworkCount>0).Select(x => (double)x.coworkCount);
  236. lessonDataAnalysis.cowork= CleanDataBySDThreshold(lessonDataAnalysis.cowork, thresholdMultiplier);
  237. lessonDataAnalysis.coworkBase = techCounts.Where(x => x.coworkCountBase>0).Select(x => (double)x.coworkCountBase);
  238. lessonDataAnalysis.coworkBase= CleanDataBySDThreshold(lessonDataAnalysis.coworkBase, thresholdMultiplier);
  239. lessonDataAnalysis.task = techCounts.Where(x => x.taskCount > 0).Select(x => (double)x.taskCount);
  240. lessonDataAnalysis.task= CleanDataBySDThreshold(lessonDataAnalysis.task, thresholdMultiplier);
  241. lessonDataAnalysis.taskBase = techCounts.Where(x => x.taskCountBase > 0).Select(x => (double)x.taskCountBase);
  242. lessonDataAnalysis.taskBase = CleanDataBySDThreshold(lessonDataAnalysis.taskBase, thresholdMultiplier);
  243. lessonDataAnalysis.exam = techCounts.Where(x => x.examCount > 0).Select(x => (double)x.examCount);
  244. lessonDataAnalysis.exam = CleanDataBySDThreshold(lessonDataAnalysis.exam, thresholdMultiplier);
  245. lessonDataAnalysis.examBase = techCounts.Where(x => x.examCountBase > 0).Select(x => (double)x.examCountBase);
  246. lessonDataAnalysis.examBase = CleanDataBySDThreshold(lessonDataAnalysis.examBase, thresholdMultiplier);
  247. lessonDataAnalysis.smartRating = techCounts.Where(x => x.smartRatingCount > 0).Select(x => (double)x.smartRatingCount);
  248. lessonDataAnalysis.smartRating = CleanDataBySDThreshold(lessonDataAnalysis.smartRating, thresholdMultiplier);
  249. lessonDataAnalysis.smartRatingBase = techCounts.Where(x => x.smartRatingCountBase > 0).Select(x => (double)x.smartRatingCountBase);
  250. lessonDataAnalysis.smartRatingBase = CleanDataBySDThreshold(lessonDataAnalysis.smartRatingBase, thresholdMultiplier);
  251. lessonDataAnalysis.irs = techCounts.Where(x => x.irsCount > 0).Select(x => (double)x.irsCount);
  252. lessonDataAnalysis.irs = CleanDataBySDThreshold(lessonDataAnalysis.irs, thresholdMultiplier);
  253. lessonDataAnalysis.interactNormal = techCounts.Where(x => x.interactNormalCount > 0).Select(x => (double)x.interactNormalCount);
  254. Console.WriteLine($"interactNormal{lessonDataAnalysis.interactNormal.Count()}");
  255. lessonDataAnalysis.interactNormal= CleanDataBySDThreshold(lessonDataAnalysis.interactNormal, thresholdMultiplier).OrderBy(x => x);
  256. Console.WriteLine($"interactNormal{lessonDataAnalysis.interactNormal.Count()}");
  257. var tcount = techCounts.Where(x => x.coworkCount > 0 || x.taskCount>0 || x.interactNormalCount>0|| x.examCount>0 || x.smartRatingCount>0);
  258. double coworkWeight = lessonDataAnalysis.cowork.Count()*1.0/tcount.Count();
  259. lessonDataAnalysis.coworkRate=coworkWeight;
  260. double taskWeight = lessonDataAnalysis.task.Count()*1.0/tcount.Count();
  261. lessonDataAnalysis.taskRate=taskWeight;
  262. double interactWeight = lessonDataAnalysis.interactNormal.Count()*1.0/tcount.Count();
  263. lessonDataAnalysis.interactRate=interactWeight;
  264. double examWeight = lessonDataAnalysis. exam.Count()*1.0/tcount.Count();
  265. lessonDataAnalysis.examRate=examWeight;
  266. double smartRatingWeight = lessonDataAnalysis. smartRating.Count()*1.0/tcount.Count();
  267. lessonDataAnalysis.smartRatingRate=smartRatingWeight;
  268. List<KeyValuePair<double, List<int>>> clustersDataInteract = new();
  269. var clusterInteract = KMeansService.KMeans(lessonDataAnalysis.interactNormal.Select(x => (int)x).OrderBy(x => x));
  270. foreach (var item in clusterInteract)
  271. {
  272. Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}");
  273. }
  274. foreach (var s in clusterInteract.OrderBy(x => x.Key))
  275. {
  276. clustersDataInteract.Add(s);
  277. }
  278. lessonDataAnalysis.clustersInteract=clustersDataInteract;
  279. var groups = techCounts.SelectMany(x => x.timeCount).GroupBy(x => x.code).Select(x => new { key = x.Key, list = x.ToList() });
  280. Dictionary<string, IEnumerable<double>> techDict = new Dictionary<string, IEnumerable<double>>();
  281. foreach (var group in groups)
  282. {
  283. var array = group.list.Where(x => x.value>0).Select(x => (double)x.value);
  284. array = CleanDataBySDThreshold(array, thresholdMultiplier);
  285. techDict.Add(group.key, array);
  286. }
  287. System.IO. File.WriteAllText(Path.Combine(path, "analysis.json"), lessonDataAnalysis.ToJsonString());
  288. }
  289. // List<LessonLocal> lessons = new List<LessonLocal>();
  290. var files_local = files.Where(x=>x.EndsWith("557838358030716928-local.json")).Take(100).ToList();
  291. List<List<StudentLessonData>> studentLessons = new List<List<StudentLessonData>>();
  292. foreach (var item in files)
  293. {
  294. if (item.EndsWith("local.json"))
  295. {
  296. string jsons = await System.IO.File.ReadAllTextAsync(item);
  297. LessonLocal lesson = jsons.ToObject<LessonLocal>();
  298. // lessons.Add(lesson);
  299. List<StudentLessonData> studentLessonDatas = lesson.studentLessonDatas.ToJsonString().ToObject<List<StudentLessonData>>();
  300. studentLessonDatas = GetIRSData(lesson.lessonBase!, lesson.timeLineData!, lesson.irsDatas, studentLessonDatas, lesson.examDatas, item);
  301. studentLessonDatas = GetCoworkData(lesson.lessonBase!, lesson.timeLineData!, lesson.coworkDatas, studentLessonDatas);
  302. studentLessonDatas = GetExamData(lesson.lessonBase!, lesson.timeLineData!, lesson.examDatas, studentLessonDatas);
  303. studentLessonDatas = GetSmartRatingData(lesson.lessonBase!, lesson.timeLineData!, lesson.smartRatingDatas, studentLessonDatas, item);
  304. studentLessonDatas = GetTaskData(lesson.lessonBase!, lesson.timeLineData!, lesson.taskDatas, studentLessonDatas);
  305. var techCount = techCounts.Find(x => !string.IsNullOrWhiteSpace(x.lessonId) && !string.IsNullOrWhiteSpace(lesson?.lessonRecord?.id) && x.lessonId.Equals(lesson.lessonRecord.id));
  306. int sumUpload =0;
  307. int taskCount = 0;
  308. int maxUpload = 0;
  309. HashSet<string> pickUp=new HashSet<string>();
  310. foreach (var stu in studentLessonDatas)
  311. {
  312. var countS = stu.taskRecord.itemRecords.Where(x => x.optCount>0);
  313. if (countS.Count()>0)
  314. {
  315. int stuUploadmax = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Max(x => x.optCount);
  316. if (stuUploadmax> maxUpload)
  317. {
  318. maxUpload=stuUploadmax;
  319. }
  320. }
  321. int stuUpload= stu.taskRecord.itemRecords.Where(x => x.optCount>0).Sum(x=>x.optCount);
  322. sumUpload+=stuUpload;
  323. if (stu.taskRecord.itemRecords.Count()> taskCount)
  324. {
  325. taskCount=stu.taskRecord.itemRecords.Count();
  326. }
  327. var stu_scores = stu.coworkRecord.itemRecords.Where(x => x.itemScore>0).Select(x=>x.itemScore);
  328. if (stu_scores!=null && stu_scores.Count()>0)
  329. {
  330. lessonDataAnalysis.stuCowork.AddRange(stu_scores);
  331. }
  332. var grp_scores = stu.group_coworkScore.Where(x => x>0);
  333. if (grp_scores!=null && grp_scores.Count()>0)
  334. {
  335. lessonDataAnalysis.groupCowork.AddRange(grp_scores);
  336. }
  337. if (stu.pickups.IsNotEmpty())
  338. {
  339. foreach (var pickup in stu.pickups)
  340. {
  341. pickUp.Add(pickup);
  342. }
  343. }
  344. }
  345. if (studentLessonDatas.Count>0&& taskCount>0 && maxUpload>0)
  346. {
  347. var avgUpload= sumUpload*1.0/(studentLessonDatas.Count *taskCount);
  348. lessonDataAnalysis.upload.Add(new List<double>() { avgUpload, maxUpload });
  349. }
  350. if (pickUp.Count>0)
  351. {
  352. lessonDataAnalysis.pickup.Add(pickUp.ToList());
  353. }
  354. System.IO.File.WriteAllText(Path.Combine(path, $"{lesson.lessonRecord!.id}-stu.json"), studentLessonDatas.ToJsonString());
  355. studentLessons.Add(studentLessonDatas);
  356. }
  357. }
  358. System.IO.File.WriteAllText(Path.Combine(path, "analysis.json"), lessonDataAnalysis.ToJsonString());
  359. return Ok(new
  360. {
  361. });
  362. }
  363. [HttpPost("process-history")]
  364. public async Task<IActionResult> ProcessHistory(JsonElement json)
  365. {
  366. List<string> localIds = new List<string>();
  367. string path = "F:\\lesson-local";
  368. var files = ListAllFiles(path);
  369. foreach (var file in files)
  370. {
  371. if (file.EndsWith("-count.json"))
  372. {
  373. string lessonId = file.Replace("-count.json", "").Replace(path, "").Replace("\\", "");
  374. localIds.Add(lessonId);
  375. }
  376. }
  377. //1709222400000 2024.3.1
  378. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
  379. .GetList<LessonRecord>($"SELECT value c FROM School AS c where c.startTime>1706716800000 and c.expire<=0 and c.status<>404 and c.duration>300 and c.pk='LessonRecord' and c.school<>'hbcn' and c.school<>'habook' and (c.tLevel>0 or c.pLevel>0) ", null);
  380. if (result.list.IsNotEmpty())
  381. {
  382. List<string> ignore = new List<string>() { "PgJump", "PgRcv", "PgAdd" };
  383. Stopwatch stopwatch = Stopwatch.StartNew();
  384. //stopwatch.Start();
  385. //await GetLessData(result, ignore);
  386. //stopwatch.Stop();
  387. //Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
  388. //stopwatch.Reset();
  389. stopwatch.Start();
  390. await foreach (var item in GetLessonLocal(result.list, localIds))
  391. {
  392. if (item.lessonBase!=null && item.lessonBase.student!=null)
  393. {
  394. TechCount techCount = new TechCount
  395. {
  396. lessonId=item.lessonRecord?.id,
  397. examCount = item.examDatas.Count,
  398. taskCount = item.taskDatas.Count,
  399. irsCount = item.irsDatas.Count,
  400. coworkCount = item.coworkDatas.Count,
  401. smartRatingCount =item.smartRatingDatas.Count,
  402. 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()
  403. };
  404. await System.IO.File.WriteAllTextAsync($"{path}\\{item.lessonRecord!.id}-local.json", item.ToJsonString());
  405. await System.IO.File.WriteAllTextAsync($"{path}\\{item.lessonRecord.id}-count.json", techCount.ToJsonString());
  406. }
  407. else {
  408. Console.WriteLine($"没有课例id的数据:{item.lessonRecord?.id}");
  409. System.IO.File.Delete($"{path}\\{item.lessonRecord!.id}-local.json");
  410. System.IO.File.Delete($"{path}\\{item.lessonRecord!.id}-count.json");
  411. }
  412. }
  413. stopwatch.Stop();
  414. Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
  415. }
  416. return Ok("Lesson records");
  417. }
  418. public static List<string> ListAllFiles(string directoryPath)
  419. {
  420. List<string> filePaths = new List<string>();
  421. DirectoryInfo dirInfo = new DirectoryInfo(directoryPath);
  422. // 获取目录下的所有文件(包括子目录中的文件)
  423. FileInfo[] files = dirInfo.GetFiles("*", SearchOption.AllDirectories);
  424. foreach (FileInfo file in files)
  425. {
  426. filePaths.Add(file.FullName);
  427. // Console.WriteLine(file.FullName);
  428. }
  429. return filePaths;
  430. }
  431. public class LessonDataAnalysis
  432. {
  433. public long updateTime { get; set; }
  434. public IEnumerable<double> cowork { get; set; } = new List<double>();
  435. public IEnumerable<double> coworkBase { get; set; } = new List<double>();
  436. public IEnumerable<double> task { get; set; } = new List<double>();
  437. public IEnumerable<double> taskBase { get; set; } = new List<double>();
  438. public IEnumerable<double> exam { get; set; } = new List<double>();
  439. public IEnumerable<double> examBase { get; set; } = new List<double>();
  440. public IEnumerable<double> smartRating { get; set; } = new List<double>();
  441. public IEnumerable<double> smartRatingBase { get; set; } = new List<double>();
  442. public IEnumerable<double> irs { get; set; } = new List<double>();
  443. public IEnumerable<double> interactNormal { get; set; } = new List<double>();
  444. public List<KeyValuePair<double, List<int>>> clustersInteract { get; set; } = new List<KeyValuePair<double, List<int>>>();
  445. public List<KeyValuePair<double, List<double>>> clustersPscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  446. public List<KeyValuePair<double, List<double>>> clustersTscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  447. public List<KeyValuePair<double, List<double>>> clustersGscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  448. /// <summary>
  449. /// 个人计分
  450. /// </summary>
  451. public IEnumerable<double> pscore { get; set; } = new List<double>();
  452. /// <summary>
  453. /// 小组计分
  454. /// </summary>
  455. public IEnumerable<double> gscore { get; set; } = new List<double>();
  456. /// <summary>
  457. /// 互动计分
  458. /// </summary>
  459. public IEnumerable<double> tscore { get; set; } = new List<double>();
  460. /// <summary>
  461. /// 作品上传数
  462. /// </summary>
  463. public List<List<double>> upload { get; set; } = new List<List<double>>();
  464. /// <summary>
  465. /// 学生协作成果数
  466. /// </summary>
  467. public List<double> stuCowork { get; set; } = new List<double>();
  468. /// <summary>
  469. /// 小组协作成果数
  470. /// </summary>
  471. public List<double> groupCowork { get; set; } = new List<double>();
  472. /// <summary>
  473. /// 挑人集合
  474. /// </summary>
  475. public List<List<string>> pickup { get; set; } = new List<List<string>>();
  476. /// <summary>
  477. /// 挑人集合-小组
  478. /// </summary>
  479. public List<List<string>> pickup_group { get; set; } = new List<List<string>>();
  480. public double interactRate { get; set; }
  481. public double taskRate { get; set; }
  482. public double coworkRate { get; set; }
  483. public double examRate { get; set; }
  484. public double smartRatingRate { get; set; }
  485. }
  486. /// <summary>
  487. /// 计算当前元素在集合中超过了多少百分比的值
  488. /// </summary>
  489. /// <param name="nums"></param>
  490. /// <param name="curr"></param>
  491. /// <returns></returns>
  492. public static double GetPersent(IEnumerable<double> nums, int curr)
  493. {
  494. int count = 0;
  495. foreach (var op in nums.OrderBy(x => x))
  496. {
  497. if (op < curr)
  498. {
  499. count++;
  500. }
  501. else if (op == curr)
  502. {
  503. count++;
  504. }
  505. else
  506. {
  507. break;
  508. }
  509. }
  510. return count *1.0/ nums.Count() * 100;
  511. }
  512. /// <summary>
  513. /// 使用标准差定义异常值。如果一个数字与平均值的偏差超过某个标准差倍数(例如2倍或3倍),则可以认为它是异常的。
  514. /// </summary>
  515. /// <param name="array"></param>
  516. /// <returns></returns>
  517. public static List<double> CleanDataBySDThreshold(IEnumerable<double> array, double thresholdMultiplier = 2)
  518. {
  519. if (array.Count() == 0) return new List<double>();
  520. double average = Math.Round(array.Sum()*1.0/array.Count(), 4);
  521. double variance = array.Select(x => Math.Round(Math.Pow(x - average, 2), 4)).Sum()*1.0/array.Count();
  522. double standardDeviation = Math.Sqrt(Math.Round(variance, 4));
  523. double threshold = Math.Round(thresholdMultiplier * standardDeviation);
  524. List<double> datas = new List<double>();
  525. foreach (double value in array)
  526. {
  527. double deviation = Math.Round(Math.Abs(value - average), 4);
  528. if (deviation <= threshold)
  529. {
  530. datas.Add(value);
  531. }
  532. }
  533. return datas;
  534. }
  535. private async IAsyncEnumerable<LessonLocal> GetLessonLocal(List<LessonRecord> lessonRecords, List<string> localIds)
  536. {
  537. foreach (var lessonRecord in lessonRecords)
  538. {
  539. if (localIds.Contains(lessonRecord.id))
  540. {
  541. continue;
  542. }
  543. LessonLocal lessonLocal = new LessonLocal { lessonRecord=lessonRecord };
  544. if (System.IO.File.Exists($"F:\\lesson-local\\{lessonRecord.id}-local.json"))
  545. {
  546. string jsonp = await System.IO.File.ReadAllTextAsync($"F:\\lesson-local\\{lessonRecord.id}-local.json");
  547. lessonLocal = jsonp.ToObject<LessonLocal>();
  548. }
  549. else
  550. {
  551. List<string> files = new List<string>()
  552. {
  553. $"/records/{lessonRecord.id}/IES/TimeLine.json",
  554. $"/records/{lessonRecord.id}/IES/base.json",
  555. $"/records/{lessonRecord.id}/IES/Task.json",
  556. $"/records/{lessonRecord.id}/IES/SmartRating.json",
  557. $"/records/{lessonRecord.id}/IES/IRS.json",
  558. $"/records/{lessonRecord.id}/IES/Cowork.json",
  559. $"/records/{lessonRecord.id}/Sokrates/SokratesRecords.json",
  560. };
  561. lessonLocal = new LessonLocal { lessonRecord=lessonRecord };
  562. lessonLocal = await GetLessonFiles(lessonLocal, files, lessonRecord.school);
  563. }
  564. if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student!=null)
  565. {
  566. var baseData = GetBaseData(lessonLocal.lessonBase!);
  567. lessonLocal.studentLessonDatas= baseData.studentLessonDatas;
  568. List<ExamData> examDatas = await GetExamData(lessonRecord, lessonLocal.timeLineData);
  569. lessonLocal.examDatas = examDatas;
  570. lessonLocal.sokratesDatas= lessonLocal.sokratesDatas.IsNotEmpty() ? lessonLocal.sokratesDatas : lessonLocal.timeLineData!=null ? lessonLocal.timeLineData.events : new List<TimeLineEvent>();
  571. }
  572. yield return lessonLocal;
  573. }
  574. }
  575. private async Task<LessonLocal> GetLessonFiles(LessonLocal lessonLocal, List<string> files, string school)
  576. {
  577. await Parallel.ForEachAsync(files, async (file, _) =>
  578. {
  579. try
  580. {
  581. BlobDownloadResult blobDownloadResult = await _azureStorage.GetBlobContainerClient(school).GetBlobClient(file).DownloadContentAsync();
  582. switch (true)
  583. {
  584. case bool when file.Contains("IES/TimeLine.json"):
  585. lessonLocal.timeLineData= blobDownloadResult.Content.ToObjectFromJson<TimeLineData>();
  586. break;
  587. case bool when file.Contains("IES/base.json"):
  588. lessonLocal.lessonBase= blobDownloadResult.Content.ToObjectFromJson<LessonBase>();
  589. break;
  590. case bool when file.Contains("IES/Task.json"):
  591. lessonLocal.taskDatas= blobDownloadResult.Content.ToObjectFromJson<List<TaskData>>();
  592. break;
  593. case bool when file.Contains("IES/SmartRating.json"):
  594. lessonLocal.smartRatingDatas= blobDownloadResult.Content.ToObjectFromJson<List<SmartRatingData>>();
  595. break;
  596. case bool when file.Contains("IES/IRS.json"):
  597. lessonLocal.irsDatas= blobDownloadResult.Content.ToObjectFromJson<List<IRSData>>();
  598. break;
  599. case bool when file.Contains("IES/Cowork.json"):
  600. lessonLocal.coworkDatas= blobDownloadResult.Content.ToObjectFromJson<List<CoworkData>>();
  601. break;
  602. case bool when file.Contains("Sokrates/SokratesRecords.json"):
  603. lessonLocal.sokratesDatas= blobDownloadResult.Content.ToObjectFromJson<List<TimeLineEvent>>();
  604. break;
  605. }
  606. }
  607. catch (Exception ex)
  608. {
  609. //Console.WriteLine($"{file}");
  610. }
  611. });
  612. return lessonLocal;
  613. }
  614. private async Task GetLessData(CosmosDBResult<LessonRecord> result, List<string> ignore)
  615. {
  616. foreach (var item in result.list)
  617. {
  618. //读取TimeLine.json
  619. TimeLineData? timeLineData = null;
  620. try
  621. {
  622. BlobDownloadResult timeLineBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/TimeLine.json").DownloadContentAsync();
  623. timeLineData = timeLineBlobDownload.Content.ToObjectFromJson<TimeLineData>();
  624. }
  625. catch (Exception ex)
  626. {
  627. if (!ex.Message.Contains("The specified blob does not exist"))
  628. {
  629. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/TimeLine.json");
  630. }
  631. }
  632. //读取基础Base信息
  633. //base.json
  634. LessonBase? lessonBase = null;
  635. List<LocalStudent> studentLessonDatas = new List<LocalStudent>();
  636. //名单出席率低于30%的 不纳入计算。,累计所有课例的科技使用次数及反馈情况,用于做科技分类比例的权重
  637. try
  638. {
  639. BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/base.json").DownloadContentAsync();
  640. string basejson = baseblobDownload.Content.ToString().Replace("\"Uncall\"", "0").Replace("Uncall", "0");
  641. lessonBase = basejson.ToObject<LessonBase>();
  642. var data = GetBaseData(lessonBase);
  643. studentLessonDatas=data.studentLessonDatas;
  644. }
  645. catch (Exception ex)
  646. {
  647. if (!ex.Message.Contains("The specified blob does not exist"))
  648. {
  649. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/base.json");
  650. }
  651. }
  652. //读取Task.json
  653. ///Event 过滤类型 : 'WrkSpaceLoad'作品收集, 'WrkCmp' 作品贴上 文件:Task.json 根据clientWorks 中的seatID 匹配base.json 中的 student
  654. List<TaskData> taskDatas = new List<TaskData>();
  655. try
  656. {
  657. BlobDownloadResult taskBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/Task.json").DownloadContentAsync();
  658. taskDatas = taskBlobDownload.Content.ToObjectFromJson<List<TaskData>>();
  659. }
  660. catch (Exception ex)
  661. {
  662. if (!ex.Message.Contains("The specified blob does not exist"))
  663. {
  664. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/Task.json");
  665. }
  666. }
  667. //读取互评信息
  668. //Event 过滤类型 'RatingStart'
  669. //smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票
  670. //读取SmartRating.json
  671. List<SmartRatingData> smartRatingDatas = new List<SmartRatingData>();
  672. try
  673. {
  674. BlobDownloadResult smartRatingBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/SmartRating.json").DownloadContentAsync();
  675. smartRatingDatas = smartRatingBlobDownload.Content.ToObjectFromJson<List<SmartRatingData>>();
  676. }
  677. catch (Exception ex)
  678. {
  679. if (!ex.Message.Contains("The specified blob does not exist"))
  680. {
  681. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/SmartRating.json");
  682. }
  683. }
  684. //读取互动信息
  685. //Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad'
  686. //TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答
  687. //读取IRS.json
  688. List<IRSData> irsDatas = new List<IRSData>();
  689. try
  690. {
  691. BlobDownloadResult irsBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/IRS.json").DownloadContentAsync();
  692. irsDatas = irsBlobDownload.Content.ToObjectFromJson<List<IRSData>>();
  693. }
  694. catch (Exception ex)
  695. {
  696. if (!ex.Message.Contains("The specified blob does not exist"))
  697. {
  698. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/IRS.json");
  699. }
  700. }
  701. //读取协作信息
  702. ///Event 过滤类型 'CoworkLoad', "CoworkType":"CoworkGroup",类型
  703. //Cowork.json 中找到对应类型,根据Pgid 去 Cowork.json 中找到对应数据
  704. List<CoworkData> coworkDatas = new List<CoworkData>();
  705. try
  706. {
  707. BlobDownloadResult blobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/Cowork.json").DownloadContentAsync();
  708. coworkDatas = blobDownload.Content.ToObjectFromJson<List<CoworkData>>();
  709. }
  710. catch (Exception ex)
  711. {
  712. if (!ex.Message.Contains("The specified blob does not exist"))
  713. {
  714. _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/Cowork.json");
  715. }
  716. }
  717. //苏格拉底SokratesRecords.json
  718. List<TimeLineEvent> sokrates = new List<TimeLineEvent>();
  719. try
  720. {
  721. BlobDownloadResult blobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/Sokrates/SokratesRecords.json").DownloadContentAsync();
  722. sokrates = blobDownload.Content.ToObjectFromJson<List<TimeLineEvent>>();
  723. }
  724. catch (Exception ex)
  725. {
  726. if (!ex.Message.Contains("The specified blob does not exist"))
  727. {
  728. _logger.LogError(ex, $"文件不存在:/records/{item.id}/Sokrates/SokratesRecords.json");
  729. }
  730. }
  731. List<ExamData> examDatas = await GetExamData(item, timeLineData);
  732. var lessonLocal = new LessonLocal
  733. {
  734. examDatas= examDatas,
  735. coworkDatas= coworkDatas,
  736. irsDatas = irsDatas,
  737. smartRatingDatas = smartRatingDatas,
  738. taskDatas = taskDatas,
  739. lessonBase = lessonBase,
  740. timeLineData = timeLineData,
  741. studentLessonDatas = studentLessonDatas,
  742. lessonRecord =item,
  743. sokratesDatas= sokrates.IsNotEmpty() ? sokrates : timeLineData!=null ? timeLineData.events : new List<TimeLineEvent>()
  744. };
  745. TechCount techCount = new TechCount
  746. {
  747. examCount = examDatas.Count,
  748. taskCount = taskDatas.Count,
  749. irsCount = irsDatas.Count,
  750. coworkCount = coworkDatas.Count,
  751. smartRatingCount = smartRatingDatas.Count,
  752. timeCount= sokrates.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()
  753. };
  754. string path = "F:\\lesson-local";
  755. await System.IO.File.WriteAllTextAsync($"{path}\\{item.id}-local.json", lessonLocal.ToJsonString());
  756. await System.IO.File.WriteAllTextAsync($"{path}\\{item.id}-count.json", techCount.ToJsonString());
  757. }
  758. }
  759. private async Task<List<ExamData>> GetExamData(LessonRecord item, TimeLineData? timeLineData)
  760. {
  761. //读取ExamData
  762. List<ExamData> examDatas = new List<ExamData>();
  763. try
  764. {
  765. var examPages = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && !string.IsNullOrWhiteSpace(x.ExamId));
  766. if (examPages!=null && examPages.Count()>0)
  767. {
  768. var examsFiles = await _azureStorage.GetBlobContainerClient(item.school).List($"records/{item.id}/Exam");
  769. var paperFiles = await _azureStorage.GetBlobContainerClient(item.school).List($"records/{item.id}/ExamPaper");
  770. foreach (var examsFile in examsFiles)
  771. {
  772. if (examsFile.EndsWith("Exam.json"))
  773. {
  774. ExamData? examData = null;
  775. try
  776. {
  777. BlobDownloadResult examDataDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient(examsFile).DownloadContentAsync();
  778. var str = examDataDownload.Content.ToString().Replace("\r\n","").Replace("\ufeff", "").Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1");
  779. examData= str.ToObject<ExamData>();
  780. // examData = examDataDownload.Content.ToObjectFromJson<ExamData>();
  781. }
  782. catch (Exception ex)
  783. {
  784. if (!ex.Message.Contains("The specified blob does not exist"))
  785. {
  786. _logger.LogError(ex, $"文件不存在:{examsFile}");
  787. }
  788. }
  789. if (examData!=null && examData.exam.papers.IsNotEmpty())
  790. {
  791. string paperId = examData.exam.papers.First().id;
  792. if (_azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").Exists())
  793. {
  794. LessonPaper lessonPaper = null;
  795. try
  796. {
  797. BlobDownloadResult paperblobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").DownloadContentAsync();
  798. lessonPaper = paperblobDownload.Content.ToObjectFromJson<LessonPaper>();
  799. examData.paper = lessonPaper;
  800. }
  801. catch (Exception ex)
  802. {
  803. if (!ex.Message.Contains("The specified blob does not exist"))
  804. {
  805. _logger.LogError(ex, $"文件不存在:/records/{item.id}/ExamPaper/{paperId}/index.json");
  806. }
  807. }
  808. }
  809. examDatas.Add(examData);
  810. }
  811. }
  812. }
  813. }
  814. }
  815. catch (Exception ex)
  816. {
  817. _logger.LogError(ex, ex.Message);
  818. }
  819. return examDatas;
  820. }
  821. public class TechCount
  822. {
  823. public string? lessonId { get; set;}
  824. /// <summary>
  825. /// 评测数量
  826. /// </summary>
  827. public int examCount { get; set;}
  828. /// <summary>
  829. /// 任务数量
  830. /// </summary>
  831. public int taskCount { get; set;}
  832. /// <summary>
  833. /// IRS次数
  834. /// </summary>
  835. public int irsCount { get; set; }
  836. /// <summary>
  837. /// 互动次数
  838. /// </summary>
  839. //public int interactExamCount { get; set; }
  840. /// <summary>
  841. /// 互动次数
  842. /// </summary>
  843. public int interactNormalCount { get; set; }
  844. /// <summary>
  845. /// 协作次数
  846. /// </summary>
  847. public int coworkCount { get; set; }
  848. /// <summary>
  849. /// 智能评分次数
  850. /// </summary>
  851. public int smartRatingCount { get; set; }
  852. public List<CodeLong> timeCount { get; set; } = new List<CodeLong>();
  853. public IEnumerable<double> pscore { get; set; } = new List<double>();
  854. public IEnumerable<double> gscore { get; set; } = new List<double>();
  855. public IEnumerable<double> tscore { get; set; } = new List<double>();
  856. /// <summary>
  857. /// 评测数量
  858. /// </summary>
  859. public int examCountBase { get; set; }
  860. /// <summary>
  861. /// 任务数量
  862. /// </summary>
  863. public int taskCountBase { get; set; }
  864. /// <summary>
  865. /// IRS次数
  866. /// </summary>
  867. public int irsCountBase { get; set; }
  868. /// <summary>
  869. /// 互动次数
  870. /// </summary>
  871. //public int interactExamCountBase { get; set; }
  872. /// <summary>
  873. /// 互动次数
  874. /// </summary>
  875. public int interactNormalCountBase { get; set; }
  876. /// <summary>
  877. /// 协作次数
  878. /// </summary>
  879. public int coworkCountBase { get; set; }
  880. /// <summary>
  881. /// 智能评分次数
  882. /// </summary>
  883. public int smartRatingCountBase { get; set; }
  884. }
  885. /// <summary>
  886. /// 处理base.json的数据
  887. /// </summary>
  888. /// <param name="lessonRecord"></param>
  889. /// <param name="lessonBase"></param>
  890. /// <returns></returns>
  891. private (LessonBase lessonBase, List<LocalStudent> studentLessonDatas) GetBaseData( LessonBase lessonBase)
  892. {
  893. //处理学生定位数据
  894. List<LocalStudent> studentLessonDatas = new List<LocalStudent>();
  895. int index = 0;
  896. try {
  897. if (lessonBase!=null)
  898. {
  899. lessonBase.student.ForEach(x =>
  900. {
  901. int attend = 0;
  902. var client = lessonBase.report.clientSummaryList.Find(y => y.seatID == x.seatID);
  903. if (client!=null)
  904. {
  905. attend=client.attendState;
  906. }
  907. studentLessonDatas.Add(new LocalStudent()
  908. {
  909. id = x.id,
  910. index = index,
  911. seatID =$"{x.seatID}",
  912. groupId = x.groupId,
  913. attend= attend
  914. });
  915. index++;
  916. });
  917. }
  918. } catch (Exception ex) {
  919. Console.WriteLine(lessonBase.ToJsonString());
  920. }
  921. return (lessonBase, studentLessonDatas);
  922. }
  923. /// <summary>
  924. ///读取互动信息
  925. ///Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad'
  926. /// 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns' 抢权(新), 'BuzrLoad'抢权(旧)
  927. ///TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答
  928. ///读取IRS.json
  929. /// </summary>
  930. /// <param name="lessonRecord"></param>
  931. /// <param name="lessonBase"></param>
  932. /// <param name="irsDatas"></param>
  933. /// <returns></returns>
  934. private List<StudentLessonData> GetIRSData( LessonBase lessonBase, TimeLineData timeLineData, List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas,List<ExamData> examDatas,string itemFiles)
  935. {
  936. List<string> interactTypes = new List<string>() { "PopQuesLoad", "ReAtmpAnsStrt", "BuzrAns", "BuzrLoad" };
  937. //去重页面
  938. var enventsInteract = timeLineData?.events?.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && interactTypes.Contains(x.Event)).GroupBy(x => x.Pgid) .Select(x => new { key = x.Key, list = x.ToList() });
  939. if (enventsInteract!= null && enventsInteract.Count()>0)
  940. {
  941. var keys = enventsInteract.Select(x => x.key).ToList();
  942. foreach (var item in enventsInteract)
  943. {
  944. ProcessIRSPageData(irsDatas, studentLessonDatas,examDatas, item);
  945. }
  946. //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中
  947. var envents_other = timeLineData.events.Where(x =>!string.IsNullOrWhiteSpace(x.Pgid) && !keys.Contains(x.Pgid)).GroupBy(x => x.Pgid) .Select(x => new { key = x.Key, list = x.ToList() });
  948. if (envents_other!=null && envents_other.Count()>0)
  949. {
  950. foreach (var item in envents_other)
  951. {
  952. ProcessIRSPageData(irsDatas, studentLessonDatas,examDatas, item);
  953. }
  954. }
  955. }
  956. else
  957. {
  958. //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中
  959. if (timeLineData!=null)
  960. {
  961. var envents_other = timeLineData.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid)).GroupBy(x => x.Pgid).Select(x => new { key = x.Key, list = x.ToList() });
  962. if (envents_other!=null && envents_other.Count()>0)
  963. {
  964. foreach (var item in envents_other)
  965. {
  966. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item);
  967. }
  968. }
  969. }
  970. else {
  971. foreach (var item in irsDatas.Select(x => x.pageID))
  972. {
  973. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, new { key = item });
  974. }
  975. }
  976. }
  977. //单独处理挑人的逻辑
  978. //是否从小组里面挑人。
  979. //不需要去重页面,直接获取挑人大类 PickupResult
  980. //小类处理:PickupRight , PickupOption , PickupNthGrp ,PickupEachGrp ,PickupDiff , PickupResult 挑人算不算互动?? 读取PickupMemberId "[\r\n 35\r\n]"
  981. var enventsPickup = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && x.Event.Equals("PickupResult"));
  982. if (enventsPickup.IsNotEmpty())
  983. {
  984. foreach (var item in enventsPickup)
  985. {
  986. List<int> mbrs = item.PickupMemberId.ToObject<List<int>>();
  987. // 挑人挑中 TT ,没有挑中 T1
  988. foreach (var studentLessonData in studentLessonDatas)
  989. {
  990. var mbr = mbrs.FindAll(x => studentLessonData.seatID!.Equals($"{x}"));
  991. if (mbr.IsNotEmpty())
  992. {
  993. foreach (var m in mbr)
  994. {
  995. studentLessonData.attend=1;
  996. //studentLessonData.interactRecord.interactRecords.Add(new ItemRecord()
  997. //{
  998. // resultWeight = InteractWeight.TT,
  999. // resultType=InteractReultType.TT,
  1000. // itemType = string.IsNullOrWhiteSpace(item.PickupType) ? "PickupResult" : item.PickupType
  1001. //});
  1002. studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "1--PickupResult" : $"1--{item.PickupType}");
  1003. }
  1004. }
  1005. else {
  1006. //处理未挑中的
  1007. if (studentLessonData.attend==1)
  1008. {
  1009. studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "0--PickupResult" : $"0--{item.PickupType}");
  1010. }
  1011. }
  1012. }
  1013. }
  1014. }
  1015. return studentLessonDatas;
  1016. }
  1017. private static List<StudentLessonData> ProcessIRSPageData(List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas, List<ExamData> examDatas, dynamic item)
  1018. {
  1019. var irsDataPages = irsDatas.Where(y => item.key.Equals(y.pageID));
  1020. foreach (var irsDataPage in irsDataPages)
  1021. {
  1022. //检查是否设置正确答案。
  1023. var answers_q = irsDataPage.question?["exercise"]?["answer"]?.ToJsonString().ToObject<List<string>>();
  1024. //根据题去找对应的试卷和评测信息
  1025. var question_id = $"{irsDataPage.question?["id"]}";
  1026. var examData = examDatas.Where(x =>x.paper!=null && x.paper.slides.Exists(x =>!string.IsNullOrWhiteSpace(x.url) && x.url.Equals($"{question_id}.json"))).FirstOrDefault();
  1027. List<string> answers = new List<string>();
  1028. answers_q?.ForEach(x => {
  1029. if (!string.IsNullOrWhiteSpace(x))
  1030. {
  1031. answers.Add(x);
  1032. }
  1033. });
  1034. var _objective = irsDataPage.question?["exercise"]?["objective"];
  1035. var scoreNode = irsDataPage.question?["exercise"]?["score"];
  1036. var _type = irsDataPage.question?["exercise"]?["type"];
  1037. var _answerType = irsDataPage.question?["exercise"]?["answerType"];//file,audio,text,image
  1038. var qitem = irsDataPage.question?["item"]?.AsArray();
  1039. if (qitem!=null && qitem.Count()>0)
  1040. {
  1041. for (var i = 0; i<qitem.Count(); i++)
  1042. {
  1043. qitem[i]!["question"]="";
  1044. }
  1045. }
  1046. double questionScore = 0;
  1047. bool objective = false;
  1048. if (_objective!=null) {
  1049. objective = _objective.GetValue<bool>();
  1050. }
  1051. //题型
  1052. string type= string.Empty;
  1053. if (_type!=null)
  1054. {
  1055. //题型
  1056. type = _type.GetValue<string>();
  1057. List<string> types = new List<string>() { "single", "multiple" , "judge" , "sortmultiple" };
  1058. if (types.Contains(type))
  1059. {
  1060. objective = true;
  1061. }
  1062. else {
  1063. objective = false;
  1064. }
  1065. }
  1066. if (_answerType!=null)
  1067. {
  1068. _answerType.GetValue<string>();
  1069. //暂不处理,可能存在依然传文字的情况
  1070. //不是文本作答的处理,题目不是客观题,答案不记录
  1071. //if (!_answerType.Equals("text"))
  1072. //{
  1073. // objective=false;
  1074. // answers=new List<string>();
  1075. //}
  1076. }
  1077. if (scoreNode!=null)
  1078. {
  1079. double.TryParse(scoreNode.ToString(), out questionScore);
  1080. }
  1081. string interactType = string.Empty;
  1082. if (irsDataPage.clientAnswers.IsNotEmpty())
  1083. {
  1084. //第一个list是几轮,一次作答,二次作答, 第二个list是学生的下标, 第三个list是 答案
  1085. List<List<List<string>>> clientAnswers = new List<List<List<string>>>();
  1086. foreach (var key in irsDataPage.clientAnswers.Keys)
  1087. {
  1088. clientAnswers.Add(irsDataPage.clientAnswers[key]);
  1089. }
  1090. // 获取第一个列表的长度作为比较基准
  1091. int firstListLength = clientAnswers.First().Count;
  1092. bool isSameLength = true;
  1093. // 遍历剩余的列表并检查它们的长度是否与第一个列表相同
  1094. foreach (var innerList in clientAnswers.Skip(1))
  1095. {
  1096. if (innerList.Count != firstListLength)
  1097. {
  1098. isSameLength = false;
  1099. break;
  1100. }
  1101. }
  1102. //并检查学生集合的长度是否与第一个列表相同
  1103. if (isSameLength && studentLessonDatas.Count()==firstListLength)
  1104. {
  1105. for (int index = 0; index< clientAnswers[0].Count; index++)
  1106. {
  1107. var student = studentLessonDatas[index];
  1108. double studentScore = 0 ;
  1109. if (examData!=null && examData.examClassResult.IsNotEmpty()) {
  1110. var examResultIndex = examData.examClassResult.First().studentIds.IndexOf(student.id);
  1111. var questionIndex = examData.paper.slides.Select(x => x.url).ToList().IndexOf($"{question_id}.json");
  1112. if (examResultIndex>=0
  1113. && examData.examClassResult.First().studentScores.Count>=(examResultIndex+1) //防止索引越界
  1114. && examData.examClassResult.First().studentScores[examResultIndex].Count>=(questionIndex+1)) //防止索引越界
  1115. {
  1116. //获取index学生在questionIndex题的分数
  1117. studentScore = examData.examClassResult.First().studentScores[examResultIndex][questionIndex];
  1118. }
  1119. }
  1120. //index 代表学生下标
  1121. List<ItemRecord> interactRecords = new List<ItemRecord>();
  1122. if (clientAnswers.Count==1)
  1123. {
  1124. //即问即答
  1125. interactType = "PopQuesLoad";
  1126. var ans0 = clientAnswers[0][index];
  1127. var IS0 = GetInteractResultHasAnswer(answers, ans0, objective,type,questionScore, studentScore);
  1128. interactRecords.Add(new ItemRecord()
  1129. {
  1130. resultWeight = IS0.weight,
  1131. resultType=IS0.reultType,
  1132. itemType= interactType,
  1133. criterion= questionScore,
  1134. itemScore= IS0.interactScore
  1135. });
  1136. }
  1137. if (clientAnswers.Count==2)
  1138. {
  1139. //二次作答
  1140. interactType="ReAtmpAnsStrt";
  1141. var ans1 = clientAnswers[1][index];
  1142. var IS1 = GetInteractResultHasAnswer(answers, ans1, objective,type,questionScore,studentScore);
  1143. interactRecords.Add(new ItemRecord()
  1144. {
  1145. resultWeight = IS1.weight,
  1146. resultType=IS1.reultType,
  1147. itemType= interactType,
  1148. criterion= questionScore,
  1149. itemScore= IS1.interactScore
  1150. });
  1151. }
  1152. if (clientAnswers.Count>2)
  1153. {
  1154. //三次作答
  1155. interactType="TeAtmpAnsStrt";
  1156. var ans2 = clientAnswers[2][index];
  1157. var IS2 = GetInteractResultHasAnswer(answers, ans2, objective, type, questionScore, studentScore);
  1158. interactRecords.Add(new ItemRecord()
  1159. {
  1160. resultWeight = IS2.weight,
  1161. resultType=IS2.reultType,
  1162. itemType= interactType,
  1163. criterion= questionScore,
  1164. itemScore= IS2.interactScore
  1165. });
  1166. }
  1167. if (studentLessonDatas[index].attend==1)
  1168. {
  1169. studentLessonDatas[index].interactRecord.interactRecords.AddRange(interactRecords);
  1170. }
  1171. }
  1172. }
  1173. }
  1174. //是否抢权作答的模式
  1175. if (irsDataPage.isBuzz)
  1176. {
  1177. interactType = "BuzrAns";
  1178. //处理参与抢权的
  1179. Dictionary<string, ItemRecord> buzzParticipants = new Dictionary<string, ItemRecord>();
  1180. foreach (var buzzParticipant in irsDataPage.buzzParticipants)
  1181. {
  1182. var studentData = studentLessonDatas.Find(x => x.seatID!.Equals(buzzParticipant));
  1183. if (studentData != null)
  1184. {
  1185. buzzParticipants[buzzParticipant]=new ItemRecord() { resultWeight = InteractWeight.T1, itemType= interactType, resultType= InteractReultType.T1 };
  1186. }
  1187. }
  1188. //处理抢权成功的
  1189. foreach (var buzzClient in irsDataPage.buzzClients)
  1190. {
  1191. buzzParticipants[buzzClient]=new ItemRecord() { resultWeight = InteractWeight.TT, itemType= interactType, resultType= InteractReultType.TT };
  1192. }
  1193. foreach (var studentLessonData in studentLessonDatas)
  1194. {
  1195. if (buzzParticipants.ContainsKey(studentLessonData.seatID!))
  1196. {
  1197. //处理已经有抢权结果的数据
  1198. studentLessonData.attend=1;
  1199. studentLessonData.interactRecord.interactRecords.Add(buzzParticipants[studentLessonData.seatID!]);
  1200. }
  1201. else
  1202. {
  1203. if (studentLessonData.attend==1) {
  1204. //处理未参与抢权的
  1205. studentLessonData.interactRecord.interactRecords.Add(new ItemRecord() { resultWeight = InteractWeight.T0, itemType = interactType,resultType= InteractReultType.T0 });
  1206. }
  1207. }
  1208. }
  1209. }
  1210. }
  1211. return studentLessonDatas;
  1212. }
  1213. private static (double weight,string reultType,double interactScore) GetInteractResultHasAnswer(List<string>? answers, List<string> ans0 , bool objective,string type, double questionScore, double studentScore)
  1214. {
  1215. //List<string> ans0 = new List<string>();
  1216. //ans?.ForEach(x => {
  1217. // if (!string.IsNullOrWhiteSpace(x))
  1218. // {
  1219. // ans0.Add(x);
  1220. // }
  1221. // else { ans.Add("");}
  1222. //});
  1223. double weight = InteractWeight.T0;
  1224. string reultType = InteractReultType.T0;
  1225. double interactScore = 0;
  1226. if (answers.IsNotEmpty())
  1227. {
  1228. if (ans0.IsNotEmpty())
  1229. {
  1230. if (objective) //客观题
  1231. {
  1232. //标准答案等于作答的结果
  1233. if (answers!.Count == ans0.Count)
  1234. {
  1235. if (answers.All(item => ans0.Contains(item)))
  1236. {
  1237. //完全正确
  1238. weight= InteractWeight.TT;
  1239. reultType= InteractReultType.TT;
  1240. interactScore= studentScore==0 ? questionScore : studentScore;
  1241. }
  1242. else
  1243. {
  1244. //作答错误
  1245. weight= InteractWeight.T1;
  1246. reultType = InteractReultType.T1;
  1247. interactScore= studentScore;
  1248. }
  1249. }
  1250. //标准答案比作答的结果多
  1251. else if (answers!.Count > ans0.Count)
  1252. {
  1253. if (ans0.All(item => answers.Contains(item)))
  1254. {
  1255. //部分正确
  1256. weight= InteractWeight.TP;
  1257. reultType = InteractReultType.TP;
  1258. // 2 * 0.3 * 10= 6
  1259. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1260. }
  1261. else
  1262. {
  1263. //作答错误
  1264. weight= InteractWeight.T1;
  1265. reultType = InteractReultType.T1;
  1266. interactScore= studentScore;
  1267. }
  1268. }
  1269. //标准答案比作答结果少
  1270. else
  1271. {
  1272. //作答错误
  1273. weight= InteractWeight.T1;
  1274. reultType = InteractReultType.T1;
  1275. interactScore= studentScore;
  1276. }
  1277. }
  1278. else
  1279. {
  1280. //填空题
  1281. if ("complete".Equals(type) && answers!.Count==ans0.Count)
  1282. {
  1283. bool hasT = false;
  1284. bool hasF = false;
  1285. for (int i = 0; i < answers!.Count; i++)
  1286. {
  1287. if (answers[i].Equals(ans0[i]))
  1288. {
  1289. hasT=true;
  1290. }
  1291. else {
  1292. hasF=true;
  1293. }
  1294. }
  1295. if (hasT && !hasF)
  1296. {
  1297. //完全正确
  1298. weight= InteractWeight.TT;
  1299. reultType = InteractReultType.TT;
  1300. interactScore= studentScore==0 ? questionScore : studentScore;
  1301. }
  1302. else if (hasT && hasF)
  1303. {
  1304. //部分正确
  1305. weight= InteractWeight.TP;
  1306. reultType = InteractReultType.TP;
  1307. // 2 * 0.3 * 10= 6
  1308. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1309. }
  1310. else if (!hasT && hasF)
  1311. {
  1312. //没有正确的,但有错误的,代表参与了
  1313. weight= InteractWeight.T1;
  1314. reultType = InteractReultType.T1;
  1315. interactScore= studentScore;
  1316. }
  1317. else if (!hasT && !hasF)
  1318. {
  1319. //没有正确的,也没有错误的,代表没有作答
  1320. weight= InteractWeight.T0;
  1321. reultType = InteractReultType.T0;
  1322. interactScore= studentScore;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. //主观题,完全匹配的
  1328. if (answers!.All(item => ans0.Contains(item)))
  1329. {
  1330. //完全正确
  1331. weight= InteractWeight.TT;
  1332. reultType = InteractReultType.TT;
  1333. interactScore= studentScore==0 ? questionScore : studentScore;
  1334. }
  1335. else
  1336. { // 使用LINQ查询来判断是否有匹配的答案
  1337. bool hasMatchingAnswer = answers!.Intersect(ans0).Any();
  1338. if (hasMatchingAnswer)
  1339. {
  1340. //主观题回答正确即为完全正确
  1341. weight= InteractWeight.TT;
  1342. reultType = InteractReultType.TT;
  1343. interactScore= studentScore==0 ? questionScore : studentScore;
  1344. }
  1345. else
  1346. {
  1347. //优先根据得分与标准分的占比算出得分率,如果没有得分率,如果是直接从互动,不知道是评测的 需要先去评测找作答得分。,则采用Levenshtein距离来评估两个字符串的相似度
  1348. //没有匹配上答案,则采用Levenshtein距离来评估两个字符串的相似度
  1349. if (questionScore>0)
  1350. {
  1351. if (studentScore>0)
  1352. {
  1353. weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1);
  1354. if (weight==InteractWeight.T1)
  1355. {
  1356. reultType = InteractReultType.T1;
  1357. interactScore=studentScore;
  1358. }
  1359. else if (weight>InteractWeight.TT)
  1360. {
  1361. reultType = InteractReultType.TT;
  1362. interactScore=studentScore==0? questionScore:studentScore;
  1363. }
  1364. else
  1365. {
  1366. reultType = InteractReultType.TP;
  1367. // 2 * 0.3 * 10= 6
  1368. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1369. }
  1370. }
  1371. else
  1372. {
  1373. weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1));
  1374. if (weight==InteractWeight.T1)
  1375. {
  1376. reultType = InteractReultType.T1;
  1377. interactScore=studentScore;
  1378. }
  1379. else if (weight>InteractWeight.TT)
  1380. {
  1381. reultType = InteractReultType.TT;
  1382. interactScore=studentScore==0? questionScore:studentScore;
  1383. }
  1384. else
  1385. {
  1386. reultType = InteractReultType.TP;
  1387. // 2 * 0.3 * 10= 6
  1388. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1389. }
  1390. }
  1391. }
  1392. else {
  1393. weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1));
  1394. if (weight==InteractWeight.T1)
  1395. {
  1396. reultType = InteractReultType.T1;
  1397. interactScore=studentScore;
  1398. }
  1399. else if (weight>InteractWeight.TT)
  1400. {
  1401. reultType = InteractReultType.TT;
  1402. interactScore=studentScore==0? questionScore:studentScore;
  1403. }
  1404. else {
  1405. reultType = InteractReultType.TP;
  1406. // 2 * 0.3 * 10= 6
  1407. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1408. }
  1409. }
  1410. }
  1411. }
  1412. }
  1413. }
  1414. }
  1415. else
  1416. {
  1417. //没有作答
  1418. weight= InteractWeight.T0;
  1419. reultType = InteractReultType.T0;
  1420. interactScore=studentScore;
  1421. }
  1422. }
  1423. else
  1424. {
  1425. //没有标准答案的情况
  1426. if (ans0.IsNotEmpty())
  1427. {
  1428. bool hasAns = false;
  1429. ans0.ForEach(x => {
  1430. if (!string.IsNullOrWhiteSpace(x)) {
  1431. hasAns = true;
  1432. }
  1433. });
  1434. if (hasAns)
  1435. {
  1436. //作答了
  1437. weight= InteractWeight.T1;
  1438. reultType = InteractReultType.T1;
  1439. interactScore=studentScore;
  1440. }
  1441. else {
  1442. //没有作答
  1443. weight= InteractWeight.T0;
  1444. reultType = InteractReultType.T0;
  1445. interactScore=studentScore;
  1446. }
  1447. }
  1448. else
  1449. {
  1450. //没有作答
  1451. weight= InteractWeight.T0;
  1452. reultType = InteractReultType.T0;
  1453. interactScore=studentScore;
  1454. }
  1455. //如果教师手动给了分或AI评分
  1456. if (questionScore>0 && studentScore>0)
  1457. {
  1458. weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1);
  1459. if (weight==InteractWeight.T1)
  1460. {
  1461. reultType = InteractReultType.T1;
  1462. interactScore=studentScore;
  1463. }
  1464. else if (weight>InteractWeight.TT)
  1465. {
  1466. reultType = InteractReultType.TT;
  1467. interactScore=studentScore==0 ? questionScore : studentScore;
  1468. }
  1469. else
  1470. {
  1471. reultType = InteractReultType.TP;
  1472. // 2 * 0.3 * 10= 6
  1473. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1474. }
  1475. }
  1476. }
  1477. return (weight,reultType,interactScore);
  1478. }
  1479. #region C# 代码 如何判断两句话是否一个意思,非机器学习的算法。使用Levenshtein距离来评估两个字符串的相似度,但是不能判断它们是否表达了同一个意思,后续借助AI实现
  1480. public static double CalculateSimilarity(string s1, string s2)
  1481. {
  1482. int n = s1.Length;
  1483. int m = s2.Length;
  1484. int[,] d = new int[n + 1, m + 1];
  1485. for (int i = 0; i <= n; i++)
  1486. {
  1487. d[i, 0] = i;
  1488. }
  1489. for (int j = 0; j <= m; j++)
  1490. {
  1491. d[0, j] = j;
  1492. }
  1493. for (int i = 1; i <= n; i++)
  1494. {
  1495. for (int j = 1; j <= m; j++)
  1496. {
  1497. int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
  1498. d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
  1499. }
  1500. }
  1501. return (1.0 - ((double)d[n, m] / Math.Max(s1.Length, s2.Length))) ;
  1502. }
  1503. #endregion
  1504. /// <summary>
  1505. /// 获取课中评测数据
  1506. /// </summary>
  1507. /// <param name="lessonRecord"></param>
  1508. /// <param name="lessonBase"></param>
  1509. /// <param name="timeLineData"></param>
  1510. /// <param name="coworkDatas"></param>
  1511. /// <param name="studentLessonDatas"></param>
  1512. /// <returns></returns>
  1513. private List<StudentLessonData> GetExamData( LessonBase lessonBase, TimeLineData timeLineData, List<ExamData> examDatas, List<StudentLessonData> studentLessonDatas)
  1514. {
  1515. foreach (var examData in examDatas)
  1516. {
  1517. //直接取第一个元素的试卷,因为在HiTeach中,只会是一个试卷(一个科目),一个班参与。
  1518. var allocation= examData?.exam?.papers?.FirstOrDefault()?.point?.Sum();
  1519. var answersStd = examData?.exam?.papers?.FirstOrDefault()?.answers;
  1520. List<List<string>> answers= new List<List<string>>();
  1521. if (answersStd!=null)
  1522. {
  1523. answersStd.ForEach(x => //去除[""]此种类型的标准答案,转为[]
  1524. {
  1525. List<string> ans = new List<string>();
  1526. if (x.Count!=0)
  1527. {
  1528. if (x.Count==1)
  1529. {
  1530. if (string.IsNullOrWhiteSpace(x[0]))
  1531. {
  1532. answers.Add(ans);
  1533. }
  1534. else
  1535. {
  1536. answers.Add(x);
  1537. }
  1538. }
  1539. else
  1540. {
  1541. answers.Add(x);
  1542. }
  1543. }
  1544. else {
  1545. answers.Add(ans);
  1546. }
  1547. });
  1548. }
  1549. examData?.examClassResult?.ForEach(item =>{
  1550. //学生下标
  1551. int index = 0;
  1552. if (item.studentAnswersArray.Count()>0
  1553. && item.studentAnswersArray.Count() == item.studentIds.Count() //学生作答数量和学生id数量一致
  1554. && item.studentScores.Count()==item.studentIds.Count()) //学生分数和学生id数量一致
  1555. {
  1556. item.studentAnswersArray.ForEach(stu => {
  1557. var student = studentLessonDatas.Find(x => x.id!.Equals(item.studentIds[index]));
  1558. if (student!=null && student.attend==1)
  1559. {
  1560. //是否要判断主观题或者客观题, 多套试卷,有主观题的
  1561. //,如果没获得结果,
  1562. //主观题有回答的:608942756458532864\Clients\18782481024\Ans\27-4341670635487887360-examExchangeAnswerlist
  1563. //27 从1开始的学生序号-4341670635487887360评测编号,内容qNo 是从1开始的题号。
  1564. if (stu.IsNotEmpty() && answers.Count()==stu.Count && examData.exam.papers[0].type.Count()==answers.Count )
  1565. {
  1566. StudentExamRecord studentExam = new StudentExamRecord();
  1567. var studentScore = item.studentScores[index];
  1568. List<ItemRecord> answerRecords = new List<ItemRecord>();
  1569. //题目下标
  1570. int itemIndex = 0;
  1571. stu.ForEach(ans =>
  1572. {
  1573. bool objective = objectiveTypes.Contains(examData.exam.papers[0].type[itemIndex]);
  1574. var questionScore = examData.exam.papers[0].point[itemIndex];
  1575. string type = examData.exam.papers[0].type[itemIndex];
  1576. var res= GetInteractResultHasAnswer(answers[itemIndex], ans, objective, type, questionScore, studentScore[itemIndex]);
  1577. ItemRecord interactRecord = new ItemRecord()
  1578. {
  1579. itemType="SPQStrt",//类型
  1580. resultType=res.reultType,//作答结果类型
  1581. resultWeight=res.weight,//得分权重
  1582. criterion= questionScore,//标准分
  1583. itemScore= studentScore[itemIndex]//得分
  1584. };
  1585. answerRecords.Add(interactRecord);
  1586. itemIndex++;
  1587. });
  1588. studentExam.score= answerRecords.Where(x => x.itemScore>=0).Select(x => x.itemScore).Sum();//得分
  1589. studentExam.scoreRate= allocation.HasValue && allocation.Value>0 ? studentExam.score * 1.0/allocation.Value : 0;//得分率
  1590. studentExam.answerRate= answerRecords.Where(x => x.resultWeight>0).Count()*1.0/studentScore.Count();//作答率
  1591. studentExam.examId=examData.exam.id;
  1592. studentExam.itemRecords=answerRecords;
  1593. student.examRecords.Add(studentExam);
  1594. }
  1595. }
  1596. index++;
  1597. });
  1598. }
  1599. });
  1600. }
  1601. return studentLessonDatas;
  1602. }
  1603. /// <summary>
  1604. /// 协作参与率 态度计算
  1605. /// </summary>
  1606. /// <param name="lessonRecord"></param>
  1607. /// <param name="lessonBase"></param>
  1608. /// <param name="timeLineData"></param>
  1609. /// <param name="coworkDatas"></param>
  1610. /// <param name="studentLessonDatas"></param>
  1611. /// <returns></returns>
  1612. private List<StudentLessonData> GetCoworkData( LessonBase lessonBase, TimeLineData timeLineData, List<CoworkData> coworkDatas, List<StudentLessonData> studentLessonDatas)
  1613. {
  1614. int p = 0;
  1615. foreach (var coworkData in coworkDatas)
  1616. {
  1617. var keys = coworkData.participateLevelList.Keys;
  1618. foreach (var key in keys)
  1619. {
  1620. var student = studentLessonDatas.Find(x => x.seatID!.Equals(key));
  1621. if (student!=null && student.attend==1)
  1622. {
  1623. var score = coworkData.participateLevelList[key];//协作得分,是否是经过指数计算的
  1624. var itemRecord = new ItemRecord { criterion=-1, itemType= coworkData.coworkType, itemScore=score, isGroup= coworkData.coworkType.Equals("Group") ? true : false };
  1625. //不能完全依赖
  1626. if (score>0)
  1627. {
  1628. itemRecord.resultWeight = InteractWeight.TP;
  1629. itemRecord.resultType = InteractReultType.TP;
  1630. }
  1631. else {
  1632. itemRecord.resultWeight = InteractWeight.T0;
  1633. itemRecord.resultType = InteractReultType.T0;
  1634. }
  1635. student.coworkRecord.itemRecords.Add(itemRecord);
  1636. }
  1637. if (key.Contains("g", StringComparison.OrdinalIgnoreCase))
  1638. {
  1639. string groupId= key.Replace("g", "").Replace("G","");
  1640. var score = coworkData.participateLevelList[key];
  1641. if (score>0)
  1642. {
  1643. var groupStu= studentLessonDatas.FindAll(x =>x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupId));
  1644. if (groupStu.IsNotEmpty())
  1645. {
  1646. foreach (var stu in groupStu)
  1647. {
  1648. stu.group_coworkScore.Add(score);
  1649. stu.coworkRecord.itemRecords[p].itemScore+=score;
  1650. stu.coworkRecord.itemRecords[p].resultWeight=InteractWeight.TP;
  1651. stu.coworkRecord.itemRecords[p].resultType=InteractReultType.TP;
  1652. }
  1653. }
  1654. }
  1655. }
  1656. }
  1657. var order = studentLessonDatas.Where(x=>x.attend==1).OrderByDescending(x => x.coworkRecord.itemRecords[p].itemScore);
  1658. var maxItems = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore);
  1659. var max = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
  1660. var min = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
  1661. var sum = studentLessonDatas.Where(x=>x.attend==1).Sum(x => x.coworkRecord.itemRecords[p].itemScore);
  1662. foreach (var student in studentLessonDatas)
  1663. {
  1664. if (student.attend==1 && student.coworkRecord.itemRecords.Count>=p+1 && student.coworkRecord.itemRecords[p].itemScore>0)
  1665. {
  1666. student.coworkRecord.itemRecords[p].resultType=InteractReultType.TP;
  1667. var data = MinMaxNormalization(min, max, student.coworkRecord.itemRecords[p].itemScore);
  1668. student.coworkRecord.itemRecords[p].resultWeight=InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1);
  1669. if (maxItems.Select(x => x.seatID).Contains(student.seatID))
  1670. {
  1671. student.coworkRecord.itemRecords[p].resultType= InteractReultType.TT;
  1672. student.coworkRecord.itemRecords[p].resultWeight= InteractWeight.TT;
  1673. }
  1674. }
  1675. }
  1676. p++;
  1677. }
  1678. return studentLessonDatas;
  1679. }
  1680. /// <summary>
  1681. /// 处理学生回推数据,并将回推纳入学习态度计算。
  1682. /// </summary>
  1683. /// <param name="lessonRecord"></param>
  1684. /// <param name="lessonBase"></param>
  1685. /// <param name="timeLineData"></param>
  1686. /// <param name="taskDatas"></param>
  1687. /// <param name="studentLessonDatas"></param>
  1688. /// <returns></returns>
  1689. private List<StudentLessonData> GetTaskData( LessonBase lessonBase, TimeLineData timeLineData, List<TaskData> taskDatas, List<StudentLessonData> studentLessonDatas)
  1690. {
  1691. //协作也算任务的一种,'WrkSpaceLoad' 作品收集, "isGroupItem": false,
  1692. int indexTask = 0;
  1693. foreach (var taskData in taskDatas)
  1694. {
  1695. //作品收集是全部人员都要参加
  1696. foreach (var student in studentLessonDatas)
  1697. {
  1698. if (student.attend==1)
  1699. {
  1700. var work = taskData.clientWorks.Find(x =>$"{x.seatID}".Equals(student.seatID));
  1701. if (work!= null)
  1702. {
  1703. student.taskRecord.itemRecords.Add(new ItemRecord { itemType="WrkSpaceLoad", itemScore=work.blobFiles.Count *10, resultWeight=InteractWeight.TT, resultType=InteractReultType.TT, isGroup= work.isGroupItem,optCount=work.blobFiles.Count });
  1704. }
  1705. else
  1706. {
  1707. student.taskRecord.itemRecords.Add(new ItemRecord { itemType="WrkSpaceLoad", itemScore=0, resultWeight=InteractWeight.T0, resultType=InteractReultType.T0, isGroup= false });
  1708. }
  1709. }
  1710. }
  1711. ////////
  1712. ///需要处理小组的情况,当前人员没有提交作品,但是有可能是小组其他人员提交了,需要判断一下。
  1713. ///
  1714. var students = studentLessonDatas.FindAll(x => x.attend==1 && x.taskRecord.itemRecords[indexTask].isGroup==true);
  1715. foreach (var student in students)
  1716. {
  1717. var groupStudents= studentLessonDatas.FindAll(x => x.id!=student.id && x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(student.groupId));
  1718. foreach (var groupstudent in groupStudents)
  1719. {
  1720. groupstudent.taskRecord.itemRecords[indexTask].isGroup=true;
  1721. groupstudent.taskRecord.itemRecords[indexTask].optCount=student.taskRecord.itemRecords[indexTask].optCount;
  1722. groupstudent.taskRecord.itemRecords[indexTask].itemScore=student.taskRecord.itemRecords[indexTask].itemScore;
  1723. groupstudent.taskRecord.itemRecords[indexTask].resultWeight=student.taskRecord.itemRecords[indexTask].resultWeight;
  1724. groupstudent.taskRecord.itemRecords[indexTask].resultType=student.taskRecord.itemRecords[indexTask].resultType;
  1725. }
  1726. }
  1727. var groupDatas = taskData.clientWorks.FindAll(x => x.seatID==0 && x.isGroupItem);
  1728. foreach (var groupData in groupDatas)
  1729. {
  1730. var groupStudents = studentLessonDatas.FindAll(x => x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupData.groupID));
  1731. foreach (var student in groupStudents)
  1732. {
  1733. student.taskRecord.itemRecords[indexTask].isGroup=true;
  1734. student.taskRecord.itemRecords[indexTask].optCount=groupData.blobFiles.Count;
  1735. student.taskRecord.itemRecords[indexTask].itemScore= 10* groupData.blobFiles.Count;
  1736. if (groupData.blobFiles.Count>0)
  1737. {
  1738. student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.TT;
  1739. student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.TT;
  1740. }
  1741. else
  1742. {
  1743. student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.T0;
  1744. student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.T0;
  1745. }
  1746. }
  1747. }
  1748. indexTask++;
  1749. }
  1750. return studentLessonDatas;
  1751. }
  1752. /// <summary>
  1753. /// 评分参与率 态度计算
  1754. /// //读取互评信息
  1755. /// /// 评分相关 在SmartRating.json 处理 GrandRating 星光大评分, 投票Voting 和 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评)
  1756. //Event 过滤类型 'RatingStart'
  1757. //smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票
  1758. //读取SmartRating.json
  1759. /// </summary>
  1760. /// <param name="lessonRecord"></param>
  1761. /// <param name="lessonBase"></param>
  1762. /// <param name="timeLineData"></param>
  1763. /// <param name="smartRatingDatas"></param>
  1764. /// <param name="studentLessonDatas"></param>
  1765. /// <returns></returns>
  1766. private List<StudentLessonData> GetSmartRatingData( LessonBase lessonBase, TimeLineData timeLineData, List<SmartRatingData> smartRatingDatas, List<StudentLessonData> studentLessonDatas,string itemf )
  1767. {
  1768. int index = 0;
  1769. foreach(var smartRatingData in smartRatingDatas)
  1770. {
  1771. string type = "";
  1772. //投票类型的
  1773. var keys_vote = smartRatingData.smartRateSummary?.meteor_VoteSummary?.Keys?.ToList();
  1774. if (keys_vote.IsNotEmpty())
  1775. {
  1776. type="Voting";
  1777. bool addData=false;
  1778. foreach (var key in keys_vote!)
  1779. {
  1780. try {
  1781. //问题数据F:\lesson-local\632424798693232640-local.json pclxxx
  1782. if (smartRatingData.smartRateSummary!.voteDetailResult.TryGetValue(key, out var value))
  1783. {
  1784. var voteDetailResults = smartRatingData.smartRateSummary!.voteDetailResult[key];
  1785. foreach (var student in studentLessonDatas)
  1786. {
  1787. if (student.attend==1)
  1788. {
  1789. //投票是全员参与
  1790. var datasS = voteDetailResults.FindAll(x => x.id.Equals(student.seatID));
  1791. if (datasS.IsNotEmpty())
  1792. {
  1793. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1794. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1795. addData=true;
  1796. }
  1797. else
  1798. { //T0 是没有评论别人,也没被别人评论,
  1799. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1800. addData=true;
  1801. }
  1802. //T0 是没有评论别人,也没被别人评论,
  1803. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1804. //TP 有被别人评论,且评论了别人,
  1805. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1806. }
  1807. }
  1808. }
  1809. } catch (Exception ex)
  1810. {
  1811. Console.WriteLine(itemf);
  1812. // throw new Exception($"{itemf}\n{ex.Message}\n{ex.StackTrace}");
  1813. }
  1814. var meteor_VoteSummary = smartRatingData.smartRateSummary!.meteor_VoteSummary[key];
  1815. var order = meteor_VoteSummary.OrderByDescending(x => x.result);
  1816. var maxItems = meteor_VoteSummary.FindAll(x => x.result==order.First().result);
  1817. var max= meteor_VoteSummary.FindAll(x => x.result==order.First().result).First().result;
  1818. var min = meteor_VoteSummary.FindAll(x => x.result==order.Last().result).First().result;
  1819. var sum = meteor_VoteSummary.Sum(x=>x.result);
  1820. //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60
  1821. //将每个人的积分转化为60-100
  1822. //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名
  1823. foreach (var datasD in meteor_VoteSummary)
  1824. {
  1825. //有被人评论或投票
  1826. var student = studentLessonDatas.Find(x => x.seatID!.Equals(datasD.id));
  1827. if (student!=null)
  1828. {
  1829. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1830. {
  1831. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1832. {
  1833. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1834. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1835. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1836. }
  1837. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1838. {
  1839. //TP 有被别人评论,且评论了别人,
  1840. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1841. var data= MinMaxNormalization(min, max, datasD.result);
  1842. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1843. student.rateingRecord.itemRecords[index].resultWeight=InteractWeight.T1+ data * 1.0 / maxRank * (InteractWeight.TT-InteractWeight.T1);
  1844. //获得的票数
  1845. student.rateingRecord.itemRecords[index].itemScore=datasD.result;
  1846. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1847. if (maxItems.Select(x => x.id).Contains(student.seatID))
  1848. {
  1849. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1850. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1851. }
  1852. }
  1853. }
  1854. }
  1855. }
  1856. if (addData)
  1857. {
  1858. index++;
  1859. }
  1860. }
  1861. }
  1862. //星光大评分,全员评分
  1863. var keys_GrandRating = smartRatingData.smartRateSummary?.scoreDetailResult?.Keys?.ToList();
  1864. if (keys_GrandRating.IsNotEmpty() && smartRatingData.smartRateSummary!=null && smartRatingData.smartRateSummary.meteor_ScoreSummary.IsNotEmpty())
  1865. {
  1866. bool addData = false;
  1867. type="GrandRating";
  1868. foreach (var student in studentLessonDatas)
  1869. {
  1870. if (student.attend==1)
  1871. {
  1872. if (keys_GrandRating!.Contains(student.seatID!))
  1873. {
  1874. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1875. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1876. addData = true;
  1877. }
  1878. else
  1879. {
  1880. //T0 是没有评论别人,也没被别人评论,
  1881. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1882. addData = true;
  1883. }
  1884. }
  1885. }
  1886. var order = smartRatingData.smartRateSummary.meteor_ScoreSummary.Where(x => x.result>0||!string.IsNullOrWhiteSpace(x.comment)).OrderByDescending(x => x.result);
  1887. if (order.Count()>0)
  1888. {
  1889. var maxItems = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result);
  1890. var max = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result).First().result;
  1891. var min = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.Last().result).First().result;
  1892. var sum = smartRatingData.smartRateSummary.meteor_ScoreSummary.Sum(x => x.result);
  1893. foreach (var meteor_ScoreSummary in smartRatingData.smartRateSummary.meteor_ScoreSummary)
  1894. {
  1895. var student = studentLessonDatas.Find(x => x.seatID!.Equals(meteor_ScoreSummary.id));
  1896. if (student!=null)
  1897. {
  1898. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1899. {
  1900. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1901. {
  1902. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1903. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1904. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1905. }
  1906. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1907. {
  1908. //TP 有被别人评论,且评论了别人,
  1909. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1910. var data = MinMaxNormalization(min, max, meteor_ScoreSummary.result);
  1911. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1912. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+ data * 1.0 / maxRank * (InteractWeight.TT-InteractWeight.T1);
  1913. //被评论次数
  1914. student.rateingRecord.itemRecords[index].itemScore=meteor_ScoreSummary.result;
  1915. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1916. if (maxItems.Select(x => x.id).Contains(student.seatID) &&student.rateingRecord.itemRecords[index].itemScore>0)
  1917. {
  1918. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1919. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1920. }
  1921. }
  1922. }
  1923. }
  1924. }
  1925. }
  1926. if (addData)
  1927. {
  1928. index++;
  1929. }
  1930. }
  1931. // 互评 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评)
  1932. var keys_PeerAssessment = smartRatingData.smartRateSummary?.mutualDetailSummary?.Keys?.ToList();
  1933. if (keys_PeerAssessment.IsNotEmpty() && smartRatingData.smartRateSummary?.mutualSummary!=null
  1934. && smartRatingData.smartRateSummary.mutualSummary.mutualResults.IsNotEmpty()
  1935. && smartRatingData.smartRateSummary.mutualSummary.materialInfos.IsNotEmpty())
  1936. {
  1937. bool addData = false;
  1938. type="PeerAssessment";
  1939. foreach (var student in studentLessonDatas)
  1940. {
  1941. if (student.attend==1)
  1942. {
  1943. if (keys_PeerAssessment!.Contains(student.seatID!))
  1944. {
  1945. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1946. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1947. addData = true;
  1948. }
  1949. else
  1950. {
  1951. //T0 是没有评论别人,也没被别人评论,
  1952. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1953. addData = true;
  1954. }
  1955. }
  1956. }
  1957. var order = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Where(x => x.result>0).OrderByDescending(x => x.result);
  1958. var maxItems = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result);
  1959. var max = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result).First().result;
  1960. var min = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.Last().result).First().result;
  1961. var sum = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Sum(x => x.result);
  1962. foreach (var mutualResult in smartRatingData.smartRateSummary.mutualSummary.mutualResults)
  1963. {
  1964. var student = studentLessonDatas.Find(x => x.seatID!.Equals(mutualResult.id));
  1965. if (student!=null)
  1966. {
  1967. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1968. {
  1969. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1970. {
  1971. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1972. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1973. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1974. }
  1975. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1976. {
  1977. //TP 有被别人评论,且评论了别人,
  1978. //最高分和最低分,票数最多和票数最少的占比来计算TP的占比
  1979. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1980. var data = MinMaxNormalization(min, max, mutualResult.result);
  1981. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1982. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+ data * 1.0 / maxRank * (InteractWeight.TT-InteractWeight.T1);
  1983. student.rateingRecord.itemRecords[index].itemScore=mutualResult.result;
  1984. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1985. if (maxItems.Select(x => x.id).Contains(student.seatID))
  1986. {
  1987. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1988. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1989. }
  1990. }
  1991. }
  1992. }
  1993. }
  1994. if (addData)
  1995. {
  1996. index++;
  1997. }
  1998. }
  1999. }
  2000. return studentLessonDatas;
  2001. }
  2002. /// <summary>
  2003. /// 最小-最大归一化(Min-Max Normalization)算法。这种算法通常用于将数据的特征值缩放到一个指定的范围内,通常是0到1之间,或者任何其他指定的范围。
  2004. /// </summary>
  2005. /// <returns></returns>
  2006. public double MinMaxNormalization(double min ,double max,double x )
  2007. {
  2008. //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60
  2009. //将每个人的积分转化为60-100
  2010. //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名
  2011. return x==0 ? 0 : max-min!=0 ? (x - min)*1.0 / (max - min) * (maxRank - minRank) + minRank : (x)*1.0 / (max) * (maxRank - minRank) + minRank;
  2012. }
  2013. }
  2014. public class LessonLocal
  2015. {
  2016. public LessonBase? lessonBase { get; set;}
  2017. public TimeLineData? timeLineData { get; set; }
  2018. public LessonRecord? lessonRecord { get; set; }
  2019. public List<LocalStudent> studentLessonDatas { get; set; } = new List<LocalStudent>();
  2020. public List<TaskData> taskDatas { get; set; } = new List<TaskData>();
  2021. public List<SmartRatingData> smartRatingDatas { get; set; } = new List<SmartRatingData>();
  2022. public List<IRSData> irsDatas { get; set; } = new List<IRSData>();
  2023. public List<CoworkData> coworkDatas { get; set; } = new List<CoworkData>();
  2024. public List<ExamData> examDatas { get; set; } = new List<ExamData>();
  2025. public List<TimeLineEvent> sokratesDatas { get; set; } = new List<TimeLineEvent>();
  2026. }
  2027. public class LocalStudent
  2028. {
  2029. /// <summary>
  2030. /// 出席状态 1出席,6公假,5事假,4病假,2缺席,0未签到
  2031. /// </summary>
  2032. public int attend { get; set; }
  2033. /// <summary>
  2034. /// 学生的学号
  2035. /// </summary>
  2036. public string? id { get; set; }
  2037. /// <summary>
  2038. /// 学生所在下标
  2039. /// </summary>
  2040. public int index { get; set; } = -1;
  2041. /// <summary>
  2042. /// 学生座位号
  2043. /// </summary>
  2044. public string? seatID { get; set; }
  2045. /// <summary>
  2046. /// 小组编号
  2047. /// </summary>
  2048. public string? groupId { get; set; }
  2049. }
  2050. }