LessonETLService.cs 106 KB

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