LessonETLService.cs 189 KB

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