LessonETLService.cs 108 KB

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