LessonETLService.cs 206 KB

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