LessonETLService.cs 148 KB

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