using Azure.Storage.Blobs.Models; using Microsoft.AspNetCore.Mvc; using System.Text.Json; using TEAMModelOS.SDK; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Models; using TEAMModelOS.SDK.Extension; using StackExchange.Redis; using System.Text.RegularExpressions; using System.Linq; using static TEAMModelOS.SDK.Models.Cosmos.Student.StudentAnalysis; using static Microsoft.Azure.Amqp.Serialization.SerializableType; using Microsoft.AspNetCore.DataProtection.KeyManagement; using System.Drawing; using TEAMModelOS.SDK.Models.Cosmos.OpenEntity; using System.Diagnostics; using Microsoft.OData.UriParser; using HTEX.Test.Service; using System.IO; using static Pipelines.Sockets.Unofficial.SocketConnection; using static HTEX.Test.Controllers.LessonRecordController; using System.Collections.Generic; using static System.Formats.Asn1.AsnWriter; namespace HTEX.Test.Controllers { [ApiController] [Route("lesson-record")] public class LessonRecordController : ControllerBase { private readonly ILogger _logger; private readonly AzureCosmosFactory _azureCosmos; private readonly AzureStorageFactory _azureStorage; private readonly List objectiveTypes=new List(){ "single", "multiple", "sortmultiple" , "judge" }; private readonly double minRank = 1; private readonly double maxRank = 100; public LessonRecordController(ILogger logger, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage) { _logger = logger; _azureCosmos = azureCosmos; _azureStorage = azureStorage; } [HttpPost("process-local")] public async Task ProcessLocal(JsonElement json) { List studentLessonDatas = new List(); string? id=json.GetProperty("id").GetString(); string path = $"C:\\Users\\CrazyIter\\Downloads\\{id}"; if (!string.IsNullOrWhiteSpace(id)) { var files = ListAllFiles(path); // var sampleJson =System.IO. File.ReadAllTextAsync(path); LessonBase? lessonBase = null; List localStudents = new List(); List taskDatas = new List(); List smartRatingDatas = new List(); List irsDatas = new List(); List coworkDatas = new List(); List examDatas = new List(); TimeLineData? timeLineData = null; foreach (var item in files) { if (item.Contains("IES\\base.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); jsons=jsons.Replace("\"Uncall\"", "0").Replace("Uncall", "0"); lessonBase = jsons.ToObject(); var data = GetBaseData( lessonBase); localStudents=data.studentLessonDatas; } if (item.Contains("IES\\TimeLine.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); timeLineData = jsons.ToObject(); } if (item.Contains("IES\\Task.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); taskDatas = jsons.ToObject>(); } if (item.Contains("IES\\SmartRating.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); smartRatingDatas = jsons.ToObject>(); } if (item.Contains("IES\\IRS.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); irsDatas = jsons.ToObject>(); } if (item.Contains("IES\\Cowork.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); coworkDatas = jsons.ToObject>(); } try { if (item.Contains($"\\{id}\\Exam\\") && item.EndsWith("Exam.json")) { string examsFile = item; if (examsFile.EndsWith("Exam.json")) { ExamData? examData = null; string jsons = await System.IO.File.ReadAllTextAsync(item); jsons= jsons.Replace("\"publish\": \"0\"", "\"publish\": 0").Replace("\"publish\": \"1\"", "\"publish\": 1"); examData = jsons.ToObject(); if (examData!=null && examData.exam.papers.IsNotEmpty()) { string paperId = examData.exam.papers.First().id; string paperPath = $"{path}\\ExamPaper\\{paperId}\\index.json"; string jsonp = await System.IO.File.ReadAllTextAsync(paperPath); LessonPaper lessonPaper = jsonp.ToObject(); examData.paper = lessonPaper; examDatas.Add(examData); } } } } catch (Exception ex) { _logger.LogError(ex, ex.Message); } } if (lessonBase!=null && timeLineData!=null) { studentLessonDatas = localStudents.ToJsonString().ToObject>(); studentLessonDatas = GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas, id); studentLessonDatas = GetCoworkData(lessonBase, timeLineData, coworkDatas, studentLessonDatas); studentLessonDatas = GetExamData(lessonBase, timeLineData, examDatas, studentLessonDatas); studentLessonDatas = GetSmartRatingData(lessonBase, timeLineData, smartRatingDatas, studentLessonDatas, id); studentLessonDatas = GetTaskData(lessonBase, timeLineData, taskDatas, studentLessonDatas); System.IO.File.WriteAllText(Path.Combine(path, $"{id}-stu.json"), studentLessonDatas.ToJsonString()); string jsons = await System.IO.File.ReadAllTextAsync("analysis.json"); LessonDataAnalysis lessonDataAnalysis = jsons.ToObject(); } return Ok( new { studentLessonDatas }); }return Ok(); } [HttpPost("process-preview")] public async Task ProcessPreview(JsonElement json) { string path = $"F:\\lesson-local"; var files = ListAllFiles(path); List techCounts = new List(); //var files_count = files.Where(x => x.EndsWith("548724334458441728-count.json")).Take(100).ToList(); /* "examCount": 0, "taskCount": 0, "irsCount": 1, "coworkCount": 0, "smartRatingCount": 0, "timeCount": */ bool loadLocal = true; LessonDataAnalysis lessonDataAnalysis = new LessonDataAnalysis(); var files_anlysis = files.Where(x => x.EndsWith("analysis.json")).Take(1).FirstOrDefault(); if (files_anlysis!=null) { string jsons = await System.IO.File.ReadAllTextAsync(files_anlysis); lessonDataAnalysis= jsons.ToObject(); loadLocal=false; } if (loadLocal) { foreach (var item in files) { if (item.EndsWith("count.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); TechCount count = jsons.ToObject(); count.interactNormalCount=count.irsCount; count.lessonId=item.Replace("-count.json", "").Replace(path, "").Replace("\\", ""); string localjson = await System.IO.File.ReadAllTextAsync($"{path}\\{count.lessonId}-local.json"); var lessonLocal = localjson.ToObject(); 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); } await System.IO.File.WriteAllTextAsync(item, count.ToJsonString()); techCounts.Add(count); } } Console.WriteLine($"techCounts:{techCounts.Count}"); //标准差偏差N倍,视为异常数据 int thresholdMultiplier = 2; lessonDataAnalysis.pscore = techCounts.SelectMany(x =>x.pscore); lessonDataAnalysis.pscore= CleanDataBySDThreshold(lessonDataAnalysis.pscore, thresholdMultiplier).OrderBy(x => x); List>> clustersDataPscore = new(); var clusterPscore = KMeansService.KMeans(lessonDataAnalysis.pscore.Select(x => x).OrderBy(x => x)); foreach (var item in clusterPscore) { Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}"); } Console.WriteLine($"avg: {lessonDataAnalysis.pscore.Average()}"); foreach (var s in clusterPscore.OrderBy(x => x.Key)) { clustersDataPscore.Add(s); } lessonDataAnalysis.clustersPscore=clustersDataPscore; lessonDataAnalysis.gscore = techCounts.SelectMany(x => x.gscore); lessonDataAnalysis.gscore= CleanDataBySDThreshold(lessonDataAnalysis.gscore, thresholdMultiplier).OrderBy(x => x); List>> clustersDataGscore = new(); var clusterGscore = KMeansService.KMeans(lessonDataAnalysis.gscore.Select(x => x).OrderBy(x => x)); foreach (var item in clusterGscore) { Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}"); } Console.WriteLine($"avg: {lessonDataAnalysis.gscore.Average()}"); foreach (var s in clusterGscore.OrderBy(x => x.Key)) { clustersDataGscore.Add(s); } lessonDataAnalysis.clustersGscore=clustersDataGscore; lessonDataAnalysis.tscore = techCounts.SelectMany(x => x.tscore); lessonDataAnalysis.tscore= CleanDataBySDThreshold(lessonDataAnalysis.tscore, thresholdMultiplier).OrderBy(x => x); List>> clustersDataTscore = new(); var clusterTscore = KMeansService.KMeans(lessonDataAnalysis.tscore.Select(x => x).OrderBy(x => x)); foreach (var item in clusterTscore) { Console.WriteLine($"dp:{item.Key} ,avg: {item.Value.Average()}, count: {item.Value.Count}, min:{item.Value.Min()}, max:{item.Value.Max()}"); } Console.WriteLine($"avg: {lessonDataAnalysis.tscore.Average()}"); foreach (var s in clusterTscore.OrderBy(x => x.Key)) { clustersDataTscore.Add(s); } lessonDataAnalysis.clustersTscore=clustersDataTscore; lessonDataAnalysis.cowork = techCounts.Where(x => x.coworkCount>0).Select(x => (double)x.coworkCount); lessonDataAnalysis.cowork= CleanDataBySDThreshold(lessonDataAnalysis.cowork, thresholdMultiplier); lessonDataAnalysis.coworkBase = techCounts.Where(x => x.coworkCountBase>0).Select(x => (double)x.coworkCountBase); lessonDataAnalysis.coworkBase= CleanDataBySDThreshold(lessonDataAnalysis.coworkBase, thresholdMultiplier); lessonDataAnalysis.task = techCounts.Where(x => x.taskCount > 0).Select(x => (double)x.taskCount); lessonDataAnalysis.task= CleanDataBySDThreshold(lessonDataAnalysis.task, thresholdMultiplier); lessonDataAnalysis.taskBase = techCounts.Where(x => x.taskCountBase > 0).Select(x => (double)x.taskCountBase); lessonDataAnalysis.taskBase = CleanDataBySDThreshold(lessonDataAnalysis.taskBase, thresholdMultiplier); lessonDataAnalysis.exam = techCounts.Where(x => x.examCount > 0).Select(x => (double)x.examCount); lessonDataAnalysis.exam = CleanDataBySDThreshold(lessonDataAnalysis.exam, thresholdMultiplier); lessonDataAnalysis.examBase = techCounts.Where(x => x.examCountBase > 0).Select(x => (double)x.examCountBase); lessonDataAnalysis.examBase = CleanDataBySDThreshold(lessonDataAnalysis.examBase, thresholdMultiplier); lessonDataAnalysis.smartRating = techCounts.Where(x => x.smartRatingCount > 0).Select(x => (double)x.smartRatingCount); lessonDataAnalysis.smartRating = CleanDataBySDThreshold(lessonDataAnalysis.smartRating, thresholdMultiplier); lessonDataAnalysis.smartRatingBase = techCounts.Where(x => x.smartRatingCountBase > 0).Select(x => (double)x.smartRatingCountBase); lessonDataAnalysis.smartRatingBase = CleanDataBySDThreshold(lessonDataAnalysis.smartRatingBase, thresholdMultiplier); lessonDataAnalysis.irs = techCounts.Where(x => x.irsCount > 0).Select(x => (double)x.irsCount); lessonDataAnalysis.irs = CleanDataBySDThreshold(lessonDataAnalysis.irs, thresholdMultiplier); lessonDataAnalysis.interactNormal = techCounts.Where(x => x.interactNormalCount > 0).Select(x => (double)x.interactNormalCount); Console.WriteLine($"interactNormal{lessonDataAnalysis.interactNormal.Count()}"); lessonDataAnalysis.interactNormal= CleanDataBySDThreshold(lessonDataAnalysis.interactNormal, thresholdMultiplier).OrderBy(x => x); Console.WriteLine($"interactNormal{lessonDataAnalysis.interactNormal.Count()}"); var tcount = techCounts.Where(x => x.coworkCount > 0 || x.taskCount>0 || x.interactNormalCount>0|| x.examCount>0 || x.smartRatingCount>0); double coworkWeight = lessonDataAnalysis.cowork.Count()*1.0/tcount.Count(); lessonDataAnalysis.coworkRate=coworkWeight; double taskWeight = lessonDataAnalysis.task.Count()*1.0/tcount.Count(); lessonDataAnalysis.taskRate=taskWeight; double interactWeight = lessonDataAnalysis.interactNormal.Count()*1.0/tcount.Count(); lessonDataAnalysis.interactRate=interactWeight; double examWeight = lessonDataAnalysis. exam.Count()*1.0/tcount.Count(); lessonDataAnalysis.examRate=examWeight; double smartRatingWeight = lessonDataAnalysis. smartRating.Count()*1.0/tcount.Count(); lessonDataAnalysis.smartRatingRate=smartRatingWeight; List>> clustersDataInteract = new(); var clusterInteract = KMeansService.KMeans(lessonDataAnalysis.interactNormal.Select(x => (int)x).OrderBy(x => x)); 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 s in clusterInteract.OrderBy(x => x.Key)) { clustersDataInteract.Add(s); } lessonDataAnalysis.clustersInteract=clustersDataInteract; var groups = techCounts.SelectMany(x => x.timeCount).GroupBy(x => x.code).Select(x => new { key = x.Key, list = x.ToList() }); Dictionary> techDict = new Dictionary>(); foreach (var group in groups) { var array = group.list.Where(x => x.value>0).Select(x => (double)x.value); array = CleanDataBySDThreshold(array, thresholdMultiplier); techDict.Add(group.key, array); } System.IO. File.WriteAllText(Path.Combine(path, "analysis.json"), lessonDataAnalysis.ToJsonString()); } // List lessons = new List(); var files_local = files.Where(x=>x.EndsWith("557838358030716928-local.json")).Take(100).ToList(); List> studentLessons = new List>(); foreach (var item in files) { if (item.EndsWith("local.json")) { string jsons = await System.IO.File.ReadAllTextAsync(item); LessonLocal lesson = jsons.ToObject(); // lessons.Add(lesson); List studentLessonDatas = lesson.studentLessonDatas.ToJsonString().ToObject>(); studentLessonDatas = GetIRSData(lesson.lessonBase!, lesson.timeLineData!, lesson.irsDatas, studentLessonDatas, lesson.examDatas, item); studentLessonDatas = GetCoworkData(lesson.lessonBase!, lesson.timeLineData!, lesson.coworkDatas, studentLessonDatas); studentLessonDatas = GetExamData(lesson.lessonBase!, lesson.timeLineData!, lesson.examDatas, studentLessonDatas); studentLessonDatas = GetSmartRatingData(lesson.lessonBase!, lesson.timeLineData!, lesson.smartRatingDatas, studentLessonDatas, item); studentLessonDatas = GetTaskData(lesson.lessonBase!, lesson.timeLineData!, lesson.taskDatas, studentLessonDatas); var techCount = techCounts.Find(x => !string.IsNullOrWhiteSpace(x.lessonId) && !string.IsNullOrWhiteSpace(lesson?.lessonRecord?.id) && x.lessonId.Equals(lesson.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) { lessonDataAnalysis.stuCowork.AddRange(stu_scores); } var grp_scores = stu.group_coworkScore.Where(x => x>0); if (grp_scores!=null && grp_scores.Count()>0) { lessonDataAnalysis.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); lessonDataAnalysis.upload.Add(new List() { avgUpload, maxUpload }); } if (pickUp.Count>0) { lessonDataAnalysis.pickup.Add(pickUp.ToList()); } System.IO.File.WriteAllText(Path.Combine(path, $"{lesson.lessonRecord!.id}-stu.json"), studentLessonDatas.ToJsonString()); studentLessons.Add(studentLessonDatas); } } System.IO.File.WriteAllText(Path.Combine(path, "analysis.json"), lessonDataAnalysis.ToJsonString()); return Ok(new { }); } [HttpPost("process-history")] public async Task ProcessHistory(JsonElement json) { List localIds = new List(); string path = "F:\\lesson-local"; var files = ListAllFiles(path); foreach (var file in files) { if (file.EndsWith("-count.json")) { string lessonId = file.Replace("-count.json", "").Replace(path, "").Replace("\\", ""); localIds.Add(lessonId); } } //1709222400000 2024.3.1 var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School) .GetList($"SELECT value c FROM School AS c where c.startTime>1706716800000 and c.expire<=0 and c.status<>404 and c.duration>300 and c.pk='LessonRecord' and c.school<>'hbcn' and c.school<>'habook' and (c.tLevel>0 or c.pLevel>0) ", null); if (result.list.IsNotEmpty()) { List ignore = new List() { "PgJump", "PgRcv", "PgAdd" }; Stopwatch stopwatch = Stopwatch.StartNew(); //stopwatch.Start(); //await GetLessData(result, ignore); //stopwatch.Stop(); //Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms"); //stopwatch.Reset(); stopwatch.Start(); await foreach (var item in GetLessonLocal(result.list, localIds)) { if (item.lessonBase!=null && item.lessonBase.student!=null) { TechCount techCount = new TechCount { lessonId=item.lessonRecord?.id, examCount = item.examDatas.Count, taskCount = item.taskDatas.Count, irsCount = item.irsDatas.Count, coworkCount = item.coworkDatas.Count, smartRatingCount =item.smartRatingDatas.Count, timeCount=item.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() }; await System.IO.File.WriteAllTextAsync($"{path}\\{item.lessonRecord!.id}-local.json", item.ToJsonString()); await System.IO.File.WriteAllTextAsync($"{path}\\{item.lessonRecord.id}-count.json", techCount.ToJsonString()); } else { Console.WriteLine($"没有课例id的数据:{item.lessonRecord?.id}"); System.IO.File.Delete($"{path}\\{item.lessonRecord!.id}-local.json"); System.IO.File.Delete($"{path}\\{item.lessonRecord!.id}-count.json"); } } stopwatch.Stop(); Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms"); } return Ok("Lesson records"); } public static List ListAllFiles(string directoryPath) { List filePaths = new List(); DirectoryInfo dirInfo = new DirectoryInfo(directoryPath); // 获取目录下的所有文件(包括子目录中的文件) FileInfo[] files = dirInfo.GetFiles("*", SearchOption.AllDirectories); foreach (FileInfo file in files) { filePaths.Add(file.FullName); // Console.WriteLine(file.FullName); } return filePaths; } public class LessonDataAnalysis { public long updateTime { get; set; } public IEnumerable cowork { get; set; } = new List(); public IEnumerable coworkBase { get; set; } = new List(); public IEnumerable 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 IEnumerable irs { get; set; } = new List(); public IEnumerable interactNormal { get; set; } = new List(); 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 IEnumerable pscore { get; set; } = new List(); /// /// 小组计分 /// public IEnumerable gscore { get; set; } = new List(); /// /// 互动计分 /// public IEnumerable 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 double interactRate { get; set; } public double taskRate { get; set; } public double coworkRate { get; set; } public double examRate { get; set; } public double smartRatingRate { get; set; } } /// /// 计算当前元素在集合中超过了多少百分比的值 /// /// /// /// public static double GetPersent(IEnumerable nums, int curr) { int count = 0; foreach (var op in nums.OrderBy(x => x)) { if (op < curr) { count++; } else if (op == curr) { count++; } else { break; } } return count *1.0/ nums.Count() * 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; } private async IAsyncEnumerable GetLessonLocal(List lessonRecords, List localIds) { foreach (var lessonRecord in lessonRecords) { if (localIds.Contains(lessonRecord.id)) { continue; } LessonLocal lessonLocal = new LessonLocal { lessonRecord=lessonRecord }; if (System.IO.File.Exists($"F:\\lesson-local\\{lessonRecord.id}-local.json")) { string jsonp = await System.IO.File.ReadAllTextAsync($"F:\\lesson-local\\{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, lessonRecord.school); } if (lessonLocal.lessonBase!=null && lessonLocal.lessonBase.student!=null) { var baseData = GetBaseData(lessonLocal.lessonBase!); lessonLocal.studentLessonDatas= baseData.studentLessonDatas; List examDatas = await GetExamData(lessonRecord, lessonLocal.timeLineData); lessonLocal.examDatas = examDatas; lessonLocal.sokratesDatas= lessonLocal.sokratesDatas.IsNotEmpty() ? lessonLocal.sokratesDatas : lessonLocal.timeLineData!=null ? lessonLocal.timeLineData.events : new List(); } yield return lessonLocal; } } private async Task GetLessonFiles(LessonLocal lessonLocal, List files, string school) { await Parallel.ForEachAsync(files, async (file, _) => { try { BlobDownloadResult blobDownloadResult = await _azureStorage.GetBlobContainerClient(school).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 (Exception ex) { //Console.WriteLine($"{file}"); } }); return lessonLocal; } private async Task GetLessData(CosmosDBResult result, List ignore) { foreach (var item in result.list) { //读取TimeLine.json TimeLineData? timeLineData = null; try { BlobDownloadResult timeLineBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/TimeLine.json").DownloadContentAsync(); timeLineData = timeLineBlobDownload.Content.ToObjectFromJson(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/TimeLine.json"); } } //读取基础Base信息 //base.json LessonBase? lessonBase = null; List studentLessonDatas = new List(); //名单出席率低于30%的 不纳入计算。,累计所有课例的科技使用次数及反馈情况,用于做科技分类比例的权重 try { BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/base.json").DownloadContentAsync(); string basejson = baseblobDownload.Content.ToString().Replace("\"Uncall\"", "0").Replace("Uncall", "0"); lessonBase = basejson.ToObject(); var data = GetBaseData(lessonBase); studentLessonDatas=data.studentLessonDatas; } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/base.json"); } } //读取Task.json ///Event 过滤类型 : 'WrkSpaceLoad'作品收集, 'WrkCmp' 作品贴上 文件:Task.json 根据clientWorks 中的seatID 匹配base.json 中的 student List taskDatas = new List(); try { BlobDownloadResult taskBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/Task.json").DownloadContentAsync(); taskDatas = taskBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/Task.json"); } } //读取互评信息 //Event 过滤类型 'RatingStart' //smartRateSummary.mutualSummary.mutualType 互评【All(每人多件评分) Two(随机分配互评) Self(自评)】 smartRateSummary.meteor_VoteSummary 投票 //读取SmartRating.json List smartRatingDatas = new List(); try { BlobDownloadResult smartRatingBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/SmartRating.json").DownloadContentAsync(); smartRatingDatas = smartRatingBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/SmartRating.json"); } } //读取互动信息 //Event 过滤类型 'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad' //TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答 //读取IRS.json List irsDatas = new List(); try { BlobDownloadResult irsBlobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/IRS.json").DownloadContentAsync(); irsDatas = irsBlobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/IRS.json"); } } //读取协作信息 ///Event 过滤类型 'CoworkLoad', "CoworkType":"CoworkGroup",类型 //Cowork.json 中找到对应类型,根据Pgid 去 Cowork.json 中找到对应数据 List coworkDatas = new List(); try { BlobDownloadResult blobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/IES/Cowork.json").DownloadContentAsync(); coworkDatas = blobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/IES/Cowork.json"); } } //苏格拉底SokratesRecords.json List sokrates = new List(); try { BlobDownloadResult blobDownload = await _azureStorage.GetBlobContainerClient(item.school).GetBlobClient($"/records/{item.id}/Sokrates/SokratesRecords.json").DownloadContentAsync(); sokrates = blobDownload.Content.ToObjectFromJson>(); } catch (Exception ex) { if (!ex.Message.Contains("The specified blob does not exist")) { _logger.LogError(ex, $"文件不存在:/records/{item.id}/Sokrates/SokratesRecords.json"); } } List examDatas = await GetExamData(item, timeLineData); var lessonLocal = new LessonLocal { examDatas= examDatas, coworkDatas= coworkDatas, irsDatas = irsDatas, smartRatingDatas = smartRatingDatas, taskDatas = taskDatas, lessonBase = lessonBase, timeLineData = timeLineData, studentLessonDatas = studentLessonDatas, lessonRecord =item, sokratesDatas= sokrates.IsNotEmpty() ? sokrates : timeLineData!=null ? timeLineData.events : new List() }; TechCount techCount = new TechCount { examCount = examDatas.Count, taskCount = taskDatas.Count, irsCount = irsDatas.Count, coworkCount = coworkDatas.Count, smartRatingCount = smartRatingDatas.Count, timeCount= sokrates.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() }; string path = "F:\\lesson-local"; await System.IO.File.WriteAllTextAsync($"{path}\\{item.id}-local.json", lessonLocal.ToJsonString()); await System.IO.File.WriteAllTextAsync($"{path}\\{item.id}-count.json", techCount.ToJsonString()); } } private async Task> GetExamData(LessonRecord item, TimeLineData? timeLineData) { //读取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(item.school).List($"records/{item.id}/Exam"); var paperFiles = await _azureStorage.GetBlobContainerClient(item.school).List($"records/{item.id}/ExamPaper"); foreach (var examsFile in examsFiles) { if (examsFile.EndsWith("Exam.json")) { ExamData? examData = null; try { BlobDownloadResult examDataDownload = await _azureStorage.GetBlobContainerClient(item.school).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(item.school).GetBlobClient($"/records/{item.id}/ExamPaper/{paperId}/index.json").Exists()) { LessonPaper lessonPaper = null; try { BlobDownloadResult paperblobDownload = await _azureStorage.GetBlobContainerClient(item.school).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 class TechCount { 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; } } /// /// 处理base.json的数据 /// /// /// /// private (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); } /// ///读取互动信息 ///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 /// /// /// /// /// private List GetIRSData( LessonBase lessonBase, TimeLineData timeLineData, List irsDatas, List studentLessonDatas,List examDatas,string itemFiles) { 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 }); } } } //单独处理挑人的逻辑 //是否从小组里面挑人。 //不需要去重页面,直接获取挑人大类 PickupResult //小类处理:PickupRight , PickupOption , PickupNthGrp ,PickupEachGrp ,PickupDiff , PickupResult 挑人算不算互动?? 读取PickupMemberId "[\r\n 35\r\n]" var enventsPickup = timeLineData?.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) && x.Event.Equals("PickupResult")); 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}"); } } } } } 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; // 2 * 0.3 * 10= 6 interactScore= studentScore==0 ? 1/(InteractWeight.TT-InteractWeight.T1) * (InteractWeight.TP-InteractWeight.T1) * questionScore : studentScore; } } } return (weight,reultType,interactScore); } #region 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))) ; } #endregion /// /// 获取课中评测数据 /// /// /// /// /// /// /// private List GetExamData( LessonBase lessonBase, TimeLineData timeLineData, List examDatas, List studentLessonDatas) { 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; } /// /// 协作参与率 态度计算 /// /// /// /// /// /// /// private List GetCoworkData( LessonBase lessonBase, TimeLineData timeLineData, List coworkDatas, List studentLessonDatas) { 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) { 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); var max = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore; var min = studentLessonDatas.FindAll(x => x.attend==1&& x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore; var sum = studentLessonDatas.Where(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; } /// /// 处理学生回推数据,并将回推纳入学习态度计算。 /// /// /// /// /// /// /// private List GetTaskData( LessonBase lessonBase, TimeLineData timeLineData, List taskDatas, List studentLessonDatas) { //协作也算任务的一种,'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) { 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 /// /// /// /// /// /// /// private List GetSmartRatingData( LessonBase lessonBase, TimeLineData timeLineData, List smartRatingDatas, List studentLessonDatas,string itemf ) { 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) { Console.WriteLine(itemf); // throw new Exception($"{itemf}\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); var max= meteor_VoteSummary.FindAll(x => x.result==order.First().result).First().result; var min = meteor_VoteSummary.FindAll(x => x.result==order.Last().result).First().result; var 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); var max = smartRatingData.smartRateSummary.meteor_ScoreSummary.FindAll(x => x.result==order.First().result).First().result; var 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); var max = smartRatingData.smartRateSummary.mutualSummary.mutualResults.FindAll(x => x.result==order.First().result).First().result; var 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 double MinMaxNormalization(double min ,double max,double x ) { //排名指数计算=( 当前值分数- 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 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 LocalStudent { /// /// 出席状态 1出席,6公假,5事假,4病假,2缺席,0未签到 /// public int attend { get; set; } /// /// 学生的学号 /// public string? id { get; set; } /// /// 学生所在下标 /// public int index { get; set; } = -1; /// /// 学生座位号 /// public string? seatID { get; set; } /// /// 小组编号 /// public string? groupId { get; set; } } }