using Azure; using Azure.Storage.Blobs.Models; using DocumentFormat.OpenXml.Drawing.Charts; using DocumentFormat.OpenXml.Office2010.Excel; using DocumentFormat.OpenXml.Office2013.Drawing.ChartStyle; using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing; using MathNet.Numerics; using MathNet.Numerics.Distributions; using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using OfficeOpenXml; using OpenXmlPowerTools; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml; using TEAMModelOS.SDK; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK.Models; using TEAMModelOS.SDK.Models.Cosmos.Common; using TEAMModelOS.SDK.Models.Cosmos.OpenEntity; using TEAMModelOS.SDK.Models.Dtos; using static TEAMModelOS.SDK.Models.ThirdService; namespace HTEX.Lib.ETL.Lesson { public class LessonETLService { /// /// 生成学生student-analysis.json /// /// /// /// /// public static async Task<(List studentLessonDatas, List lessonItems)> DoStudentLessonData(List objectiveTypes, AzureStorageFactory azureStorage, LessonLocal lessonLocal,DingDing _dingDing, CosmosClient client,string location ,AzureRedisFactory azureRedis, List studentSemesterRecords, List overallEducations, LessonDataAnalysisCluster lessonDataAnalysis, List studentsBase,List schools ) { List studentLessonDatas = lessonLocal.studentLessonDatas.ToJsonString().ToObject>(); studentLessonDatas = LessonETLService.GetBaseInfo(lessonLocal.lessonBase!, studentLessonDatas, lessonLocal?.lessonRecord?.id); studentLessonDatas = LessonETLService.GetIRSData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.irsDatas, studentLessonDatas, lessonLocal.examDatas, lessonLocal?.lessonRecord?.id); studentLessonDatas = LessonETLService.GetCoworkData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.coworkDatas, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetExamData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.examDatas, studentLessonDatas, objectiveTypes, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetSmartRatingData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.smartRatingDatas, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetTaskData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.taskDatas, studentLessonDatas, lessonLocal.lessonRecord.id); var pickupData = LessonETLService.GetPickupData(lessonLocal.lessonBase!, lessonLocal. timeLineData!, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas= pickupData.studentLessonDatas; List lessonItems = new List(); string owner = lessonLocal.lessonRecord.scope.Equals("school") ? lessonLocal.lessonRecord.school : lessonLocal.lessonRecord.tmdid; try { bool exists= await azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis.json").ExistsAsync(); if (exists) { if (lessonDataAnalysis==null) { BlobDownloadResult blobDownload = await azureStorage.GetBlobContainerClient("0-public").GetBlobClient($"/lesson/analysis/analysis.json").DownloadContentAsync(); lessonDataAnalysis = blobDownload.Content.ToObjectFromJson(); } lessonItems = LessonETLService.ProcessStudentDataV2(studentLessonDatas, lessonDataAnalysis); XmlDocument xmlDocument = new XmlDocument(); var runtimePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); xmlDocument.Load($"{runtimePath}\\summary.xml"); await LessonETLService.ExportToExcelAzureBlob(lessonItems,azureStorage, owner,$"{lessonLocal.lessonRecord.id}/student-analysis.xlsx", xmlDocument); // await _dingDing.SendBotMsg($"学生课中报告已经生成:{owner},records/{lessonLocal.lessonRecord.id}/student-analysis.xlsx",GroupNames.成都开发測試群組); } } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},lesson/analysis/analysis.json转换异常,{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組); } } await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studentLessonDatas.ToJsonString(), "records", $"{lessonLocal.lessonRecord.id}/student-analysis.json"); if (lessonLocal.lessonRecord.scope.Equals("school")&& !string.IsNullOrWhiteSpace(lessonLocal.lessonRecord.school) && location.Equals("China",StringComparison.OrdinalIgnoreCase)) { School school = schools.Find(x=>x.id.Equals(lessonLocal.lessonRecord.school)); if (school==null) { ResponseMessage response = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemStreamAsync(lessonLocal.lessonRecord.school, new PartitionKey("Base")); if (response.IsSuccessStatusCode) { school = JsonDocument.Parse(response.Content).RootElement.ToObject(); schools.Add(school); } } if (school!=null) { var sdtus= studentLessonDatas.Where(y =>!studentsBase.Where(z=>z.schoolId.Equals(school.id)).Select(x => x.id).Contains(y.id) ); if (sdtus!=null && sdtus.Count()>0) { var result = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetList($"select value c from c where c.id in ({string.Join(",", sdtus.Select(d => $"'{d.id}'"))})", $"Base-{school.id}"); if (result.list.Any()) { studentsBase.AddRange(result.list); } } string? periodId = !string.IsNullOrWhiteSpace(lessonLocal.lessonRecord.periodId) ? lessonLocal.lessonRecord.periodId : school.period.FirstOrDefault()?.id; var period = school.period.Find(x => x.id.Equals(periodId)); if (period!=null) { var semester = SchoolService.GetSemester(period, lessonLocal.lessonRecord.startTime); string pre_id = $"{semester.studyYear}-{semester.currSemester.id}"; string code = $"StudentSemesterRecord-{school.id}"; foreach (var studentLessonData in studentLessonDatas) { StudentSemesterRecord studentSemester = studentSemesterRecords.Find(x=>x.id.Equals($"{pre_id}-{studentLessonData.id}") && x.code.Equals(code)); if (studentSemester==null) { ResponseMessage responseMessage = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).ReadItemStreamAsync($"{pre_id}-{studentLessonData.id}", new PartitionKey(code)); if (responseMessage.IsSuccessStatusCode) { studentSemester= JsonDocument.Parse(responseMessage.Content).ToObject(); } else { studentSemester= new StudentSemesterRecord { id= $"{pre_id}-{studentLessonData.id}", code=code, stuid= studentLessonData.id, userType=Constant.ScopeStudent, school=school.id, studyYear=semester.studyYear, semesterId=semester.currSemester.id, period= period?.id, pk="StudentSemesterRecord" }; } studentSemesterRecords.Add(studentSemester); } var les = studentSemester.les.Find(x => x.id.Equals(lessonLocal.lessonRecord.id)); if (les==null) { les = new StuLesson() { id= lessonLocal.lessonRecord.id, time= lessonLocal.lessonRecord.startTime, attend=0 }; studentSemester.les.Add(les); } else { les.time= lessonLocal.lessonRecord.startTime; } var lesson = studentSemester.lessons.Find(x => x.id.Equals(lessonLocal.lessonRecord.id)); if (lesson!=null) { if (studentLessonData.cooperation>0 || studentLessonData.achieve>0|| studentLessonData.attitude>0 || studentLessonData.cowork>0 || studentLessonData.appraise>0) { les.attend=1; lesson.tmdid = lessonLocal.lessonRecord.tmdid; lesson.sid = lessonLocal.lessonRecord.subjectId; lesson.cid = lessonLocal.lessonRecord.courseId; lesson.hrate = studentLessonData.cooperation; lesson.crate = studentLessonData.achieve; lesson.trate = studentLessonData.attitude; lesson.xrate = studentLessonData.cowork; lesson.prate = studentLessonData.appraise; } else { if (studentLessonData.attend==1) { les.attend=1; } else { les.attend=0; } } } else { if (studentLessonData.cooperation>0 || studentLessonData.achieve>0|| studentLessonData.attitude>0 || studentLessonData.cowork>0 || studentLessonData.appraise>0) { les.attend=1; lesson = new StuLessonLite { id=lessonLocal.lessonRecord.id, tmdid=lessonLocal.lessonRecord.tmdid, sid= lessonLocal.lessonRecord.subjectId, cid= lessonLocal.lessonRecord.courseId, hrate=studentLessonData.cooperation, crate=studentLessonData.achieve, trate=studentLessonData.attitude, xrate=studentLessonData.cowork, prate=studentLessonData.appraise }; studentSemester.lessons.Add(lesson); } else { if (studentLessonData.attend==1) { les.attend=1; } else { les.attend=0; } } } //await client.GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(studentSemester, new PartitionKey(studentSemester.code)); string oid = $"{semester.studyYear}-{semester.currSemester.id}-{studentSemester.stuid}"; string ocode = $"OverallEducation-{school.id}"; var student= studentsBase.Find(x => x.id.Equals(studentSemester.stuid)); if (student!=null) { OverallEducation overallEducation = overallEducations.Find(x => x.id.Equals(oid) && x.code.Equals(ocode)); if (overallEducation== null) { ResponseMessage oresponse = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).ReadItemStreamAsync(oid, new PartitionKey(ocode)); if (!oresponse.IsSuccessStatusCode) { overallEducation = new OverallEducation { id =oid, code = $"OverallEducation-{school.id}", pk = "OverallEducation", ttl = -1, name = student.name, classId = student?.classId, schoolCode = $"{school.id}", semesterId = semester.currSemester.id, year = semester.studyYear, periodId = $"{period.id}", stuYear = student.year, studentId = student.id, lessonScore= new List() }; } else { overallEducation = JsonDocument.Parse(oresponse.Content).RootElement.ToObject(); } overallEducations.Add(overallEducation); } var hasrecord = overallEducation.lessonScore.Find(x => x.lessonId.Equals(lessonLocal.lessonRecord.id)); if (hasrecord==null) { hasrecord= new StudentLessonRecord(); overallEducation.lessonScore.Add(hasrecord); } hasrecord.gscore = studentLessonData.gscore; hasrecord.pscore = studentLessonData.pscore; hasrecord.tscore = studentLessonData.tscore; hasrecord.tmdid = lessonLocal.lessonRecord.tmdid; hasrecord.school = school.id; hasrecord.scope = lessonLocal.lessonRecord.scope; hasrecord.lessonId = lessonLocal.lessonRecord.id; hasrecord.courseId = lessonLocal.lessonRecord.courseId; periodId = period?.id; hasrecord.subjectId = lessonLocal.lessonRecord.subjectId; hasrecord.time= lessonLocal.lessonRecord.startTime; hasrecord.attend=les.attend; if (lesson!=null) { hasrecord.hrate=lesson.hrate; hasrecord.crate=lesson.crate; hasrecord.trate=lesson.trate; hasrecord.xrate=lesson.xrate; hasrecord.prate=lesson.prate; } //if (overallEducation!=null) //{ // await client.GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(overallEducation, partitionKey: new PartitionKey(overallEducation.code)); // string key = $"OverallEducation:{school.id}:{period.id}:{semester.studyYear}:{semester.currSemester.id}:{student?.classId}"; // await azureRedis.GetRedisClient(8).HashSetAsync(key, studentLessonData.id, overallEducation.ToJsonString()); // await azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(180 *24, 0, 0)); //} } } } } } return (studentLessonDatas, lessonItems); } public static async Task GenAnalysisData(string pathAnalysis, long newest, List techCounts,AzureStorageFactory azureStorage) { var yearMonthDatas = techCounts.GroupBy(x => x.yearMonth).Select(x => new { key = x.Key, list = x.ToList() }); // lessonDataAnalysisMonths = new List(); LessonDataAnalysisCluster lessonDataAnalysisCluster = new LessonDataAnalysisCluster(); foreach (var yearMonthData in yearMonthDatas) { if (string.IsNullOrWhiteSpace(yearMonthData.key)) { Console.WriteLine(yearMonthData.list.Select(x => x.lessonId).ToJsonString()); } LessonDataAnalysisMonth lessonDataAnalysisMonth = new LessonDataAnalysisMonth() { updateTime= newest, yearMonth= yearMonthData.key }; lessonDataAnalysisMonth.pscore = yearMonthData.list.SelectMany(x => x.pscore).ToList(); lessonDataAnalysisMonth.tscore = yearMonthData.list.SelectMany(x => x.tscore).ToList(); lessonDataAnalysisMonth.gscore = yearMonthData.list.SelectMany(x => x.gscore).ToList(); lessonDataAnalysisMonth.irs= yearMonthData.list.Where(x => x.irsCount>0).Select(x => (double)x.irsCount).ToList(); lessonDataAnalysisMonth.interactNormal= yearMonthData.list.Where(x => x.interactNormalCount > 0).Select(x => (double)x.interactNormalCount).ToList(); lessonDataAnalysisMonth.task = yearMonthData.list.Where(x => x.taskCount > 0).Select(x => (double)x.taskCount).ToList(); lessonDataAnalysisMonth.stuCowork=yearMonthData.list.SelectMany(x => x.stuCowork).ToList(); lessonDataAnalysisMonth.groupCowork=yearMonthData.list.SelectMany(x => x.groupCowork).ToList(); lessonDataAnalysisMonth.pickup= yearMonthData.list.Where(x => x.pickup.Count()>0).Select(x=>x.pickup).ToList(); System.IO.File.WriteAllText(Path.Combine(pathAnalysis, $"{yearMonthData.key}-m-analysis.json"), lessonDataAnalysisMonth.ToJsonString()); // lessonDataAnalysisMonths.Add( lessonDataAnalysisMonth); if (lessonDataAnalysisMonth.task.IsNotEmpty()) { lessonDataAnalysisCluster.task.AddRange(lessonDataAnalysisMonth.task); } if (lessonDataAnalysisMonth.irs.IsNotEmpty()) { lessonDataAnalysisCluster.irs.AddRange(lessonDataAnalysisMonth.irs); } if (lessonDataAnalysisMonth.interactNormal.IsNotEmpty()) { lessonDataAnalysisCluster.interactNormal.AddRange(lessonDataAnalysisMonth.interactNormal); } if (lessonDataAnalysisMonth.pscore.IsNotEmpty()) { lessonDataAnalysisCluster.pscore.AddRange(lessonDataAnalysisMonth.pscore); } if (lessonDataAnalysisMonth.gscore.IsNotEmpty()) { lessonDataAnalysisCluster.gscore.AddRange(lessonDataAnalysisMonth.gscore); } if (lessonDataAnalysisMonth.tscore.IsNotEmpty()) { lessonDataAnalysisCluster.tscore.AddRange(lessonDataAnalysisMonth.tscore); } if (lessonDataAnalysisMonth.stuCowork.IsNotEmpty()) { lessonDataAnalysisCluster.stuCowork.AddRange(lessonDataAnalysisMonth.stuCowork); } if (lessonDataAnalysisMonth.groupCowork.IsNotEmpty()) { lessonDataAnalysisCluster.groupCowork.AddRange(lessonDataAnalysisMonth.groupCowork); } lessonDataAnalysisCluster.pickup.AddRange(lessonDataAnalysisMonth.pickup); } //标准差偏差N倍,视为异常数据 int thresholdMultiplier = 2; lessonDataAnalysisCluster.task= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.task.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.pscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.pscore.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.gscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.gscore.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.tscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.tscore.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.irs = LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.irs.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.interactNormal=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.interactNormal.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.stuCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.stuCowork.OrderBy(x => x), thresholdMultiplier); lessonDataAnalysisCluster.groupCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.groupCowork.OrderBy(x => x), thresholdMultiplier); var all = lessonDataAnalysisCluster.interactNormal.OrderBy(x => x).ToArray(); //超过60 80的 double n = all.Max()+1; //var clusterInteract = HTEX.Lib.ETL.KMeansService.KMeansOptimized(d, 3); //foreach (var item in clusterInteract.OrderBy(x => x.Key)) //{ // lessonDataAnalysisCluster.clustersInteract.Add(new KeyValuePair>(item.Value.Average(), item.Value)); // 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()}"); //} //IEnumerable all = lessonDataAnalysisCluster.clustersInteract.SelectMany(x => x.Value); int pass = 0; for (var i = 1; i=60) { pass = i; break; } } var low = all.Where(x => x=80) { good = i; break; } } var medium = all.Where(x => x>=pass &&x x>=good).Average(); lessonDataAnalysisCluster.interactGood=good; lessonDataAnalysisCluster.interactPass=pass; lessonDataAnalysisCluster.interactHigh=high; lessonDataAnalysisCluster.interactMedium=medium; lessonDataAnalysisCluster.interactLow=low; List>> levelInteract = new List>>() { new KeyValuePair>(low, new List { all.Where(x => x x>(low, new List { all.Where(x => x>=pass &&x x>=pass &&x>(low, new List { all.Where(x => x>=good).Min(), all.Where(x => x>=good).Max() }) }; lessonDataAnalysisCluster.levelInteract= levelInteract; //// 设置0 为自动规划聚类,11 则为自动规划后得到的数字。 //var clusterInteract = KMeansService.KMeansOptimized(d,11,10); ////foreach (var item in clusterInteract) ////{ //// Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}"); ////} //foreach (var item in clusterInteract.OrderBy(x => x.Key)) //{ // lessonDataAnalysisCluster.clustersInteract.Add(new KeyValuePair>(item.Value.Average(), item.Value)); // //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()}"); //} await azureStorage.GetBlobContainerClient("0-public").UploadFileByContainer(lessonDataAnalysisCluster.ToJsonString(), "lesson", $"analysis/analysis.json"); System.IO.File.WriteAllText(Path.Combine(pathAnalysis, "analysis.json"), lessonDataAnalysisCluster.ToJsonString()); } public static async IAsyncEnumerable GetTeachCount(List lessonRecords, List filesLessons, string pathLessons, List ignore, List objectiveTypes, AzureStorageFactory azureStorage,bool force) { foreach (var item in filesLessons) { if (item.EndsWith("-local.json")) { TechCount count = null; string lessonId = item.Split("\\").Last().Replace("-local.json", ""); string countFile = item.Replace("-local.json", "-count.json"); if (System.IO.File.Exists(countFile)) { if (System.IO.File.Exists(countFile)) { string contjson = await System.IO.File.ReadAllTextAsync(countFile); count = contjson.ToObject(); } } if (string.IsNullOrWhiteSpace(count?.yearMonth)|| force) { string localjson = await System.IO.File.ReadAllTextAsync(item); var lessonLocal = localjson.ToObject(); count = new TechCount(); count.lessonId=item.Split("\\").Last().Replace("-local.json", ""); count.examCount= lessonLocal.examDatas.Count; count.taskCount= lessonLocal.taskDatas.Count; count.irsCount= lessonLocal.irsDatas.Count; count.interactNormalCount=count.irsCount; count.coworkCount= lessonLocal.coworkDatas.Count; count.smartRatingCount= lessonLocal.smartRatingDatas.Count; 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(); if (lessonLocal.lessonRecord!=null) { count.yearMonth=DateTimeOffset.FromUnixTimeMilliseconds(lessonLocal.lessonRecord.startTime).ToString("yyyyMM"); if (lessonLocal?.lessonBase?.summary!=null) { count.smartRatingCountBase=lessonLocal.lessonBase.summary.smartRatingCount; count.irsCountBase=lessonLocal.lessonBase.summary.interactionCount; count.taskCountBase=lessonLocal.lessonBase.summary.collateTaskCount; count.coworkCountBase=lessonLocal.lessonBase.summary.coworkTaskCount; count.examCountBase=lessonLocal.lessonBase.summary.examCount; count.interactNormalCountBase= count.interactNormalCount; } if (lessonLocal?.lessonBase?.report?.clientSummaryList!=null) { count.pscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.score>0).Select(x => x.score); count.gscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.groupScore>0).Select(x => x.groupScore); count.tscore= lessonLocal.lessonBase.report.clientSummaryList.Where(x => x.interactScore>0).Select(x => x.interactScore); } ///处理学生数据 { List studentLessonDatas = lessonLocal.studentLessonDatas.ToJsonString().ToObject>(); studentLessonDatas = LessonETLService.GetBaseInfo(lessonLocal.lessonBase!, studentLessonDatas, lessonLocal?.lessonRecord?.id); studentLessonDatas = LessonETLService.GetIRSData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.irsDatas, studentLessonDatas, lessonLocal.examDatas, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetCoworkData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.coworkDatas, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetExamData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.examDatas, studentLessonDatas, objectiveTypes, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetSmartRatingData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.smartRatingDatas, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas = LessonETLService.GetTaskData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.taskDatas, studentLessonDatas, lessonLocal.lessonRecord.id); var pickupData = LessonETLService.GetPickupData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, studentLessonDatas, lessonLocal.lessonRecord.id); studentLessonDatas= pickupData.studentLessonDatas; count.pickup= pickupData.pickup ; // var techCount = techCounts.Find(x => !string.IsNullOrWhiteSpace(x.lessonId) && !string.IsNullOrWhiteSpace(lessonLocal?.lessonRecord?.id) && x.lessonId.Equals(lessonLocal.lessonRecord.id)); int sumUpload = 0; int taskCount = 0; int maxUpload = 0; HashSet pickUp = new HashSet(); foreach (var stu in studentLessonDatas) { var countS = stu.taskRecord.itemRecords.Where(x => x.optCount>0); if (countS.Count()>0) { int stuUploadmax = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Max(x => x.optCount); if (stuUploadmax> maxUpload) { maxUpload=stuUploadmax; } } int stuUpload = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Sum(x => x.optCount); sumUpload+=stuUpload; if (stu.taskRecord.itemRecords.Count()> taskCount) { taskCount=stu.taskRecord.itemRecords.Count(); } var stu_scores = stu.coworkRecord.itemRecords.Where(x => x.itemScore>0).Select(x => x.itemScore); if (stu_scores!=null && stu_scores.Count()>0) { count.stuCowork.AddRange(stu_scores); } var grp_scores = stu.group_coworkScore.Where(x => x>0); if (grp_scores!=null && grp_scores.Count()>0) { count.groupCowork.AddRange(grp_scores); } //if (stu.pickups.IsNotEmpty()) //{ // foreach (var pickup in stu.pickups) // { // pickUp.Add(pickup); // } //} } if (studentLessonDatas.Count>0&& taskCount>0 && maxUpload>0) { var avgUpload = sumUpload*1.0/(studentLessonDatas.Count *taskCount); count.upload.Add(new List() { avgUpload, maxUpload }); } //if (pickUp.Count>0) //{ // count.pickup.AddRange(pickUp.ToList()); //} string owner = lessonLocal.lessonRecord.scope.Equals("school") ? lessonLocal.lessonRecord.school : lessonLocal.lessonRecord.tmdid; // if (!azureStorage.GetBlobContainerClient(owner).GetBlobClient($"records/{lessonLocal.lessonRecord.id}/student-analysis.json").Exists()) { } // if (!System.IO.File.Exists($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json")) { } if (force) { await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-count.json", count.ToJsonString()); } else { if (lessonRecords.FindAll(x => x.id.Equals(lessonLocal.lessonRecord.id)).IsNotEmpty()) { // await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json", studentLessonDatas.ToJsonString()); //只有不是强制更新的时候再去刷新线上的json文件。 await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studentLessonDatas.ToJsonString(), "records", $"{lessonLocal.lessonRecord.id}/student-analysis.json"); await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-count.json", count.ToJsonString()); } } } } } if (count!=null) { yield return count; } } } } public static List GetBaseInfo(LessonBase lessonBase, List studentLessonDatas, string? id) { if (lessonBase?.report?.clientSummaryList !=null && lessonBase.report.clientSummaryList.IsNotEmpty()) { foreach (var item in lessonBase.report.clientSummaryList) { var student = studentLessonDatas.Find(x => x.seatID!=null && x.seatID.Equals($"{item.seatID}")); if (student!= null) { student.gscore= item.groupScore; student.tscore= item.interactScore; student.pscore= item.score; } } } return studentLessonDatas; } public static async IAsyncEnumerable GetLessonLocal(List lessonRecords, List localIds, AzureStorageFactory _azureStorage, string pathLessons) { foreach (var lessonRecord in lessonRecords) { string scope = lessonRecord.scope; string owner = lessonRecord.scope.Equals("school") ? lessonRecord.school : lessonRecord.tmdid; string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(lessonRecord.startTime).ToString("yyyyMM"); LessonLocal lessonLocal = new LessonLocal { lessonRecord=lessonRecord }; if (System.IO.File.Exists($"{pathLessons}\\MM{yearMonthPath}\\{lessonRecord.id}-local.json")) { string jsonp = await System.IO.File.ReadAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{lessonRecord.id}-local.json"); lessonLocal = jsonp.ToObject(); } else { List files = new List() { $"/records/{lessonRecord.id}/IES/TimeLine.json", $"/records/{lessonRecord.id}/IES/base.json", $"/records/{lessonRecord.id}/IES/Task.json", $"/records/{lessonRecord.id}/IES/SmartRating.json", $"/records/{lessonRecord.id}/IES/IRS.json", $"/records/{lessonRecord.id}/IES/Cowork.json", $"/records/{lessonRecord.id}/Sokrates/SokratesRecords.json", }; lessonLocal = new LessonLocal { lessonRecord=lessonRecord }; lessonLocal = await GetLessonFiles(lessonLocal, files, owner, _azureStorage); } if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student!=null) { var baseData = GetBaseData(lessonLocal.lessonBase!); lessonLocal.studentLessonDatas= baseData.studentLessonDatas; List examDatas = await GetExamInfo(lessonRecord, lessonLocal.timeLineData, _azureStorage, owner); lessonLocal.examDatas = examDatas; lessonLocal.sokratesDatas= lessonLocal.sokratesDatas.IsNotEmpty() ? lessonLocal.sokratesDatas : lessonLocal.timeLineData!=null ? lessonLocal.timeLineData.events : new List(); } yield return lessonLocal; } } private static async Task GetLessonFiles(LessonLocal lessonLocal, List files, string owner, AzureStorageFactory _azureStorage) { await Parallel.ForEachAsync(files, async (file, _) => { try { var exists = _azureStorage.GetBlobContainerClient(owner).GetBlobClient(file).Exists(); if (exists) { BlobDownloadResult blobDownloadResult = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient(file).DownloadContentAsync(); switch (true) { case bool when file.Contains("IES/TimeLine.json"): lessonLocal.timeLineData= blobDownloadResult.Content.ToObjectFromJson(); break; case bool when file.Contains("IES/base.json"): lessonLocal.lessonBase= blobDownloadResult.Content.ToObjectFromJson(); break; case bool when file.Contains("IES/Task.json"): lessonLocal.taskDatas= blobDownloadResult.Content.ToObjectFromJson>(); break; case bool when file.Contains("IES/SmartRating.json"): lessonLocal.smartRatingDatas= blobDownloadResult.Content.ToObjectFromJson>(); break; case bool when file.Contains("IES/IRS.json"): lessonLocal.irsDatas= blobDownloadResult.Content.ToObjectFromJson>(); break; case bool when file.Contains("IES/Cowork.json"): lessonLocal.coworkDatas= blobDownloadResult.Content.ToObjectFromJson>(); break; case bool when file.Contains("Sokrates/SokratesRecords.json"): lessonLocal.sokratesDatas= blobDownloadResult.Content.ToObjectFromJson>(); break; } } } catch (RequestFailedException ex) { Console.WriteLine($"{file},{ex.Message},{ex.StackTrace}"); } catch (Exception ex) { Console.WriteLine($"{file},{ex.Message},{ex.StackTrace}"); } }); return lessonLocal; } /// /// 处理base.json的数据 /// /// /// /// public static (LessonBase lessonBase, List studentLessonDatas) GetBaseData(LessonBase lessonBase) { //处理学生定位数据 List studentLessonDatas = new List(); int index = 0; try { if (lessonBase!=null) { lessonBase.student.ForEach(x => { int attend = 0; var client = lessonBase.report.clientSummaryList.Find(y => y.seatID == x.seatID); if (client!=null) { attend=client.attendState; } studentLessonDatas.Add(new LocalStudent() { id = x.id, index = index, seatID =$"{x.seatID}", groupId = x.groupId, attend= attend }); index++; }); } } catch (Exception ex) { Console.WriteLine(lessonBase.ToJsonString()); } return (lessonBase, studentLessonDatas); } /// ///单独处理挑人的逻辑 ///是否从小组里面挑人。 ///不需要去重页面,直接获取挑人大类 PickupResult ///小类处理:PickupRight , PickupOption , PickupNthGrp ,PickupEachGrp ,PickupDiff , PickupResult 挑人算不算互动?? 读取PickupMemberId "[\r\n 35\r\n]" /// /// /// /// /// /// public static (List studentLessonDatas, List pickup) GetPickupData(LessonBase lessonBase, TimeLineData timeLineData, List studentLessonDatas, string lessonId) { var enventsPickup = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && x.Event.Equals("PickupResult")); List pickup = new List(); if (enventsPickup.IsNotEmpty()) { foreach (var item in enventsPickup) { List mbrs = item.PickupMemberId.ToObject>(); // 挑人挑中 TT ,没有挑中 T1 foreach (var studentLessonData in studentLessonDatas) { var mbr = mbrs.FindAll(x => studentLessonData.seatID!.Equals($"{x}")); if (mbr.IsNotEmpty()) { foreach (var m in mbr) { studentLessonData.attend=1; //studentLessonData.interactRecord.interactRecords.Add(new ItemRecord() //{ // resultWeight = InteractWeight.TT, // resultType=InteractReultType.TT, // itemType = string.IsNullOrWhiteSpace(item.PickupType) ? "PickupResult" : item.PickupType //}); studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "1--PickupResult" : $"1--{item.PickupType}"); } } else { //处理未挑中的 if (studentLessonData.attend==1) { studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "0--PickupResult" : $"0--{item.PickupType}"); } } } pickup.Add(item.PickupType); } } return (studentLessonDatas, pickup); } /// ///读取互动信息 ///Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad' /// 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns' 抢权(新), 'BuzrLoad'抢权(旧) ///TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答 ///读取IRS.json /// /// /// /// /// /// /// /// public static List GetIRSData(LessonBase lessonBase, TimeLineData timeLineData, List irsDatas, List studentLessonDatas, List examDatas, string lessonId) { List interactTypes = new List() { "PopQuesLoad", "ReAtmpAnsStrt", "BuzrAns", "BuzrLoad" }; //去重页面 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() }); if (enventsInteract!= null && enventsInteract.Count()>0) { var keys = enventsInteract.Select(x => x.key).ToList(); foreach (var item in enventsInteract) { ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item); } //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中 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() }); if (envents_other!=null && envents_other.Count()>0) { foreach (var item in envents_other) { ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item); } } } else { //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中 if (timeLineData!=null) { var envents_other = timeLineData.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid)).GroupBy(x => x.Pgid).Select(x => new { key = x.Key, list = x.ToList() }); if (envents_other!=null && envents_other.Count()>0) { foreach (var item in envents_other) { ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, item); } } } else { foreach (var item in irsDatas.Select(x => x.pageID)) { ProcessIRSPageData(irsDatas, studentLessonDatas, examDatas, new { key = item }); } } } return studentLessonDatas; } private static List ProcessIRSPageData(List irsDatas, List studentLessonDatas, List examDatas, dynamic item) { var irsDataPages = irsDatas.Where(y => item.key.Equals(y.pageID)); foreach (var irsDataPage in irsDataPages) { //检查是否设置正确答案。 var answers_q = irsDataPage.question?["exercise"]?["answer"]?.ToJsonString().ToObject>(); //根据题去找对应的试卷和评测信息 var question_id = $"{irsDataPage.question?["id"]}"; var examData = examDatas.Where(x => x.paper!=null && x.paper.slides.Exists(x => !string.IsNullOrWhiteSpace(x.url) && x.url.Equals($"{question_id}.json"))).FirstOrDefault(); List answers = new List(); answers_q?.ForEach(x => { if (!string.IsNullOrWhiteSpace(x)) { answers.Add(x); } }); var _objective = irsDataPage.question?["exercise"]?["objective"]; var scoreNode = irsDataPage.question?["exercise"]?["score"]; var _type = irsDataPage.question?["exercise"]?["type"]; var _answerType = irsDataPage.question?["exercise"]?["answerType"];//file,audio,text,image var qitem = irsDataPage.question?["item"]?.AsArray(); if (qitem!=null && qitem.Count()>0) { for (var i = 0; i(); } //题型 string type = string.Empty; if (_type!=null) { //题型 type = _type.GetValue(); List types = new List() { "single", "multiple", "judge", "sortmultiple" }; if (types.Contains(type)) { objective = true; } else { objective = false; } } if (_answerType!=null) { _answerType.GetValue(); //暂不处理,可能存在依然传文字的情况 //不是文本作答的处理,题目不是客观题,答案不记录 //if (!_answerType.Equals("text")) //{ // objective=false; // answers=new List(); //} } if (scoreNode!=null) { double.TryParse(scoreNode.ToString(), out questionScore); } string interactType = string.Empty; if (irsDataPage.clientAnswers.IsNotEmpty()) { //第一个list是几轮,一次作答,二次作答, 第二个list是学生的下标, 第三个list是 答案 List>> clientAnswers = new List>>(); foreach (var key in irsDataPage.clientAnswers.Keys) { clientAnswers.Add(irsDataPage.clientAnswers[key]); } // 获取第一个列表的长度作为比较基准 int firstListLength = clientAnswers.First().Count; bool isSameLength = true; // 遍历剩余的列表并检查它们的长度是否与第一个列表相同 foreach (var innerList in clientAnswers.Skip(1)) { if (innerList.Count != firstListLength) { isSameLength = false; break; } } //并检查学生集合的长度是否与第一个列表相同 if (isSameLength && studentLessonDatas.Count()==firstListLength) { for (int index = 0; index< clientAnswers[0].Count; index++) { var student = studentLessonDatas[index]; double studentScore = 0; if (examData!=null && examData.examClassResult.IsNotEmpty()) { var examResultIndex = examData.examClassResult.First().studentIds.IndexOf(student.id); var questionIndex = examData.paper.slides.Select(x => x.url).ToList().IndexOf($"{question_id}.json"); if (examResultIndex>=0 && examData.examClassResult.First().studentScores.Count>=(examResultIndex+1) //防止索引越界 && examData.examClassResult.First().studentScores[examResultIndex].Count>=(questionIndex+1)) //防止索引越界 { //获取index学生在questionIndex题的分数 studentScore = examData.examClassResult.First().studentScores[examResultIndex][questionIndex]; } } //index 代表学生下标 List interactRecords = new List(); if (clientAnswers.Count==1) { //即问即答 interactType = "PopQuesLoad"; var ans0 = clientAnswers[0][index]; var IS0 = GetInteractResultHasAnswer(answers, ans0, objective, type, questionScore, studentScore); interactRecords.Add(new ItemRecord() { resultWeight = IS0.weight, resultType=IS0.reultType, itemType= interactType, criterion= questionScore, itemScore= IS0.interactScore }); } if (clientAnswers.Count==2) { //二次作答 interactType="ReAtmpAnsStrt"; var ans1 = clientAnswers[1][index]; var IS1 = GetInteractResultHasAnswer(answers, ans1, objective, type, questionScore, studentScore); interactRecords.Add(new ItemRecord() { resultWeight = IS1.weight, resultType=IS1.reultType, itemType= interactType, criterion= questionScore, itemScore= IS1.interactScore }); } if (clientAnswers.Count>2) { //三次作答 interactType="TeAtmpAnsStrt"; var ans2 = clientAnswers[2][index]; var IS2 = GetInteractResultHasAnswer(answers, ans2, objective, type, questionScore, studentScore); interactRecords.Add(new ItemRecord() { resultWeight = IS2.weight, resultType=IS2.reultType, itemType= interactType, criterion= questionScore, itemScore= IS2.interactScore }); } if (studentLessonDatas[index].attend==1) { studentLessonDatas[index].interactRecord.interactRecords.AddRange(interactRecords); } } } } //是否抢权作答的模式 if (irsDataPage.isBuzz) { interactType = "BuzrAns"; //处理参与抢权的 Dictionary buzzParticipants = new Dictionary(); foreach (var buzzParticipant in irsDataPage.buzzParticipants) { var studentData = studentLessonDatas.Find(x => x.seatID!.Equals(buzzParticipant)); if (studentData != null) { buzzParticipants[buzzParticipant]=new ItemRecord() { resultWeight = InteractWeight.T1, itemType= interactType, resultType= InteractReultType.T1 }; } } //处理抢权成功的 foreach (var buzzClient in irsDataPage.buzzClients) { buzzParticipants[buzzClient]=new ItemRecord() { resultWeight = InteractWeight.TT, itemType= interactType, resultType= InteractReultType.TT }; } foreach (var studentLessonData in studentLessonDatas) { if (buzzParticipants.ContainsKey(studentLessonData.seatID!)) { //处理已经有抢权结果的数据 studentLessonData.attend=1; studentLessonData.interactRecord.interactRecords.Add(buzzParticipants[studentLessonData.seatID!]); } else { if (studentLessonData.attend==1) { //处理未参与抢权的 studentLessonData.interactRecord.interactRecords.Add(new ItemRecord() { resultWeight = InteractWeight.T0, itemType = interactType, resultType= InteractReultType.T0 }); } } } } } return studentLessonDatas; } private static (double weight, string reultType, double interactScore) GetInteractResultHasAnswer(List? answers, List ans0, bool objective, string type, double questionScore, double studentScore) { //List ans0 = new List(); //ans?.ForEach(x => { // if (!string.IsNullOrWhiteSpace(x)) // { // ans0.Add(x); // } // else { ans.Add("");} //}); double weight = InteractWeight.T0; string reultType = InteractReultType.T0; double interactScore = 0; if (answers.IsNotEmpty()) { if (ans0.IsNotEmpty()) { if (objective) //客观题 { //标准答案等于作答的结果 if (answers!.Count == ans0.Count) { if (answers.All(item => ans0.Contains(item))) { //完全正确 weight= InteractWeight.TT; reultType= InteractReultType.TT; interactScore= studentScore==0 ? questionScore : studentScore; } else { //作答错误 weight= InteractWeight.T1; reultType = InteractReultType.T1; interactScore= studentScore; } } //标准答案比作答的结果多 else if (answers!.Count > ans0.Count) { if (ans0.All(item => answers.Contains(item))) { //部分正确 weight= InteractWeight.TP; reultType = InteractReultType.TP; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } else { //作答错误 weight= InteractWeight.T1; reultType = InteractReultType.T1; interactScore= studentScore; } } //标准答案比作答结果少 else { //作答错误 weight= InteractWeight.T1; reultType = InteractReultType.T1; interactScore= studentScore; } } else { //填空题 if ("complete".Equals(type) && answers!.Count==ans0.Count) { bool hasT = false; bool hasF = false; for (int i = 0; i < answers!.Count; i++) { if (answers[i].Equals(ans0[i])) { hasT=true; } else { hasF=true; } } if (hasT && !hasF) { //完全正确 weight= InteractWeight.TT; reultType = InteractReultType.TT; interactScore= studentScore==0 ? questionScore : studentScore; } else if (hasT && hasF) { //部分正确 weight= InteractWeight.TP; reultType = InteractReultType.TP; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } else if (!hasT && hasF) { //没有正确的,但有错误的,代表参与了 weight= InteractWeight.T1; reultType = InteractReultType.T1; interactScore= studentScore; } else if (!hasT && !hasF) { //没有正确的,也没有错误的,代表没有作答 weight= InteractWeight.T0; reultType = InteractReultType.T0; interactScore= studentScore; } } else { //主观题,完全匹配的 if (answers!.All(item => ans0.Contains(item))) { //完全正确 weight= InteractWeight.TT; reultType = InteractReultType.TT; interactScore= studentScore==0 ? questionScore : studentScore; } else { // 使用LINQ查询来判断是否有匹配的答案 bool hasMatchingAnswer = answers!.Intersect(ans0).Any(); if (hasMatchingAnswer) { //主观题回答正确即为完全正确 weight= InteractWeight.TT; reultType = InteractReultType.TT; interactScore= studentScore==0 ? questionScore : studentScore; } else { //优先根据得分与标准分的占比算出得分率,如果没有得分率,如果是直接从互动,不知道是评测的 需要先去评测找作答得分。,则采用Levenshtein距离来评估两个字符串的相似度 //没有匹配上答案,则采用Levenshtein距离来评估两个字符串的相似度 if (questionScore>0) { if (studentScore>0) { weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1); if (weight==InteractWeight.T1) { reultType = InteractReultType.T1; interactScore=studentScore; } else if (weight>InteractWeight.TT) { reultType = InteractReultType.TT; interactScore=studentScore==0 ? questionScore : studentScore; } else { reultType = InteractReultType.TP; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } } else { weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1)); if (weight==InteractWeight.T1) { reultType = InteractReultType.T1; interactScore=studentScore; } else if (weight>InteractWeight.TT) { reultType = InteractReultType.TT; interactScore=studentScore==0 ? questionScore : studentScore; } else { reultType = InteractReultType.TP; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } } } else { weight=InteractWeight.T1+(CalculateSimilarity(answers![0], ans0[0]) *(InteractWeight.TT-InteractWeight.T1)); if (weight==InteractWeight.T1) { reultType = InteractReultType.T1; interactScore=studentScore; } else if (weight>InteractWeight.TT) { reultType = InteractReultType.TT; interactScore=studentScore==0 ? questionScore : studentScore; } else { reultType = InteractReultType.TP; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } } } } } } } else { //没有作答 weight= InteractWeight.T0; reultType = InteractReultType.T0; interactScore=studentScore; } } else { //没有标准答案的情况 if (ans0.IsNotEmpty()) { bool hasAns = false; ans0.ForEach(x => { if (!string.IsNullOrWhiteSpace(x)) { hasAns = true; } }); if (hasAns) { //作答了 weight= InteractWeight.T1; reultType = InteractReultType.T1; interactScore=studentScore; } else { //没有作答 weight= InteractWeight.T0; reultType = InteractReultType.T0; interactScore=studentScore; } } else { //没有作答 weight= InteractWeight.T0; reultType = InteractReultType.T0; interactScore=studentScore; } //如果教师手动给了分或AI评分 if (questionScore>0 && studentScore>0) { weight = studentScore * 1.0 / questionScore* (InteractWeight.TT-InteractWeight.T1); if (weight==InteractWeight.T1) { reultType = InteractReultType.T1; interactScore=studentScore; } else if (weight>InteractWeight.TT) { reultType = InteractReultType.TT; interactScore=studentScore==0 ? questionScore : studentScore; } else { reultType = InteractReultType.TP; interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } } } return (weight, reultType, interactScore); } /// /// C# 代码 如何判断两句话是否一个意思,非机器学习的算法。使用Levenshtein距离来评估两个字符串的相似度,但是不能判断它们是否表达了同一个意思,后续借助AI实现 /// /// /// /// public static double CalculateSimilarity(string s1, string s2) { int n = s1.Length; int m = s2.Length; int[,] d = new int[n + 1, m + 1]; for (int i = 0; i <= n; i++) { d[i, 0] = i; } for (int j = 0; j <= m; j++) { d[0, j] = j; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); } } return (1.0 - ((double)d[n, m] / Math.Max(s1.Length, s2.Length))); } public static async Task> GetExamInfo(LessonRecord item, TimeLineData? timeLineData, AzureStorageFactory _azureStorage/*,ILogger _logger*/, string owner) { //读取ExamData List examDatas = new List(); try { var examPages = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && !string.IsNullOrWhiteSpace(x.ExamId)); if (examPages!=null && examPages.Count()>0) { var examsFiles = await _azureStorage.GetBlobContainerClient(owner).List($"records/{item.id}/Exam"); var paperFiles = await _azureStorage.GetBlobContainerClient(owner).List($"records/{item.id}/ExamPaper"); foreach (var examsFile in examsFiles) { if (examsFile.EndsWith("Exam.json")) { ExamData? examData = null; try { BlobDownloadResult examDataDownload = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient(examsFile).DownloadContentAsync(); var str = examDataDownload.Content.ToString().Replace("\r\n", "").Replace("\ufeff", "").Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1"); examData= str.ToObject(); // examData = examDataDownload.Content.ToObjectFromJson(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { // _logger.LogError(ex, $"文件不存在:{examsFile}"); } } if (examData!=null && examData.exam.papers.IsNotEmpty()) { string paperId = examData.exam.papers.First().id; if (_azureStorage.GetBlobContainerClient(owner).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").Exists()) { LessonPaper lessonPaper = null; try { BlobDownloadResult paperblobDownload = await _azureStorage.GetBlobContainerClient(owner).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").DownloadContentAsync(); lessonPaper = paperblobDownload.Content.ToObjectFromJson(); examData.paper = lessonPaper; } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { // _logger.LogError(ex, $"文件不存在:/records/{item.id}/ExamPaper/{paperId}/index.json"); } } } examDatas.Add(examData); } } } } } catch (Exception ex) { // _logger.LogError(ex, ex.Message); } return examDatas; } /// /// 获取课中评测数据 /// /// /// /// /// /// /// public static List GetExamData(LessonBase lessonBase, TimeLineData timeLineData, List examDatas, List studentLessonDatas, List objectiveTypes, string lessonId) { foreach (var examData in examDatas) { //直接取第一个元素的试卷,因为在HiTeach中,只会是一个试卷(一个科目),一个班参与。 var allocation = examData?.exam?.papers?.FirstOrDefault()?.point?.Sum(); var answersStd = examData?.exam?.papers?.FirstOrDefault()?.answers; List> answers = new List>(); if (answersStd!=null) { answersStd.ForEach(x => //去除[""]此种类型的标准答案,转为[] { List ans = new List(); if (x.Count!=0) { if (x.Count==1) { if (string.IsNullOrWhiteSpace(x[0])) { answers.Add(ans); } else { answers.Add(x); } } else { answers.Add(x); } } else { answers.Add(ans); } }); } examData?.examClassResult?.ForEach(item => { //学生下标 int index = 0; if (item.studentAnswersArray.Count()>0 && item.studentAnswersArray.Count() == item.studentIds.Count() //学生作答数量和学生id数量一致 && item.studentScores.Count()==item.studentIds.Count()) //学生分数和学生id数量一致 { item.studentAnswersArray.ForEach(stu => { var student = studentLessonDatas.Find(x => x.id!.Equals(item.studentIds[index])); if (student!=null && student.attend==1) { //是否要判断主观题或者客观题, 多套试卷,有主观题的 //,如果没获得结果, //主观题有回答的:608942756458532864\Clients\18782481024\Ans\27-4341670635487887360-examExchangeAnswerlist //27 从1开始的学生序号-4341670635487887360评测编号,内容qNo 是从1开始的题号。 if (stu.IsNotEmpty() && answers.Count()==stu.Count && examData.exam.papers[0].type.Count()==answers.Count) { StudentExamRecord studentExam = new StudentExamRecord(); var studentScore = item.studentScores[index]; List answerRecords = new List(); //题目下标 int itemIndex = 0; stu.ForEach(ans => { bool objective = objectiveTypes.Contains(examData.exam.papers[0].type[itemIndex]); var questionScore = examData.exam.papers[0].point[itemIndex]; string type = examData.exam.papers[0].type[itemIndex]; var res = GetInteractResultHasAnswer(answers[itemIndex], ans, objective, type, questionScore, studentScore[itemIndex]); ItemRecord interactRecord = new ItemRecord() { itemType="SPQStrt",//类型 resultType=res.reultType,//作答结果类型 resultWeight=res.weight,//得分权重 criterion= questionScore,//标准分 itemScore= studentScore[itemIndex]//得分 }; answerRecords.Add(interactRecord); itemIndex++; }); studentExam.score= answerRecords.Where(x => x.itemScore>=0).Select(x => x.itemScore).Sum();//得分 studentExam.scoreRate= allocation.HasValue && allocation.Value>0 ? studentExam.score * 1.0/allocation.Value : 0;//得分率 studentExam.answerRate= answerRecords.Where(x => x.resultWeight>0).Count()*1.0/studentScore.Count();//作答率 studentExam.examId=examData.exam.id; studentExam.itemRecords=answerRecords; student.examRecords.Add(studentExam); } } index++; }); } }); } return studentLessonDatas; } /// /// 协作参与率 态度计算 /// /// /// /// /// /// /// public static List GetCoworkData(LessonBase lessonBase, TimeLineData timeLineData, List coworkDatas, List studentLessonDatas, string lessonId) { int p = 0; foreach (var coworkData in coworkDatas) { var keys = coworkData.participateLevelList.Keys; foreach (var key in keys) { var student = studentLessonDatas.Find(x => x.seatID!.Equals(key)); if (student!=null && student.attend==1) { var score = coworkData.participateLevelList[key];//协作得分,是否是经过指数计算的 var itemRecord = new ItemRecord { criterion=-1, itemType= coworkData.coworkType, itemScore=score, isGroup= coworkData.coworkType.Equals("Group") ? true : false }; //不能完全依赖 if (score>0) { student.coworkScore.Add(score); itemRecord.resultWeight = InteractWeight.TP; itemRecord.resultType = InteractReultType.TP; } else { itemRecord.resultWeight = InteractWeight.T0; itemRecord.resultType = InteractReultType.T0; } student.coworkRecord.itemRecords.Add(itemRecord); } if (key.Contains("g", StringComparison.OrdinalIgnoreCase)) { string groupId = key.Replace("g", "").Replace("G", ""); var score = coworkData.participateLevelList[key]; if (score>0) { var groupStu = studentLessonDatas.FindAll(x => x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupId)); if (groupStu.IsNotEmpty()) { foreach (var stu in groupStu) { stu.group_coworkScore.Add(score); stu.coworkRecord.itemRecords[p].itemScore+=score; stu.coworkRecord.itemRecords[p].resultWeight=InteractWeight.TP; stu.coworkRecord.itemRecords[p].resultType=InteractReultType.TP; } } } } } var order = studentLessonDatas.Where(x => x.attend==1).OrderByDescending(x => x.coworkRecord.itemRecords[p].itemScore); var maxItems = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore); double max = 0; if (studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).IsNotEmpty()) { max=studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore; } double min = 0; if (studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).IsNotEmpty()) { min= studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore; } double sum = 0; if (studentLessonDatas.FindAll(x => x.attend==1).IsNotEmpty()) { sum= studentLessonDatas.FindAll(x => x.attend==1).Sum(x => x.coworkRecord.itemRecords[p].itemScore); } foreach (var student in studentLessonDatas) { if (student.attend==1 && student.coworkRecord.itemRecords.Count>=p+1 && student.coworkRecord.itemRecords[p].itemScore>0) { student.coworkRecord.itemRecords[p].resultType=InteractReultType.TP; var data = MinMaxNormalization(min, max, student.coworkRecord.itemRecords[p].itemScore); student.coworkRecord.itemRecords[p].resultWeight=InteractWeight.T1+ data * 1.0 / 100 * (InteractWeight.TT-InteractWeight.T1); if (maxItems.Select(x => x.seatID).Contains(student.seatID)) { student.coworkRecord.itemRecords[p].resultType= InteractReultType.TT; student.coworkRecord.itemRecords[p].resultWeight= InteractWeight.TT; } } } p++; } return studentLessonDatas; } /// /// 处理学生回推数据,并将回推纳入学习态度计算。 /// /// /// /// /// /// public static List GetTaskData(LessonBase lessonBase, TimeLineData timeLineData, List taskDatas, List studentLessonDatas, string lessonId) { //协作也算任务的一种,'WrkSpaceLoad' 作品收集, "isGroupItem": false, int indexTask = 0; foreach (var taskData in taskDatas) { //作品收集是全部人员都要参加 foreach (var student in studentLessonDatas) { if (student.attend==1) { var work = taskData.clientWorks.Find(x => $"{x.seatID}".Equals(student.seatID)); if (work!= null) { if (work.blobFiles.Count>0) { student.uploadCount.Add(work.blobFiles.Count); } 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 }); } else { student.taskRecord.itemRecords.Add(new ItemRecord { itemType="WrkSpaceLoad", itemScore=0, resultWeight=InteractWeight.T0, resultType=InteractReultType.T0, isGroup= false }); } } } //////// ///需要处理小组的情况,当前人员没有提交作品,但是有可能是小组其他人员提交了,需要判断一下。 /// var students = studentLessonDatas.FindAll(x => x.attend==1 && x.taskRecord.itemRecords[indexTask].isGroup==true); foreach (var student in students) { var groupStudents = studentLessonDatas.FindAll(x => x.id!=student.id && x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(student.groupId)); foreach (var groupstudent in groupStudents) { groupstudent.taskRecord.itemRecords[indexTask].isGroup=true; groupstudent.taskRecord.itemRecords[indexTask].optCount=student.taskRecord.itemRecords[indexTask].optCount; groupstudent.taskRecord.itemRecords[indexTask].itemScore=student.taskRecord.itemRecords[indexTask].itemScore; groupstudent.taskRecord.itemRecords[indexTask].resultWeight=student.taskRecord.itemRecords[indexTask].resultWeight; groupstudent.taskRecord.itemRecords[indexTask].resultType=student.taskRecord.itemRecords[indexTask].resultType; } } var groupDatas = taskData.clientWorks.FindAll(x => x.seatID==0 && x.isGroupItem); foreach (var groupData in groupDatas) { var groupStudents = studentLessonDatas.FindAll(x => x.attend==1 && !string.IsNullOrWhiteSpace(x.groupId) && x.groupId.Equals(groupData.groupID)); foreach (var student in groupStudents) { student.taskRecord.itemRecords[indexTask].isGroup=true; student.taskRecord.itemRecords[indexTask].optCount=groupData.blobFiles.Count; student.taskRecord.itemRecords[indexTask].itemScore= 10* groupData.blobFiles.Count; if (groupData.blobFiles.Count>0) { student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.TT; student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.TT; } else { student.taskRecord.itemRecords[indexTask].resultWeight=InteractWeight.T0; student.taskRecord.itemRecords[indexTask].resultType=InteractReultType.T0; } } } indexTask++; } return studentLessonDatas; } /// ///评分参与率 态度计算 ///读取互评信息 ///评分相关 在SmartRating.json 处理 GrandRating 星光大评分, 投票Voting 和 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评) ///Event 过滤类型 'RatingStart' ///smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票 ///读取SmartRating.json /// /// /// /// /// /// public static List GetSmartRatingData(LessonBase lessonBase, TimeLineData timeLineData, List smartRatingDatas, List studentLessonDatas, string lessonId) { int index = 0; foreach (var smartRatingData in smartRatingDatas) { string type = ""; //投票类型的 var keys_vote = smartRatingData.smartRateSummary?.meteor_VoteSummary?.Keys?.ToList(); if (keys_vote.IsNotEmpty()) { type="Voting"; bool addData = false; foreach (var key in keys_vote!) { try { //问题数据F:\lesson-local\632424798693232640-local.json pclxxx if (smartRatingData.smartRateSummary!.voteDetailResult.TryGetValue(key, out var value)) { var voteDetailResults = smartRatingData.smartRateSummary!.voteDetailResult[key]; foreach (var student in studentLessonDatas) { if (student.attend==1) { //投票是全员参与 var datasS = voteDetailResults.FindAll(x => x.id.Equals(student.seatID)); if (datasS.IsNotEmpty()) { //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 }); addData=true; } else { //T0 是没有评论别人,也没被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 }); addData=true; } //T0 是没有评论别人,也没被别人评论, //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论, //TP 有被别人评论,且评论了别人, //TT是评论了别人,且被别人评论次数最高,或者分值最高。 } } } } catch (Exception ex) { throw new Exception($"{lessonId}\n{ex.Message}\n{ex.StackTrace}"); } var meteor_VoteSummary = smartRatingData.smartRateSummary!.meteor_VoteSummary[key]; var order = meteor_VoteSummary.OrderByDescending(x => x.result); var maxItems = meteor_VoteSummary.FindAll(x => x.result==order.First().result); double max = 0; if (maxItems.IsNotEmpty()) { max = meteor_VoteSummary.FindAll(x => x.result==order.First().result).First().result; } double min = 0; if (meteor_VoteSummary.FindAll(x => x.result==order.Last().result).IsNotEmpty()) { min = meteor_VoteSummary.FindAll(x => x.result==order.Last().result).First().result; } double sum = 0; if (meteor_VoteSummary.IsNotEmpty()) { sum = meteor_VoteSummary.Sum(x => x.result); } //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60 //将每个人的积分转化为60-100 //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名 foreach (var datasD in meteor_VoteSummary) { //有被人评论或投票 var student = studentLessonDatas.Find(x => x.seatID!.Equals(datasD.id)); if (student!=null) { if (index x.id).Contains(student.seatID)) { student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT; student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT; } } } } } if (addData) { index++; } } } //星光大评分,全员评分 var keys_GrandRating = smartRatingData.smartRateSummary?.scoreDetailResult?.Keys?.ToList(); if (keys_GrandRating.IsNotEmpty() && smartRatingData.smartRateSummary!=null && smartRatingData.smartRateSummary.meteor_ScoreSummary.IsNotEmpty()) { bool addData = false; type="GrandRating"; foreach (var student in studentLessonDatas) { if (student.attend==1) { if (keys_GrandRating!.Contains(student.seatID!)) { //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 }); addData = true; } else { //T0 是没有评论别人,也没被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 }); addData = true; } } } var order = smartRatingData.smartRateSummary.meteor_ScoreSummary.Where(x => x.result>0||!string.IsNullOrWhiteSpace(x.comment)).OrderByDescending(x => x.result); if (order.Count()>0) { var maxItems = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result); double max = 0; if (maxItems.IsNotEmpty()) { max = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result).First().result; } double min = 0; if (smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.Last().result).IsNotEmpty()) { min = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.Last().result).First().result; } var sum = smartRatingData.smartRateSummary.meteor_ScoreSummary.Sum(x => x.result); foreach (var meteor_ScoreSummary in smartRatingData.smartRateSummary.meteor_ScoreSummary) { var student = studentLessonDatas.Find(x => x.seatID!.Equals(meteor_ScoreSummary.id)); if (student!=null) { if (index x.id).Contains(student.seatID) &&student.rateingRecord.itemRecords[index].itemScore>0) { student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT; student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT; } } } } } } if (addData) { index++; } } // 互评 PeerAssessment(All每人多件评分,Two随机分配互评, Self自评) var keys_PeerAssessment = smartRatingData.smartRateSummary?.mutualDetailSummary?.Keys?.ToList(); if (keys_PeerAssessment.IsNotEmpty() && smartRatingData.smartRateSummary?.mutualSummary!=null && smartRatingData.smartRateSummary.mutualSummary.mutualResults.IsNotEmpty() && smartRatingData.smartRateSummary.mutualSummary.materialInfos.IsNotEmpty()) { bool addData = false; type="PeerAssessment"; foreach (var student in studentLessonDatas) { if (student.attend==1) { if (keys_PeerAssessment!.Contains(student.seatID!)) { //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T1, resultWeight = InteractWeight.T1 }); addData = true; } else { //T0 是没有评论别人,也没被别人评论, student.rateingRecord.itemRecords.Add(new ItemRecord { itemType=type, resultType=InteractReultType.T0, resultWeight = InteractWeight.T0 }); addData = true; } } } var order = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Where(x => x.result>0).OrderByDescending(x => x.result); var maxItems = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result); double max = 0; if (maxItems.IsNotEmpty()) { max = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result).First().result; } double min = 0; if (smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.Last().result).IsNotEmpty()) { min = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.Last().result).First().result; } var sum = smartRatingData.smartRateSummary.mutualSummary.mutualResults.Sum(x => x.result); foreach (var mutualResult in smartRatingData.smartRateSummary.mutualSummary.mutualResults) { var student = studentLessonDatas.Find(x => x.seatID!.Equals(mutualResult.id)); if (student!=null) { if (index x.id).Contains(student.seatID)) { student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT; student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT; } } } } } if (addData) { index++; } } } return studentLessonDatas; } /// /// 最小-最大归一化(Min-Max Normalization)算法。这种算法通常用于将数据的特征值缩放到一个指定的范围内,通常是0到1之间,或者任何其他指定的范围。 /// /// public static double MinMaxNormalization(double min, double max, double x, double minRank = 1, double maxRank = 100) { //排名指数计算=( 当前值分数- 298) / (9992 - 298) * (99 - 60) + 60 //将每个人的积分转化为60-100 //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名 return x==0 ? 0 : max-min!=0 ? (x - min)*1.0 / (max - min) * (maxRank - minRank) + minRank : (x)*1.0 / (max) * (maxRank - minRank) + minRank; } /// /// 处理学生课中数据 /// /// /// /// public static List ProcessStudentDataV2(List studentLessonDatas, LessonDataAnalysisCluster lessonDataAnalysis) { //历史记录的个人计分集合,通过“2倍标准差规则”移除异常值后得到的集合 var max_q = lessonDataAnalysis.pscore.Max(); //历史记录的互动计分集合,通过“2倍标准差规则”移除异常值后得到的集合 var max_t = lessonDataAnalysis.tscore.Max(); //历史记录的小组计分集合,通过“2倍标准差规则”移除异常值后得到的集合 var max_h = lessonDataAnalysis.gscore.Max(); var j = InteractWeight.T1; double t = InteractWeight.TT; List lessonItems = new List(); foreach (var studentLessonData in studentLessonDatas) { StudentLessonItem lessonItem = new StudentLessonItem() { studentId= studentLessonData.id! }; double u = 0.0; if (studentLessonData.attend==1) { u=100.0; } //c个人计分指数,d互动计分指数,e小组计分指数 double d = 0, e = 0; //本节课教师手动给学生的个人计分 var s = studentLessonData.pscore; //个人计分指数 double c = GetPersent(lessonDataAnalysis.pscore, s).persent;// s*1.0/max_q; { //互动相关的计分 //课例互动次数 double n = studentLessonData.interactRecord.interactRecords.Count()*1.0; if (n>0) { //是IES大陆正式站历史课例数据,自2024-03-01至2024-10-08日,互动指数或学法指数黄灯或绿灯,不包含醍摩豆学校及测试学校,课例时长超过5分钟的有效课例(10,680笔数据) 的IRS互动+抢权+挑人的次数集合, //通过“2倍标准差规则” 移除异常值后得到的集合,再通过K-Means聚类算法得到高低位阶互动频次两个集合,并根据当前课例互动次数位阶的集合的质心值,该值定为m值 IEnumerable all = lessonDataAnalysis.levelInteract.SelectMany(x => x.Value); var currMacth = lessonDataAnalysis.levelInteract.FindAll(x => x.Value.Min()<=n && x.Value.Max()>=n); KeyValuePair> curr = new KeyValuePair>(); if (currMacth!=null && currMacth.Count()>0) { curr = currMacth.MinBy(x => x.Key); } else { curr = lessonDataAnalysis.levelInteract.MaxBy(x => x.Key); } var p = LessonETLService.GetPersent(all, n); var l = nlessonDataAnalysis.interactGood ? lessonDataAnalysis.interactHigh : lessonDataAnalysis.interactMedium; //出题系数=当前互动次数与互动通过次数之间的比例*当前互动次数与互动中位数之间的比例*当前互动次数与互动高阶互动频次的比例 var m = (n*1.0/l) *(p.persent/100) * (curr.Value.Count*1.0/all.Count()); //学生作答次数 var w = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>=InteractWeight.T1).Count()*1.0; //作答正确数(包括部分正确) var r = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Count()*1.0; //有参与的权重集合60≤k(x)≤100 var kw = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>=InteractWeight.T1).Sum(x => x.resultWeight*1.0); //有得分的权重集合60 x.resultWeight>InteractWeight.T1).Sum(x => x.resultWeight*1.0); //本节课的所有互动计分 var i = studentLessonData.interactRecord.interactRecords.Sum(x => x.itemScore*1.0); //互动计分指数 d = GetPersent(lessonDataAnalysis.tscore, i).persent; //i*1.0/max_t; //互动成效指数 var a = 1.0; if (w==0) { a = kw/10/n; } else { if (r==0) { a = (kw/(10*w))*(w/n); } else { a = (kw/(10*w))*(w/n)+er/(r*10)*(r/w); } } //c+a= 个人计分指数+ 个人互动成效指数 //互动成效 var f1 = Math.Round(190*1.0/(1+Math.Exp(-(a*m)))-95, 4); if (f1==0) { if (d>0 && c==0) { f1=d*0.6; } else if (d==0 && c>0) { f1=c*0.6; } else { f1=(d*0.6+c*0.6)/2; } } else { //如果有互动计分,则互动成效指数取0.8 0.2 var dy = (95-f1)/95/2; if (dy>0.2) { dy=0.2; } if (dy<0.1) { dy=0.1; } f1=f1*(dy>0.1? 1-dy : 1-dy*2)+d*dy+c*dy; } if (f1>95) { f1=95; } //var f1 = Math.Round(a*m); lessonItem.hd_cx=f1; //互动专注指数 // var b = ((w*w/n)+(r*r/w))*1.0*m; // var f2 = Math.Round(200*1.0/(1+Math.Exp(-(b)))-100, 4); double f2 = 0; //if (w>0) //{ // double xs = m/n>0.1 ? m/n/10 : m/n; // if (xs>0.05) // { // xs=xs/3; // } // lessonItem.hd_xs=Math.Round(xs, 4); // var b = xs* ((w/n)*0.8+ 0.2 * (w/n)* (r/w)); // f2 = Math.Round(200*1.0/(1+Math.Exp(-(b)))-100, 4); // if (f2>1) // { // f2=100; // } // else // { // f2=f2*100; // } //} if (w>0) { //作答正确的也算在参与度中,只是占比比作答率占比更小,占20%,即 0.2 * (w/n)* (r/w)*100,计算结果可能会大于100,则强制限定 f2=(w/n)*100+ 0.2 * (w/n)* (r/w)*100; if (f2>100) { f2=100; } } lessonItem.hd_cy=f2; lessonItem.hd_cyc=w; lessonItem.hd_fqc=n; lessonItem.hd_zqc=r; lessonItem.gr_jf=s; } //studentLessonData.achieve=f1; //studentLessonData.attitude=f2; // _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)}"); } { //评测相关指数 double n = studentLessonData.examRecords.Count()*1.0; if (n>0) { //题目数量 double nq = studentLessonData.examRecords.Sum(x => x.qcount)*1.0; // double max_e = lessonDataAnalysis.exam.Max(); //得分率 double sum_s = studentLessonData.examRecords.Sum(x => x.scoreRate); //作答率 double sum_a = studentLessonData.examRecords.Sum(x => x.answerRate); double f8 = Math.Round(sum_s/n*100, 4); double f9 = Math.Round(sum_a/n*100, 4); lessonItem.pc_df=f8; lessonItem.pc_zd=f9; } // _logger.LogInformation($"{studentLessonData.id}=>评测指数:{f8}\t得分率:{Math.Round(sum_s/n,4)}\t专注指数:{f9}\t作答率:{Math.Round(sum_a/n,4)}"); } { //小组相关指数 /* PickupNameLst PickupOption PickupNthGrp PickupGrp PickupRange PickupEachGrp PickupDiff PickupWrong PickupNoDiff PickupRight PickupGener PickupWtoW PickupWtoR PickupLSA_WordFreq PickupLSA_Classify Pickup0_49*/ var grpPicks = studentLessonData.pickups.Where(x => x.StartsWith("1--") && x.Contains("Grp", StringComparison.OrdinalIgnoreCase)); var groups= studentLessonDatas.Where(x => !string.IsNullOrWhiteSpace(x.groupId)).Select(x => x.groupId).Distinct(); int groupCount = 1; int memberCount = studentLessonDatas.Count; if (groups!=null &&groups.Count()>0) { groupCount=groups.Count(); var members = studentLessonDatas.Where(x => !string.IsNullOrWhiteSpace(x.groupId) && !string.IsNullOrWhiteSpace(studentLessonData.groupId)&&x.groupId.Equals(studentLessonData.groupId)); if (members!=null && members.Count()>0) { memberCount= members.Count(); } } var grpPickCount = grpPicks.Count(); double pickRate = 0; double groupType = 0; foreach (var grppick in grpPicks) { pickRate+= (1.0/groupCount) * (1.0/memberCount)* 100 ; } if (grpPickCount>0) { pickRate=100- Math.Round(pickRate/grpPickCount,4); groupType+=1; lessonItem.xz_tr=grpPickCount; } double coworkRate = 0; if (studentLessonData.group_coworkScore.IsNotEmpty()) { double coworkGrp = studentLessonData.group_coworkScore.Average(); var coworkData = GetPersent(lessonDataAnalysis.groupCowork, coworkGrp); coworkRate= coworkData.persent; groupType+=1; lessonItem.xz_xz=studentLessonData.group_coworkScore.Sum(); } double gscoreRate = 0; if (studentLessonData.gscore>0) { var gscoreData = GetPersent(lessonDataAnalysis.gscore, studentLessonData.gscore); gscoreRate= gscoreData.persent; groupType+=1; lessonItem.xz_jf=studentLessonData.gscore; } double groupTask = 0; var groupTasks= studentLessonData.taskRecord.itemRecords.Where(x => x.isGroup); if(groupTasks!=null && groupTasks.Count()>0) { double score = CalculateScore(groupTasks.Sum(x=>x.optCount)); groupType+=1; lessonItem.xz_sc=groupTasks.Count(); } double fxGrp = 0; if (groupType>0) { fxGrp = (pickRate+coworkRate+gscoreRate+groupTask)/groupType; } if (groupType==2 &&(pickRate>0 && gscoreRate>0) ) { fxGrp = (pickRate*0.8+coworkRate+gscoreRate*0.8+groupTask)/groupType; } if (groupType==1) { fxGrp=(pickRate+coworkRate+gscoreRate+groupTask)*0.8/groupType; } lessonItem.hz_nl=Math.Round(fxGrp,4); } { //任务相关指数 double n = studentLessonData.taskRecord.itemRecords.Count()*1.0; if (n>0) { double max_m = lessonDataAnalysis.task.Max(); double w = studentLessonData.taskRecord.itemRecords.Where(x => x.resultWeight>0).Count()*1.0; double y = (10 *w/n+(j/t) *w)/max_m; double l = max_m*(w*w/n+(j/t) * w)/n; double f4 = Math.Round(190*1.0/(1+Math.Exp(-(y)))-95, 4); double f5 = Math.Round(200*1.0/(1+Math.Exp(-(l)))-100, 4); lessonItem.rw_fqc =n; lessonItem.rw_cyc =w; lessonItem.rw_zpc =studentLessonData.taskRecord.itemRecords.Sum(x=>x.optCount); lessonItem.rw_cx =f4; lessonItem.rw_cy =f5; double score = 0; if (w>0) { foreach (var item in studentLessonData.taskRecord.itemRecords) { score += CalculateScore(item.optCount); } } lessonItem.rw_cx =score*1.0/n; } // _logger.LogInformation($"{studentLessonData.id}=>任务指数:{f4}\t专注指数:{f5}\t任务次数:{n}\t参与次数:{w}\t"); } { //评价相关指数 double n = studentLessonData.rateingRecord.itemRecords.Count()*1.0; if (n>0) { var v = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("Voting")); double vc = v.Count()*1.0; var g = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("GrandRating")); double gc = g.Count()*1.0; var p = studentLessonData.rateingRecord.itemRecords.Where(x => x.itemType.Equals("PeerAssessment")); double pc = p.Count()*1.0; var vg = v.Sum(x => x.itemScore); var vo = v.Sum(x => x.optCount); double vs = vc/n* (vg+ vo); var gg = g.Sum(x => x.itemScore); var go = g.Sum(x => x.optCount); double gs = gc/n* (gg+ go); var pg = p.Sum(x => x.itemScore); var po = p.Sum(x => x.optCount); double ps = pc/n* (pg+ po); double h = vs+ps+gs; double f3 = Math.Round(190*1.0/(1+Math.Exp(-(h)))-95, 4); studentLessonData.appraise=f3; // _logger.LogInformation($"{studentLessonData.id}=>评价能力:{f3}\t评价次数:{n}\t投票次数:{vc}-{vg}-{vo}\t星光次数:{gc}-{gg}-{go}\t互评次数:{pc}-{pg}-{po}"); lessonItem.pj_nl =f3; lessonItem.pj_cs =n; lessonItem.pj_vc =vc; lessonItem.pj_vg =vg; lessonItem.pj_vo =vo; lessonItem.pj_gc =gc; lessonItem.pj_gg =gg; lessonItem.pj_go =go; lessonItem.pj_pc =pc; lessonItem.pj_pg =pg; lessonItem.pj_po =po; } } { //协作相关指数 var n = studentLessonData.coworkRecord.itemRecords.Count()*1.0; if (n>0) { //总的协作成果数 var w = studentLessonData.coworkRecord.itemRecords.Where(x => x.resultWeight>0); double ss = w.Sum(x => x.itemScore)*1.0; double sw = w.Sum(x => x.resultWeight)*1.0; double wc = w.Count()*1.0; double x = 0.0; if (wc>0) { x=sw/(j *wc); } double max_xzcg = 40; double k = (wc*wc/n+x)/n+ wc*(ss/max_xzcg)* (wc/n); double f6 = Math.Round(190*1.0/(1+Math.Exp(-(k)))-95, 4); double f7 = Math.Round(200*1.0/(1+Math.Exp(-(k)))-100, 4); lessonItem.xz_fqc =n; lessonItem.xz_cyc =wc; lessonItem.xz_cgf =ss; lessonItem.xz_cx =f6; lessonItem.xz_cy =f7; } //_logger.LogInformation($"{studentLessonData.id}=>协作指数:{f6}\t专注指数:{f7}\t协作次数:{n}\t参与次数:{wc}\t协作成果分数:{ss}\t{k}"); } double xx_cx = 0, xx_cy = 0; int avg_cx = 0, avg_cy = 0; if (lessonItem.xz_cx>0) { avg_cx+=1; } if (lessonItem.pj_nl>0) { avg_cx+=1; } if (lessonItem.rw_cx>0) { avg_cx+=1; } if (lessonItem.pc_df>0) { avg_cx+=1; } if (lessonItem.hd_cx>0) { avg_cx+=1; } if (lessonItem.hz_nl>0) { avg_cx+=1; } if (avg_cx>0) { 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); } if (lessonItem.xz_cy>0) { avg_cy+=1; } if (lessonItem.pj_nl>0) { avg_cy+=1; } if (lessonItem.rw_cy>0) { avg_cy+=1; } if (lessonItem.pc_zd>0) { avg_cy+=1; } if (lessonItem.hd_cy>0) { avg_cy+=1; } if (lessonItem.hz_nl>0) { avg_cy+=1; } if (avg_cy>0) { 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); } lessonItem.xx_cx=xx_cx; lessonItem.xx_cy=xx_cy; lessonItems.Add(lessonItem); studentLessonData.achieve= lessonItem.xx_cx; studentLessonData.attitude= lessonItem.xx_cy; studentLessonData.appraise= lessonItem.pj_nl; studentLessonData.cowork= lessonItem.xz_cx; studentLessonData.cooperation=lessonItem.hz_nl; } return lessonItems; } static double CalculateScore(int n, double k = 1) { if (n == 0) { return 0; } else { double score = 60 + (40 / (1 + Math.Exp(-k * (n - 1)))); return Math.Max(80, Math.Min(score, 100)); } } /// /// 使用标准差定义异常值。如果一个数字与平均值的偏差超过某个标准差倍数(例如2倍或3倍),则可以认为它是异常的。 /// /// /// public static List CleanDataBySDThreshold(IEnumerable array, double thresholdMultiplier = 2) { if (array.Count() == 0) return new List(); double average = Math.Round(array.Sum()*1.0/array.Count(), 4); double variance = array.Select(x => Math.Round(Math.Pow(x - average, 2), 4)).Sum()*1.0/array.Count(); double standardDeviation = Math.Sqrt(Math.Round(variance, 4)); double threshold = Math.Round(thresholdMultiplier * standardDeviation); List datas = new List(); foreach (double value in array) { double deviation = Math.Round(Math.Abs(value - average), 4); if (deviation <= threshold) { datas.Add(value); } } return datas; } /// /// /// /// /// /// public static async Task ExportToExcelAzureBlob(List items, AzureStorageFactory azureStorage,string owner , string path , XmlDocument xmlDocument) { ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial; using (var memoryStream = new MemoryStream()) { using (ExcelPackage package = new ExcelPackage()) { ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("学生课中数据"); // 获取类的属性 PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties(); // 添加表头 int currentRow = 1; for (int i = 0; i < properties.Length; i++) { string summary = Regex.Replace(GetPropertySummary(properties[i], xmlDocument), @"\s+", ""); worksheet.Cells[currentRow, i + 1].Value = summary; } // 填充数据 currentRow = 2; foreach (var item in items) { for (int i = 0; i < properties.Length; i++) { worksheet.Cells[currentRow, i + 1].Value = properties[i].GetValue(item); } currentRow++; } // 设置表格样式 worksheet.Cells[worksheet.Dimension.Address].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Left; worksheet.Cells[worksheet.Dimension.Address].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Top; // 保存到文件流 await package.SaveAsAsync(memoryStream); memoryStream.Position=0; } await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(memoryStream, "records", path); } } /// /// 导出Excel /// /// /// /// public static async Task ExportToExcelLocal(List items, string filePath, XmlDocument xmlDocument) { ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial; using (ExcelPackage package = new ExcelPackage()) { ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("学生课中数据"); // 获取类的属性 PropertyInfo[] properties = typeof(StudentLessonItem).GetProperties(); // 添加表头 int currentRow = 1; for (int i = 0; i < properties.Length; i++) { string summary = Regex.Replace(GetPropertySummary(properties[i], xmlDocument), @"\s+", ""); worksheet.Cells[currentRow, i + 1].Value = summary; } // 填充数据 currentRow = 2; foreach (var item in items) { for (int i = 0; i < properties.Length; i++) { worksheet.Cells[currentRow, i + 1].Value = properties[i].GetValue(item); } currentRow++; } // 设置表格样式 worksheet.Cells[worksheet.Dimension.Address].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Left; worksheet.Cells[worksheet.Dimension.Address].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Top; // 保存到文件 FileInfo fileInfo = new System.IO.FileInfo(filePath); await package.SaveAsAsync(fileInfo); } } private static string GetPropertySummary(PropertyInfo property, XmlDocument xmlDocument) { XmlNodeList? xmlNodeList = xmlDocument.DocumentElement?.SelectNodes("//member[@name='P:" + property.DeclaringType?.FullName + "." + property.Name + "']"); if (xmlNodeList!= null && xmlNodeList.Count > 0) { XmlNode? xmlNode = xmlNodeList[0]; if (xmlNode != null && xmlNode.FirstChild != null) { return xmlNode.FirstChild.InnerText; } } return property.Name; } /// /// 当前数超越集合的百分比 /// /// /// /// public static (double persent, int count) GetPersent(IEnumerable nums, double curr) { int count = 0; foreach (var op in nums.OrderBy(x => x)) { if (op <= curr) { count++; } else { break; } } return (count *1.0/ nums.Count() * 100, count); } } /// /// 学生导出Excel的Entity /// public class StudentLessonItem { /// /// 学生id /// public string? studentId { get; set; } /// /// 互动发起次数 /// public double hd_fqc { get; set; } = 0; /// /// 互动参与次数 /// public double hd_cyc { get; set; } = 0; /// /// 互动正确次数 /// public double hd_zqc { get; set; } = 0; /// /// 个人计分 /// public double gr_jf { get; set; } = 0; /// /// 互动成效指数 /// public double hd_cx { get; set; } = 0; /// /// 互动专注指数 /// public double hd_cy { get; set; } = 0; /// /// 评测得分率 /// public double pc_df { get; set; } = 0; /// /// 评测作答率 /// public double pc_zd { get; set; } = 0; /// /// 任务发起次数 /// public double rw_fqc { get; set; } = 0; /// /// 任务参与次数 /// public double rw_cyc { get; set; } = 0; /// /// 任务作品数 /// public double rw_zpc { get; set; } = 0; /// /// 任务成效指数 /// public double rw_cx { get; set; } = 0; /// /// 任务专注指数 /// public double rw_cy { get; set; } = 0; /// /// 评价发起次数 /// public double pj_cs { get; set; } = 0; /// /// 投票发起次数 /// public double pj_vc { get; set; } = 0; /// /// 投票得票数 /// public double pj_vg { get; set; } = 0; /// /// 投票次数 /// public double pj_vo { get; set; } = 0; /// /// 星光发起次数 /// public double pj_gc { get; set; } = 0; /// /// 星光得分数 /// public double pj_gg { get; set; } = 0; /// /// 星光评分次数 /// public double pj_go { get; set; } = 0; /// /// 互评发起次数 /// public double pj_pc { get; set; } = 0; /// /// 互评得分数 /// public double pj_pg { get; set; } = 0; /// /// 互评评分次数 /// public double pj_po { get; set; } = 0; /// /// 评价能力 /// public double pj_nl { get; set; } = 0; /// /// 协作发起次数 /// public double xz_fqc { get; set; } = 0; /// /// 协作参与次数 /// public double xz_cyc { get; set; } = 0; /// /// 协作成果分数 /// public double xz_cgf { get; set; } = 0; /// /// 协作能力指数 /// public double xz_cx { get; set; } = 0; /// /// 协作专注指数 /// public double xz_cy { get; set; } = 0; /// /// 小组挑人 /// public double xz_tr { get; set; } = 0; /// /// 小组计分 /// public double xz_jf { get; set; } = 0; /// /// 小组协作成果分 /// public double xz_xz { get; set; } = 0; /// /// 组任务上传数 /// public double xz_sc { get; set; } = 0; /// /// 合作能力 /// public double hz_nl { get; set; } = 0; /// /// 学习成效 /// public double xx_cx { get; set; } = 0; /// /// 学习专注度 /// public double xx_cy { get; set; } = 0; } /// /// 历史课例的关键数据模型 /// public class LessonDataAnalysisCluster : LessonDataAnalysisBase { /// /// /// // public List>> clustersInteract { get; set; } = new List>>(); ///// ///// ///// //public List>> clustersPscore { get; set; } = new List>>(); ///// ///// ///// //public List>> clustersTscore { get; set; } = new List>>(); ///// ///// ///// //public List>> clustersGscore { get; set; } = new List>>(); public List>> levelInteract { get; set; } = new List>>(); public double interactPass { get; set; } public double interactGood { get; set; } public double interactLow { get; set; } public double interactMedium { get; set; } public double interactHigh { get; set; } } /// /// /// public abstract class LessonDataAnalysisBase { ///// ///// 协作次数 ///// //public IEnumerable cowork { get; set; } = new List(); ///// ///// ///// //public IEnumerable coworkBase { get; set; } = new List(); /// /// /// public List task { get; set; } = new List(); ///// ///// ///// //public IEnumerable taskBase { get; set; } = new List(); ///// ///// ///// //public IEnumerable exam { get; set; } = new List(); ///// ///// ///// //public IEnumerable examBase { get; set; } = new List(); ///// ///// ///// //public IEnumerable smartRating { get; set; } = new List(); ///// ///// ///// //public IEnumerable smartRatingBase { get; set; } = new List(); /// /// /// public List irs { get; set; } = new List(); /// /// /// public List interactNormal { get; set; } = new List(); /// /// 个人计分 /// public List pscore { get; set; } = new List(); /// /// 小组计分 /// public List gscore { get; set; } = new List(); /// /// 互动计分 /// public List tscore { get; set; } = new List(); /// /// 作品上传数 /// public List> upload { get; set; } = new List>(); /// /// 学生协作成果数 /// public List stuCowork { get; set; } = new List(); /// /// 小组协作成果数 /// public List groupCowork { get; set; } = new List(); /// /// 挑人集合 /// public List> pickup { get; set; } = new List>(); ///// ///// 挑人集合-小组 ///// //public List> pickup_group { get; set; } = new List>(); } /// /// 每月的课例模型数据 /// public class LessonDataAnalysisMonth : LessonDataAnalysisBase { /// /// 时间戳 /// public long updateTime { get; set; } /// /// yyyyMM /// public string? yearMonth { get; set; } } /// /// /// public class LessonLocal { /// /// /// public LessonBase? lessonBase { get; set; } /// /// /// public TimeLineData? timeLineData { get; set; } /// /// /// public LessonRecord? lessonRecord { get; set; } /// /// /// public List studentLessonDatas { get; set; } = new List(); /// /// /// public List taskDatas { get; set; } = new List(); /// /// /// public List smartRatingDatas { get; set; } = new List(); /// /// /// public List irsDatas { get; set; } = new List(); /// /// /// public List coworkDatas { get; set; } = new List(); /// /// /// public List examDatas { get; set; } = new List(); /// /// /// public List sokratesDatas { get; set; } = new List(); } /// /// /// public class TechCount { public string? yearMonth { get; set; } /// /// /// public string? lessonId { get; set; } /// /// 评测数量 /// public int examCount { get; set; } /// /// 任务数量 /// public int taskCount { get; set; } /// /// IRS次数 /// public int irsCount { get; set; } /// /// 互动次数 /// //public int interactExamCount { get; set; } /// /// 互动次数 /// public int interactNormalCount { get; set; } /// /// 协作次数 /// public int coworkCount { get; set; } /// /// 智能评分次数 /// public int smartRatingCount { get; set; } /// /// /// public List timeCount { get; set; } = new List(); /// /// /// public IEnumerable pscore { get; set; } = new List(); /// /// /// public IEnumerable gscore { get; set; } = new List(); /// /// /// public IEnumerable tscore { get; set; } = new List(); /// /// 评测数量 /// public int examCountBase { get; set; } /// /// 任务数量 /// public int taskCountBase { get; set; } /// /// IRS次数 /// public int irsCountBase { get; set; } /// /// 互动次数 /// //public int interactExamCountBase { get; set; } /// /// 互动次数 /// public int interactNormalCountBase { get; set; } /// /// 协作次数 /// public int coworkCountBase { get; set; } /// /// 智能评分次数 /// public int smartRatingCountBase { get; set; } /// /// 作品上传数 /// public List> upload { get; set; } = new List>(); /// /// 学生协作成果数 /// public List stuCowork { get; set; } = new List(); /// /// 小组协作成果数 /// public List groupCowork { get; set; } = new List(); public List pickup { get; set; } = new List(); } }