LessonETLService.cs 143 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768
  1. using Azure;
  2. using Azure.Storage.Blobs.Models;
  3. using DocumentFormat.OpenXml.Office2010.Excel;
  4. using DocumentFormat.OpenXml.Office2013.Drawing.ChartStyle;
  5. using MathNet.Numerics;
  6. using Microsoft.Extensions.Logging;
  7. using OfficeOpenXml;
  8. using OpenXmlPowerTools;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Reflection;
  14. using System.Text;
  15. using System.Text.RegularExpressions;
  16. using System.Threading.Tasks;
  17. using System.Xml;
  18. using TEAMModelOS.SDK;
  19. using TEAMModelOS.SDK.DI;
  20. using TEAMModelOS.SDK.Extension;
  21. using TEAMModelOS.SDK.Models;
  22. using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
  23. namespace HTEX.Lib.ETL.Lesson
  24. {
  25. public class LessonETLService
  26. {
  27. /// <summary>
  28. /// 生成学生student-analysis.json
  29. /// </summary>
  30. /// <param name="objectiveTypes"></param>
  31. /// <param name="azureStorage"></param>
  32. /// <param name="lessonLocal"></param>
  33. /// <returns></returns>
  34. public static async Task DoStudentLessonData(List<string> objectiveTypes, AzureStorageFactory azureStorage, LessonLocal lessonLocal,DingDing _dingDing)
  35. {
  36. List<StudentLessonData> studentLessonDatas = lessonLocal.studentLessonDatas.ToJsonString().ToObject<List<StudentLessonData>>();
  37. studentLessonDatas = LessonETLService.GetBaseInfo(lessonLocal.lessonBase!, studentLessonDatas, lessonLocal?.lessonRecord?.id);
  38. studentLessonDatas = LessonETLService.GetIRSData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.irsDatas, studentLessonDatas, lessonLocal.examDatas, lessonLocal?.lessonRecord?.id);
  39. studentLessonDatas = LessonETLService.GetCoworkData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.coworkDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  40. studentLessonDatas = LessonETLService.GetExamData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.examDatas, studentLessonDatas, objectiveTypes, lessonLocal.lessonRecord.id);
  41. studentLessonDatas = LessonETLService.GetSmartRatingData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.smartRatingDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  42. studentLessonDatas = LessonETLService.GetTaskData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.taskDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  43. var pickupData = LessonETLService.GetPickupData(lessonLocal.lessonBase!, lessonLocal. timeLineData!, studentLessonDatas, lessonLocal.lessonRecord.id);
  44. studentLessonDatas= pickupData.studentLessonDatas;
  45. LessonDataAnalysisCluster lessonDataAnalysis = null ;
  46. string owner = lessonLocal.lessonRecord.scope.Equals("school") ? lessonLocal.lessonRecord.school : lessonLocal.lessonRecord.tmdid;
  47. try
  48. {
  49. bool exists= await azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis.json").ExistsAsync();
  50. if (exists)
  51. {
  52. BlobDownloadResult blobDownload = await azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis.json").DownloadContentAsync();
  53. lessonDataAnalysis = blobDownload.Content.ToObjectFromJson<LessonDataAnalysisCluster>();
  54. var lessonItems = LessonETLService.ProcessStudentDataV2(studentLessonDatas, lessonDataAnalysis);
  55. XmlDocument xmlDocument = new XmlDocument();
  56. var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
  57. xmlDocument.Load($"{runtimePath}\\summary.xml");
  58. await LessonETLService.ExportToExcelAzureBlob(lessonItems,azureStorage, owner,$"{lessonLocal.lessonRecord.id}/student-analysis.xlsx", xmlDocument);
  59. }
  60. }
  61. catch (Exception ex)
  62. {
  63. if (!ex.Message.Contains("The specified blob does not exist"))
  64. {
  65. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},lesson/analysis/analysis.json转换异常,{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
  66. }
  67. }
  68. await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studentLessonDatas.ToJsonString(), "records", $"{lessonLocal.lessonRecord.id}/student-analysis.json");
  69. }
  70. public static void GenAnalysisData(string pathAnalysis, long newest, List<TechCount> techCounts)
  71. {
  72. var yearMonthDatas = techCounts.GroupBy(x => x.yearMonth).Select(x => new { key = x.Key, list = x.ToList() });
  73. // lessonDataAnalysisMonths = new List<LessonDataAnalysisMonth>();
  74. LessonDataAnalysisCluster lessonDataAnalysisCluster = new LessonDataAnalysisCluster();
  75. foreach (var yearMonthData in yearMonthDatas)
  76. {
  77. if (string.IsNullOrWhiteSpace(yearMonthData.key))
  78. {
  79. Console.WriteLine(yearMonthData.list.Select(x => x.lessonId).ToJsonString());
  80. }
  81. LessonDataAnalysisMonth lessonDataAnalysisMonth = new LessonDataAnalysisMonth() { updateTime= newest, yearMonth= yearMonthData.key };
  82. lessonDataAnalysisMonth.pscore = yearMonthData.list.SelectMany(x => x.pscore).ToList();
  83. lessonDataAnalysisMonth.tscore = yearMonthData.list.SelectMany(x => x.tscore).ToList();
  84. lessonDataAnalysisMonth.gscore = yearMonthData.list.SelectMany(x => x.gscore).ToList();
  85. lessonDataAnalysisMonth.irs= yearMonthData.list.Where(x => x.irsCount>0).Select(x => (double)x.irsCount).ToList();
  86. lessonDataAnalysisMonth.interactNormal= yearMonthData.list.Where(x => x.interactNormalCount > 0).Select(x => (double)x.interactNormalCount).ToList();
  87. lessonDataAnalysisMonth.task = yearMonthData.list.Where(x => x.taskCount > 0).Select(x => (double)x.taskCount).ToList();
  88. lessonDataAnalysisMonth.stuCowork=yearMonthData.list.SelectMany(x => x.stuCowork).ToList();
  89. lessonDataAnalysisMonth.groupCowork=yearMonthData.list.SelectMany(x => x.groupCowork).ToList();
  90. lessonDataAnalysisMonth.pickup= yearMonthData.list.Where(x => x.pickup.Count()>0).Select(x=>x.pickup).ToList();
  91. System.IO.File.WriteAllText(Path.Combine(pathAnalysis, $"{yearMonthData.key}-m-analysis.json"), lessonDataAnalysisMonth.ToJsonString());
  92. // lessonDataAnalysisMonths.Add( lessonDataAnalysisMonth);
  93. if (lessonDataAnalysisMonth.task.IsNotEmpty())
  94. {
  95. lessonDataAnalysisCluster.task.AddRange(lessonDataAnalysisMonth.task);
  96. }
  97. if (lessonDataAnalysisMonth.irs.IsNotEmpty())
  98. {
  99. lessonDataAnalysisCluster.irs.AddRange(lessonDataAnalysisMonth.irs);
  100. }
  101. if (lessonDataAnalysisMonth.interactNormal.IsNotEmpty())
  102. {
  103. lessonDataAnalysisCluster.interactNormal.AddRange(lessonDataAnalysisMonth.interactNormal);
  104. }
  105. if (lessonDataAnalysisMonth.pscore.IsNotEmpty())
  106. {
  107. lessonDataAnalysisCluster.pscore.AddRange(lessonDataAnalysisMonth.pscore);
  108. }
  109. if (lessonDataAnalysisMonth.gscore.IsNotEmpty())
  110. {
  111. lessonDataAnalysisCluster.gscore.AddRange(lessonDataAnalysisMonth.gscore);
  112. }
  113. if (lessonDataAnalysisMonth.tscore.IsNotEmpty())
  114. {
  115. lessonDataAnalysisCluster.tscore.AddRange(lessonDataAnalysisMonth.tscore);
  116. }
  117. if (lessonDataAnalysisMonth.stuCowork.IsNotEmpty())
  118. {
  119. lessonDataAnalysisCluster.stuCowork.AddRange(lessonDataAnalysisMonth.stuCowork);
  120. }
  121. if (lessonDataAnalysisMonth.groupCowork.IsNotEmpty())
  122. {
  123. lessonDataAnalysisCluster.groupCowork.AddRange(lessonDataAnalysisMonth.groupCowork);
  124. }
  125. lessonDataAnalysisCluster.pickup.AddRange(lessonDataAnalysisMonth.pickup);
  126. }
  127. //标准差偏差N倍,视为异常数据
  128. int thresholdMultiplier = 2;
  129. lessonDataAnalysisCluster.task= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.task.OrderBy(x => x), thresholdMultiplier);
  130. lessonDataAnalysisCluster.pscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.pscore.OrderBy(x => x), thresholdMultiplier);
  131. lessonDataAnalysisCluster.gscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.gscore.OrderBy(x => x), thresholdMultiplier);
  132. lessonDataAnalysisCluster.tscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.tscore.OrderBy(x => x), thresholdMultiplier);
  133. lessonDataAnalysisCluster.irs = LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.irs.OrderBy(x => x), thresholdMultiplier);
  134. lessonDataAnalysisCluster.interactNormal=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.interactNormal.OrderBy(x => x), thresholdMultiplier);
  135. lessonDataAnalysisCluster.stuCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.stuCowork.OrderBy(x => x), thresholdMultiplier);
  136. lessonDataAnalysisCluster.groupCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.groupCowork.OrderBy(x => x), thresholdMultiplier);
  137. var all = lessonDataAnalysisCluster.interactNormal.OrderBy(x => x).ToArray();
  138. //超过60 80的
  139. double n = all.Max()+1;
  140. //var clusterInteract = HTEX.Lib.ETL.KMeansService.KMeansOptimized(d, 3);
  141. //foreach (var item in clusterInteract.OrderBy(x => x.Key))
  142. //{
  143. // lessonDataAnalysisCluster.clustersInteract.Add(new KeyValuePair<double, List<double>>(item.Value.Average(), item.Value));
  144. // Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()},weight:{item.Value.Count*1.0/d.Count()}");
  145. //}
  146. //IEnumerable<double> all = lessonDataAnalysisCluster.clustersInteract.SelectMany(x => x.Value);
  147. int pass = 0;
  148. for (var i = 1; i<n; i++)
  149. {
  150. var p = LessonETLService.GetPersent(all, i);
  151. if (p.persent>=60)
  152. {
  153. pass = i;
  154. break;
  155. }
  156. }
  157. var low = all.Where(x => x<pass).Average();
  158. int good = 0;
  159. for (var i = 1; i<n; i++)
  160. {
  161. var p = LessonETLService.GetPersent(all, i);
  162. if (p.persent>=80)
  163. {
  164. good = i;
  165. break;
  166. }
  167. }
  168. var medium = all.Where(x => x>=pass &&x<good).Average();
  169. var high = all.Where(x => x>=good).Average();
  170. lessonDataAnalysisCluster.interactGood=good;
  171. lessonDataAnalysisCluster.interactPass=pass;
  172. lessonDataAnalysisCluster.interactHigh=high;
  173. lessonDataAnalysisCluster.interactMedium=medium;
  174. lessonDataAnalysisCluster.interactLow=low;
  175. List<KeyValuePair<double, List<double>>> levelInteract = new List<KeyValuePair<double, List<double>>>()
  176. {
  177. new KeyValuePair<double, List<double>>(low, new List<double> { all.Where(x => x<pass).Min(), all.Where(x => x<pass).Max() }),
  178. new KeyValuePair<double, List<double>>(low, new List<double> { all.Where(x => x>=pass &&x<good).Min(), all.Where(x => x>=pass &&x<good).Max() }),
  179. new KeyValuePair<double, List<double>>(low, new List<double> { all.Where(x => x>=good).Min(), all.Where(x => x>=good).Max() })
  180. };
  181. lessonDataAnalysisCluster.levelInteract= levelInteract;
  182. //// 设置0 为自动规划聚类,11 则为自动规划后得到的数字。
  183. //var clusterInteract = KMeansService.KMeansOptimized(d,11,10);
  184. ////foreach (var item in clusterInteract)
  185. ////{
  186. //// Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}");
  187. ////}
  188. //foreach (var item in clusterInteract.OrderBy(x => x.Key))
  189. //{
  190. // lessonDataAnalysisCluster.clustersInteract.Add(new KeyValuePair<double, List<double>>(item.Value.Average(), item.Value));
  191. // //Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()},weight:{item.Value.Count*1.0/d.Count()}");
  192. //}
  193. System.IO.File.WriteAllText(Path.Combine(pathAnalysis, "analysis.json"), lessonDataAnalysisCluster.ToJsonString());
  194. }
  195. public static async IAsyncEnumerable<TechCount> GetTeachCount(List<LessonRecord> lessonRecords, List<string> filesLessons, string pathLessons, List<string> ignore, List<string> objectiveTypes, AzureStorageFactory azureStorage,bool force)
  196. {
  197. foreach (var item in filesLessons)
  198. {
  199. if (item.EndsWith("-local.json"))
  200. {
  201. TechCount count = null;
  202. string lessonId = item.Split("\\").Last().Replace("-local.json", "");
  203. string countFile = item.Replace("-local.json", "-count.json");
  204. if (System.IO.File.Exists(countFile))
  205. {
  206. if (System.IO.File.Exists(countFile))
  207. {
  208. string contjson = await System.IO.File.ReadAllTextAsync(countFile);
  209. count = contjson.ToObject<TechCount>();
  210. }
  211. }
  212. if (string.IsNullOrWhiteSpace(count?.yearMonth)|| force)
  213. {
  214. string localjson = await System.IO.File.ReadAllTextAsync(item);
  215. var lessonLocal = localjson.ToObject<LessonLocal>();
  216. count = new TechCount();
  217. count.lessonId=item.Split("\\").Last().Replace("-local.json", "");
  218. count.examCount= lessonLocal.examDatas.Count;
  219. count.taskCount= lessonLocal.taskDatas.Count;
  220. count.irsCount= lessonLocal.irsDatas.Count;
  221. count.interactNormalCount=count.irsCount;
  222. count.coworkCount= lessonLocal.coworkDatas.Count;
  223. count.smartRatingCount= lessonLocal.smartRatingDatas.Count;
  224. count.timeCount=lessonLocal.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();
  225. if (lessonLocal.lessonRecord!=null)
  226. {
  227. count.yearMonth=DateTimeOffset.FromUnixTimeMilliseconds(lessonLocal.lessonRecord.startTime).ToString("yyyyMM");
  228. if (lessonLocal?.lessonBase?.summary!=null)
  229. {
  230. count.smartRatingCountBase=lessonLocal.lessonBase.summary.smartRatingCount;
  231. count.irsCountBase=lessonLocal.lessonBase.summary.interactionCount;
  232. count.taskCountBase=lessonLocal.lessonBase.summary.collateTaskCount;
  233. count.coworkCountBase=lessonLocal.lessonBase.summary.coworkTaskCount;
  234. count.examCountBase=lessonLocal.lessonBase.summary.examCount;
  235. count.interactNormalCountBase= count.interactNormalCount;
  236. }
  237. if (lessonLocal?.lessonBase?.report?.clientSummaryList!=null)
  238. {
  239. count.pscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.score>0).Select(x => x.score);
  240. count.gscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.groupScore>0).Select(x => x.groupScore);
  241. count.tscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.interactScore>0).Select(x => x.interactScore);
  242. }
  243. ///处理学生数据
  244. {
  245. List<StudentLessonData> studentLessonDatas = lessonLocal.studentLessonDatas.ToJsonString().ToObject<List<StudentLessonData>>();
  246. studentLessonDatas = LessonETLService.GetBaseInfo(lessonLocal.lessonBase!, studentLessonDatas, lessonLocal?.lessonRecord?.id);
  247. studentLessonDatas = LessonETLService.GetIRSData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.irsDatas, studentLessonDatas, lessonLocal.examDatas, lessonLocal.lessonRecord.id);
  248. studentLessonDatas = LessonETLService.GetCoworkData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.coworkDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  249. studentLessonDatas = LessonETLService.GetExamData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.examDatas, studentLessonDatas, objectiveTypes, lessonLocal.lessonRecord.id);
  250. studentLessonDatas = LessonETLService.GetSmartRatingData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.smartRatingDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  251. studentLessonDatas = LessonETLService.GetTaskData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.taskDatas, studentLessonDatas, lessonLocal.lessonRecord.id);
  252. var pickupData = LessonETLService.GetPickupData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, studentLessonDatas, lessonLocal.lessonRecord.id);
  253. studentLessonDatas= pickupData.studentLessonDatas;
  254. count.pickup= pickupData.pickup ;
  255. // var techCount = techCounts.Find(x => !string.IsNullOrWhiteSpace(x.lessonId) && !string.IsNullOrWhiteSpace(lessonLocal?.lessonRecord?.id) && x.lessonId.Equals(lessonLocal.lessonRecord.id));
  256. int sumUpload = 0;
  257. int taskCount = 0;
  258. int maxUpload = 0;
  259. HashSet<string> pickUp = new HashSet<string>();
  260. foreach (var stu in studentLessonDatas)
  261. {
  262. var countS = stu.taskRecord.itemRecords.Where(x => x.optCount>0);
  263. if (countS.Count()>0)
  264. {
  265. int stuUploadmax = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Max(x => x.optCount);
  266. if (stuUploadmax> maxUpload)
  267. {
  268. maxUpload=stuUploadmax;
  269. }
  270. }
  271. int stuUpload = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Sum(x => x.optCount);
  272. sumUpload+=stuUpload;
  273. if (stu.taskRecord.itemRecords.Count()> taskCount)
  274. {
  275. taskCount=stu.taskRecord.itemRecords.Count();
  276. }
  277. var stu_scores = stu.coworkRecord.itemRecords.Where(x => x.itemScore>0).Select(x => x.itemScore);
  278. if (stu_scores!=null && stu_scores.Count()>0)
  279. {
  280. count.stuCowork.AddRange(stu_scores);
  281. }
  282. var grp_scores = stu.group_coworkScore.Where(x => x>0);
  283. if (grp_scores!=null && grp_scores.Count()>0)
  284. {
  285. count.groupCowork.AddRange(grp_scores);
  286. }
  287. //if (stu.pickups.IsNotEmpty())
  288. //{
  289. // foreach (var pickup in stu.pickups)
  290. // {
  291. // pickUp.Add(pickup);
  292. // }
  293. //}
  294. }
  295. if (studentLessonDatas.Count>0&& taskCount>0 && maxUpload>0)
  296. {
  297. var avgUpload = sumUpload*1.0/(studentLessonDatas.Count *taskCount);
  298. count.upload.Add(new List<double>() { avgUpload, maxUpload });
  299. }
  300. //if (pickUp.Count>0)
  301. //{
  302. // count.pickup.AddRange(pickUp.ToList());
  303. //}
  304. string owner = lessonLocal.lessonRecord.scope.Equals("school") ? lessonLocal.lessonRecord.school : lessonLocal.lessonRecord.tmdid;
  305. // if (!azureStorage.GetBlobContainerClient(owner).GetBlobClient($"records/{lessonLocal.lessonRecord.id}/student-analysis.json").Exists())
  306. {
  307. }
  308. // if (!System.IO.File.Exists($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json"))
  309. {
  310. }
  311. if (force)
  312. {
  313. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-count.json", count.ToJsonString());
  314. }
  315. else
  316. {
  317. if (lessonRecords.FindAll(x => x.id.Equals(lessonLocal.lessonRecord.id)).IsNotEmpty())
  318. {
  319. // await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json", studentLessonDatas.ToJsonString());
  320. //只有不是强制更新的时候再去刷新线上的json文件。
  321. await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studentLessonDatas.ToJsonString(), "records", $"{lessonLocal.lessonRecord.id}/student-analysis.json");
  322. await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-count.json", count.ToJsonString());
  323. }
  324. }
  325. }
  326. }
  327. }
  328. if (count!=null)
  329. {
  330. yield return count;
  331. }
  332. }
  333. }
  334. }
  335. public static List<StudentLessonData> GetBaseInfo(LessonBase lessonBase, List<StudentLessonData> studentLessonDatas, string? id)
  336. {
  337. if (lessonBase?.report?.clientSummaryList !=null && lessonBase.report.clientSummaryList.IsNotEmpty())
  338. {
  339. foreach (var item in lessonBase.report.clientSummaryList)
  340. {
  341. var student = studentLessonDatas.Find(x => x.seatID!=null && x.seatID.Equals($"{item.seatID}"));
  342. if (student!= null)
  343. {
  344. student.gscore= item.groupScore;
  345. student.tscore= item.interactScore;
  346. student.pscore= item.score;
  347. }
  348. }
  349. }
  350. return studentLessonDatas;
  351. }
  352. public static async IAsyncEnumerable<LessonLocal> GetLessonLocal(List<LessonRecord> lessonRecords, List<string> localIds, AzureStorageFactory _azureStorage, string pathLessons)
  353. {
  354. foreach (var lessonRecord in lessonRecords)
  355. {
  356. string scope = lessonRecord.scope;
  357. string owner = lessonRecord.scope.Equals("school") ? lessonRecord.school : lessonRecord.tmdid;
  358. if (localIds.Contains(lessonRecord.id))
  359. {
  360. continue;
  361. }
  362. string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(lessonRecord.startTime).ToString("yyyyMM");
  363. LessonLocal lessonLocal = new LessonLocal { lessonRecord=lessonRecord };
  364. if (System.IO.File.Exists($"{pathLessons}\\MM{yearMonthPath}\\{lessonRecord.id}-local.json"))
  365. {
  366. string jsonp = await System.IO.File.ReadAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{lessonRecord.id}-local.json");
  367. lessonLocal = jsonp.ToObject<LessonLocal>();
  368. }
  369. else
  370. {
  371. List<string> files = new List<string>()
  372. {
  373. $"/records/{lessonRecord.id}/IES/TimeLine.json",
  374. $"/records/{lessonRecord.id}/IES/base.json",
  375. $"/records/{lessonRecord.id}/IES/Task.json",
  376. $"/records/{lessonRecord.id}/IES/SmartRating.json",
  377. $"/records/{lessonRecord.id}/IES/IRS.json",
  378. $"/records/{lessonRecord.id}/IES/Cowork.json",
  379. $"/records/{lessonRecord.id}/Sokrates/SokratesRecords.json",
  380. };
  381. lessonLocal = new LessonLocal { lessonRecord=lessonRecord };
  382. lessonLocal = await GetLessonFiles(lessonLocal, files, owner, _azureStorage);
  383. }
  384. if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student!=null)
  385. {
  386. var baseData = GetBaseData(lessonLocal.lessonBase!);
  387. lessonLocal.studentLessonDatas= baseData.studentLessonDatas;
  388. List<ExamData> examDatas = await GetExamInfo(lessonRecord, lessonLocal.timeLineData, _azureStorage, owner);
  389. lessonLocal.examDatas = examDatas;
  390. lessonLocal.sokratesDatas= lessonLocal.sokratesDatas.IsNotEmpty() ? lessonLocal.sokratesDatas : lessonLocal.timeLineData!=null ? lessonLocal.timeLineData.events : new List<TimeLineEvent>();
  391. }
  392. yield return lessonLocal;
  393. }
  394. }
  395. private static async Task<LessonLocal> GetLessonFiles(LessonLocal lessonLocal, List<string> files, string owner, AzureStorageFactory _azureStorage)
  396. {
  397. await Parallel.ForEachAsync(files, async (file, _) =>
  398. {
  399. try
  400. {
  401. var exists = _azureStorage.GetBlobContainerClient(owner).GetBlobClient(file).Exists();
  402. if (exists)
  403. {
  404. BlobDownloadResult blobDownloadResult = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient(file).DownloadContentAsync();
  405. switch (true)
  406. {
  407. case bool when file.Contains("IES/TimeLine.json"):
  408. lessonLocal.timeLineData= blobDownloadResult.Content.ToObjectFromJson<TimeLineData>();
  409. break;
  410. case bool when file.Contains("IES/base.json"):
  411. lessonLocal.lessonBase= blobDownloadResult.Content.ToObjectFromJson<LessonBase>();
  412. break;
  413. case bool when file.Contains("IES/Task.json"):
  414. lessonLocal.taskDatas= blobDownloadResult.Content.ToObjectFromJson<List<TaskData>>();
  415. break;
  416. case bool when file.Contains("IES/SmartRating.json"):
  417. lessonLocal.smartRatingDatas= blobDownloadResult.Content.ToObjectFromJson<List<SmartRatingData>>();
  418. break;
  419. case bool when file.Contains("IES/IRS.json"):
  420. lessonLocal.irsDatas= blobDownloadResult.Content.ToObjectFromJson<List<IRSData>>();
  421. break;
  422. case bool when file.Contains("IES/Cowork.json"):
  423. lessonLocal.coworkDatas= blobDownloadResult.Content.ToObjectFromJson<List<CoworkData>>();
  424. break;
  425. case bool when file.Contains("Sokrates/SokratesRecords.json"):
  426. lessonLocal.sokratesDatas= blobDownloadResult.Content.ToObjectFromJson<List<TimeLineEvent>>();
  427. break;
  428. }
  429. }
  430. }
  431. catch (RequestFailedException ex)
  432. {
  433. Console.WriteLine($"{file},{ex.Message},{ex.StackTrace}");
  434. }
  435. catch (Exception ex)
  436. {
  437. Console.WriteLine($"{file},{ex.Message},{ex.StackTrace}");
  438. }
  439. });
  440. return lessonLocal;
  441. }
  442. /// <summary>
  443. /// 处理base.json的数据
  444. /// </summary>
  445. /// <param name="lessonRecord"></param>
  446. /// <param name="lessonBase"></param>
  447. /// <returns></returns>
  448. public static (LessonBase lessonBase, List<LocalStudent> studentLessonDatas) GetBaseData(LessonBase lessonBase)
  449. {
  450. //处理学生定位数据
  451. List<LocalStudent> studentLessonDatas = new List<LocalStudent>();
  452. int index = 0;
  453. try
  454. {
  455. if (lessonBase!=null)
  456. {
  457. lessonBase.student.ForEach(x =>
  458. {
  459. int attend = 0;
  460. var client = lessonBase.report.clientSummaryList.Find(y => y.seatID == x.seatID);
  461. if (client!=null)
  462. {
  463. attend=client.attendState;
  464. }
  465. studentLessonDatas.Add(new LocalStudent()
  466. {
  467. id = x.id,
  468. index = index,
  469. seatID =$"{x.seatID}",
  470. groupId = x.groupId,
  471. attend= attend
  472. });
  473. index++;
  474. });
  475. }
  476. }
  477. catch (Exception ex)
  478. {
  479. Console.WriteLine(lessonBase.ToJsonString());
  480. }
  481. return (lessonBase, studentLessonDatas);
  482. }
  483. /// <summary>
  484. ///单独处理挑人的逻辑
  485. ///是否从小组里面挑人。
  486. ///不需要去重页面,直接获取挑人大类 PickupResult
  487. ///小类处理:PickupRight , PickupOption , PickupNthGrp ,PickupEachGrp ,PickupDiff , PickupResult 挑人算不算互动?? 读取PickupMemberId "[\r\n 35\r\n]"
  488. /// </summary>
  489. /// <param name="lessonBase"></param>
  490. /// <param name="timeLineData"></param>
  491. /// <param name="studentLessonDatas"></param>
  492. /// <param name="lessonId"></param>
  493. /// <returns></returns>
  494. public static (List<StudentLessonData> studentLessonDatas, List<string> pickup) GetPickupData(LessonBase lessonBase, TimeLineData timeLineData, List<StudentLessonData> studentLessonDatas, string lessonId)
  495. {
  496. var enventsPickup = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && x.Event.Equals("PickupResult"));
  497. List<string> pickup = new List<string>();
  498. if (enventsPickup.IsNotEmpty())
  499. {
  500. foreach (var item in enventsPickup)
  501. {
  502. List<int> mbrs = item.PickupMemberId.ToObject<List<int>>();
  503. // 挑人挑中 TT ,没有挑中 T1
  504. foreach (var studentLessonData in studentLessonDatas)
  505. {
  506. var mbr = mbrs.FindAll(x => studentLessonData.seatID!.Equals($"{x}"));
  507. if (mbr.IsNotEmpty())
  508. {
  509. foreach (var m in mbr)
  510. {
  511. studentLessonData.attend=1;
  512. //studentLessonData.interactRecord.interactRecords.Add(new ItemRecord()
  513. //{
  514. // resultWeight = InteractWeight.TT,
  515. // resultType=InteractReultType.TT,
  516. // itemType = string.IsNullOrWhiteSpace(item.PickupType) ? "PickupResult" : item.PickupType
  517. //});
  518. studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "1--PickupResult" : $"1--{item.PickupType}");
  519. }
  520. }
  521. else
  522. {
  523. //处理未挑中的
  524. if (studentLessonData.attend==1)
  525. {
  526. studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "0--PickupResult" : $"0--{item.PickupType}");
  527. }
  528. }
  529. }
  530. pickup.Add(item.PickupType);
  531. }
  532. }
  533. return (studentLessonDatas, pickup);
  534. }
  535. /// <summary>
  536. ///读取互动信息
  537. ///Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad'
  538. /// 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns' 抢权(新), 'BuzrLoad'抢权(旧)
  539. ///TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答
  540. ///读取IRS.json
  541. /// </summary>
  542. /// <param name="lessonBase"></param>
  543. /// <param name="timeLineData"></param>
  544. /// <param name="irsDatas"></param>
  545. /// <param name="studentLessonDatas"></param>
  546. /// <param name="examDatas"></param>
  547. /// <param name="itemFiles"></param>
  548. /// <returns></returns>
  549. public static List<StudentLessonData> GetIRSData(LessonBase lessonBase, TimeLineData timeLineData, List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas, List<ExamData> examDatas, string lessonId)
  550. {
  551. List<string> interactTypes = new List<string>() { "PopQuesLoad", "ReAtmpAnsStrt", "BuzrAns", "BuzrLoad" };
  552. //去重页面
  553. 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() });
  554. if (enventsInteract!= null && enventsInteract.Count()>0)
  555. {
  556. var keys = enventsInteract.Select(x => x.key).ToList();
  557. foreach (var item in enventsInteract)
  558. {
  559. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item);
  560. }
  561. //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中
  562. 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() });
  563. if (envents_other!=null && envents_other.Count()>0)
  564. {
  565. foreach (var item in envents_other)
  566. {
  567. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item);
  568. }
  569. }
  570. }
  571. else
  572. {
  573. //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中
  574. if (timeLineData!=null)
  575. {
  576. var envents_other = timeLineData.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid)).GroupBy(x => x.Pgid).Select(x => new { key = x.Key, list = x.ToList() });
  577. if (envents_other!=null && envents_other.Count()>0)
  578. {
  579. foreach (var item in envents_other)
  580. {
  581. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item);
  582. }
  583. }
  584. }
  585. else
  586. {
  587. foreach (var item in irsDatas.Select(x => x.pageID))
  588. {
  589. ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, new { key = item });
  590. }
  591. }
  592. }
  593. return studentLessonDatas;
  594. }
  595. private static List<StudentLessonData> ProcessIRSPageData(List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas, List<ExamData> examDatas, dynamic item)
  596. {
  597. var irsDataPages = irsDatas.Where(y => item.key.Equals(y.pageID));
  598. foreach (var irsDataPage in irsDataPages)
  599. {
  600. //检查是否设置正确答案。
  601. var answers_q = irsDataPage.question?["exercise"]?["answer"]?.ToJsonString().ToObject<List<string>>();
  602. //根据题去找对应的试卷和评测信息
  603. var question_id = $"{irsDataPage.question?["id"]}";
  604. var examData = examDatas.Where(x => x.paper!=null && x.paper.slides.Exists(x => !string.IsNullOrWhiteSpace(x.url) && x.url.Equals($"{question_id}.json"))).FirstOrDefault();
  605. List<string> answers = new List<string>();
  606. answers_q?.ForEach(x => {
  607. if (!string.IsNullOrWhiteSpace(x))
  608. {
  609. answers.Add(x);
  610. }
  611. });
  612. var _objective = irsDataPage.question?["exercise"]?["objective"];
  613. var scoreNode = irsDataPage.question?["exercise"]?["score"];
  614. var _type = irsDataPage.question?["exercise"]?["type"];
  615. var _answerType = irsDataPage.question?["exercise"]?["answerType"];//file,audio,text,image
  616. var qitem = irsDataPage.question?["item"]?.AsArray();
  617. if (qitem!=null && qitem.Count()>0)
  618. {
  619. for (var i = 0; i<qitem.Count(); i++)
  620. {
  621. qitem[i]!["question"]="";
  622. }
  623. }
  624. double questionScore = 0;
  625. bool objective = false;
  626. if (_objective!=null)
  627. {
  628. objective = _objective.GetValue<bool>();
  629. }
  630. //题型
  631. string type = string.Empty;
  632. if (_type!=null)
  633. {
  634. //题型
  635. type = _type.GetValue<string>();
  636. List<string> types = new List<string>() { "single", "multiple", "judge", "sortmultiple" };
  637. if (types.Contains(type))
  638. {
  639. objective = true;
  640. }
  641. else
  642. {
  643. objective = false;
  644. }
  645. }
  646. if (_answerType!=null)
  647. {
  648. _answerType.GetValue<string>();
  649. //暂不处理,可能存在依然传文字的情况
  650. //不是文本作答的处理,题目不是客观题,答案不记录
  651. //if (!_answerType.Equals("text"))
  652. //{
  653. // objective=false;
  654. // answers=new List<string>();
  655. //}
  656. }
  657. if (scoreNode!=null)
  658. {
  659. double.TryParse(scoreNode.ToString(), out questionScore);
  660. }
  661. string interactType = string.Empty;
  662. if (irsDataPage.clientAnswers.IsNotEmpty())
  663. {
  664. //第一个list是几轮,一次作答,二次作答, 第二个list是学生的下标, 第三个list是 答案
  665. List<List<List<string>>> clientAnswers = new List<List<List<string>>>();
  666. foreach (var key in irsDataPage.clientAnswers.Keys)
  667. {
  668. clientAnswers.Add(irsDataPage.clientAnswers[key]);
  669. }
  670. // 获取第一个列表的长度作为比较基准
  671. int firstListLength = clientAnswers.First().Count;
  672. bool isSameLength = true;
  673. // 遍历剩余的列表并检查它们的长度是否与第一个列表相同
  674. foreach (var innerList in clientAnswers.Skip(1))
  675. {
  676. if (innerList.Count != firstListLength)
  677. {
  678. isSameLength = false;
  679. break;
  680. }
  681. }
  682. //并检查学生集合的长度是否与第一个列表相同
  683. if (isSameLength && studentLessonDatas.Count()==firstListLength)
  684. {
  685. for (int index = 0; index< clientAnswers[0].Count; index++)
  686. {
  687. var student = studentLessonDatas[index];
  688. double studentScore = 0;
  689. if (examData!=null && examData.examClassResult.IsNotEmpty())
  690. {
  691. var examResultIndex = examData.examClassResult.First().studentIds.IndexOf(student.id);
  692. var questionIndex = examData.paper.slides.Select(x => x.url).ToList().IndexOf($"{question_id}.json");
  693. if (examResultIndex>=0
  694. && examData.examClassResult.First().studentScores.Count>=(examResultIndex+1) //防止索引越界
  695. && examData.examClassResult.First().studentScores[examResultIndex].Count>=(questionIndex+1)) //防止索引越界
  696. {
  697. //获取index学生在questionIndex题的分数
  698. studentScore = examData.examClassResult.First().studentScores[examResultIndex][questionIndex];
  699. }
  700. }
  701. //index 代表学生下标
  702. List<ItemRecord> interactRecords = new List<ItemRecord>();
  703. if (clientAnswers.Count==1)
  704. {
  705. //即问即答
  706. interactType = "PopQuesLoad";
  707. var ans0 = clientAnswers[0][index];
  708. var IS0 = GetInteractResultHasAnswer(answers, ans0, objective, type, questionScore, studentScore);
  709. interactRecords.Add(new ItemRecord()
  710. {
  711. resultWeight = IS0.weight,
  712. resultType=IS0.reultType,
  713. itemType= interactType,
  714. criterion= questionScore,
  715. itemScore= IS0.interactScore
  716. });
  717. }
  718. if (clientAnswers.Count==2)
  719. {
  720. //二次作答
  721. interactType="ReAtmpAnsStrt";
  722. var ans1 = clientAnswers[1][index];
  723. var IS1 = GetInteractResultHasAnswer(answers, ans1, objective, type, questionScore, studentScore);
  724. interactRecords.Add(new ItemRecord()
  725. {
  726. resultWeight = IS1.weight,
  727. resultType=IS1.reultType,
  728. itemType= interactType,
  729. criterion= questionScore,
  730. itemScore= IS1.interactScore
  731. });
  732. }
  733. if (clientAnswers.Count>2)
  734. {
  735. //三次作答
  736. interactType="TeAtmpAnsStrt";
  737. var ans2 = clientAnswers[2][index];
  738. var IS2 = GetInteractResultHasAnswer(answers, ans2, objective, type, questionScore, studentScore);
  739. interactRecords.Add(new ItemRecord()
  740. {
  741. resultWeight = IS2.weight,
  742. resultType=IS2.reultType,
  743. itemType= interactType,
  744. criterion= questionScore,
  745. itemScore= IS2.interactScore
  746. });
  747. }
  748. if (studentLessonDatas[index].attend==1)
  749. {
  750. studentLessonDatas[index].interactRecord.interactRecords.AddRange(interactRecords);
  751. }
  752. }
  753. }
  754. }
  755. //是否抢权作答的模式
  756. if (irsDataPage.isBuzz)
  757. {
  758. interactType = "BuzrAns";
  759. //处理参与抢权的
  760. Dictionary<string, ItemRecord> buzzParticipants = new Dictionary<string, ItemRecord>();
  761. foreach (var buzzParticipant in irsDataPage.buzzParticipants)
  762. {
  763. var studentData = studentLessonDatas.Find(x => x.seatID!.Equals(buzzParticipant));
  764. if (studentData != null)
  765. {
  766. buzzParticipants[buzzParticipant]=new ItemRecord() { resultWeight = InteractWeight.T1, itemType= interactType, resultType= InteractReultType.T1 };
  767. }
  768. }
  769. //处理抢权成功的
  770. foreach (var buzzClient in irsDataPage.buzzClients)
  771. {
  772. buzzParticipants[buzzClient]=new ItemRecord() { resultWeight = InteractWeight.TT, itemType= interactType, resultType= InteractReultType.TT };
  773. }
  774. foreach (var studentLessonData in studentLessonDatas)
  775. {
  776. if (buzzParticipants.ContainsKey(studentLessonData.seatID!))
  777. {
  778. //处理已经有抢权结果的数据
  779. studentLessonData.attend=1;
  780. studentLessonData.interactRecord.interactRecords.Add(buzzParticipants[studentLessonData.seatID!]);
  781. }
  782. else
  783. {
  784. if (studentLessonData.attend==1)
  785. {
  786. //处理未参与抢权的
  787. studentLessonData.interactRecord.interactRecords.Add(new ItemRecord() { resultWeight = InteractWeight.T0, itemType = interactType, resultType= InteractReultType.T0 });
  788. }
  789. }
  790. }
  791. }
  792. }
  793. return studentLessonDatas;
  794. }
  795. private static (double weight, string reultType, double interactScore) GetInteractResultHasAnswer(List<string>? answers, List<string> ans0, bool objective, string type, double questionScore, double studentScore)
  796. {
  797. //List<string> ans0 = new List<string>();
  798. //ans?.ForEach(x => {
  799. // if (!string.IsNullOrWhiteSpace(x))
  800. // {
  801. // ans0.Add(x);
  802. // }
  803. // else { ans.Add("");}
  804. //});
  805. double weight = InteractWeight.T0;
  806. string reultType = InteractReultType.T0;
  807. double interactScore = 0;
  808. if (answers.IsNotEmpty())
  809. {
  810. if (ans0.IsNotEmpty())
  811. {
  812. if (objective) //客观题
  813. {
  814. //标准答案等于作答的结果
  815. if (answers!.Count == ans0.Count)
  816. {
  817. if (answers.All(item => ans0.Contains(item)))
  818. {
  819. //完全正确
  820. weight= InteractWeight.TT;
  821. reultType= InteractReultType.TT;
  822. interactScore= studentScore==0 ? questionScore : studentScore;
  823. }
  824. else
  825. {
  826. //作答错误
  827. weight= InteractWeight.T1;
  828. reultType = InteractReultType.T1;
  829. interactScore= studentScore;
  830. }
  831. }
  832. //标准答案比作答的结果多
  833. else if (answers!.Count > ans0.Count)
  834. {
  835. if (ans0.All(item => answers.Contains(item)))
  836. {
  837. //部分正确
  838. weight= InteractWeight.TP;
  839. reultType = InteractReultType.TP;
  840. // 2 * 0.3 * 10= 6
  841. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  842. }
  843. else
  844. {
  845. //作答错误
  846. weight= InteractWeight.T1;
  847. reultType = InteractReultType.T1;
  848. interactScore= studentScore;
  849. }
  850. }
  851. //标准答案比作答结果少
  852. else
  853. {
  854. //作答错误
  855. weight= InteractWeight.T1;
  856. reultType = InteractReultType.T1;
  857. interactScore= studentScore;
  858. }
  859. }
  860. else
  861. {
  862. //填空题
  863. if ("complete".Equals(type) && answers!.Count==ans0.Count)
  864. {
  865. bool hasT = false;
  866. bool hasF = false;
  867. for (int i = 0; i < answers!.Count; i++)
  868. {
  869. if (answers[i].Equals(ans0[i]))
  870. {
  871. hasT=true;
  872. }
  873. else
  874. {
  875. hasF=true;
  876. }
  877. }
  878. if (hasT && !hasF)
  879. {
  880. //完全正确
  881. weight= InteractWeight.TT;
  882. reultType = InteractReultType.TT;
  883. interactScore= studentScore==0 ? questionScore : studentScore;
  884. }
  885. else if (hasT && hasF)
  886. {
  887. //部分正确
  888. weight= InteractWeight.TP;
  889. reultType = InteractReultType.TP;
  890. // 2 * 0.3 * 10= 6
  891. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  892. }
  893. else if (!hasT && hasF)
  894. {
  895. //没有正确的,但有错误的,代表参与了
  896. weight= InteractWeight.T1;
  897. reultType = InteractReultType.T1;
  898. interactScore= studentScore;
  899. }
  900. else if (!hasT && !hasF)
  901. {
  902. //没有正确的,也没有错误的,代表没有作答
  903. weight= InteractWeight.T0;
  904. reultType = InteractReultType.T0;
  905. interactScore= studentScore;
  906. }
  907. }
  908. else
  909. {
  910. //主观题,完全匹配的
  911. if (answers!.All(item => ans0.Contains(item)))
  912. {
  913. //完全正确
  914. weight= InteractWeight.TT;
  915. reultType = InteractReultType.TT;
  916. interactScore= studentScore==0 ? questionScore : studentScore;
  917. }
  918. else
  919. { // 使用LINQ查询来判断是否有匹配的答案
  920. bool hasMatchingAnswer = answers!.Intersect(ans0).Any();
  921. if (hasMatchingAnswer)
  922. {
  923. //主观题回答正确即为完全正确
  924. weight= InteractWeight.TT;
  925. reultType = InteractReultType.TT;
  926. interactScore= studentScore==0 ? questionScore : studentScore;
  927. }
  928. else
  929. {
  930. //优先根据得分与标准分的占比算出得分率,如果没有得分率,如果是直接从互动,不知道是评测的 需要先去评测找作答得分。,则采用Levenshtein距离来评估两个字符串的相似度
  931. //没有匹配上答案,则采用Levenshtein距离来评估两个字符串的相似度
  932. if (questionScore>0)
  933. {
  934. if (studentScore>0)
  935. {
  936. weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1);
  937. if (weight==InteractWeight.T1)
  938. {
  939. reultType = InteractReultType.T1;
  940. interactScore=studentScore;
  941. }
  942. else if (weight>InteractWeight.TT)
  943. {
  944. reultType = InteractReultType.TT;
  945. interactScore=studentScore==0 ? questionScore : studentScore;
  946. }
  947. else
  948. {
  949. reultType = InteractReultType.TP;
  950. // 2 * 0.3 * 10= 6
  951. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  952. }
  953. }
  954. else
  955. {
  956. weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1));
  957. if (weight==InteractWeight.T1)
  958. {
  959. reultType = InteractReultType.T1;
  960. interactScore=studentScore;
  961. }
  962. else if (weight>InteractWeight.TT)
  963. {
  964. reultType = InteractReultType.TT;
  965. interactScore=studentScore==0 ? questionScore : studentScore;
  966. }
  967. else
  968. {
  969. reultType = InteractReultType.TP;
  970. // 2 * 0.3 * 10= 6
  971. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  972. }
  973. }
  974. }
  975. else
  976. {
  977. weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1));
  978. if (weight==InteractWeight.T1)
  979. {
  980. reultType = InteractReultType.T1;
  981. interactScore=studentScore;
  982. }
  983. else if (weight>InteractWeight.TT)
  984. {
  985. reultType = InteractReultType.TT;
  986. interactScore=studentScore==0 ? questionScore : studentScore;
  987. }
  988. else
  989. {
  990. reultType = InteractReultType.TP;
  991. // 2 * 0.3 * 10= 6
  992. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  993. }
  994. }
  995. }
  996. }
  997. }
  998. }
  999. }
  1000. else
  1001. {
  1002. //没有作答
  1003. weight= InteractWeight.T0;
  1004. reultType = InteractReultType.T0;
  1005. interactScore=studentScore;
  1006. }
  1007. }
  1008. else
  1009. {
  1010. //没有标准答案的情况
  1011. if (ans0.IsNotEmpty())
  1012. {
  1013. bool hasAns = false;
  1014. ans0.ForEach(x => {
  1015. if (!string.IsNullOrWhiteSpace(x))
  1016. {
  1017. hasAns = true;
  1018. }
  1019. });
  1020. if (hasAns)
  1021. {
  1022. //作答了
  1023. weight= InteractWeight.T1;
  1024. reultType = InteractReultType.T1;
  1025. interactScore=studentScore;
  1026. }
  1027. else
  1028. {
  1029. //没有作答
  1030. weight= InteractWeight.T0;
  1031. reultType = InteractReultType.T0;
  1032. interactScore=studentScore;
  1033. }
  1034. }
  1035. else
  1036. {
  1037. //没有作答
  1038. weight= InteractWeight.T0;
  1039. reultType = InteractReultType.T0;
  1040. interactScore=studentScore;
  1041. }
  1042. //如果教师手动给了分或AI评分
  1043. if (questionScore>0 && studentScore>0)
  1044. {
  1045. weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1);
  1046. if (weight==InteractWeight.T1)
  1047. {
  1048. reultType = InteractReultType.T1;
  1049. interactScore=studentScore;
  1050. }
  1051. else if (weight>InteractWeight.TT)
  1052. {
  1053. reultType = InteractReultType.TT;
  1054. interactScore=studentScore==0 ? questionScore : studentScore;
  1055. }
  1056. else
  1057. {
  1058. reultType = InteractReultType.TP;
  1059. interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore;
  1060. }
  1061. }
  1062. }
  1063. return (weight, reultType, interactScore);
  1064. }
  1065. /// <summary>
  1066. /// C# 代码 如何判断两句话是否一个意思,非机器学习的算法。使用Levenshtein距离来评估两个字符串的相似度,但是不能判断它们是否表达了同一个意思,后续借助AI实现
  1067. /// </summary>
  1068. /// <param name="s1"></param>
  1069. /// <param name="s2"></param>
  1070. /// <returns></returns>
  1071. public static double CalculateSimilarity(string s1, string s2)
  1072. {
  1073. int n = s1.Length;
  1074. int m = s2.Length;
  1075. int[,] d = new int[n + 1, m + 1];
  1076. for (int i = 0; i <= n; i++)
  1077. {
  1078. d[i, 0] = i;
  1079. }
  1080. for (int j = 0; j <= m; j++)
  1081. {
  1082. d[0, j] = j;
  1083. }
  1084. for (int i = 1; i <= n; i++)
  1085. {
  1086. for (int j = 1; j <= m; j++)
  1087. {
  1088. int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
  1089. d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
  1090. }
  1091. }
  1092. return (1.0 - ((double)d[n, m] / Math.Max(s1.Length, s2.Length)));
  1093. }
  1094. public static async Task<List<ExamData>> GetExamInfo(LessonRecord item, TimeLineData? timeLineData, AzureStorageFactory _azureStorage/*,ILogger<LessonETLService> _logger*/, string owner)
  1095. {
  1096. //读取ExamData
  1097. List<ExamData> examDatas = new List<ExamData>();
  1098. try
  1099. {
  1100. var examPages = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && !string.IsNullOrWhiteSpace(x.ExamId));
  1101. if (examPages!=null && examPages.Count()>0)
  1102. {
  1103. var examsFiles = await _azureStorage.GetBlobContainerClient(owner).List($"records/{item.id}/Exam");
  1104. var paperFiles = await _azureStorage.GetBlobContainerClient(owner).List($"records/{item.id}/ExamPaper");
  1105. foreach (var examsFile in examsFiles)
  1106. {
  1107. if (examsFile.EndsWith("Exam.json"))
  1108. {
  1109. ExamData? examData = null;
  1110. try
  1111. {
  1112. BlobDownloadResult examDataDownload = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient(examsFile).DownloadContentAsync();
  1113. var str = examDataDownload.Content.ToString().Replace("\r\n", "").Replace("\ufeff", "").Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1");
  1114. examData= str.ToObject<ExamData>();
  1115. // examData = examDataDownload.Content.ToObjectFromJson<ExamData>();
  1116. }
  1117. catch (Exception ex)
  1118. {
  1119. if (!ex.Message.Contains("The specified blob does not exist"))
  1120. {
  1121. // _logger.LogError(ex, $"文件不存在:{examsFile}");
  1122. }
  1123. }
  1124. if (examData!=null && examData.exam.papers.IsNotEmpty())
  1125. {
  1126. string paperId = examData.exam.papers.First().id;
  1127. if (_azureStorage.GetBlobContainerClient(owner).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").Exists())
  1128. {
  1129. LessonPaper lessonPaper = null;
  1130. try
  1131. {
  1132. BlobDownloadResult paperblobDownload = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").DownloadContentAsync();
  1133. lessonPaper = paperblobDownload.Content.ToObjectFromJson<LessonPaper>();
  1134. examData.paper = lessonPaper;
  1135. }
  1136. catch (Exception ex)
  1137. {
  1138. if (!ex.Message.Contains("The specified blob does not exist"))
  1139. {
  1140. // _logger.LogError(ex, $"文件不存在:/records/{item.id}/ExamPaper/{paperId}/index.json");
  1141. }
  1142. }
  1143. }
  1144. examDatas.Add(examData);
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. catch (Exception ex)
  1151. {
  1152. // _logger.LogError(ex, ex.Message);
  1153. }
  1154. return examDatas;
  1155. }
  1156. /// <summary>
  1157. /// 获取课中评测数据
  1158. /// </summary>
  1159. /// <param name="lessonBase"></param>
  1160. /// <param name="timeLineData"></param>
  1161. /// <param name="examDatas"></param>
  1162. /// <param name="studentLessonDatas"></param>
  1163. /// <param name="objectiveTypes"></param>
  1164. /// <returns></returns>
  1165. public static List<StudentLessonData> GetExamData(LessonBase lessonBase, TimeLineData timeLineData, List<ExamData> examDatas, List<StudentLessonData> studentLessonDatas, List<string> objectiveTypes, string lessonId)
  1166. {
  1167. foreach (var examData in examDatas)
  1168. {
  1169. //直接取第一个元素的试卷,因为在HiTeach中,只会是一个试卷(一个科目),一个班参与。
  1170. var allocation = examData?.exam?.papers?.FirstOrDefault()?.point?.Sum();
  1171. var answersStd = examData?.exam?.papers?.FirstOrDefault()?.answers;
  1172. List<List<string>> answers = new List<List<string>>();
  1173. if (answersStd!=null)
  1174. {
  1175. answersStd.ForEach(x => //去除[""]此种类型的标准答案,转为[]
  1176. {
  1177. List<string> ans = new List<string>();
  1178. if (x.Count!=0)
  1179. {
  1180. if (x.Count==1)
  1181. {
  1182. if (string.IsNullOrWhiteSpace(x[0]))
  1183. {
  1184. answers.Add(ans);
  1185. }
  1186. else
  1187. {
  1188. answers.Add(x);
  1189. }
  1190. }
  1191. else
  1192. {
  1193. answers.Add(x);
  1194. }
  1195. }
  1196. else
  1197. {
  1198. answers.Add(ans);
  1199. }
  1200. });
  1201. }
  1202. examData?.examClassResult?.ForEach(item => {
  1203. //学生下标
  1204. int index = 0;
  1205. if (item.studentAnswersArray.Count()>0
  1206. && item.studentAnswersArray.Count() == item.studentIds.Count() //学生作答数量和学生id数量一致
  1207. && item.studentScores.Count()==item.studentIds.Count()) //学生分数和学生id数量一致
  1208. {
  1209. item.studentAnswersArray.ForEach(stu => {
  1210. var student = studentLessonDatas.Find(x => x.id!.Equals(item.studentIds[index]));
  1211. if (student!=null && student.attend==1)
  1212. {
  1213. //是否要判断主观题或者客观题, 多套试卷,有主观题的
  1214. //,如果没获得结果,
  1215. //主观题有回答的:608942756458532864\Clients\18782481024\Ans\27-4341670635487887360-examExchangeAnswerlist
  1216. //27 从1开始的学生序号-4341670635487887360评测编号,内容qNo 是从1开始的题号。
  1217. if (stu.IsNotEmpty() && answers.Count()==stu.Count && examData.exam.papers[0].type.Count()==answers.Count)
  1218. {
  1219. StudentExamRecord studentExam = new StudentExamRecord();
  1220. var studentScore = item.studentScores[index];
  1221. List<ItemRecord> answerRecords = new List<ItemRecord>();
  1222. //题目下标
  1223. int itemIndex = 0;
  1224. stu.ForEach(ans =>
  1225. {
  1226. bool objective = objectiveTypes.Contains(examData.exam.papers[0].type[itemIndex]);
  1227. var questionScore = examData.exam.papers[0].point[itemIndex];
  1228. string type = examData.exam.papers[0].type[itemIndex];
  1229. var res = GetInteractResultHasAnswer(answers[itemIndex], ans, objective, type, questionScore, studentScore[itemIndex]);
  1230. ItemRecord interactRecord = new ItemRecord()
  1231. {
  1232. itemType="SPQStrt",//类型
  1233. resultType=res.reultType,//作答结果类型
  1234. resultWeight=res.weight,//得分权重
  1235. criterion= questionScore,//标准分
  1236. itemScore= studentScore[itemIndex]//得分
  1237. };
  1238. answerRecords.Add(interactRecord);
  1239. itemIndex++;
  1240. });
  1241. studentExam.score= answerRecords.Where(x => x.itemScore>=0).Select(x => x.itemScore).Sum();//得分
  1242. studentExam.scoreRate= allocation.HasValue && allocation.Value>0 ? studentExam.score * 1.0/allocation.Value : 0;//得分率
  1243. studentExam.answerRate= answerRecords.Where(x => x.resultWeight>0).Count()*1.0/studentScore.Count();//作答率
  1244. studentExam.examId=examData.exam.id;
  1245. studentExam.itemRecords=answerRecords;
  1246. student.examRecords.Add(studentExam);
  1247. }
  1248. }
  1249. index++;
  1250. });
  1251. }
  1252. });
  1253. }
  1254. return studentLessonDatas;
  1255. }
  1256. /// <summary>
  1257. /// 协作参与率 态度计算
  1258. /// </summary>
  1259. /// <param name="lessonBase"></param>
  1260. /// <param name="timeLineData"></param>
  1261. /// <param name="coworkDatas"></param>
  1262. /// <param name="studentLessonDatas"></param>
  1263. /// <param name="lessonId"></param>
  1264. /// <returns></returns>
  1265. public static List<StudentLessonData> GetCoworkData(LessonBase lessonBase, TimeLineData timeLineData, List<CoworkData> coworkDatas, List<StudentLessonData> studentLessonDatas, string lessonId)
  1266. {
  1267. int p = 0;
  1268. foreach (var coworkData in coworkDatas)
  1269. {
  1270. var keys = coworkData.participateLevelList.Keys;
  1271. foreach (var key in keys)
  1272. {
  1273. var student = studentLessonDatas.Find(x => x.seatID!.Equals(key));
  1274. if (student!=null && student.attend==1)
  1275. {
  1276. var score = coworkData.participateLevelList[key];//协作得分,是否是经过指数计算的
  1277. var itemRecord = new ItemRecord { criterion=-1, itemType= coworkData.coworkType, itemScore=score, isGroup= coworkData.coworkType.Equals("Group") ? true : false };
  1278. //不能完全依赖
  1279. if (score>0)
  1280. {
  1281. student.coworkScore.Add(score);
  1282. itemRecord.resultWeight = InteractWeight.TP;
  1283. itemRecord.resultType = InteractReultType.TP;
  1284. }
  1285. else
  1286. {
  1287. itemRecord.resultWeight = InteractWeight.T0;
  1288. itemRecord.resultType = InteractReultType.T0;
  1289. }
  1290. student.coworkRecord.itemRecords.Add(itemRecord);
  1291. }
  1292. if (key.Contains("g", StringComparison.OrdinalIgnoreCase))
  1293. {
  1294. string groupId = key.Replace("g", "").Replace("G", "");
  1295. var score = coworkData.participateLevelList[key];
  1296. if (score>0)
  1297. {
  1298. var groupStu = studentLessonDatas.FindAll(x => x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupId));
  1299. if (groupStu.IsNotEmpty())
  1300. {
  1301. foreach (var stu in groupStu)
  1302. {
  1303. stu.group_coworkScore.Add(score);
  1304. stu.coworkRecord.itemRecords[p].itemScore+=score;
  1305. stu.coworkRecord.itemRecords[p].resultWeight=InteractWeight.TP;
  1306. stu.coworkRecord.itemRecords[p].resultType=InteractReultType.TP;
  1307. }
  1308. }
  1309. }
  1310. }
  1311. }
  1312. var order = studentLessonDatas.Where(x => x.attend==1).OrderByDescending(x => x.coworkRecord.itemRecords[p].itemScore);
  1313. var maxItems = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore);
  1314. var max = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
  1315. var min = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
  1316. var sum = studentLessonDatas.Where(x => x.attend==1).Sum(x => x.coworkRecord.itemRecords[p].itemScore);
  1317. foreach (var student in studentLessonDatas)
  1318. {
  1319. if (student.attend==1 && student.coworkRecord.itemRecords.Count>=p+1 && student.coworkRecord.itemRecords[p].itemScore>0)
  1320. {
  1321. student.coworkRecord.itemRecords[p].resultType=InteractReultType.TP;
  1322. var data = MinMaxNormalization(min, max, student.coworkRecord.itemRecords[p].itemScore);
  1323. student.coworkRecord.itemRecords[p].resultWeight=InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1);
  1324. if (maxItems.Select(x => x.seatID).Contains(student.seatID))
  1325. {
  1326. student.coworkRecord.itemRecords[p].resultType= InteractReultType.TT;
  1327. student.coworkRecord.itemRecords[p].resultWeight= InteractWeight.TT;
  1328. }
  1329. }
  1330. }
  1331. p++;
  1332. }
  1333. return studentLessonDatas;
  1334. }
  1335. /// <summary>
  1336. /// 处理学生回推数据,并将回推纳入学习态度计算。
  1337. /// </summary>
  1338. /// <param name="lessonBase"></param>
  1339. /// <param name="timeLineData"></param>
  1340. /// <param name="taskDatas"></param>
  1341. /// <param name="studentLessonDatas"></param>
  1342. /// <returns></returns>
  1343. public static List<StudentLessonData> GetTaskData(LessonBase lessonBase, TimeLineData timeLineData, List<TaskData> taskDatas, List<StudentLessonData> studentLessonDatas, string lessonId)
  1344. {
  1345. //协作也算任务的一种,'WrkSpaceLoad' 作品收集, "isGroupItem": false,
  1346. int indexTask = 0;
  1347. foreach (var taskData in taskDatas)
  1348. {
  1349. //作品收集是全部人员都要参加
  1350. foreach (var student in studentLessonDatas)
  1351. {
  1352. if (student.attend==1)
  1353. {
  1354. var work = taskData.clientWorks.Find(x => $"{x.seatID}".Equals(student.seatID));
  1355. if (work!= null)
  1356. {
  1357. if (work.blobFiles.Count>0)
  1358. {
  1359. student.uploadCount.Add(work.blobFiles.Count);
  1360. }
  1361. 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 });
  1362. }
  1363. else
  1364. {
  1365. student.taskRecord.itemRecords.Add(new ItemRecord { itemType="WrkSpaceLoad", itemScore=0, resultWeight=InteractWeight.T0, resultType=InteractReultType.T0, isGroup= false });
  1366. }
  1367. }
  1368. }
  1369. ////////
  1370. ///需要处理小组的情况,当前人员没有提交作品,但是有可能是小组其他人员提交了,需要判断一下。
  1371. ///
  1372. var students = studentLessonDatas.FindAll(x => x.attend==1 && x.taskRecord.itemRecords[indexTask].isGroup==true);
  1373. foreach (var student in students)
  1374. {
  1375. var groupStudents = studentLessonDatas.FindAll(x => x.id!=student.id && x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(student.groupId));
  1376. foreach (var groupstudent in groupStudents)
  1377. {
  1378. groupstudent.taskRecord.itemRecords[indexTask].isGroup=true;
  1379. groupstudent.taskRecord.itemRecords[indexTask].optCount=student.taskRecord.itemRecords[indexTask].optCount;
  1380. groupstudent.taskRecord.itemRecords[indexTask].itemScore=student.taskRecord.itemRecords[indexTask].itemScore;
  1381. groupstudent.taskRecord.itemRecords[indexTask].resultWeight=student.taskRecord.itemRecords[indexTask].resultWeight;
  1382. groupstudent.taskRecord.itemRecords[indexTask].resultType=student.taskRecord.itemRecords[indexTask].resultType;
  1383. }
  1384. }
  1385. var groupDatas = taskData.clientWorks.FindAll(x => x.seatID==0 && x.isGroupItem);
  1386. foreach (var groupData in groupDatas)
  1387. {
  1388. var groupStudents = studentLessonDatas.FindAll(x => x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupData.groupID));
  1389. foreach (var student in groupStudents)
  1390. {
  1391. student.taskRecord.itemRecords[indexTask].isGroup=true;
  1392. student.taskRecord.itemRecords[indexTask].optCount=groupData.blobFiles.Count;
  1393. student.taskRecord.itemRecords[indexTask].itemScore= 10* groupData.blobFiles.Count;
  1394. if (groupData.blobFiles.Count>0)
  1395. {
  1396. student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.TT;
  1397. student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.TT;
  1398. }
  1399. else
  1400. {
  1401. student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.T0;
  1402. student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.T0;
  1403. }
  1404. }
  1405. }
  1406. indexTask++;
  1407. }
  1408. return studentLessonDatas;
  1409. }
  1410. /// <summary>
  1411. ///评分参与率 态度计算
  1412. ///读取互评信息
  1413. ///评分相关 在SmartRating.json 处理 GrandRating 星光大评分, 投票Voting 和 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评)
  1414. ///Event 过滤类型 'RatingStart'
  1415. ///smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票
  1416. ///读取SmartRating.json
  1417. /// </summary>
  1418. /// <param name="lessonBase"></param>
  1419. /// <param name="timeLineData"></param>
  1420. /// <param name="smartRatingDatas"></param>
  1421. /// <param name="studentLessonDatas"></param>
  1422. /// <returns></returns>
  1423. public static List<StudentLessonData> GetSmartRatingData(LessonBase lessonBase, TimeLineData timeLineData, List<SmartRatingData> smartRatingDatas, List<StudentLessonData> studentLessonDatas, string lessonId)
  1424. {
  1425. int index = 0;
  1426. foreach (var smartRatingData in smartRatingDatas)
  1427. {
  1428. string type = "";
  1429. //投票类型的
  1430. var keys_vote = smartRatingData.smartRateSummary?.meteor_VoteSummary?.Keys?.ToList();
  1431. if (keys_vote.IsNotEmpty())
  1432. {
  1433. type="Voting";
  1434. bool addData = false;
  1435. foreach (var key in keys_vote!)
  1436. {
  1437. try
  1438. {
  1439. //问题数据F:\lesson-local\632424798693232640-local.json pclxxx
  1440. if (smartRatingData.smartRateSummary!.voteDetailResult.TryGetValue(key, out var value))
  1441. {
  1442. var voteDetailResults = smartRatingData.smartRateSummary!.voteDetailResult[key];
  1443. foreach (var student in studentLessonDatas)
  1444. {
  1445. if (student.attend==1)
  1446. {
  1447. //投票是全员参与
  1448. var datasS = voteDetailResults.FindAll(x => x.id.Equals(student.seatID));
  1449. if (datasS.IsNotEmpty())
  1450. {
  1451. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1452. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1453. addData=true;
  1454. }
  1455. else
  1456. { //T0 是没有评论别人,也没被别人评论,
  1457. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1458. addData=true;
  1459. }
  1460. //T0 是没有评论别人,也没被别人评论,
  1461. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1462. //TP 有被别人评论,且评论了别人,
  1463. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1464. }
  1465. }
  1466. }
  1467. }
  1468. catch (Exception ex)
  1469. {
  1470. throw new Exception($"{lessonId}\n{ex.Message}\n{ex.StackTrace}");
  1471. }
  1472. var meteor_VoteSummary = smartRatingData.smartRateSummary!.meteor_VoteSummary[key];
  1473. var order = meteor_VoteSummary.OrderByDescending(x => x.result);
  1474. var maxItems = meteor_VoteSummary.FindAll(x => x.result==order.First().result);
  1475. var max = meteor_VoteSummary.FindAll(x => x.result==order.First().result).First().result;
  1476. var min = meteor_VoteSummary.FindAll(x => x.result==order.Last().result).First().result;
  1477. var sum = meteor_VoteSummary.Sum(x => x.result);
  1478. //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60
  1479. //将每个人的积分转化为60-100
  1480. //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名
  1481. foreach (var datasD in meteor_VoteSummary)
  1482. {
  1483. //有被人评论或投票
  1484. var student = studentLessonDatas.Find(x => x.seatID!.Equals(datasD.id));
  1485. if (student!=null)
  1486. {
  1487. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1488. {
  1489. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1490. {
  1491. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1492. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1493. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1494. }
  1495. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1496. {
  1497. //TP 有被别人评论,且评论了别人,
  1498. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1499. var data = MinMaxNormalization(min, max, datasD.result);
  1500. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1501. student.rateingRecord.itemRecords[index].resultWeight=InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1);
  1502. //获得的票数
  1503. student.rateingRecord.itemRecords[index].itemScore=datasD.result;
  1504. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1505. if (maxItems.Select(x => x.id).Contains(student.seatID))
  1506. {
  1507. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1508. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1509. }
  1510. }
  1511. }
  1512. }
  1513. }
  1514. if (addData)
  1515. {
  1516. index++;
  1517. }
  1518. }
  1519. }
  1520. //星光大评分,全员评分
  1521. var keys_GrandRating = smartRatingData.smartRateSummary?.scoreDetailResult?.Keys?.ToList();
  1522. if (keys_GrandRating.IsNotEmpty() && smartRatingData.smartRateSummary!=null && smartRatingData.smartRateSummary.meteor_ScoreSummary.IsNotEmpty())
  1523. {
  1524. bool addData = false;
  1525. type="GrandRating";
  1526. foreach (var student in studentLessonDatas)
  1527. {
  1528. if (student.attend==1)
  1529. {
  1530. if (keys_GrandRating!.Contains(student.seatID!))
  1531. {
  1532. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1533. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1534. addData = true;
  1535. }
  1536. else
  1537. {
  1538. //T0 是没有评论别人,也没被别人评论,
  1539. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1540. addData = true;
  1541. }
  1542. }
  1543. }
  1544. var order = smartRatingData.smartRateSummary.meteor_ScoreSummary.Where(x => x.result>0||!string.IsNullOrWhiteSpace(x.comment)).OrderByDescending(x => x.result);
  1545. if (order.Count()>0)
  1546. {
  1547. var maxItems = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result);
  1548. var max = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result).First().result;
  1549. var min = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.Last().result).First().result;
  1550. var sum = smartRatingData.smartRateSummary.meteor_ScoreSummary.Sum(x => x.result);
  1551. foreach (var meteor_ScoreSummary in smartRatingData.smartRateSummary.meteor_ScoreSummary)
  1552. {
  1553. var student = studentLessonDatas.Find(x => x.seatID!.Equals(meteor_ScoreSummary.id));
  1554. if (student!=null)
  1555. {
  1556. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1557. {
  1558. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1559. {
  1560. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1561. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1562. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1563. }
  1564. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1565. {
  1566. //TP 有被别人评论,且评论了别人,
  1567. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1568. var data = MinMaxNormalization(min, max, meteor_ScoreSummary.result);
  1569. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1570. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1);
  1571. //被评论次数
  1572. student.rateingRecord.itemRecords[index].itemScore=meteor_ScoreSummary.result;
  1573. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1574. if (maxItems.Select(x => x.id).Contains(student.seatID) &&student.rateingRecord.itemRecords[index].itemScore>0)
  1575. {
  1576. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1577. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1578. }
  1579. }
  1580. }
  1581. }
  1582. }
  1583. }
  1584. if (addData)
  1585. {
  1586. index++;
  1587. }
  1588. }
  1589. // 互评 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评)
  1590. var keys_PeerAssessment = smartRatingData.smartRateSummary?.mutualDetailSummary?.Keys?.ToList();
  1591. if (keys_PeerAssessment.IsNotEmpty() && smartRatingData.smartRateSummary?.mutualSummary!=null
  1592. && smartRatingData.smartRateSummary.mutualSummary.mutualResults.IsNotEmpty()
  1593. && smartRatingData.smartRateSummary.mutualSummary.materialInfos.IsNotEmpty())
  1594. {
  1595. bool addData = false;
  1596. type="PeerAssessment";
  1597. foreach (var student in studentLessonDatas)
  1598. {
  1599. if (student.attend==1)
  1600. {
  1601. if (keys_PeerAssessment!.Contains(student.seatID!))
  1602. {
  1603. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1604. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 });
  1605. addData = true;
  1606. }
  1607. else
  1608. {
  1609. //T0 是没有评论别人,也没被别人评论,
  1610. student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 });
  1611. addData = true;
  1612. }
  1613. }
  1614. }
  1615. var order = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Where(x => x.result>0).OrderByDescending(x => x.result);
  1616. var maxItems = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result);
  1617. var max = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result).First().result;
  1618. var min = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.Last().result).First().result;
  1619. var sum = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Sum(x => x.result);
  1620. foreach (var mutualResult in smartRatingData.smartRateSummary.mutualSummary.mutualResults)
  1621. {
  1622. var student = studentLessonDatas.Find(x => x.seatID!.Equals(mutualResult.id));
  1623. if (student!=null)
  1624. {
  1625. if (index<student.rateingRecord.itemRecords.Count && student.rateingRecord.itemRecords[index].itemType!.Equals(type))
  1626. {
  1627. if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
  1628. {
  1629. //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
  1630. student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
  1631. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
  1632. }
  1633. else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
  1634. {
  1635. //TP 有被别人评论,且评论了别人,
  1636. //最高分和最低分,票数最多和票数最少的占比来计算TP的占比
  1637. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
  1638. var data = MinMaxNormalization(min, max, mutualResult.result);
  1639. //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
  1640. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1);
  1641. student.rateingRecord.itemRecords[index].itemScore=mutualResult.result;
  1642. //TT是评论了别人,且被别人评论次数最高,或者分值最高。
  1643. if (maxItems.Select(x => x.id).Contains(student.seatID))
  1644. {
  1645. student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
  1646. student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
  1647. }
  1648. }
  1649. }
  1650. }
  1651. }
  1652. if (addData)
  1653. {
  1654. index++;
  1655. }
  1656. }
  1657. }
  1658. return studentLessonDatas;
  1659. }
  1660. /// <summary>
  1661. /// 最小-最大归一化(Min-Max Normalization)算法。这种算法通常用于将数据的特征值缩放到一个指定的范围内,通常是0到1之间,或者任何其他指定的范围。
  1662. /// </summary>
  1663. /// <returns></returns>
  1664. public static double MinMaxNormalization(double min, double max, double x, double minRank = 1, double maxRank = 100)
  1665. {
  1666. //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60
  1667. //将每个人的积分转化为60-100
  1668. //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名
  1669. return x==0 ? 0 : max-min!=0 ? (x - min)*1.0 / (max - min) * (maxRank - minRank) + minRank : (x)*1.0 / (max) * (maxRank - minRank) + minRank;
  1670. }
  1671. /// <summary>
  1672. /// 处理学生课中数据
  1673. /// </summary>
  1674. /// <param name="studentLessonDatas"></param>
  1675. /// <param name="lessonDataAnalysis"></param>
  1676. /// <returns></returns>
  1677. public static List<StudentLessonItem> ProcessStudentDataV2(List<StudentLessonData> studentLessonDatas, LessonDataAnalysisCluster lessonDataAnalysis)
  1678. {
  1679. //历史记录的个人计分集合,通过“2倍标准差规则”移除异常值后得到的集合
  1680. var max_q = lessonDataAnalysis.pscore.Max();
  1681. //历史记录的互动计分集合,通过“2倍标准差规则”移除异常值后得到的集合
  1682. var max_t = lessonDataAnalysis.tscore.Max();
  1683. //历史记录的小组计分集合,通过“2倍标准差规则”移除异常值后得到的集合
  1684. var max_h = lessonDataAnalysis.gscore.Max();
  1685. var j = InteractWeight.T1;
  1686. double t = InteractWeight.TT;
  1687. List<StudentLessonItem> lessonItems = new List<StudentLessonItem>();
  1688. foreach (var studentLessonData in studentLessonDatas)
  1689. {
  1690. StudentLessonItem lessonItem = new StudentLessonItem() { studentId= studentLessonData.id! };
  1691. if (studentLessonData.id.Equals("1529649295")) {
  1692. int s1 = 1;
  1693. }
  1694. double u = 0.0;
  1695. if (studentLessonData.attend==1)
  1696. {
  1697. u=100.0;
  1698. }
  1699. //c个人计分指数,d互动计分指数,e小组计分指数
  1700. double d = 0, e = 0;
  1701. //本节课教师手动给学生的个人计分
  1702. var s = studentLessonData.pscore;
  1703. //个人计分指数
  1704. double c = GetPersent(lessonDataAnalysis.pscore, s).persent;// s*1.0/max_q;
  1705. {
  1706. //互动相关的计分
  1707. //课例互动次数
  1708. double n = studentLessonData.interactRecord.interactRecords.Count()*1.0;
  1709. if (n>0)
  1710. {
  1711. //是IES大陆正式站历史课例数据,自2024-03-01至2024-10-08日,互动指数或学法指数黄灯或绿灯,不包含醍摩豆学校及测试学校,课例时长超过5分钟的有效课例(10,680笔数据) 的IRS互动+抢权+挑人的次数集合,
  1712. //通过“2倍标准差规则” 移除异常值后得到的集合,再通过K-Means聚类算法得到高低位阶互动频次两个集合,并根据当前课例互动次数位阶的集合的质心值,该值定为m值
  1713. IEnumerable<double> all = lessonDataAnalysis.levelInteract.SelectMany(x => x.Value);
  1714. var currMacth = lessonDataAnalysis.levelInteract.FindAll(x => x.Value.Min()<=n && x.Value.Max()>=n);
  1715. KeyValuePair<double, List<double>> curr = new KeyValuePair<double, List<double>>();
  1716. if (currMacth!=null && currMacth.Count()>0)
  1717. {
  1718. curr = currMacth.MinBy(x => x.Key);
  1719. }
  1720. else
  1721. {
  1722. curr = lessonDataAnalysis.levelInteract.MaxBy(x => x.Key);
  1723. }
  1724. var p = LessonETLService.GetPersent(all, n);
  1725. var l = n<lessonDataAnalysis.interactPass ? lessonDataAnalysis.interactLow : n>lessonDataAnalysis.interactGood ? lessonDataAnalysis.interactHigh : lessonDataAnalysis.interactMedium;
  1726. //出题系数=当前互动次数与互动通过次数之间的比例*当前互动次数与互动中位数之间的比例*当前互动次数与互动高阶互动频次的比例
  1727. var m = (n*1.0/l) *(p.persent/100) * (curr.Value.Count*1.0/all.Count());
  1728. //学生作答次数
  1729. var w = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>=InteractWeight.T1).Count()*1.0;
  1730. //作答正确数(包括部分正确)
  1731. var r = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Count()*1.0;
  1732. //有参与的权重集合60≤k(x)≤100
  1733. var kw = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>=InteractWeight.T1).Sum(x => x.resultWeight*1.0);
  1734. //有得分的权重集合60<e(x)≤100
  1735. var er = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Sum(x => x.resultWeight*1.0);
  1736. //本节课的所有互动计分
  1737. var i = studentLessonData.interactRecord.interactRecords.Sum(x => x.itemScore*1.0);
  1738. //互动计分指数
  1739. d = GetPersent(lessonDataAnalysis.tscore, i).persent; //i*1.0/max_t;
  1740. //互动成效指数
  1741. var a = 1.0;
  1742. if (w==0)
  1743. {
  1744. a = kw/10/n;
  1745. }
  1746. else
  1747. {
  1748. if (r==0)
  1749. {
  1750. a = (kw/(10*w))*(w/n);
  1751. }
  1752. else
  1753. {
  1754. a = (kw/(10*w))*(w/n)+er/(r*10)*(r/w);
  1755. }
  1756. }
  1757. //c+a= 个人计分指数+ 个人互动成效指数
  1758. //互动成效
  1759. var f1 = Math.Round(190*1.0/(1+Math.Exp(-(a*m)))-95, 4);
  1760. if (f1==0)
  1761. {
  1762. if (d>0 && c==0)
  1763. {
  1764. f1=d*0.6;
  1765. }
  1766. else if (d==0 && c>0)
  1767. {
  1768. f1=c*0.6;
  1769. }
  1770. else
  1771. {
  1772. f1=(d*0.6+c*0.6)/2;
  1773. }
  1774. }
  1775. else
  1776. {
  1777. //如果有互动计分,则互动成效指数取0.8 0.2
  1778. var dy = (95-f1)/95/2;
  1779. if (dy>0.2)
  1780. {
  1781. dy=0.2;
  1782. }
  1783. if (dy<0.1) {
  1784. dy=0.1;
  1785. }
  1786. f1=f1*(dy>0.1? 1-dy : 1-dy*2)+d*dy+c*dy;
  1787. }
  1788. if (f1>95)
  1789. {
  1790. f1=95;
  1791. }
  1792. //var f1 = Math.Round(a*m);
  1793. lessonItem.hd_cx=f1;
  1794. //互动专注指数
  1795. // var b = ((w*w/n)+(r*r/w))*1.0*m;
  1796. // var f2 = Math.Round(200*1.0/(1+Math.Exp(-(b)))-100, 4);
  1797. double f2 = 0;
  1798. //if (w>0)
  1799. //{
  1800. // double xs = m/n>0.1 ? m/n/10 : m/n;
  1801. // if (xs>0.05)
  1802. // {
  1803. // xs=xs/3;
  1804. // }
  1805. // lessonItem.hd_xs=Math.Round(xs, 4);
  1806. // var b = xs* ((w/n)*0.8+ 0.2 * (w/n)* (r/w));
  1807. // f2 = Math.Round(200*1.0/(1+Math.Exp(-(b)))-100, 4);
  1808. // if (f2>1)
  1809. // {
  1810. // f2=100;
  1811. // }
  1812. // else
  1813. // {
  1814. // f2=f2*100;
  1815. // }
  1816. //}
  1817. if (w>0) {
  1818. //作答正确的也算在参与度中,只是占比比作答率占比更小,占20%,即 0.2 * (w/n)* (r/w)*100,计算结果可能会大于100,则强制限定
  1819. f2=(w/n)*100+ 0.2 * (w/n)* (r/w)*100;
  1820. if (f2>100) { f2=100; }
  1821. }
  1822. lessonItem.hd_cy=f2;
  1823. lessonItem.hd_cyc=w;
  1824. lessonItem.hd_fqc=n;
  1825. lessonItem.hd_zqc=r;
  1826. lessonItem.gr_jf=s;
  1827. }
  1828. //studentLessonData.achieve=f1;
  1829. //studentLessonData.attitude=f2;
  1830. // _logger.LogInformation($"{studentLessonData.id}=>学习成效:{f1}\t学习态度:{f2}\t互动次数:{n}\t参与次数:{w}\t正确次数:{r}\t个人计分:{s}\t{Math.Round(c, 2)}\t互动计分:{i}\t{Math.Round(d, 2)}");
  1831. }
  1832. {
  1833. //评测相关指数
  1834. double n = studentLessonData.examRecords.Count()*1.0;
  1835. if (n>0)
  1836. {
  1837. //题目数量
  1838. double nq = studentLessonData.examRecords.Sum(x => x.qcount)*1.0;
  1839. // double max_e = lessonDataAnalysis.exam.Max();
  1840. //得分率
  1841. double sum_s = studentLessonData.examRecords.Sum(x => x.scoreRate);
  1842. //作答率
  1843. double sum_a = studentLessonData.examRecords.Sum(x => x.answerRate);
  1844. double f8 = Math.Round(sum_s/n*100, 4);
  1845. double f9 = Math.Round(sum_a/n*100, 4);
  1846. lessonItem.pc_df=f8;
  1847. lessonItem.pc_zd=f9;
  1848. }
  1849. // _logger.LogInformation($"{studentLessonData.id}=>评测指数:{f8}\t得分率:{Math.Round(sum_s/n,4)}\t专注指数:{f9}\t作答率:{Math.Round(sum_a/n,4)}");
  1850. }
  1851. {
  1852. //小组相关指数
  1853. /* PickupNameLst PickupOption PickupNthGrp PickupGrp PickupRange PickupEachGrp PickupDiff PickupWrong PickupNoDiff PickupRight PickupGener PickupWtoW PickupWtoR PickupLSA_WordFreq PickupLSA_Classify Pickup0_49*/
  1854. var grpPicks = studentLessonData.pickups.Where(x => x.StartsWith("1--") && x.Contains("Grp", StringComparison.OrdinalIgnoreCase));
  1855. var groups= studentLessonDatas.Where(x => !string.IsNullOrWhiteSpace(x.groupId)).Select(x => x.groupId).Distinct();
  1856. int groupCount = 1;
  1857. int memberCount = studentLessonDatas.Count;
  1858. if (groups!=null &&groups.Count()>0)
  1859. {
  1860. groupCount=groups.Count();
  1861. var members = studentLessonDatas.Where(x => !string.IsNullOrWhiteSpace(x.groupId) && !string.IsNullOrWhiteSpace(studentLessonData.groupId)&&x.groupId.Equals(studentLessonData.groupId));
  1862. if (members!=null && members.Count()>0)
  1863. {
  1864. memberCount= members.Count();
  1865. }
  1866. }
  1867. var grpPickCount = grpPicks.Count();
  1868. double pickRate = 0;
  1869. double groupType = 0;
  1870. foreach (var grppick in grpPicks)
  1871. {
  1872. pickRate+= (1.0/groupCount) * (1.0/memberCount)* 100 ;
  1873. }
  1874. if (grpPickCount>0)
  1875. {
  1876. pickRate=100- Math.Round(pickRate/grpPickCount,4);
  1877. groupType+=1;
  1878. lessonItem.xz_tr=grpPickCount;
  1879. }
  1880. double coworkRate = 0;
  1881. if (studentLessonData.group_coworkScore.IsNotEmpty())
  1882. {
  1883. double coworkGrp = studentLessonData.group_coworkScore.Average();
  1884. var coworkData = GetPersent(lessonDataAnalysis.groupCowork, coworkGrp);
  1885. coworkRate= coworkData.persent;
  1886. groupType+=1;
  1887. lessonItem.xz_xz=studentLessonData.group_coworkScore.Sum();
  1888. }
  1889. double gscoreRate = 0;
  1890. if (studentLessonData.gscore>0)
  1891. {
  1892. var gscoreData = GetPersent(lessonDataAnalysis.gscore, studentLessonData.gscore);
  1893. gscoreRate= gscoreData.persent;
  1894. groupType+=1;
  1895. lessonItem.xz_jf=studentLessonData.gscore;
  1896. }
  1897. double groupTask = 0;
  1898. var groupTasks= studentLessonData.taskRecord.itemRecords.Where(x => x.isGroup);
  1899. if(groupTasks!=null && groupTasks.Count()>0)
  1900. {
  1901. double score = CalculateScore(groupTasks.Sum(x=>x.optCount));
  1902. groupType+=1;
  1903. lessonItem.xz_sc=groupTasks.Count();
  1904. }
  1905. double fxGrp = 0;
  1906. if (groupType>0) {
  1907. fxGrp = (pickRate+coworkRate+gscoreRate+groupTask)/groupType; }
  1908. if (groupType==2 &&(pickRate>0 && gscoreRate>0) )
  1909. {
  1910. fxGrp = (pickRate*0.8+coworkRate+gscoreRate*0.8+groupTask)/groupType;
  1911. }
  1912. if (groupType==1)
  1913. {
  1914. fxGrp=(pickRate+coworkRate+gscoreRate+groupTask)*0.8/groupType;
  1915. }
  1916. lessonItem.hz_nl=fxGrp;
  1917. }
  1918. {
  1919. //任务相关指数
  1920. double n = studentLessonData.taskRecord.itemRecords.Count()*1.0;
  1921. if (n>0)
  1922. {
  1923. double max_m = lessonDataAnalysis.task.Max();
  1924. double w = studentLessonData.taskRecord.itemRecords.Where(x => x.resultWeight>0).Count()*1.0;
  1925. double y = (10 *w/n+(j/t) *w)/max_m;
  1926. double l = max_m*(w*w/n+(j/t) * w)/n;
  1927. double f4 = Math.Round(190*1.0/(1+Math.Exp(-(y)))-95, 4);
  1928. double f5 = Math.Round(200*1.0/(1+Math.Exp(-(l)))-100, 4);
  1929. lessonItem.rw_fqc =n;
  1930. lessonItem.rw_cyc =w;
  1931. lessonItem.rw_zpc =studentLessonData.taskRecord.itemRecords.Sum(x=>x.optCount);
  1932. lessonItem.rw_cx =f4;
  1933. lessonItem.rw_cy =f5;
  1934. double score = 0;
  1935. if (w>0)
  1936. {
  1937. foreach (var item in studentLessonData.taskRecord.itemRecords)
  1938. {
  1939. score += CalculateScore(item.optCount);
  1940. }
  1941. }
  1942. lessonItem.rw_cx =score*1.0/n;
  1943. }
  1944. // _logger.LogInformation($"{studentLessonData.id}=>任务指数:{f4}\t专注指数:{f5}\t任务次数:{n}\t参与次数:{w}\t");
  1945. }
  1946. {
  1947. //评价相关指数
  1948. double n = studentLessonData.rateingRecord.itemRecords.Count()*1.0;
  1949. if (n>0)
  1950. {
  1951. var v = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("Voting"));
  1952. double vc = v.Count()*1.0;
  1953. var g = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("GrandRating"));
  1954. double gc = g.Count()*1.0;
  1955. var p = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("PeerAssessment"));
  1956. double pc = p.Count()*1.0;
  1957. var vg = v.Sum(x => x.itemScore);
  1958. var vo = v.Sum(x => x.optCount);
  1959. double vs = vc/n* (vg+ vo);
  1960. var gg = g.Sum(x => x.itemScore);
  1961. var go = g.Sum(x => x.optCount);
  1962. double gs = gc/n* (gg+ go);
  1963. var pg = p.Sum(x => x.itemScore);
  1964. var po = p.Sum(x => x.optCount);
  1965. double ps = pc/n* (pg+ po);
  1966. double h = vs+ps+gs;
  1967. double f3 = Math.Round(190*1.0/(1+Math.Exp(-(h)))-95, 4);
  1968. studentLessonData.appraise=f3;
  1969. // _logger.LogInformation($"{studentLessonData.id}=>评价能力:{f3}\t评价次数:{n}\t投票次数:{vc}-{vg}-{vo}\t星光次数:{gc}-{gg}-{go}\t互评次数:{pc}-{pg}-{po}");
  1970. lessonItem.pj_nl =f3;
  1971. lessonItem.pj_cs =n;
  1972. lessonItem.pj_vc =vc;
  1973. lessonItem.pj_vg =vg;
  1974. lessonItem.pj_vo =vo;
  1975. lessonItem.pj_gc =gc;
  1976. lessonItem.pj_gg =gg;
  1977. lessonItem.pj_go =go;
  1978. lessonItem.pj_pc =pc;
  1979. lessonItem.pj_pg =pg;
  1980. lessonItem.pj_po =po;
  1981. }
  1982. }
  1983. {
  1984. //协作相关指数
  1985. var n = studentLessonData.coworkRecord.itemRecords.Count()*1.0;
  1986. if (n>0)
  1987. {
  1988. //总的协作成果数
  1989. var w = studentLessonData.coworkRecord.itemRecords.Where(x => x.resultWeight>0);
  1990. double ss = w.Sum(x => x.itemScore)*1.0;
  1991. double sw = w.Sum(x => x.resultWeight)*1.0;
  1992. double wc = w.Count()*1.0;
  1993. double x = 0.0;
  1994. if (wc>0)
  1995. {
  1996. x=sw/(j *wc);
  1997. }
  1998. double max_xzcg = 40;
  1999. double k = (wc*wc/n+x)/n+ wc*(ss/max_xzcg)* (wc/n);
  2000. double f6 = Math.Round(190*1.0/(1+Math.Exp(-(k)))-95, 4);
  2001. double f7 = Math.Round(200*1.0/(1+Math.Exp(-(k)))-100, 4);
  2002. lessonItem.xz_fqc =n;
  2003. lessonItem.xz_cyc =wc;
  2004. lessonItem.xz_cgf =ss;
  2005. lessonItem.xz_cx =f6;
  2006. lessonItem.xz_cy =f7;
  2007. }
  2008. //_logger.LogInformation($"{studentLessonData.id}=>协作指数:{f6}\t专注指数:{f7}\t协作次数:{n}\t参与次数:{wc}\t协作成果分数:{ss}\t{k}");
  2009. }
  2010. double xx_cx = 0, xx_cy = 0;
  2011. int avg_cx = 0, avg_cy = 0;
  2012. if (lessonItem.xz_cx>0)
  2013. {
  2014. avg_cx+=1;
  2015. }
  2016. if (lessonItem.pj_nl>0)
  2017. {
  2018. avg_cx+=1;
  2019. }
  2020. if (lessonItem.rw_cx>0)
  2021. {
  2022. avg_cx+=1;
  2023. }
  2024. if (lessonItem.pc_df>0)
  2025. {
  2026. avg_cx+=1;
  2027. }
  2028. if (lessonItem.hd_cx>0)
  2029. {
  2030. avg_cx+=1;
  2031. }
  2032. if (lessonItem.hz_nl>0)
  2033. {
  2034. avg_cx+=1;
  2035. }
  2036. if (avg_cx>0)
  2037. {
  2038. xx_cx+=lessonItem.hd_cx * 1.0/avg_cx+ lessonItem.pc_df* 1.0/avg_cx+ lessonItem.rw_cx* 1.0/avg_cx+ lessonItem.pj_nl* 1.0/avg_cx+ lessonItem.xz_cx* 1.0/avg_cx+ lessonItem.hz_nl* 1.0/avg_cx;
  2039. }
  2040. if (lessonItem.xz_cy>0)
  2041. {
  2042. avg_cy+=1;
  2043. }
  2044. if (lessonItem.pj_nl>0)
  2045. {
  2046. avg_cy+=1;
  2047. }
  2048. if (lessonItem.rw_cy>0)
  2049. {
  2050. avg_cy+=1;
  2051. }
  2052. if (lessonItem.pc_zd>0)
  2053. {
  2054. avg_cy+=1;
  2055. }
  2056. if (lessonItem.hd_cy>0)
  2057. {
  2058. avg_cy+=1;
  2059. }
  2060. if (lessonItem.hz_nl>0)
  2061. {
  2062. avg_cy+=1;
  2063. }
  2064. if (avg_cy>0)
  2065. {
  2066. xx_cy+=lessonItem.hd_cy * 1.0/avg_cy+ lessonItem.pc_zd* 1.0/avg_cy+ lessonItem.rw_cy* 1.0/avg_cy+ lessonItem.pj_nl* 1.0/avg_cy+ lessonItem.xz_cy* 1.0/avg_cy+ lessonItem.hz_nl* 1.0/avg_cy;
  2067. }
  2068. lessonItem.xx_cx=xx_cx;
  2069. lessonItem.xx_cy=xx_cy;
  2070. lessonItems.Add(lessonItem);
  2071. studentLessonData.achieve= lessonItem.xx_cx;
  2072. studentLessonData.attitude= lessonItem.xx_cy;
  2073. studentLessonData.appraise= lessonItem.pj_nl;
  2074. studentLessonData.cowork= lessonItem.xz_cx;
  2075. studentLessonData.cooperation=lessonItem.hz_nl;
  2076. }
  2077. return lessonItems;
  2078. }
  2079. static double CalculateScore(int n, double k = 1)
  2080. {
  2081. if (n == 0)
  2082. {
  2083. return 0;
  2084. }
  2085. else
  2086. {
  2087. double score = 60 + (40 / (1 + Math.Exp(-k * (n - 1))));
  2088. return Math.Max(80, Math.Min(score, 100));
  2089. }
  2090. }
  2091. /// <summary>
  2092. /// 使用标准差定义异常值。如果一个数字与平均值的偏差超过某个标准差倍数(例如2倍或3倍),则可以认为它是异常的。
  2093. /// </summary>
  2094. /// <param name="array"></param>
  2095. /// <returns></returns>
  2096. public static List<double> CleanDataBySDThreshold(IEnumerable<double> array, double thresholdMultiplier = 2)
  2097. {
  2098. if (array.Count() == 0) return new List<double>();
  2099. double average = Math.Round(array.Sum()*1.0/array.Count(), 4);
  2100. double variance = array.Select(x => Math.Round(Math.Pow(x - average, 2), 4)).Sum()*1.0/array.Count();
  2101. double standardDeviation = Math.Sqrt(Math.Round(variance, 4));
  2102. double threshold = Math.Round(thresholdMultiplier * standardDeviation);
  2103. List<double> datas = new List<double>();
  2104. foreach (double value in array)
  2105. {
  2106. double deviation = Math.Round(Math.Abs(value - average), 4);
  2107. if (deviation <= threshold)
  2108. {
  2109. datas.Add(value);
  2110. }
  2111. }
  2112. return datas;
  2113. }
  2114. /// <summary>
  2115. ///
  2116. /// </summary>
  2117. /// <param name="items"></param>
  2118. /// <param name="filePath"></param>
  2119. /// <returns></returns>
  2120. public static async Task ExportToExcelAzureBlob(List<StudentLessonItem> items, AzureStorageFactory azureStorage,string owner , string path , XmlDocument xmlDocument)
  2121. {
  2122. ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
  2123. using (var memoryStream = new MemoryStream())
  2124. {
  2125. using (ExcelPackage package = new ExcelPackage())
  2126. {
  2127. ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("学生课中数据");
  2128. // 获取类的属性
  2129. PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties();
  2130. // 添加表头
  2131. int currentRow = 1;
  2132. for (int i = 0; i < properties.Length; i++)
  2133. {
  2134. string summary = Regex.Replace(GetPropertySummary(properties[i], xmlDocument), @"\s+", "");
  2135. worksheet.Cells[currentRow, i + 1].Value = summary;
  2136. }
  2137. // 填充数据
  2138. currentRow = 2;
  2139. foreach (var item in items)
  2140. {
  2141. for (int i = 0; i < properties.Length; i++)
  2142. {
  2143. worksheet.Cells[currentRow, i + 1].Value = properties[i].GetValue(item);
  2144. }
  2145. currentRow++;
  2146. }
  2147. // 设置表格样式
  2148. worksheet.Cells[worksheet.Dimension.Address].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Left;
  2149. worksheet.Cells[worksheet.Dimension.Address].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Top;
  2150. // 保存到文件流
  2151. await package.SaveAsAsync(memoryStream);
  2152. memoryStream.Position=0;
  2153. }
  2154. await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(memoryStream, "records", path);
  2155. }
  2156. }
  2157. /// <summary>
  2158. /// 导出Excel
  2159. /// </summary>
  2160. /// <param name="items"></param>
  2161. /// <param name="filePath"></param>
  2162. /// <returns></returns>
  2163. public static async Task ExportToExcelLocal(List<StudentLessonItem> items, string filePath, XmlDocument xmlDocument)
  2164. {
  2165. ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
  2166. using (ExcelPackage package = new ExcelPackage())
  2167. {
  2168. ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("学生课中数据");
  2169. // 获取类的属性
  2170. PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties();
  2171. // 添加表头
  2172. int currentRow = 1;
  2173. for (int i = 0; i < properties.Length; i++)
  2174. {
  2175. string summary = Regex.Replace(GetPropertySummary(properties[i], xmlDocument), @"\s+", "");
  2176. worksheet.Cells[currentRow, i + 1].Value = summary;
  2177. }
  2178. // 填充数据
  2179. currentRow = 2;
  2180. foreach (var item in items)
  2181. {
  2182. for (int i = 0; i < properties.Length; i++)
  2183. {
  2184. worksheet.Cells[currentRow, i + 1].Value = properties[i].GetValue(item);
  2185. }
  2186. currentRow++;
  2187. }
  2188. // 设置表格样式
  2189. worksheet.Cells[worksheet.Dimension.Address].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Left;
  2190. worksheet.Cells[worksheet.Dimension.Address].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Top;
  2191. // 保存到文件
  2192. FileInfo fileInfo = new System.IO.FileInfo(filePath);
  2193. await package.SaveAsAsync(fileInfo);
  2194. }
  2195. }
  2196. private static string GetPropertySummary(PropertyInfo property, XmlDocument xmlDocument)
  2197. {
  2198. XmlNodeList? xmlNodeList = xmlDocument.DocumentElement?.SelectNodes("//member[@name='P:" + property.DeclaringType?.FullName + "." + property.Name + "']");
  2199. if (xmlNodeList!= null && xmlNodeList.Count > 0)
  2200. {
  2201. XmlNode? xmlNode = xmlNodeList[0];
  2202. if (xmlNode != null && xmlNode.FirstChild != null)
  2203. {
  2204. return xmlNode.FirstChild.InnerText;
  2205. }
  2206. }
  2207. return property.Name;
  2208. }
  2209. /// <summary>
  2210. /// 当前数超越集合的百分比
  2211. /// </summary>
  2212. /// <param name="nums"></param>
  2213. /// <param name="curr"></param>
  2214. /// <returns></returns>
  2215. public static (double persent, int count) GetPersent(IEnumerable<double> nums, double curr)
  2216. {
  2217. int count = 0;
  2218. foreach (var op in nums.OrderBy(x => x))
  2219. {
  2220. if (op <= curr)
  2221. {
  2222. count++;
  2223. }
  2224. else
  2225. {
  2226. break;
  2227. }
  2228. }
  2229. return (count *1.0/ nums.Count() * 100, count);
  2230. }
  2231. }
  2232. /// <summary>
  2233. /// 学生导出Excel的Entity
  2234. /// </summary>
  2235. public class StudentLessonItem
  2236. {
  2237. /// <summary>
  2238. /// 学生id
  2239. /// </summary>
  2240. public string? studentId { get; set; }
  2241. /// <summary>
  2242. /// 互动发起次数
  2243. /// </summary>
  2244. public double hd_fqc { get; set; } = 0;
  2245. /// <summary>
  2246. /// 互动参与次数
  2247. /// </summary>
  2248. public double hd_cyc { get; set; } = 0;
  2249. /// <summary>
  2250. /// 互动正确次数
  2251. /// </summary>
  2252. public double hd_zqc { get; set; } = 0;
  2253. /// <summary>
  2254. /// 个人计分
  2255. /// </summary>
  2256. public double gr_jf { get; set; } = 0;
  2257. /// <summary>
  2258. /// 互动成效指数
  2259. /// </summary>
  2260. public double hd_cx { get; set; } = 0;
  2261. /// <summary>
  2262. /// 互动专注指数
  2263. /// </summary>
  2264. public double hd_cy { get; set; } = 0;
  2265. /// <summary>
  2266. /// 评测得分率
  2267. /// </summary>
  2268. public double pc_df { get; set; } = 0;
  2269. /// <summary>
  2270. /// 评测作答率
  2271. /// </summary>
  2272. public double pc_zd { get; set; } = 0;
  2273. /// <summary>
  2274. /// 任务发起次数
  2275. /// </summary>
  2276. public double rw_fqc { get; set; } = 0;
  2277. /// <summary>
  2278. /// 任务参与次数
  2279. /// </summary>
  2280. public double rw_cyc { get; set; } = 0;
  2281. /// <summary>
  2282. /// 任务作品数
  2283. /// </summary>
  2284. public double rw_zpc { get; set; } = 0;
  2285. /// <summary>
  2286. /// 任务成效指数
  2287. /// </summary>
  2288. public double rw_cx { get; set; } = 0;
  2289. /// <summary>
  2290. /// 任务专注指数
  2291. /// </summary>
  2292. public double rw_cy { get; set; } = 0;
  2293. /// <summary>
  2294. /// 评价发起次数
  2295. /// </summary>
  2296. public double pj_cs { get; set; } = 0;
  2297. /// <summary>
  2298. /// 投票发起次数
  2299. /// </summary>
  2300. public double pj_vc { get; set; } = 0;
  2301. /// <summary>
  2302. /// 投票得票数
  2303. /// </summary>
  2304. public double pj_vg { get; set; } = 0;
  2305. /// <summary>
  2306. /// 投票次数
  2307. /// </summary>
  2308. public double pj_vo { get; set; } = 0;
  2309. /// <summary>
  2310. /// 星光发起次数
  2311. /// </summary>
  2312. public double pj_gc { get; set; } = 0;
  2313. /// <summary>
  2314. /// 星光得分数
  2315. /// </summary>
  2316. public double pj_gg { get; set; } = 0;
  2317. /// <summary>
  2318. /// 星光评分次数
  2319. /// </summary>
  2320. public double pj_go { get; set; } = 0;
  2321. /// <summary>
  2322. /// 互评发起次数
  2323. /// </summary>
  2324. public double pj_pc { get; set; } = 0;
  2325. /// <summary>
  2326. /// 互评得分数
  2327. /// </summary>
  2328. public double pj_pg { get; set; } = 0;
  2329. /// <summary>
  2330. /// 互评评分次数
  2331. /// </summary>
  2332. public double pj_po { get; set; } = 0;
  2333. /// <summary>
  2334. /// 评价能力
  2335. /// </summary>
  2336. public double pj_nl { get; set; } = 0;
  2337. /// <summary>
  2338. /// 协作发起次数
  2339. /// </summary>
  2340. public double xz_fqc { get; set; } = 0;
  2341. /// <summary>
  2342. /// 协作参与次数
  2343. /// </summary>
  2344. public double xz_cyc { get; set; } = 0;
  2345. /// <summary>
  2346. /// 协作成果分数
  2347. /// </summary>
  2348. public double xz_cgf { get; set; } = 0;
  2349. /// <summary>
  2350. /// 协作能力指数
  2351. /// </summary>
  2352. public double xz_cx { get; set; } = 0;
  2353. /// <summary>
  2354. /// 协作专注指数
  2355. /// </summary>
  2356. public double xz_cy { get; set; } = 0;
  2357. /// <summary>
  2358. /// 小组挑人
  2359. /// </summary>
  2360. public double xz_tr { get; set; } = 0;
  2361. /// <summary>
  2362. /// 小组计分
  2363. /// </summary>
  2364. public double xz_jf { get; set; } = 0;
  2365. /// <summary>
  2366. /// 小组协作成果分
  2367. /// </summary>
  2368. public double xz_xz { get; set; } = 0;
  2369. /// <summary>
  2370. /// 组任务上传数
  2371. /// </summary>
  2372. public double xz_sc { get; set; } = 0;
  2373. /// <summary>
  2374. /// 合作能力
  2375. /// </summary>
  2376. public double hz_nl { get; set; } = 0;
  2377. /// <summary>
  2378. /// 学习成效
  2379. /// </summary>
  2380. public double xx_cx { get; set; } = 0;
  2381. /// <summary>
  2382. /// 学习专注度
  2383. /// </summary>
  2384. public double xx_cy { get; set; } = 0;
  2385. }
  2386. /// <summary>
  2387. /// 历史课例的关键数据模型
  2388. /// </summary>
  2389. public class LessonDataAnalysisCluster : LessonDataAnalysisBase
  2390. {
  2391. /// <summary>
  2392. ///
  2393. /// </summary>
  2394. // public List<KeyValuePair<double, List<double>>> clustersInteract { get; set; } = new List<KeyValuePair<double, List<double>>>();
  2395. ///// <summary>
  2396. /////
  2397. ///// </summary>
  2398. //public List<KeyValuePair<double, List<double>>> clustersPscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  2399. ///// <summary>
  2400. /////
  2401. ///// </summary>
  2402. //public List<KeyValuePair<double, List<double>>> clustersTscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  2403. ///// <summary>
  2404. /////
  2405. ///// </summary>
  2406. //public List<KeyValuePair<double, List<double>>> clustersGscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
  2407. public List<KeyValuePair<double, List<double>>> levelInteract { get; set; } = new List<KeyValuePair<double, List<double>>>();
  2408. public double interactPass { get; set; }
  2409. public double interactGood { get; set; }
  2410. public double interactLow { get; set; }
  2411. public double interactMedium { get; set; }
  2412. public double interactHigh { get; set; }
  2413. }
  2414. /// <summary>
  2415. ///
  2416. /// </summary>
  2417. public abstract class LessonDataAnalysisBase
  2418. {
  2419. ///// <summary>
  2420. ///// 协作次数
  2421. ///// </summary>
  2422. //public IEnumerable<double> cowork { get; set; } = new List<double>();
  2423. ///// <summary>
  2424. /////
  2425. ///// </summary>
  2426. //public IEnumerable<double> coworkBase { get; set; } = new List<double>();
  2427. /// <summary>
  2428. ///
  2429. /// </summary>
  2430. public List<double> task { get; set; } = new List<double>();
  2431. ///// <summary>
  2432. /////
  2433. ///// </summary>
  2434. //public IEnumerable<double> taskBase { get; set; } = new List<double>();
  2435. ///// <summary>
  2436. /////
  2437. ///// </summary>
  2438. //public IEnumerable<double> exam { get; set; } = new List<double>();
  2439. ///// <summary>
  2440. /////
  2441. ///// </summary>
  2442. //public IEnumerable<double> examBase { get; set; } = new List<double>();
  2443. ///// <summary>
  2444. /////
  2445. ///// </summary>
  2446. //public IEnumerable<double> smartRating { get; set; } = new List<double>();
  2447. ///// <summary>
  2448. /////
  2449. ///// </summary>
  2450. //public IEnumerable<double> smartRatingBase { get; set; } = new List<double>();
  2451. /// <summary>
  2452. ///
  2453. /// </summary>
  2454. public List<double> irs { get; set; } = new List<double>();
  2455. /// <summary>
  2456. ///
  2457. /// </summary>
  2458. public List<double> interactNormal { get; set; } = new List<double>();
  2459. /// <summary>
  2460. /// 个人计分
  2461. /// </summary>
  2462. public List<double> pscore { get; set; } = new List<double>();
  2463. /// <summary>
  2464. /// 小组计分
  2465. /// </summary>
  2466. public List<double> gscore { get; set; } = new List<double>();
  2467. /// <summary>
  2468. /// 互动计分
  2469. /// </summary>
  2470. public List<double> tscore { get; set; } = new List<double>();
  2471. /// <summary>
  2472. /// 作品上传数
  2473. /// </summary>
  2474. public List<List<double>> upload { get; set; } = new List<List<double>>();
  2475. /// <summary>
  2476. /// 学生协作成果数
  2477. /// </summary>
  2478. public List<double> stuCowork { get; set; } = new List<double>();
  2479. /// <summary>
  2480. /// 小组协作成果数
  2481. /// </summary>
  2482. public List<double> groupCowork { get; set; } = new List<double>();
  2483. /// <summary>
  2484. /// 挑人集合
  2485. /// </summary>
  2486. public List<List<string>> pickup { get; set; } = new List<List<string>>();
  2487. ///// <summary>
  2488. ///// 挑人集合-小组
  2489. ///// </summary>
  2490. //public List<List<string>> pickup_group { get; set; } = new List<List<string>>();
  2491. }
  2492. /// <summary>
  2493. /// 每月的课例模型数据
  2494. /// </summary>
  2495. public class LessonDataAnalysisMonth : LessonDataAnalysisBase
  2496. {
  2497. /// <summary>
  2498. /// 时间戳
  2499. /// </summary>
  2500. public long updateTime { get; set; }
  2501. /// <summary>
  2502. /// yyyyMM
  2503. /// </summary>
  2504. public string? yearMonth { get; set; }
  2505. }
  2506. /// <summary>
  2507. ///
  2508. /// </summary>
  2509. public class LessonLocal
  2510. {
  2511. /// <summary>
  2512. ///
  2513. /// </summary>
  2514. public LessonBase? lessonBase { get; set; }
  2515. /// <summary>
  2516. ///
  2517. /// </summary>
  2518. public TimeLineData? timeLineData { get; set; }
  2519. /// <summary>
  2520. ///
  2521. /// </summary>
  2522. public LessonRecord? lessonRecord { get; set; }
  2523. /// <summary>
  2524. ///
  2525. /// </summary>
  2526. public List<LocalStudent> studentLessonDatas { get; set; } = new List<LocalStudent>();
  2527. /// <summary>
  2528. ///
  2529. /// </summary>
  2530. public List<TaskData> taskDatas { get; set; } = new List<TaskData>();
  2531. /// <summary>
  2532. ///
  2533. /// </summary>
  2534. public List<SmartRatingData> smartRatingDatas { get; set; } = new List<SmartRatingData>();
  2535. /// <summary>
  2536. ///
  2537. /// </summary>
  2538. public List<IRSData> irsDatas { get; set; } = new List<IRSData>();
  2539. /// <summary>
  2540. ///
  2541. /// </summary>
  2542. public List<CoworkData> coworkDatas { get; set; } = new List<CoworkData>();
  2543. /// <summary>
  2544. ///
  2545. /// </summary>
  2546. public List<ExamData> examDatas { get; set; } = new List<ExamData>();
  2547. /// <summary>
  2548. ///
  2549. /// </summary>
  2550. public List<TimeLineEvent> sokratesDatas { get; set; } = new List<TimeLineEvent>();
  2551. }
  2552. /// <summary>
  2553. ///
  2554. /// </summary>
  2555. public class TechCount
  2556. {
  2557. public string? yearMonth { get; set; }
  2558. /// <summary>
  2559. ///
  2560. /// </summary>
  2561. public string? lessonId { get; set; }
  2562. /// <summary>
  2563. /// 评测数量
  2564. /// </summary>
  2565. public int examCount { get; set; }
  2566. /// <summary>
  2567. /// 任务数量
  2568. /// </summary>
  2569. public int taskCount { get; set; }
  2570. /// <summary>
  2571. /// IRS次数
  2572. /// </summary>
  2573. public int irsCount { get; set; }
  2574. /// <summary>
  2575. /// 互动次数
  2576. /// </summary>
  2577. //public int interactExamCount { get; set; }
  2578. /// <summary>
  2579. /// 互动次数
  2580. /// </summary>
  2581. public int interactNormalCount { get; set; }
  2582. /// <summary>
  2583. /// 协作次数
  2584. /// </summary>
  2585. public int coworkCount { get; set; }
  2586. /// <summary>
  2587. /// 智能评分次数
  2588. /// </summary>
  2589. public int smartRatingCount { get; set; }
  2590. /// <summary>
  2591. ///
  2592. /// </summary>
  2593. public List<CodeLong> timeCount { get; set; } = new List<CodeLong>();
  2594. /// <summary>
  2595. ///
  2596. /// </summary>
  2597. public IEnumerable<double> pscore { get; set; } = new List<double>();
  2598. /// <summary>
  2599. ///
  2600. /// </summary>
  2601. public IEnumerable<double> gscore { get; set; } = new List<double>();
  2602. /// <summary>
  2603. ///
  2604. /// </summary>
  2605. public IEnumerable<double> tscore { get; set; } = new List<double>();
  2606. /// <summary>
  2607. /// 评测数量
  2608. /// </summary>
  2609. public int examCountBase { get; set; }
  2610. /// <summary>
  2611. /// 任务数量
  2612. /// </summary>
  2613. public int taskCountBase { get; set; }
  2614. /// <summary>
  2615. /// IRS次数
  2616. /// </summary>
  2617. public int irsCountBase { get; set; }
  2618. /// <summary>
  2619. /// 互动次数
  2620. /// </summary>
  2621. //public int interactExamCountBase { get; set; }
  2622. /// <summary>
  2623. /// 互动次数
  2624. /// </summary>
  2625. public int interactNormalCountBase { get; set; }
  2626. /// <summary>
  2627. /// 协作次数
  2628. /// </summary>
  2629. public int coworkCountBase { get; set; }
  2630. /// <summary>
  2631. /// 智能评分次数
  2632. /// </summary>
  2633. public int smartRatingCountBase { get; set; }
  2634. /// <summary>
  2635. /// 作品上传数
  2636. /// </summary>
  2637. public List<List<double>> upload { get; set; } = new List<List<double>>();
  2638. /// <summary>
  2639. /// 学生协作成果数
  2640. /// </summary>
  2641. public List<double> stuCowork { get; set; } = new List<double>();
  2642. /// <summary>
  2643. /// 小组协作成果数
  2644. /// </summary>
  2645. public List<double> groupCowork { get; set; } = new List<double>();
  2646. public List<string> pickup { get; set; } = new List<string>();
  2647. }
  2648. }