CrazyIter_Bin 7 月之前
父节点
当前提交
06c734f6e3
共有 27 个文件被更改,包括 12692 次插入541 次删除
  1. 459 0
      HTEX.DataETL/Controllers/LessonRecordController.cs
  2. 18 0
      HTEX.DataETL/HTEX.DataETL.csproj
  3. 6 0
      HTEX.DataETL/HTEX.DataETL.http
  4. 36 0
      HTEX.DataETL/Program.cs
  5. 41 0
      HTEX.DataETL/Properties/launchSettings.json
  6. 8 0
      HTEX.DataETL/appsettings.Development.json
  7. 32 0
      HTEX.DataETL/appsettings.json
  8. 119 0
      TEAMModelOS.Extension/HTEX.Lib/ETL/KMeansService.cs
  9. 53 0
      TEAMModelOS.Extension/HTEX.Lib/ETL/Lesson/LessonETL.cs
  10. 2117 0
      TEAMModelOS.Extension/HTEX.Lib/ETL/Lesson/LessonETLService.cs
  11. 14 3
      TEAMModelOS.Extension/HTEX.Lib/HTEX.Lib.csproj
  12. 8695 0
      TEAMModelOS.Extension/HTEX.Lib/summary.xml
  13. 305 335
      TEAMModelOS.Extension/HTEX.Test/Controllers/LessonRecordController.cs
  14. 291 154
      TEAMModelOS.Extension/HTEX.Test/Controllers/MockDataController.cs
  15. 3 1
      TEAMModelOS.Extension/HTEX.Test/HTEX.Test.csproj
  16. 397 0
      TEAMModelOS.Extension/HTEX.Test/x.xml
  17. 22 1
      TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelper.cs
  18. 7 0
      TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs
  19. 4 2
      TEAMModelOS.SDK/Models/Service/OpenApiService.cs
  20. 7 0
      TEAMModelOS.sln
  21. 4 2
      TEAMModelOS/Controllers/Analysis/ClassAnalysisController.cs
  22. 2 1
      TEAMModelOS/Controllers/Both/KnowledgeController.cs
  23. 18 9
      TEAMModelOS/Controllers/Both/LessonRecordController.cs
  24. 2 1
      TEAMModelOS/Controllers/Both/ScoreCalcController.cs
  25. 4 3
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  26. 0 1
      TEAMModelOS/TEAMModelOS.csproj
  27. 28 28
      TEAMModelOS/appsettings.Development.json

+ 459 - 0
HTEX.DataETL/Controllers/LessonRecordController.cs

@@ -0,0 +1,459 @@
+using DocumentFormat.OpenXml.Office2010.Excel;
+using HTEX.Lib.ETL;
+using HTEX.Lib.ETL.Lesson;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Azure.Cosmos;
+using Microsoft.OData.UriParser;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text.Json;
+using System.Xml;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Helper.Common.FileHelper;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
+
+namespace HTEX.DataETL.Controllers
+{
+    [ApiController]
+    [Route("lesson-record")]
+    public class LessonRecordController : ControllerBase
+    {
+        private readonly ILogger<LessonRecordController> _logger;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly IConfiguration _configuration;
+        private readonly IWebHostEnvironment _webHostEnvironment;
+
+        private readonly List<string> objectiveTypes = new List<string>() { "single", "multiple", "sortmultiple", "judge" };
+        public LessonRecordController(ILogger<LessonRecordController> logger, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage
+            , IConfiguration configuration, IWebHostEnvironment environment)
+        {
+            _logger = logger;
+            _azureCosmos = azureCosmos;
+            _azureStorage = azureStorage;
+            _configuration = configuration;
+            _webHostEnvironment = environment;
+        }
+        [HttpPost("process-local")]
+        public async Task<IActionResult> ProcessLocal(JsonElement json)
+        {
+            List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
+            string? id = json.GetProperty("id").GetString();
+            if (!string.IsNullOrWhiteSpace(id))
+            {
+                string? lessonPath= _configuration.GetValue<string>("LessonPath");
+                string? path = $"{lessonPath}\\locals\\{id}";
+                var files = FileHelper.ListAllFiles(path);
+                // var sampleJson =System.IO. File.ReadAllTextAsync(path);
+                LessonBase? lessonBase = null;
+                List<LocalStudent> localStudents = new List<LocalStudent>();
+                List<TaskData> taskDatas = new List<TaskData>();
+                List<SmartRatingData> smartRatingDatas = new List<SmartRatingData>();
+                List<IRSData> irsDatas = new List<IRSData>();
+                List<CoworkData> coworkDatas = new List<CoworkData>();
+                List<ExamData> examDatas = new List<ExamData>();
+                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<LessonBase>();
+                        var data = LessonETLService.GetBaseData(lessonBase);
+                        localStudents = data.studentLessonDatas;
+                    }
+
+                    if (item.Contains("IES\\TimeLine.json"))
+                    {
+                        string jsons = await System.IO.File.ReadAllTextAsync(item);
+                        timeLineData = jsons.ToObject<TimeLineData>();
+                    }
+                    if (item.Contains("IES\\Task.json"))
+                    {
+                        string jsons = await System.IO.File.ReadAllTextAsync(item);
+                        taskDatas = jsons.ToObject<List<TaskData>>();
+                    }
+
+                    if (item.Contains("IES\\SmartRating.json"))
+                    {
+                        string jsons = await System.IO.File.ReadAllTextAsync(item);
+                        smartRatingDatas = jsons.ToObject<List<SmartRatingData>>();
+                    }
+
+                    if (item.Contains("IES\\IRS.json"))
+                    {
+                        string jsons = await System.IO.File.ReadAllTextAsync(item);
+                        irsDatas = jsons.ToObject<List<IRSData>>();
+                    }
+                    if (item.Contains("IES\\Cowork.json"))
+                    {
+                        string jsons = await System.IO.File.ReadAllTextAsync(item);
+                        coworkDatas = jsons.ToObject<List<CoworkData>>();
+                    }
+                    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<ExamData>();
+
+                                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<LessonPaper>();
+                                    examData.paper = lessonPaper;
+                                    examDatas.Add(examData);
+                                }
+                            }
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, ex.Message);
+                    }
+                }
+                if (lessonBase!=null  && timeLineData!=null)
+                {
+                    studentLessonDatas = localStudents.ToJsonString().ToObject<List<StudentLessonData>>();
+                    studentLessonDatas = LessonETLService.GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas, id);
+                    studentLessonDatas = LessonETLService.GetCoworkData(lessonBase, timeLineData, coworkDatas, studentLessonDatas);
+                    studentLessonDatas = LessonETLService.GetExamData(lessonBase, timeLineData, examDatas, studentLessonDatas, objectiveTypes);
+                    studentLessonDatas = LessonETLService.GetSmartRatingData(lessonBase, timeLineData, smartRatingDatas, studentLessonDatas, id);
+                    studentLessonDatas = LessonETLService.GetTaskData(lessonBase, timeLineData, taskDatas, studentLessonDatas);
+                    await System.IO.File.WriteAllTextAsync(Path.Combine(path, $"student-analysis.json"), studentLessonDatas.ToJsonString());
+                    string jsons = await System.IO.File.ReadAllTextAsync($"{lessonPath}\\analysis.json");
+                    LessonDataAnalysisCluster lessonDataAnalysis = jsons.ToObject<LessonDataAnalysisCluster>();
+                    var lessonItems=  LessonETLService.ProcessStudentData(studentLessonDatas, lessonDataAnalysis);
+                    XmlDocument xmlDocument = new XmlDocument();
+                    var runtimePath=  System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
+                    xmlDocument.Load($"{runtimePath}\\summary.xml");
+                    await  LessonETLService. ExportToExcel(lessonItems, $"{path}\\analysis.xlsx", xmlDocument);
+                }
+            }
+            return Ok();
+        }
+
+        /// <summary>
+        /// 课例数据ETL处理过程
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [HttpPost("process-history-students")]
+        public async Task<IActionResult> ProcessHistoryStudents(JsonElement json) 
+        {
+            return Ok();
+        }
+
+
+
+        /// <summary>
+        /// 课例数据ETL处理过程
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [HttpPost("process-history")]
+        public async Task<IActionResult> ProcessHistory(JsonElement json) 
+        {
+            
+            List<string> localIds = new List<string>();
+            string? lessonBasePath = _configuration.GetValue<string>("LessonPath");
+            string? pathLessons = $"{lessonBasePath}\\lessons";
+            string? pathAnalysis = $"{lessonBasePath}\\analysis";
+            var filesLessons = FileHelper.ListAllFiles(pathLessons);
+            foreach (var file in filesLessons)
+            {
+                if (file.EndsWith("-local.json"))
+                {
+                    string lessonId = file.Split("\\").Last().Replace("-local.json", "");
+                    localIds.Add(lessonId);
+                }
+            }
+            bool loadLocal = true;
+            List<LessonDataAnalysisMonth> lessonDataAnalysisMonths = new List<LessonDataAnalysisMonth>();
+            var filesAnalysis = FileHelper.ListAllFiles(pathAnalysis);
+            long stime = 1690819200000;//2023-08-01 00:00:00
+            foreach (var file in filesAnalysis) {
+                //读取每月的数据
+                if (file.EndsWith("-m-analysis.json"))
+                {
+                    string jsons = await System.IO.File.ReadAllTextAsync(file);
+                    LessonDataAnalysisMonth lessonDataAnalysis = jsons.ToObject<LessonDataAnalysisMonth>();
+                    lessonDataAnalysisMonths.Add(lessonDataAnalysis);
+                }
+            }
+            if (lessonDataAnalysisMonths.IsNotEmpty()) {
+                var maxUpdateTime = lessonDataAnalysisMonths.Max(x => x.updateTime);
+                if (maxUpdateTime>0)
+                {
+                    //更新周期是一周
+                    if (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()- maxUpdateTime>604800000)
+                    {
+                        stime=maxUpdateTime;
+                        loadLocal =true;
+                    }
+                    else
+                    {
+                        stime=maxUpdateTime;
+                        loadLocal=false;
+                    }
+                }
+            }
+            HashSet<string> yearMonth = new HashSet<string>();
+            long newest = 0;
+            if (loadLocal ||(json.TryGetProperty("force", out JsonElement _force)&& _force.ValueKind.Equals(JsonValueKind.True)))
+            {
+                List<LessonRecord> lessonRecords = new List<LessonRecord>();
+                var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
+                    .GetList<LessonRecord>($"SELECT value c FROM   c  where c.startTime>={stime} 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 (resultSchool.list.IsNotEmpty())
+                {
+                    newest= resultSchool.list.Max(x => x.startTime);
+                    lessonRecords.AddRange(resultSchool.list);
+                }
+                else {
+                    newest=stime;
+                }
+                var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                   .GetList<LessonRecord>($"SELECT value c FROM   c  where c.startTime>={stime} and c.expire<=0  and c.status<>404  and c.duration>300 and c.pk='LessonRecord'  and (c.tLevel>0 or c.pLevel>0) ", null);
+                if (resultTeacher.list.IsNotEmpty())
+                {
+                    long max = resultTeacher.list.Max(x => x.startTime);
+                    if (max<newest)
+                    {
+                        newest=max;
+                    }
+                    lessonRecords.AddRange(resultTeacher.list);
+                }
+                List<string> ignore = new List<string>() { "PgJump", "PgRcv", "PgAdd" };
+                if (lessonRecords.IsNotEmpty())
+                {
+                  
+                    await foreach (var item in LessonETLService.GetLessonLocal(lessonRecords, localIds, _azureStorage, pathLessons))
+                    {
+                        string yearMonthPath = DateTimeOffset.FromUnixTimeMilliseconds(item.lessonRecord.startTime).ToString("yyyyMM");
+                        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($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord.id}-count.json", techCount.ToJsonString());
+                            await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json", item.ToJsonString());
+                        }
+                        else
+                        {
+
+                            System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-local.json");
+                            System.IO.File.Delete($"{pathLessons}\\MM{yearMonthPath}\\{item.lessonRecord!.id}-count.json");
+                        }
+                    }
+                }
+                List<TechCount> techCounts = new List<TechCount>();
+                filesLessons = FileHelper.ListAllFiles(pathLessons);
+                await Parallel.ForEachAsync(filesLessons, async (file, _) => {
+                    await GetTeachCount(file, pathLessons, yearMonth, ignore, techCounts, objectiveTypes, _azureStorage);
+                });
+                var yearMonthDatas = techCounts.GroupBy(x => x.yearMonth).Select(x => new { key = x.Key, list = x.ToList() });
+               // lessonDataAnalysisMonths = new List<LessonDataAnalysisMonth>();
+                LessonDataAnalysisCluster lessonDataAnalysisCluster = new LessonDataAnalysisCluster();
+                foreach (var yearMonthData in yearMonthDatas) 
+                {
+                    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();
+                    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);
+                    }
+                }
+
+                //标准差偏差N倍,视为异常数据
+                int thresholdMultiplier = 2;
+                lessonDataAnalysisCluster.pscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.pscore, thresholdMultiplier);
+                lessonDataAnalysisCluster.gscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.gscore, thresholdMultiplier);
+                lessonDataAnalysisCluster.tscore= LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.tscore, thresholdMultiplier);
+                lessonDataAnalysisCluster.irs = LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.irs, thresholdMultiplier);
+                lessonDataAnalysisCluster.interactNormal=LessonETLService. CleanDataBySDThreshold(lessonDataAnalysisCluster.interactNormal, thresholdMultiplier);
+                lessonDataAnalysisCluster.stuCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.stuCowork, thresholdMultiplier);
+                lessonDataAnalysisCluster.groupCowork=LessonETLService.CleanDataBySDThreshold(lessonDataAnalysisCluster.groupCowork, thresholdMultiplier);
+                List<KeyValuePair<double, List<int>>> clustersDataInteract = new();
+                var clusterInteract = KMeansService.KMeans(lessonDataAnalysisCluster.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);
+                }
+                lessonDataAnalysisCluster.clustersInteract= clustersDataInteract;
+                System.IO.File.WriteAllText(Path.Combine(pathAnalysis, "analysis.json"), lessonDataAnalysisCluster.ToJsonString());
+            }
+            return Ok(new { yearMonth });
+        }
+
+        private static async Task GetTeachCount(string item, string pathLessons, HashSet<string> yearMonth, List<string> ignore, List<TechCount> techCounts,List<string> objectiveTypes,AzureStorageFactory azureStorage)
+        {
+            if (item.EndsWith("-local.json"))
+            {
+                string localjson = await System.IO.File.ReadAllTextAsync(item);
+                var lessonLocal = localjson.ToObject<LessonLocal>();
+                TechCount 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");
+                    yearMonth.Add(count.yearMonth);
+                    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<StudentLessonData> studentLessonDatas = lessonLocal.studentLessonDatas.ToJsonString().ToObject<List<StudentLessonData>>();
+                        studentLessonDatas = LessonETLService.GetIRSData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.irsDatas, studentLessonDatas, lessonLocal.examDatas, item);
+                        studentLessonDatas =  LessonETLService.GetCoworkData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.coworkDatas, studentLessonDatas);
+                        studentLessonDatas =  LessonETLService.GetExamData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.examDatas, studentLessonDatas, objectiveTypes);
+                        studentLessonDatas = LessonETLService.GetSmartRatingData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.smartRatingDatas, studentLessonDatas, item);
+                        studentLessonDatas = LessonETLService.GetTaskData(lessonLocal.lessonBase!, lessonLocal.timeLineData!, lessonLocal.taskDatas, studentLessonDatas);
+                     //  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<string> pickUp = new HashSet<string>();
+                        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<double>() { avgUpload, maxUpload });
+                        }
+                        //if (pickUp.Count>0)
+                        //{
+                        //    count.pickup.Add(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())
+                        {
+                            await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(studentLessonDatas.ToJsonString(), "records", $"{lessonLocal.lessonRecord.id}/student-analysis.json");
+                            
+                        }
+                        if (!System.IO.File.Exists($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json"))
+                        {
+                            await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-stu.json", studentLessonDatas.ToJsonString());
+                        }
+                    }
+                    await System.IO.File.WriteAllTextAsync($"{pathLessons}\\MM{count.yearMonth}\\{lessonLocal.lessonRecord.id}-count.json", count.ToJsonString());
+                    techCounts.Add(count);
+                }
+            }
+        }
+    }
+}

+ 18 - 0
HTEX.DataETL/HTEX.DataETL.csproj

@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\TEAMModelOS.Extension\HTEX.Lib\HTEX.Lib.csproj" />
+    <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
+  </ItemGroup>
+	<ItemGroup>
+		<Content Include="..\TEAMModelOS.Extension\HTEX.Lib\summary.xml">
+			<Link>summary.xml</Link>
+		</Content>
+	</ItemGroup>
+</Project>

+ 6 - 0
HTEX.DataETL/HTEX.DataETL.http

@@ -0,0 +1,6 @@
+@HTEX.DataETL_HostAddress = http://localhost:5296
+
+GET {{HTEX.DataETL_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###

+ 36 - 0
HTEX.DataETL/Program.cs

@@ -0,0 +1,36 @@
+using TEAMModelOS.SDK.DI;
+
+namespace HTEX.DataETL
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            var builder = WebApplication.CreateBuilder(args);
+
+            // Add services to the container.
+            string? StorageConnectionString = builder.Configuration.GetValue<string>("Azure:Storage:ConnectionString");
+            string? RedisConnectionString = builder.Configuration.GetValue<string>("Azure:Redis:ConnectionString");
+            string? CosmosConnectionString = builder.Configuration.GetValue<string>("Azure:Cosmos:ConnectionString");
+            //Storage
+            builder.Services.AddAzureStorage(StorageConnectionString, "Default");
+            //Redis
+            builder.Services.AddAzureRedis(RedisConnectionString, "Default");
+            builder.Services.AddAzureCosmos(CosmosConnectionString, "Default");
+            builder.Services.AddControllers();
+
+            var app = builder.Build();
+
+            // Configure the HTTP request pipeline.
+
+            app.UseHttpsRedirection();
+
+            app.UseAuthorization();
+
+
+            app.MapControllers();
+
+            app.Run();
+        }
+    }
+}

+ 41 - 0
HTEX.DataETL/Properties/launchSettings.json

@@ -0,0 +1,41 @@
+{
+  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:30536",
+      "sslPort": 44328
+    }
+  },
+  "profiles": {
+    "http": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "applicationUrl": "http://localhost:5296",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "https": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "applicationUrl": "https://localhost:7034;http://localhost:5296",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "launchUrl": "weatherforecast",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 8 - 0
HTEX.DataETL/appsettings.Development.json

@@ -0,0 +1,8 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.AspNetCore": "Warning"
+    }
+  }
+}

+ 32 - 0
HTEX.DataETL/appsettings.json

@@ -0,0 +1,32 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.AspNetCore": "Warning"
+    }
+  },
+  "AllowedHosts": "*",
+  "Kestrel": {
+    "Endpoints": {
+      "Http": {
+        "Url": "http://*:7907"
+      },
+      "Https": {
+        "Url": "https://*:7908"
+      }
+    }
+  },
+  "Azure": {
+    "Storage": {
+      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelos;AccountKey=Dl04mfZ9hE9cdPVO1UtqTUQYN/kz/dD/p1nGvSq4tUu/4WhiKcNRVdY9tbe8620nPXo/RaXxs+1F9sVrWRo0bg==;EndpointSuffix=core.chinacloudapi.cn"
+    },
+    "Cosmos": {
+      "ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;"
+    },
+    "Redis": {
+      "ConnectionString": "CoreRedisCN.redis.cache.chinacloudapi.cn:6380,password=LyJWP1ORJdv+poXWofAF97lhCEQPg1wXWqvtzXGXQuE=,ssl=True,abortConnect=False"
+    }
+  },
+  "LessonPath": "F:\\lesson-local"
+  //"LessonPath": "/datadisk/net8-project/lesson-local",
+}

+ 119 - 0
TEAMModelOS.Extension/HTEX.Lib/ETL/KMeansService.cs

@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HTEX.Lib.ETL
+{
+    public class KMeansService
+    {
+        public static Dictionary<double, List<double>> KMeans(IEnumerable<double> datas)
+        {
+
+            // 初始化质心,可以选择数据中的k个随机点作为初始质心
+            List<double> centroids = new List<double>();
+
+            centroids.Add(datas.Min());
+            centroids.Add(datas.Max());
+            centroids.Sort(); // 对质心进行排序以避免重复
+
+            Dictionary<double, List<double>> clustersResults = new Dictionary<double, List<double>>();
+            // 迭代次数
+            int maxIterations = 100;
+            for (int iteration = 0; iteration < maxIterations; iteration++)
+            {
+                // 将数据点分配给最近的质心
+                Dictionary<double, List<double>> clusters = new Dictionary<double, List<double>>();
+                foreach (int point in datas)
+                {
+                    double nearestCentroid = centroids.OrderBy(c => Math.Abs(point - c)).First();
+                    if (!clusters.ContainsKey(nearestCentroid))
+                    {
+                        clusters[nearestCentroid] = new List<double>();
+                    }
+                    clusters[nearestCentroid].Add(point);
+                }
+
+                // 更新质心位置
+                List<double> newCentroids = new List<double>();
+                foreach (KeyValuePair<double, List<double>> cluster in clusters)
+                {
+                    double sum = cluster.Value.Sum();
+                    int count = cluster.Value.Count;
+                    double newCentroid = sum *1.0/ count; // 计算均值
+                    newCentroids.Add(newCentroid);
+                    // 输出当前聚类的信息
+                    //Console.WriteLine($"Cluster with centroid {cluster.Key}:");
+                    //Console.WriteLine($"Min: {cluster.Value.Min()}, Max: {cluster.Value.Max()}, Average: {sum / count}");
+                }
+
+                // 检查质心是否改变
+                newCentroids.Sort();
+
+                clustersResults=clusters;
+
+                if (newCentroids.SequenceEqual(centroids))
+                {
+                    break;
+                }
+                centroids = newCentroids;
+            }
+            return clustersResults;
+        }
+
+        public static Dictionary<double, List<int>> KMeans(IEnumerable<int> datas)
+        {
+            // 初始化质心,可以选择数据中的k个随机点作为初始质心
+            List<double> centroids = new List<double>();
+
+            centroids.Add(datas.Min());
+            centroids.Add(datas.Max());
+            centroids.Sort(); // 对质心进行排序以避免重复
+
+            Dictionary<double, List<int>> clustersResults = new Dictionary<double, List<int>>();
+            // 迭代次数
+            int maxIterations = 100;
+            for (int iteration = 0; iteration < maxIterations; iteration++)
+            {
+                // 将数据点分配给最近的质心
+                Dictionary<double, List<int>> clusters = new Dictionary<double, List<int>>();
+                foreach (int point in datas)
+                {
+                    double nearestCentroid = centroids.OrderBy(c => Math.Abs(point - c)).First();
+                    if (!clusters.ContainsKey(nearestCentroid))
+                    {
+                        clusters[nearestCentroid] = new List<int>();
+                    }
+                    clusters[nearestCentroid].Add(point);
+                }
+
+                // 更新质心位置
+                List<double> newCentroids = new List<double>();
+                foreach (KeyValuePair<double, List<int>> cluster in clusters)
+                {
+                    int sum = cluster.Value.Sum();
+                    int count = cluster.Value.Count;
+                    double newCentroid = sum *1.0/ count; // 计算均值
+                    newCentroids.Add(newCentroid);
+                    // 输出当前聚类的信息
+                    //Console.WriteLine($"Cluster with centroid {cluster.Key}:");
+                    //Console.WriteLine($"Min: {cluster.Value.Min()}, Max: {cluster.Value.Max()}, Average: {sum / count}");
+                }
+
+                // 检查质心是否改变
+                newCentroids.Sort();
+
+                clustersResults=clusters;
+
+                if (newCentroids.SequenceEqual(centroids))
+                {
+                    break;
+                }
+                centroids = newCentroids;
+            }
+            return clustersResults;
+        }
+
+    }
+}

+ 53 - 0
TEAMModelOS.Extension/HTEX.Lib/ETL/Lesson/LessonETL.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace HTEX.Lib.ETL.Lesson
+{
+    public class LocalStudent
+    {
+        /// <summary>
+        /// 出席状态 1出席,6公假,5事假,4病假,2缺席,0未签到
+        /// </summary>
+        public int attend { get; set; }
+        /// <summary>
+        /// 学生的学号
+        /// </summary>
+        public string? id { get; set; }
+        /// <summary>
+        /// 学生所在下标
+        /// </summary>
+        public int index { get; set; } = -1;
+        /// <summary>
+        /// 学生座位号
+        /// </summary>
+        public string? seatID { get; set; }
+        /// <summary>
+        /// 小组编号
+        /// </summary>
+        public string? groupId { get; set; }
+    }
+
+    /*
+     * 
+     /// 事件
+        /// 推送相关 在Push.json处理 DifObjPush 推送给学生 差异化推送   "PushMemberId":[1,4,8,12,17,18,19,23,24,27,32,36]  ,FastPgPush 同一推送
+        /// 互动相关 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns'  抢权(新), 'BuzrLoad'抢权(旧), PickupResult   挑人算不算互动??  读取PickupMemberId "[\r\n  35\r\n]"
+        /// 测验相关 在IRS.json处理 SPQStrt 测验模式
+        /// 任务相关 在Task.json处理 'WrkSpaceLoad' 作品收集, 'WrkCmp' 作品贴上  是什么操作
+        /// 评分相关 在SmartRating.json 处理 'SmartRating' 评分模式,分 投票Voting 和 GrandRating  星光大评分(All每人多件评分,Two随机分配互评, Self自评)    
+        /// 协作相关 在Cowork.json 处理   CoworkLoad 协作类型   coworkType   All: '全体协作',    Group: '分组协作',   其他的为  : '差异化协作',
+     问题汇总:
+    TimeLine.json 
+    挑人大类 Event= PickupResult 里面 分小类
+                PickupType=PickupRight  , PickupOption  , PickupNthGrp  ,PickupEachGrp  ,PickupDiff   五种类型分别是什么意思,是否还有其他类型的。  
+                ,"PickupCount":1,"PickupOption":2  ,"PickupGroup":2  这三个字段是什么意思。
+                "PickupMemberId":"[\r\n  1\r\n]"  里面是学生的座号还是数组下标。
+    "Event":"PressGood","TargetClass":0,"MemberId":"[\r\n  1\r\n]","Count":1},
+        是代表什么意思。 MemberId  是座号还说下标。
+    8月27日 17:581.   Task.json的  clientWorks.isGroupItem    bool 类型  false代表什么意思   true代表什么意思
+2.   Cowork.json 的  coworkType   All: '全体协作',    Group: '分组协作',      : '差异化协作',    差异化协作的简码是什么?  
+3.   TimeLine.json 的  类型 PickupResult   挑人算不算互动??  读取PickupMemberId "[\r\n  35\r\n]"
+4.   小组任务具体详细信息如何获取? 需要精确到 所有小组任务信息和 某一小组的参与情况  用于计算小组任务参与率
+     */
+}

文件差异内容过多而无法显示
+ 2117 - 0
TEAMModelOS.Extension/HTEX.Lib/ETL/Lesson/LessonETLService.cs


+ 14 - 3
TEAMModelOS.Extension/HTEX.Lib/HTEX.Lib.csproj

@@ -1,9 +1,10 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.1</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
-	<LangVersion>8.0</LangVersion>
+	<GenerateDocumentationFile>True</GenerateDocumentationFile>
+	<DocumentationFile>./summary.xml</DocumentationFile>
   </PropertyGroup>
 
   <ItemGroup>
@@ -11,9 +12,19 @@
 	  <PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
 	  <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
 	  <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
-	 
+	  <PackageReference Include="EPPlus" Version="7.4.1" />
 	  <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
 	  <PackageReference Include="System.Drawing.Common" Version="8.0.6" />
 	  <PackageReference Include="System.Text.Json" Version="8.0.3" />
   </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="summary.xml">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
 </Project>

文件差异内容过多而无法显示
+ 8695 - 0
TEAMModelOS.Extension/HTEX.Lib/summary.xml


+ 305 - 335
TEAMModelOS.Extension/HTEX.Test/Controllers/LessonRecordController.cs

@@ -19,6 +19,8 @@ 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
 {
@@ -42,14 +44,16 @@ namespace HTEX.Test.Controllers
         [HttpPost("process-local")]
         public async Task<IActionResult> ProcessLocal(JsonElement json)
         {
+            List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
             string? id=json.GetProperty("id").GetString();
+            string path = $"C:\\Users\\CrazyIter\\Downloads\\{id}";
             if (!string.IsNullOrWhiteSpace(id))
             {
-                string path = $"C:\\Users\\CrazyIter\\Downloads\\{id}";
+                
                 var files = ListAllFiles(path);
                 // var sampleJson =System.IO. File.ReadAllTextAsync(path);
                 LessonBase? lessonBase = null;
-                List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
+                List<LocalStudent> localStudents = new List<LocalStudent>();
                 List<TaskData> taskDatas = new List<TaskData>();
                 List<SmartRatingData> smartRatingDatas = new List<SmartRatingData>();
                 List<IRSData> irsDatas = new List<IRSData>();
@@ -65,7 +69,7 @@ namespace HTEX.Test.Controllers
                         jsons=jsons.Replace("\"Uncall\"", "0").Replace("Uncall", "0");
                         lessonBase = jsons.ToObject<LessonBase>();
                         var data = GetBaseData( lessonBase);
-                        studentLessonDatas=data.studentLessonDatas;
+                        localStudents=data.studentLessonDatas;
                     }
 
                     if (item.Contains("IES\\TimeLine.json"))
@@ -124,17 +128,22 @@ namespace HTEX.Test.Controllers
                     {
                         _logger.LogError(ex, ex.Message);
                     }
-                   
-
                 }
+
                 if (lessonBase!=null  && timeLineData!=null)
                 {
-                    studentLessonDatas =  GetIRSData(lessonBase, timeLineData, irsDatas, studentLessonDatas, examDatas);
+                    studentLessonDatas = localStudents.ToJsonString().ToObject<List<StudentLessonData>>();
+                    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);
+                    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<LessonDataAnalysis>();
                 }
+
+
                 return Ok( new { studentLessonDatas });
             }return Ok();
           
@@ -171,14 +180,7 @@ namespace HTEX.Test.Controllers
                     {
                         string jsons = await System.IO.File.ReadAllTextAsync(item);
                         TechCount count = jsons.ToObject<TechCount>();
-                        var pickupResult = count.timeCount.Where(x => x.code.Equals("PickupResult", StringComparison.OrdinalIgnoreCase));
-                        int pickCount = 0;
-                        if (pickupResult!=null && pickupResult.Count()>0)
-                        {
-                            pickCount+=pickupResult.Sum(x => (int)x.value);
-                        }
-                        count.interactNormalCount=pickCount+  count.irsCount;
-
+                        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<LessonLocal>();
@@ -291,15 +293,15 @@ namespace HTEX.Test.Controllers
 
                 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.coworkWeight=coworkWeight;
+                lessonDataAnalysis.coworkRate=coworkWeight;
                 double taskWeight = lessonDataAnalysis.task.Count()*1.0/tcount.Count();
-                lessonDataAnalysis.taskWeight=taskWeight;
+                lessonDataAnalysis.taskRate=taskWeight;
                 double interactWeight = lessonDataAnalysis.interactNormal.Count()*1.0/tcount.Count();
-                lessonDataAnalysis.interactWeight=interactWeight;
+                lessonDataAnalysis.interactRate=interactWeight;
                 double examWeight = lessonDataAnalysis. exam.Count()*1.0/tcount.Count();
-                lessonDataAnalysis.examWeight=examWeight;
+                lessonDataAnalysis.examRate=examWeight;
                 double smartRatingWeight = lessonDataAnalysis. smartRating.Count()*1.0/tcount.Count();
-                lessonDataAnalysis.smartRatingWeight=smartRatingWeight;
+                lessonDataAnalysis.smartRatingRate=smartRatingWeight;
 
                 List<KeyValuePair<double, List<int>>> clustersDataInteract = new();
                 var clusterInteract = KMeansService.KMeans(lessonDataAnalysis.interactNormal.Select(x => (int)x).OrderBy(x => x));
@@ -325,107 +327,81 @@ namespace HTEX.Test.Controllers
           
      
 
-            List<LessonLocal> lessons = new List<LessonLocal>();
-            var files_local = files.Where(x=>x.EndsWith("608942756458532864-local.json")).Take(100).ToList();
-            foreach (var item in files_local)
+           // List<LessonLocal> lessons = new List<LessonLocal>();
+            var files_local = files.Where(x=>x.EndsWith("557838358030716928-local.json")).Take(100).ToList();
+            List<List<StudentLessonData>> studentLessons = new List<List<StudentLessonData>>();
+            foreach (var item in files)
             {
                 if (item.EndsWith("local.json"))
                 {
                     string jsons = await System.IO.File.ReadAllTextAsync(item);
                     LessonLocal lesson = jsons.ToObject<LessonLocal>();
-                    lessons.Add(lesson);
-                    lesson.studentLessonDatas =  GetIRSData(lesson.lessonBase!, lesson.timeLineData!, lesson.irsDatas, lesson.studentLessonDatas, lesson.examDatas);
-                    lesson.studentLessonDatas =  GetCoworkData(lesson.lessonBase!, lesson.timeLineData!, lesson.coworkDatas, lesson.studentLessonDatas);
-                    lesson.studentLessonDatas =  GetExamData(lesson.lessonBase!, lesson.timeLineData!, lesson.examDatas, lesson.studentLessonDatas);
-                    lesson.studentLessonDatas =  GetSmartRatingData(lesson.lessonBase!, lesson.timeLineData!, lesson.smartRatingDatas, lesson.studentLessonDatas);
-                    lesson.studentLessonDatas =  GetTaskData(lesson.lessonBase!, lesson.timeLineData!, lesson.taskDatas, lesson.studentLessonDatas);
+                   // lessons.Add(lesson);
+                    List<StudentLessonData> studentLessonDatas = lesson.studentLessonDatas.ToJsonString().ToObject<List<StudentLessonData>>();
+                    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));
-                    if (techCount != null)
+                    int sumUpload =0;
+                    int taskCount = 0;
+                    int maxUpload = 0;
+                    HashSet<string> pickUp=new HashSet<string>();
+                    foreach (var stu in studentLessonDatas) 
                     {
-                        
-                        IEnumerable<double> interact = lessonDataAnalysis.interactNormal;
-                        int interactCount = techCount.interactNormalCount;
-                        //当前课例的互动次数在互动总列表超过N%;
-                        //var persent = GetPersent(interact, interactCount);
-                        foreach (var studentLessonData in lesson.studentLessonDatas)
+                        var countS = stu.taskRecord.itemRecords.Where(x => x.optCount>0);
+                        if (countS.Count()>0) 
                         {
-
-
-
-
-
-                            //互动的计分相关的 
-                            var x = 0.0;
-                            //互动参与度相关的
-                            var y = 0.0;
-                            //i互动计分,s个人计分
-                            double i = 0, s = 0;
-                            if (lesson?.lessonBase?.report?.clientSummaryList!=null)
-                            {
-                                var d = lesson?.lessonBase?.report?.clientSummaryList.Find(x => $"{x.seatID}".Equals(studentLessonData.seatID));
-                                if (d!=null)
-                                {
-                                    i=d.interactScore;
-                                    s =d.score;
-                                }
-                            }
-                            //个人计分最大值
-                            var q = lessonDataAnalysis.pscore.Max();
-                            //互动计分最大值
-                            var t = lessonDataAnalysis.tscore.Max();
-                            if (studentLessonData.interactRecord.interactRecords.Count==interactCount && interactCount>0)
-                            {
-                                //互动次数
-                                var n = studentLessonData.interactRecord.interactRecords.Count()*1.0;
-                                //聚类分数量大和数量小的类群。判断当前出题数在两个类群的最大范围内,再取其质心。
-                                var m = n<=lessonDataAnalysis.clustersInteract.First().Value.Max() ? lessonDataAnalysis.clustersInteract.First().Key*1.0 : lessonDataAnalysis.clustersInteract.Last().Key *1.0;
-                                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;
-                                var k = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>=InteractWeight.T1).Sum(x=> x.itemScore*1.0);
-                                var e = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Sum(x => x.itemScore*1.0);
-                                var j = InteractWeight.T1;
-                                //学习成效算法
-                                //x=((i/t) + ((n/m)*(w*(w/n)+(p/j)* (r/n) *r)))/n;
-                                x=((i/t) +(w*k)/(j*m) +(r*e)/(j*m) )/n;
-
-                                y=((n/m)*(w*(w/n)+  (r/n) *r))/n;
-                               // var rate = w*100.0/interactCount;
-                               // studentLessonData.interactRecord.interactScore=data;
-                               // studentLessonData.interactRecord.interactRate=rate;
-                            }
-                            else
-                            {
-                                if (studentLessonData.interactRecord.interactRecords.Count()!=0)
-                                {
-                                    Console.WriteLine($"{studentLessonData.id}{lesson?.lessonRecord?.id}的互动次数不匹配。");
-                                }
-                            }
-
-                            if (studentLessonData.taskRecord.itemRecords.Count>0) 
+                            int stuUploadmax = stu.taskRecord.itemRecords.Where(x => x.optCount>0).Max(x => x.optCount);
+                            if (stuUploadmax> maxUpload)
                             {
-                            
+                                maxUpload=stuUploadmax;
                             }
-                           var attend = 0.0;
-                            if (studentLessonData.attend==1) 
+                        }
+                        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) 
                             {
-                                attend= 100/100;
+                                pickUp.Add(pickup);
                             }
-                            var achieve = 190.0/(1+Math.Exp(-(s/q+x)))-95.0;
-                            var attitude= 200 / (1 + Math.Exp(-(s/q + y +attend))) - 100.0;
-                            studentLessonData.attitude=attitude;
-                            studentLessonData.achieve=achieve;
                         }
-
                     }
+                    if (studentLessonDatas.Count>0&& taskCount>0  && maxUpload>0) 
+                    {
+                        var avgUpload=   sumUpload*1.0/(studentLessonDatas.Count *taskCount);
+                        lessonDataAnalysis.upload.Add(new List<double>() { 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
             {
-                lessons,
-                interactNormal = lessonDataAnalysis.interactNormal.OrderByDescending(x=>x),
-                cowork = lessonDataAnalysis.cowork.OrderByDescending(x => x),
-                exam = lessonDataAnalysis.exam.OrderByDescending(x => x),
-                smartRating = lessonDataAnalysis.smartRating.OrderByDescending(x => x),
+               
+
             });
         }
 
@@ -433,6 +409,17 @@ namespace HTEX.Test.Controllers
         [HttpPost("process-history")]
         public async Task<IActionResult> ProcessHistory(JsonElement json)
         {
+            List<string> localIds = new List<string>();
+            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<LessonRecord>($"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);
@@ -446,8 +433,8 @@ namespace HTEX.Test.Controllers
                 //Console.WriteLine($"Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
                 //stopwatch.Reset();
                 stopwatch.Start();
-                string path = "F:\\lesson-local";
-                await foreach (var item in GetLessonLocal(result.list))
+               
+                await foreach (var item in GetLessonLocal(result.list, localIds))
                 {
                     if (item.lessonBase!=null && item.lessonBase.student!=null)
                     {
@@ -511,6 +498,7 @@ namespace HTEX.Test.Controllers
             public List<KeyValuePair<double, List<double>>> clustersPscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
             public List<KeyValuePair<double, List<double>>> clustersTscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
             public List<KeyValuePair<double, List<double>>> clustersGscore { get; set; } = new List<KeyValuePair<double, List<double>>>();
+           
             /// <summary>
             /// 个人计分
             /// </summary>
@@ -523,14 +511,31 @@ namespace HTEX.Test.Controllers
             /// 互动计分
             /// </summary>
             public IEnumerable<double> tscore { get; set; } = new List<double>();
-
-
-
-            public double coworkWeight { get; set; }
-            public double taskWeight { get; set; }
-            public double interactWeight { get; set; }
-            public double examWeight { get; set; }
-            public double smartRatingWeight { get; set; }
+            /// <summary>
+            /// 作品上传数
+            /// </summary>
+            public List<List<double>> upload { get; set; } = new List<List<double>>();
+            /// <summary>
+            /// 学生协作成果数
+            /// </summary>
+            public List<double> stuCowork { get; set; } = new List<double>();
+            /// <summary>
+            /// 小组协作成果数
+            /// </summary>
+            public List<double> groupCowork { get; set; } = new List<double>();
+            /// <summary>
+            /// 挑人集合
+            /// </summary>
+            public List<List<string>> pickup { get; set; } = new List<List<string>>();
+            /// <summary>
+            /// 挑人集合-小组
+            /// </summary>
+            public List<List<string>> pickup_group { get; set; } = new List<List<string>>();
+            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; }
         }
 
 
@@ -584,10 +589,15 @@ namespace HTEX.Test.Controllers
             }
             return datas;
         }
-        private async IAsyncEnumerable<LessonLocal> GetLessonLocal(List<LessonRecord> lessonRecords)
+        private async IAsyncEnumerable<LessonLocal> GetLessonLocal(List<LessonRecord> lessonRecords, List<string> 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"))
                 {
@@ -683,7 +693,7 @@ namespace HTEX.Test.Controllers
                 //读取基础Base信息
                 //base.json
                 LessonBase? lessonBase = null;
-                List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
+                List<LocalStudent> studentLessonDatas = new List<LocalStudent>();
                 //名单出席率低于30%的 不纳入计算。,累计所有课例的科技使用次数及反馈情况,用于做科技分类比例的权重
                 try
                 {
@@ -958,10 +968,10 @@ namespace HTEX.Test.Controllers
         /// <param name="lessonRecord"></param>
         /// <param name="lessonBase"></param>
         /// <returns></returns>
-        private (LessonBase lessonBase, List<StudentLessonData> studentLessonDatas) GetBaseData( LessonBase lessonBase)
+        private (LessonBase lessonBase, List<LocalStudent> studentLessonDatas) GetBaseData( LessonBase lessonBase)
         {
             //处理学生定位数据
-            List<StudentLessonData> studentLessonDatas = new List<StudentLessonData>();
+            List<LocalStudent> studentLessonDatas = new List<LocalStudent>();
             int index = 0;
             try {
                 if (lessonBase!=null)
@@ -974,7 +984,7 @@ namespace HTEX.Test.Controllers
                         {
                             attend=client.attendState;
                         }
-                        studentLessonDatas.Add(new StudentLessonData()
+                        studentLessonDatas.Add(new LocalStudent()
                         {
                             id = x.id,
                             index = index,
@@ -995,7 +1005,7 @@ namespace HTEX.Test.Controllers
         /// <summary>
         ///读取互动信息
         ///Event 过滤类型  'PopQuesLoad', 'ReAtmpAnsStrt', 'BuzrAns','BuzrLoad'
-        /// 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns'  抢权(新), 'BuzrLoad'抢权(旧), PickupResult   挑人算不算互动??  读取PickupMemberId "[\r\n  35\r\n]"
+        /// 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns'  抢权(新), 'BuzrLoad'抢权(旧)
         ///TimeLine.json 中找到对应类型,根据Pgid 去 IRS.json 中找到对应数据,从clientAnswers 的下标对应 base.json 中的 student 找到对应学生信息 clientAnswers.length > 1 则表示有二次作答
         ///读取IRS.json
         /// </summary>
@@ -1003,11 +1013,11 @@ namespace HTEX.Test.Controllers
         /// <param name="lessonBase"></param>
         /// <param name="irsDatas"></param>
         /// <returns></returns>
-        private   List<StudentLessonData> GetIRSData( LessonBase lessonBase, TimeLineData timeLineData, List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas,List<ExamData> examDatas)
+        private   List<StudentLessonData> GetIRSData( LessonBase lessonBase, TimeLineData timeLineData, List<IRSData> irsDatas, List<StudentLessonData> studentLessonDatas,List<ExamData> examDatas,string itemFiles)
         {
-            List<string> interactTypes = new List<string>() { "PopQuesLoad", "ReAtmpAnsStrt", "BuzrAns", "BuzrLoad", "PickupResult" };
+            List<string> interactTypes = new List<string>() { "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() });
+            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)
             {
@@ -1030,20 +1040,31 @@ namespace HTEX.Test.Controllers
             else 
             {
                 //处理其他,评测类型的互动,因为有可能不会记录在TimeLine.json中
-                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)
+
+                if (timeLineData!=null)
                 {
-                    foreach (var item in envents_other)
+                    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)
                     {
-                        ProcessIRSPageData(irsDatas, studentLessonDatas,examDatas, item);
+                        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
-            var enventsPickup = timeLineData.events.Where(x => !string.IsNullOrWhiteSpace(x.Pgid) &&  x.Event.Equals("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)
@@ -1058,24 +1079,20 @@ namespace HTEX.Test.Controllers
                             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.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.interactRecord.interactRecords.Add(new ItemRecord()
-                                {
-                                    resultWeight = InteractWeight.T1,
-                                    resultType=InteractReultType.T1,
-                                    itemType = string.IsNullOrWhiteSpace(item.PickupType) ? "PickupResult" : item.PickupType
-                                });
+                                studentLessonData.pickups.Add(string.IsNullOrWhiteSpace(item.PickupType) ? "0--PickupResult" : $"0--{item.PickupType}");
                             }
                         }
                     }
@@ -1105,6 +1122,14 @@ namespace HTEX.Test.Controllers
                 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<qitem.Count(); i++) 
+                    {
+                        qitem[i]!["question"]="";
+                    }
+                }
                 double questionScore = 0;
                 bool objective = false;
               
@@ -1712,13 +1737,32 @@ namespace HTEX.Test.Controllers
                         }
                         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.OrderByDescending(x => x.coworkRecord.itemRecords[p].itemScore);
-                var maxItems = studentLessonDatas.FindAll(x => x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore);
-                var max = studentLessonDatas.FindAll(x => x.coworkRecord.itemRecords[p].itemScore==order.First().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
-                var min = studentLessonDatas.FindAll(x => x.coworkRecord.itemRecords[p].itemScore==order.Last().coworkRecord.itemRecords[p].itemScore).First().coworkRecord.itemRecords[p].itemScore;
-                var sum = studentLessonDatas.Sum(x => x.coworkRecord.itemRecords[p].itemScore);
+                }
+                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)
@@ -1762,7 +1806,7 @@ namespace HTEX.Test.Controllers
                         var work = taskData.clientWorks.Find(x =>$"{x.seatID}".Equals(student.seatID));
                         if (work!= null)
                         {
-                            student.taskRecord.itemRecords.Add(new ItemRecord { itemType="WrkSpaceLoad", itemScore=10, resultWeight=InteractWeight.T1, resultType=InteractReultType.T1, isGroup= work.isGroupItem });
+                            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
                         {
@@ -1773,22 +1817,38 @@ namespace HTEX.Test.Controllers
                 ////////
                 ///需要处理小组的情况,当前人员没有提交作品,但是有可能是小组其他人员提交了,需要判断一下。
                 ///
-                var groupIdsA =  studentLessonDatas.FindAll(x => x.attend==1   &&  x.taskRecord.itemRecords[indexTask].isGroup==true).GroupBy(x=>x.groupId).Select(x=>x.Key);
-                foreach (var groupId in groupIdsA)
+                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.attend==1 &&  !string.IsNullOrWhiteSpace(x.groupId) &&  x.groupId.Equals(groupId));
-                    foreach (var student in groupStudents)
+                   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)
                     {
-                        student.taskRecord.itemRecords[indexTask].isGroup=true;
+                        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 groupIdsB =  taskData.clientWorks.FindAll(x => x.seatID==0 && x.isGroupItem).Select(x => x.groupID).Distinct() ;
-                foreach (var groupId in groupIdsB)
+                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(groupId));
+                    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++;
@@ -1810,7 +1870,7 @@ namespace HTEX.Test.Controllers
         /// <param name="smartRatingDatas"></param>
         /// <param name="studentLessonDatas"></param>
         /// <returns></returns>
-        private List<StudentLessonData>  GetSmartRatingData( LessonBase lessonBase, TimeLineData timeLineData, List<SmartRatingData> smartRatingDatas, List<StudentLessonData> studentLessonDatas)
+        private List<StudentLessonData>  GetSmartRatingData( LessonBase lessonBase, TimeLineData timeLineData, List<SmartRatingData> smartRatingDatas, List<StudentLessonData> studentLessonDatas,string itemf )
         {
             int index = 0;
             foreach(var smartRatingData in smartRatingDatas) 
@@ -1824,30 +1884,41 @@ namespace HTEX.Test.Controllers
                     bool addData=false;
                     foreach (var key in keys_vote!) 
                     {
-                       
-                        var voteDetailResults = smartRatingData.smartRateSummary!.voteDetailResult[key];
-                        foreach (var student in studentLessonDatas)
-                        {
-                            if (student.attend==1) 
+
+                        try {
+                            //问题数据F:\lesson-local\632424798693232640-local.json   pclxxx
+                            if (smartRatingData.smartRateSummary!.voteDetailResult.TryGetValue(key, out var value))
                             {
-                                //投票是全员参与
-                                var datasS = voteDetailResults.FindAll(x => x.id.Equals(student.seatID));
-                                if (datasS.IsNotEmpty())
+                                var voteDetailResults = smartRatingData.smartRateSummary!.voteDetailResult[key];
+                                foreach (var student in studentLessonDatas)
                                 {
-                                    //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;
+                                    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是评论了别人,且被别人评论次数最高,或者分值最高。
+                                    }
                                 }
-                                //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);
@@ -1922,39 +1993,42 @@ namespace HTEX.Test.Controllers
                             }
                         }
                     }
-                    var order = smartRatingData.smartRateSummary.meteor_ScoreSummary.Where(x => x.result>0).OrderByDescending(x => x.result);
-                    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 order = smartRatingData.smartRateSummary.meteor_ScoreSummary.Where(x => x.result>0||!string.IsNullOrWhiteSpace(x.comment)).OrderByDescending(x => x.result);
+                    if (order.Count()>0) 
                     {
-                       var student=  studentLessonDatas.Find(x => x.seatID!.Equals(meteor_ScoreSummary.id));
-                        if (student!=null)
+                        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)
                         {
-                            if (index<student.rateingRecord.itemRecords.Count &&   student.rateingRecord.itemRecords[index].itemType!.Equals(type))
+                            var student = studentLessonDatas.Find(x => x.seatID!.Equals(meteor_ScoreSummary.id));
+                            if (student!=null)
                             {
-                                if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
-                                {
-                                    //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
-                                    student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
-                                    student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
-                                }
-                                else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
+                                if (index<student.rateingRecord.itemRecords.Count &&   student.rateingRecord.itemRecords[index].itemType!.Equals(type))
                                 {
-                                    //TP 有被别人评论,且评论了别人,
-                                    student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
-                                    var data = MinMaxNormalization(min, max, meteor_ScoreSummary.result);
-                                    //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
-                                    student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+  data * 1.0  / maxRank * (InteractWeight.TT-InteractWeight.T1);
-                                    //被评论次数
-                                    student.rateingRecord.itemRecords[index].itemScore=meteor_ScoreSummary.result;
-                                    //TT是评论了别人,且被别人评论次数最高,或者分值最高。
-                                    if (maxItems.Select(x => x.id).Contains(student.seatID))
+                                    if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T0))
                                     {
-                                        student.rateingRecord.itemRecords[index].resultType= InteractReultType.TT;
-                                        student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TT;
+                                        //T1,只有评论别人,没被别人评论 或者是评论了别人,但是没有被别人评论,
+                                        student.rateingRecord.itemRecords[index].resultType= InteractReultType.T1;
+                                        student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1;
+                                    }
+                                    else if (student.rateingRecord.itemRecords[index].resultType!.Equals(InteractReultType.T1))
+                                    {
+                                        //TP 有被别人评论,且评论了别人,
+                                        student.rateingRecord.itemRecords[index].resultType= InteractReultType.TP;
+                                        var data = MinMaxNormalization(min, max, meteor_ScoreSummary.result);
+                                        //student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.TP;
+                                        student.rateingRecord.itemRecords[index].resultWeight= InteractWeight.T1+  data * 1.0  / maxRank * (InteractWeight.TT-InteractWeight.T1);
+                                        //被评论次数
+                                        student.rateingRecord.itemRecords[index].itemScore=meteor_ScoreSummary.result;
+                                        //TT是评论了别人,且被别人评论次数最高,或者分值最高。
+                                        if (maxItems.Select(x => 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;
+                                        }
                                     }
                                 }
                             }
@@ -2054,7 +2128,7 @@ namespace HTEX.Test.Controllers
         public LessonBase? lessonBase { get; set;}
         public TimeLineData? timeLineData { get; set; }
         public LessonRecord? lessonRecord { get; set; }
-        public List<StudentLessonData> studentLessonDatas { get; set; } = new List<StudentLessonData>();
+        public List<LocalStudent> studentLessonDatas { get; set; } = new List<LocalStudent>();
         public List<TaskData> taskDatas { get; set; } = new List<TaskData>();
         public List<SmartRatingData> smartRatingDatas { get; set; } = new List<SmartRatingData>();
         public List<IRSData> irsDatas { get; set; } = new List<IRSData>();
@@ -2062,134 +2136,30 @@ namespace HTEX.Test.Controllers
         public List<ExamData> examDatas { get; set; } = new List<ExamData>();
         public List<TimeLineEvent> sokratesDatas { get; set; } = new List<TimeLineEvent>();
     }
-   
-    /// 互动参与指数(按倍数的权重设计)
-    /// 无二次作答的互动,且未设置正确答案:1.未作答0
-    ///                                     2.已作答1
-    ///                                     
-    /// 无二次作答的互动,且设置了正确答案:3.未作答0
-    ///                                     4.已作答1
-    ///                                     5.不完全正确1.3 
-    ///                                     6.作答正确1.5
-    ///                     
-    ///                                     
-    /// 有二次作答的互动,且未设置正确答案:7.第一次未作答0,第二次未作答0=》0
-    ///                                     8.第一次已作答1,第二次未作答0=》1
-    ///                                     9.第一次未作答0,第二次已作答1=》1
-    ///                                     10.第一次已作答1,第二次已作答1=》2
-    ///                                     
-    /// 有二次作答的互动,且设置了正确答案:(16种可能)
-    ///                                     11.第一次未作答0,第二次未作答0=》0
-    ///                                     12.第一次已作答,不完全正确1.3,第二次未作答0=》1.3
-    ///                                     13.第一次已作答,作答正确1.5,第二次未作答0=》1.5
-    ///                                     14.第一次已作答,作答错误1,第二次未作答0=》1
-    ///                                     15.第一次未作答0,第二次已作答,不完全正确1.3=》1.3
-    ///                                     16.第一次未作答0,第二次已作答,作答正确1.5=》1.5
-    ///                                     17.第一次未作答0,第二次已作答,作答错误1=》1
-    ///                                     18.第一次已作答,作答错误1,第二次已作答,作答错误1=》2
-    ///                                     19.第一次已作答,不完全正确1.3,第二次已作答,作答错误1=》2.3
-    ///                                     20.第一次已作答,作答正确1.5,第二次已作答,作答错误1=》2.5
-    ///                                     21.第一次已作答,作答错误1,第二次已作答,不完全正确1.3=》2.3
-    ///                                     22.第一次已作答,作答错误1,第二次已作答,作答正确1.5=》2.5
-    ///                                     23.第一次已作答,不完全正确1.3,第二次已作答,不完全正确1.3=》2.6
-    ///                                     24.第一次已作答,不完全正确1.3,第二次已作答,作答正确1.5=》2.8
-    ///                                     25.第一次已作答,作答正确1.5,第二次已作答,不完全正确1.3=》2.8
-    ///                                     26.第一次已作答,作答正确1.5,第二次已作答,作答正确1.5=》3
-    ///                                     
-    /// 抢权模式:                          27.未参与抢权 0
-    ///                                     28.参与抢权 1
-    ///                                     29.抢权成功 1.5                                   
-    /// 挑人时被挑到                        30.被挑到 1.5                                    
-    /// 有三次作答的互动,且设置了正确答案:(64种可能)
-    /*
-   1	未作答0	未作答0	未作答0	0
-   2	未作答0	未作答0	不完全正确1.3	1.3
-   3	未作答0	未作答0	作答正确1.5	1.5
-   4	未作答0	未作答0	作答错误1	1
-   5	未作答0	不完全正确1.3	未作答0	1.3
-   6	未作答0	不完全正确1.3	不完全正确1.3	2.6
-   7	未作答0	不完全正确1.3	作答正确1.5	2.8
-   8	未作答0	不完全正确1.3	作答错误1	2.3
-   9	未作答0	作答正确1.5	未作答0	1.5
-   10	未作答0	作答正确1.5	不完全正确1.3	2.8
-   11	未作答0	作答正确1.5	作答正确1.5	3
-   12	未作答0	作答正确1.5	作答错误1	2.5
-   13	未作答0	作答错误1	未作答0	1
-   14	未作答0	作答错误1	不完全正确1.3	2.3
-   15	未作答0	作答错误1	作答正确1.5	2.5
-   16	未作答0	作答错误1	作答错误1	2
-   17	已作答错误1	未作答0	未作答0	1
-   18	已作答错误1	未作答0	不完全正确1.3	2.3
-   19	已作答错误1	未作答0	作答正确1.5	2.5
-   20	已作答错误1	未作答0	作答错误1	2
-   21	已作答错误1	不完全正确1.3	未作答0	2.3
-   22	已作答错误1	不完全正确1.3	不完全正确1.3	3.6
-   23	已作答错误1	不完全正确1.3	作答正确1.5	3.8
-   24	已作答错误1	不完全正确1.3	作答错误1	3.3
-   25	已作答错误1	作答正确1.5	未作答0	2.5
-   26	已作答错误1	作答正确1.5	不完全正确1.3	3.8
-   27	已作答错误1	作答正确1.5	作答正确1.5	4
-   28	已作答错误1	作答正确1.5	作答错误1	3.5
-   29	已作答错误1	作答错误1	未作答0	2
-   30	已作答错误1	作答错误1	不完全正确1.3	3.3
-   31	已作答错误1	作答错误1	作答正确1.5	3.5
-   32	已作答错误1	作答错误1	作答错误1	3
-   33	已作答不完全正确1.3	未作答0	未作答0	1.3
-   34	已作答不完全正确1.3	未作答0	不完全正确1.3	2.6
-   35	已作答不完全正确1.3	未作答0	作答正确1.5	2.8
-   36	已作答不完全正确1.3	未作答0	作答错误1	2.3
-   37	已作答不完全正确1.3	不完全正确1.3	未作答0	2.6
-   38	已作答不完全正确1.3	不完全正确1.3	不完全正确1.3	3.9
-   39	已作答不完全正确1.3	不完全正确1.3	作答正确1.5	4.1
-   40	已作答不完全正确1.3	不完全正确1.3	作答错误1	3.6
-   41	已作答不完全正确1.3	作答正确1.5	未作答0	2.8
-   42	已作答不完全正确1.3	作答正确1.5	不完全正确1.3	4.1
-   43	已作答不完全正确1.3	作答正确1.5	作答正确1.5	4.3
-   44	已作答不完全正确1.3	作答正确1.5	作答错误1	3.8
-   45	已作答不完全正确1.3	作答错误1	未作答0	2.3
-   46	已作答不完全正确1.3	作答错误1	不完全正确1.3	3.6
-   47	已作答不完全正确1.3	作答错误1	作答正确1.5	3.8
-   48	已作答不完全正确1.3	作答错误1	作答错误1	3.3
-   49	已作答正确1.5	未作答0	未作答0	1.5
-   50	已作答正确1.5	未作答0	不完全正确1.3	2.8
-   51	已作答正确1.5	未作答0	作答正确1.5	3
-   52	已作答正确1.5	未作答0	作答错误1	2.5
-   53	已作答正确1.5	不完全正确1.3	未作答0	2.8
-   54	已作答正确1.5	不完全正确1.3	不完全正确1.3	4.1
-   55	已作答正确1.5	不完全正确1.3	作答正确1.5	4.3
-   56	已作答正确1.5	不完全正确1.3	作答错误1	3.8
-   57	已作答正确1.5	作答正确1.5	未作答0	3
-   58   已作答正确1.5   作答正确1.5 不完全正确1.3 4.3
-   59   已作答正确1.5   作答正确1.5作答正确1.5  4.5
-   60   已作答正确1.5   作答正确1.5作答错误1 4  
-   61   已作答正确1.5   作答错误1 未作答0   2.5
-   62   已作答正确1.5   作答错误1 不完全正确1.3 3.8
-   63   已作答正确1.5   作答错误1 作答正确1.54  
-   64   已作答正确1.5   作答错误1 作答错误1 3.5
-         */
-
-
-
-    /*
-     * 
-     /// 事件
-        /// 推送相关 在Push.json处理 DifObjPush 推送给学生 差异化推送   "PushMemberId":[1,4,8,12,17,18,19,23,24,27,32,36]  ,FastPgPush 同一推送
-        /// 互动相关 在IRS.json处理 'PopQuesLoad'互动问答 , 'ReAtmpAnsStrt' 二次作答 , 'BuzrAns'  抢权(新), 'BuzrLoad'抢权(旧), PickupResult   挑人算不算互动??  读取PickupMemberId "[\r\n  35\r\n]"
-        /// 测验相关 在IRS.json处理 SPQStrt 测验模式
-        /// 任务相关 在Task.json处理 'WrkSpaceLoad' 作品收集, 'WrkCmp' 作品贴上  是什么操作
-        /// 评分相关 在SmartRating.json 处理 'SmartRating' 评分模式,分 投票Voting 和 GrandRating  星光大评分(All每人多件评分,Two随机分配互评, Self自评)    
-        /// 协作相关 在Cowork.json 处理   CoworkLoad 协作类型   coworkType   All: '全体协作',    Group: '分组协作',   其他的为  : '差异化协作',
-     问题汇总:
-    TimeLine.json 
-    挑人大类 Event= PickupResult 里面 分小类
-                PickupType=PickupRight  , PickupOption  , PickupNthGrp  ,PickupEachGrp  ,PickupDiff   五种类型分别是什么意思,是否还有其他类型的。  
-                ,"PickupCount":1,"PickupOption":2  ,"PickupGroup":2  这三个字段是什么意思。
-                "PickupMemberId":"[\r\n  1\r\n]"  里面是学生的座号还是数组下标。
-    "Event":"PressGood","TargetClass":0,"MemberId":"[\r\n  1\r\n]","Count":1},
-        是代表什么意思。 MemberId  是座号还说下标。
-    8月27日 17:581.   Task.json的  clientWorks.isGroupItem    bool 类型  false代表什么意思   true代表什么意思
-2.   Cowork.json 的  coworkType   All: '全体协作',    Group: '分组协作',      : '差异化协作',    差异化协作的简码是什么?  
-3.   TimeLine.json 的  类型 PickupResult   挑人算不算互动??  读取PickupMemberId "[\r\n  35\r\n]"
-4.   小组任务具体详细信息如何获取? 需要精确到 所有小组任务信息和 某一小组的参与情况  用于计算小组任务参与率
-     */
+
+    public class LocalStudent
+    {
+        /// <summary>
+        /// 出席状态 1出席,6公假,5事假,4病假,2缺席,0未签到
+        /// </summary>
+        public int attend { get; set; }
+        /// <summary>
+        /// 学生的学号
+        /// </summary>
+        public string? id { get; set; }
+        /// <summary>
+        /// 学生所在下标
+        /// </summary>
+        public int index { get; set; } = -1;
+        /// <summary>
+        /// 学生座位号
+        /// </summary>
+        public string? seatID { get; set; }
+        /// <summary>
+        /// 小组编号
+        /// </summary>
+        public string? groupId { get; set; }
+    }
+
+
 }

+ 291 - 154
TEAMModelOS.Extension/HTEX.Test/Controllers/MockDataController.cs

@@ -4,6 +4,10 @@ using static HTEX.Test.Controllers.LessonRecordController;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using System;
+using OfficeOpenXml;
+using System.Reflection;
+using System.Xml;
+using System.Text.RegularExpressions;
 
 namespace HTEX.Test.Controllers
 {
@@ -24,17 +28,17 @@ namespace HTEX.Test.Controllers
             //学生人数
             int scount = Random.Shared.Next(40, 45);
             //评测次数
-            int ecount = Random.Shared.Next(1, 3);
+            int ecount = Random.Shared.Next(0, 3);
             //题目个数
             int qcount = Random.Shared.Next(8, 15);
             //互动次数
-            int icount = Random.Shared.Next(3, 20);
+            int icount = Random.Shared.Next(3, 12);
             //任务次数
-            int tcount = Random.Shared.Next(1, 4);
+            int tcount = Random.Shared.Next(1, 3);
             //评价次数
-            int pcount = Random.Shared.Next(2, 4);
+            int pcount = Random.Shared.Next(1, 4);
             //协作次数
-            int xcount = Random.Shared.Next(3, 4);
+            int xcount = Random.Shared.Next(1, 3);
             List<StudentLessonData> students = new List<StudentLessonData>();
             //个人计分,小组计分
             List<WeightedItem> gpitems = new List<WeightedItem>
@@ -380,10 +384,11 @@ namespace HTEX.Test.Controllers
             }
             #endregion 数据模拟
             string jsons = await System.IO.File.ReadAllTextAsync("F:\\lesson-local\\analysis.json");
+            long time = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
             LessonDataAnalysis lessonDataAnalysis = jsons.ToObject<LessonDataAnalysis>();
-            ProcessStudentData(students, lessonDataAnalysis, scount, ecount, qcount, icount, tcount, pcount, xcount);
+            ProcessStudentData(students, lessonDataAnalysis, scount, ecount, qcount, icount, tcount, pcount, xcount, time );
             try {
-                await System.IO.File.WriteAllTextAsync($"F:\\mock-data\\{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}.json", new { scount, ecount, qcount, icount, tcount, pcount, xcount, students }.ToJsonString());
+                await System.IO.File.WriteAllTextAsync($"F:\\mock-data\\{time}.json", new { scount, ecount, qcount, icount, tcount, pcount, xcount, students }.ToJsonString());
             } catch (Exception ex) {
                 Console.WriteLine(scount);
             }
@@ -392,7 +397,7 @@ namespace HTEX.Test.Controllers
 
         
 
-        private  void ProcessStudentData(List<StudentLessonData> studentLessonDatas, LessonDataAnalysis lessonDataAnalysis, int scount, int ecount, int qcount, int icount, int tcount, int pcount, int xcount)
+        private  void ProcessStudentData(List<StudentLessonData> studentLessonDatas, LessonDataAnalysis lessonDataAnalysis, int scount, int ecount, int qcount, int icount, int tcount, int pcount, int xcount,long time )
         {
             //历史记录的个人计分集合,通过“2倍标准差规则”移除异常值后得到的集合
             var max_q = lessonDataAnalysis.pscore.Max();
@@ -413,78 +418,46 @@ namespace HTEX.Test.Controllers
                 }
                 //c个人计分指数,d互动计分指数,e小组计分指数
                 double c = 0, d = 0, e = 0;
-             
-                ////互动
-                //double hd_cx = 0;//互动成效指数
-                //double hd_cy = 0;//互动参与指数
-                //double hd_fqc = 0;//互动发起次数
-                //double hd_cyc = 0;//互动参与次数
-                //double hd_zqc = 0;//互动正确次数
-                //double gr_jf = 0;//个人计分
-                ////评测
-                //double pc_df = 0;//评测得分率
-                //double pc_zd = 0;//评测作答率
-                ////任务
-                //double rw_fqc = 0;//任务发起次数
-                //double rw_cyc = 0;//任务参与次数
-                //double rw_cx = 0;//任务成效指数
-                //double rw_cy = 0;//任务参与指数
-                ////评价
-                //double pj_nl = 0;//评价能力
-                //double pj_cs = 0;//评价发起次数
-                //double pj_vc = 0;//评价-投票发起次数
-                //double pj_vg = 0;//投票得票数
-                //double pj_vo = 0;//投票次数
-                //double pj_gc = 0;//评价-星光发起次数
-                //double pj_gg = 0;//星光得分数
-                //double pj_go = 0;//星光评分次数
-                //double pj_pc = 0;//评价-互评发起次数
-                //double pj_pg = 0;//互评的分数
-                //double pj_po = 0;//互评评分次数
-                ////协作
-                //double xz_fqc = 0;//协作发起次数
-                //double xz_cyc = 0;//协作参与次数
-                //double xz_cgf = 0;//协作成果分数
-                //double xz_cx = 0;//协作能力指数
-                //double xz_cy = 0;//协作参与指数
-
                 {
                     //互动相关的计分
                     //课例互动次数
                     double n = studentLessonData.interactRecord.interactRecords.Count()*1.0;
-                    //是IES大陆正式站历史课例数据,自2024-03-01至2024-10-08日,互动指数或学法指数黄灯或绿灯,不包含醍摩豆学校及测试学校,课例时长超过5分钟的有效课例(10,680笔数据) 的IRS互动+抢权+挑人的次数集合,
-                    //通过“2倍标准差规则” 移除异常值后得到的集合,再通过K-Means聚类算法得到高低位阶互动频次两个集合,并根据当前课例互动次数位阶的集合的质心值,该值定为m值
-                    var m = n<=lessonDataAnalysis.clustersInteract.First().Value.Max() ? lessonDataAnalysis.clustersInteract.First().Key*1.0 : lessonDataAnalysis.clustersInteract.Last().Key *1.0;
-                    //学生作答次数
-                    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<e(x)≤100
-                    var er = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Sum(x => x.resultWeight*1.0);
-                    //本节课的所有互动计分
-                    var i = studentLessonData.interactRecord.interactRecords.Sum(x => x.itemScore*1.0);
-                    //本节课教师手动给学生的个人计分
-                    var s = studentLessonData.pscore;
-                    //个人计分指数
-                    c = s*1.0/max_q;
-                    //互动计分指数
-                    d = i*1.0/max_t;
-                    //互动成效指数
-                    var a = (d+w*kw/(j*m)+r*er/(j*m))*1.0/n;
-                    //互动参与指数
-                    var b = ((w*w)/m+(r*r)/m)*1.0/n;
-                    //c+a= 个人计分指数+ 个人互动成效指数
-                    //学习成效
-                    var f1 = Math.Round(190*1.0/(1+Math.Exp(-(c+a)))-95, 4);
-                    lessonItem.hd_cx=f1;
-                    var f2 = Math.Round(200*1.0/(1+Math.Exp(-(b+u/100)))-100, 4);
-                    lessonItem.hd_cy=f2;
-                    lessonItem.hd_cyc=w;
-                    lessonItem.hd_fqc=n;
-                    lessonItem.hd_zqc=r;
-                    lessonItem.gr_jf=s;
+                    if (n>0) 
+                    {
+                        //是IES大陆正式站历史课例数据,自2024-03-01至2024-10-08日,互动指数或学法指数黄灯或绿灯,不包含醍摩豆学校及测试学校,课例时长超过5分钟的有效课例(10,680笔数据) 的IRS互动+抢权+挑人的次数集合,
+                        //通过“2倍标准差规则” 移除异常值后得到的集合,再通过K-Means聚类算法得到高低位阶互动频次两个集合,并根据当前课例互动次数位阶的集合的质心值,该值定为m值
+                        var m = n<=lessonDataAnalysis.clustersInteract.First().Value.Max() ? lessonDataAnalysis.clustersInteract.First().Key*1.0 : lessonDataAnalysis.clustersInteract.Last().Key *1.0;
+                        //学生作答次数
+                        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<e(x)≤100
+                        var er = studentLessonData.interactRecord.interactRecords.Where(x => x.resultWeight>InteractWeight.T1).Sum(x => x.resultWeight*1.0);
+                        //本节课的所有互动计分
+                        var i = studentLessonData.interactRecord.interactRecords.Sum(x => x.itemScore*1.0);
+                        //本节课教师手动给学生的个人计分
+                        var s = studentLessonData.pscore;
+                        //个人计分指数
+                        c =  GetPersent(lessonDataAnalysis.pscore,s)/100;// s*1.0/max_q;
+                        //互动计分指数
+                        d =  GetPersent(lessonDataAnalysis.tscore, i)/100; //i*1.0/max_t;
+                        //互动成效指数
+                        var a = (d+w*kw/(j*m)+r*er/(j*m))*1.0/n;
+                        //互动参与指数
+                        var b = ((w*w)/m+(r*r)/m)*1.0/n;
+                        //c+a= 个人计分指数+ 个人互动成效指数
+                        //学习成效
+                        var f1 = Math.Round(190*1.0/(1+Math.Exp(-(c+a)))-95, 4);
+                        lessonItem.hd_cx=f1;
+                        var f2 = Math.Round(200*1.0/(1+Math.Exp(-(b+u/100)))-100, 4);
+                        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)}"); 
@@ -492,17 +465,20 @@ namespace HTEX.Test.Controllers
                 {
                     //评测相关指数
                     double n = studentLessonData.examRecords.Count()*1.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;
+                    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)}");
                 }
                 { 
@@ -511,16 +487,19 @@ namespace HTEX.Test.Controllers
                 { 
                     //任务相关指数
                     double n = studentLessonData.taskRecord.itemRecords.Count()*1.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_cx =f4;
-                    lessonItem.rw_cy =f5;
+                    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_cx =f4;
+                        lessonItem.rw_cy =f5;
+                    }
                    // _logger.LogInformation($"{studentLessonData.id}=>任务指数:{f4}\t参与指数:{f5}\t任务次数:{n}\t参与次数:{w}\t");
 
                 }
@@ -528,65 +507,187 @@ namespace HTEX.Test.Controllers
                     //评价相关指数
                     
                     double n = studentLessonData.rateingRecord.itemRecords.Count()*1.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;
+                    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 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 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}");
+                        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;
+                        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;
-                    double max_m = lessonDataAnalysis.cowork.Max();
-                    double max_s = 35;
-                    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 k = ((wc*wc/n+(j/t) * wc))/n+ ss/(10 *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;
+                   
+                    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;
+                }
+                xx_cx+=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;
+
+                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;
+                }
+                xx_cy+=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.xx_cx=xx_cx;
+                lessonItem.xx_cy=xx_cy;
                 lessonItems.Add(lessonItem);
             }
+            ExportToExcel(lessonItems, $"F:\\mock-data\\{time}.xlsx");
+        }
+
+        private static  void ExportToExcel(List<StudentLessonItem> items, string filePath)
+        {
+            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]), @"\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);
+                package.SaveAs(fileInfo);
+            }
         }
+        private static string GetPropertySummary(PropertyInfo property)
+        {
+            var xmlDocumentationFile = "./x.xml"; // XML注释文件路径
+            if (System.IO. File.Exists(xmlDocumentationFile))
+            {
+                XmlDocument xmlDocument = new XmlDocument();
+                xmlDocument.Load(xmlDocumentationFile);
+
+                XmlNodeList xmlNodeList = xmlDocument.DocumentElement.SelectNodes("//member[@name='P:" + property.DeclaringType.FullName + "." + property.Name + "']");
 
+                if (xmlNodeList.Count > 0)
+                {
+                    XmlNode xmlNode = xmlNodeList[0];
+                    if (xmlNode != null && xmlNode.FirstChild != null)
+                    {
+                        return xmlNode.FirstChild.InnerText;
+                    }
+                }
+            }
+            return property.Name;
+        }
         private static int GetRandomValueByWeight(List<WeightedItem> items)
         {
             Random random = new Random();
@@ -612,6 +713,32 @@ namespace HTEX.Test.Controllers
             //排名 = (积分 - 最低积分) / (最高积分 - 最低积分) * (最大排名 - 最小排名) + 最小排名
             return Math.Round(x==0 ? 0 : max-min!=0 ? (x - min)*1.0 / (max - min) * (maxRank - minRank) + minRank : (x)*1.0 / (max) * (maxRank - minRank) + minRank,4);
         }
+        /// <summary>
+        /// 计算当前元素在集合中超过了多少百分比的值
+        /// </summary>
+        /// <param name="nums"></param>
+        /// <param name="curr"></param>
+        /// <returns></returns>
+        public static double GetPersent(IEnumerable<double> nums, double 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;
+        }
     }
     class WeightedItem
     {
@@ -624,14 +751,7 @@ namespace HTEX.Test.Controllers
         /// 学生id
         /// </summary>
         public string studentId { get; set; }
-        /// <summary>
-        /// 互动成效指数
-        /// </summary>
-        public double hd_cx { get; set; } = 0;
-        /// <summary>
-        /// 互动参与指数
-        /// </summary>
-        public double hd_cy { get; set; } = 0;
+      
         /// <summary>
         /// 互动发起次数
         /// </summary>
@@ -649,6 +769,14 @@ namespace HTEX.Test.Controllers
         /// </summary>
         public double gr_jf { get; set; } = 0;
         /// <summary>
+        /// 互动成效指数
+        /// </summary>
+        public double hd_cx { get; set; } = 0;
+        /// <summary>
+        /// 互动参与指数
+        /// </summary>
+        public double hd_cy { get; set; } = 0;
+        /// <summary>
         /// 评测得分率
         /// </summary>
         public double pc_df { get; set; } = 0;
@@ -672,10 +800,7 @@ namespace HTEX.Test.Controllers
         /// 任务参与指数
         /// </summary>
         public double rw_cy { get; set; } = 0; 
-        /// <summary>
-        /// 评价能力
-        /// </summary>
-        public double pj_nl { get; set; } = 0;
+      
         /// <summary>
         /// 评价发起次数
         /// </summary>
@@ -717,6 +842,10 @@ namespace HTEX.Test.Controllers
         /// </summary>
         public double pj_po { get; set; } = 0;
         /// <summary>
+        /// 评价能力
+        /// </summary>
+        public double pj_nl { get; set; } = 0;
+        /// <summary>
         /// 协作发起次数
         /// </summary>
         public double xz_fqc { get; set; } = 0;
@@ -736,5 +865,13 @@ namespace HTEX.Test.Controllers
         /// 协作参与指数
         /// </summary>
         public double xz_cy { get; set; } = 0;
+        /// <summary>
+        /// 学习成效
+        /// </summary>
+        public double xx_cx { get; set; } = 0;
+        /// <summary>
+        /// 学习参与
+        /// </summary>
+        public double xx_cy { get; set; } = 0;
     }
 }

+ 3 - 1
TEAMModelOS.Extension/HTEX.Test/HTEX.Test.csproj

@@ -1,9 +1,11 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    <DocumentationFile>./x.xml</DocumentationFile>
   </PropertyGroup>
 
   <ItemGroup>

+ 397 - 0
TEAMModelOS.Extension/HTEX.Test/x.xml

@@ -0,0 +1,397 @@
+<?xml version="1.0"?>
+<doc>
+    <assembly>
+        <name>HTEX.Test</name>
+    </assembly>
+    <members>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.pscore">
+            <summary>
+            个人计分
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.gscore">
+            <summary>
+            小组计分
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.tscore">
+            <summary>
+            互动计分
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.upload">
+            <summary>
+            作品上传数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.stuCowork">
+            <summary>
+            学生协作成果数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.groupCowork">
+            <summary>
+            小组协作成果数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.pickup">
+            <summary>
+            挑人集合
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.LessonDataAnalysis.pickup_group">
+            <summary>
+            挑人集合-小组
+            </summary>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetPersent(System.Collections.Generic.IEnumerable{System.Double},System.Int32)">
+            <summary>
+            计算当前元素在集合中超过了多少百分比的值
+            </summary>
+            <param name="nums"></param>
+            <param name="curr"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.CleanDataBySDThreshold(System.Collections.Generic.IEnumerable{System.Double},System.Double)">
+            <summary>
+            使用标准差定义异常值。如果一个数字与平均值的偏差超过某个标准差倍数(例如2倍或3倍),则可以认为它是异常的。
+            </summary>
+            <param name="array"></param>
+            <returns></returns>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.examCount">
+            <summary>
+            评测数量
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.taskCount">
+            <summary>
+            任务数量
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.irsCount">
+            <summary>
+            IRS次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.interactNormalCount">
+            <summary>
+            互动次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.coworkCount">
+            <summary>
+            协作次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.smartRatingCount">
+            <summary>
+            智能评分次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.examCountBase">
+            <summary>
+            评测数量
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.taskCountBase">
+            <summary>
+            任务数量
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.irsCountBase">
+            <summary>
+            IRS次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.interactNormalCountBase">
+            <summary>
+            互动次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.coworkCountBase">
+            <summary>
+            协作次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LessonRecordController.TechCount.smartRatingCountBase">
+            <summary>
+            智能评分次数
+            </summary>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetBaseData(TEAMModelOS.SDK.Models.LessonBase)">
+            <summary>
+            处理base.json的数据
+            </summary>
+            <param name="lessonRecord"></param>
+            <param name="lessonBase"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetIRSData(TEAMModelOS.SDK.Models.LessonBase,TEAMModelOS.SDK.Models.TimeLineData,System.Collections.Generic.List{TEAMModelOS.SDK.Models.IRSData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.StudentLessonData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.ExamData},System.String)">
+             <summary>
+            读取互动信息
+            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
+             </summary>
+             <param name="lessonRecord"></param>
+             <param name="lessonBase"></param>
+             <param name="irsDatas"></param>
+             <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetExamData(TEAMModelOS.SDK.Models.LessonBase,TEAMModelOS.SDK.Models.TimeLineData,System.Collections.Generic.List{TEAMModelOS.SDK.Models.ExamData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.StudentLessonData})">
+            <summary>
+            获取课中评测数据
+            </summary>
+            <param name="lessonRecord"></param>
+            <param name="lessonBase"></param>
+            <param name="timeLineData"></param>
+            <param name="coworkDatas"></param>
+            <param name="studentLessonDatas"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetCoworkData(TEAMModelOS.SDK.Models.LessonBase,TEAMModelOS.SDK.Models.TimeLineData,System.Collections.Generic.List{TEAMModelOS.SDK.Models.CoworkData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.StudentLessonData})">
+            <summary>
+            协作参与率 态度计算
+            </summary>
+            <param name="lessonRecord"></param>
+            <param name="lessonBase"></param>
+            <param name="timeLineData"></param>
+            <param name="coworkDatas"></param>
+            <param name="studentLessonDatas"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.GetTaskData(TEAMModelOS.SDK.Models.LessonBase,TEAMModelOS.SDK.Models.TimeLineData,System.Collections.Generic.List{TEAMModelOS.SDK.Models.TaskData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.StudentLessonData})">
+            <summary>
+            处理学生回推数据,并将回推纳入学习态度计算。
+            </summary>
+            <param name="lessonRecord"></param>
+            <param name="lessonBase"></param>
+            <param name="timeLineData"></param>
+            <param name="taskDatas"></param>
+            <param name="studentLessonDatas"></param>
+            <returns></returns>
+        </member>
+        <!-- Badly formed XML comment ignored for member "M:HTEX.Test.Controllers.LessonRecordController.GetSmartRatingData(TEAMModelOS.SDK.Models.LessonBase,TEAMModelOS.SDK.Models.TimeLineData,System.Collections.Generic.List{TEAMModelOS.SDK.Models.SmartRatingData},System.Collections.Generic.List{TEAMModelOS.SDK.Models.StudentLessonData},System.String)" -->
+        <member name="M:HTEX.Test.Controllers.LessonRecordController.MinMaxNormalization(System.Double,System.Double,System.Double)">
+            <summary>
+            最小-最大归一化(Min-Max Normalization)算法。这种算法通常用于将数据的特征值缩放到一个指定的范围内,通常是0到1之间,或者任何其他指定的范围。
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LocalStudent.attend">
+            <summary>
+            出席状态 1出席,6公假,5事假,4病假,2缺席,0未签到
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LocalStudent.id">
+            <summary>
+            学生的学号
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LocalStudent.index">
+            <summary>
+            学生所在下标
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LocalStudent.seatID">
+            <summary>
+            学生座位号
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.LocalStudent.groupId">
+            <summary>
+            小组编号
+            </summary>
+        </member>
+        <member name="M:HTEX.Test.Controllers.MockDataController.GetPersent(System.Collections.Generic.IEnumerable{System.Double},System.Double)">
+            <summary>
+            计算当前元素在集合中超过了多少百分比的值
+            </summary>
+            <param name="nums"></param>
+            <param name="curr"></param>
+            <returns></returns>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.studentId">
+            <summary>
+            学生id
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.hd_fqc">
+            <summary>
+            互动发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.hd_cyc">
+            <summary>
+            互动参与次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.hd_zqc">
+            <summary>
+            互动正确次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.gr_jf">
+            <summary>
+            个人计分
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.hd_cx">
+            <summary>
+            互动成效指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.hd_cy">
+            <summary>
+            互动参与指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pc_df">
+            <summary>
+            评测得分率
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pc_zd">
+            <summary>
+            评测作答率
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.rw_fqc">
+            <summary>
+            任务发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.rw_cyc">
+            <summary>
+            任务参与次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.rw_cx">
+            <summary>
+            任务成效指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.rw_cy">
+            <summary>
+            任务参与指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_cs">
+            <summary>
+            评价发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_vc">
+            <summary>
+            投票发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_vg">
+            <summary>
+            投票得票数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_vo">
+            <summary>
+            投票次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_gc">
+            <summary>
+            星光发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_gg">
+            <summary>
+            星光得分数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_go">
+            <summary>
+            星光评分次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_pc">
+            <summary>
+            互评发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_pg">
+            <summary>
+            互评得分数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_po">
+            <summary>
+            互评评分次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.pj_nl">
+            <summary>
+            评价能力
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xz_fqc">
+            <summary>
+            协作发起次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xz_cyc">
+            <summary>
+            协作参与次数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xz_cgf">
+            <summary>
+            协作成果分数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xz_cx">
+            <summary>
+            协作能力指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xz_cy">
+            <summary>
+            协作参与指数
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xx_cx">
+            <summary>
+            学习成效
+            </summary>
+        </member>
+        <member name="P:HTEX.Test.Controllers.StudentLessonItem.xx_cy">
+            <summary>
+            学习参与
+            </summary>
+        </member>
+        <member name="M:HTEX.Test.Program.GetPersent(System.Collections.Generic.IEnumerable{System.Double},System.Int32)">
+            <summary>
+            计算当前元素在集合中超过了多少百分比的值
+            </summary>
+            <param name="nums"></param>
+            <param name="curr"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Service.MLService.KMeans(System.Single[],System.Int32)">
+            <summary>
+            
+            </summary>
+            <param name="datas">数据需要去掉0</param>
+            <param name="numberOfClusters"></param>
+            <returns></returns>
+        </member>
+        <member name="M:HTEX.Test.Service.MLService.GetNormalCluster(System.Single[],System.Int32,System.Double)">
+            <summary>
+            
+            </summary>
+            <param name="datas"></param>
+            <param name="numberOfClusters"></param>
+            <param name="dropPercent">最大平均数的聚类与数量最多的聚类数量的落差小于30% 则以更高的为准</param>
+            <returns></returns>
+        </member>
+    </members>
+</doc>

+ 22 - 1
TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelper.cs

@@ -270,7 +270,7 @@ namespace TEAMModelOS.SDK.Helper.Common.FileHelper
                 else
                 {
                     //将文件移动到指定目录
-                    File.Move(sourceFilePath, descDirectoryPath + "\\" + sourceFilePath);
+                    File.Move(sourceFilePath, descDirectoryPath + "\\" + sourceName);
                 }
             }
         }
@@ -653,6 +653,27 @@ namespace TEAMModelOS.SDK.Helper.Common.FileHelper
             }
         }
         #endregion
+
+        /// <summary>
+        /// 列出文件夹下的所有子文件及子文件夹的子文件
+        /// </summary>
+        /// <param name="directoryPath"></param>
+        /// <returns></returns>
+        public static List<string> ListAllFiles(string directoryPath)
+        {
+            List<string> filePaths = new List<string>();
+            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;
+        }
     }
 
     /// <summary>

+ 7 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs

@@ -557,7 +557,14 @@ namespace TEAMModelOS.SDK.Models
         /// 互动计分
         /// </summary>
         public double tscore { get; set; }
+
+        public List<string> pickups { get; set; } = new List<string>();
+        public List<double> coworkScore { get; set; } = new List<double>();
+        public List<double> group_coworkScore { get; set; } = new List<double>();
+        public List<int> uploadCount { get; set; } = new List<int>();
     }
+
+  
     public class StudentRateingRecord
     {
         /// <summary>

+ 4 - 2
TEAMModelOS.SDK/Models/Service/OpenApiService.cs

@@ -166,7 +166,8 @@ namespace TEAMModelOS.SDK
                 sql_status_managePage = "";
             }
             long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
-            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} 1=1 {sqlPrivate} {sqlShow}  and  ");
             if (json.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
             {
                 cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");
@@ -341,7 +342,8 @@ namespace TEAMModelOS.SDK
                 {
                     sql_status_managePage = "";
                 }
-                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                // cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  1=1 {sqlPrivate}  {sqlShow} and  ");
                 await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
                    .GetItemQueryStreamIteratorQuery(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, continuationToken: continuationToken,
                    requestOptions: new QueryRequestOptions() { MaxItemCount = pageCount, PartitionKey = new PartitionKey(code) }))

+ 7 - 0
TEAMModelOS.sln

@@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTEX.ScreenClient", "TEAMMo
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTEX.Test", "TEAMModelOS.Extension\HTEX.Test\HTEX.Test.csproj", "{0AB3F94D-9698-4DB1-B532-5E6C8E56F770}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTEX.DataETL", "HTEX.DataETL\HTEX.DataETL.csproj", "{1F30D9C2-3E52-4E7C-AE46-8C13916153D2}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -79,6 +81,10 @@ Global
 		{0AB3F94D-9698-4DB1-B532-5E6C8E56F770}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{0AB3F94D-9698-4DB1-B532-5E6C8E56F770}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{0AB3F94D-9698-4DB1-B532-5E6C8E56F770}.Release|Any CPU.Build.0 = Release|Any CPU
+		{1F30D9C2-3E52-4E7C-AE46-8C13916153D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1F30D9C2-3E52-4E7C-AE46-8C13916153D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1F30D9C2-3E52-4E7C-AE46-8C13916153D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1F30D9C2-3E52-4E7C-AE46-8C13916153D2}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -91,6 +97,7 @@ Global
 		{46B42E81-D613-47DD-BB8D-B5D6B4284FCE} = {9B74B53F-20E8-46CC-903B-62AEB1583DD7}
 		{BB3DD2CC-CAFA-4DE9-97FA-866465A274F1} = {9B74B53F-20E8-46CC-903B-62AEB1583DD7}
 		{0AB3F94D-9698-4DB1-B532-5E6C8E56F770} = {9B74B53F-20E8-46CC-903B-62AEB1583DD7}
+		{1F30D9C2-3E52-4E7C-AE46-8C13916153D2} = {9B74B53F-20E8-46CC-903B-62AEB1583DD7}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {76440725-5E50-4288-851F-BA5C0BC8E8C6}

+ 4 - 2
TEAMModelOS/Controllers/Analysis/ClassAnalysisController.cs

@@ -631,11 +631,13 @@ namespace TEAMModelOS.Controllers.Analysis
                 //获取当前学期所有的课程记录
                 List<LessonRecord> records = new();
                 var client = _azureCosmos.GetCosmosClient();
-                var queryClass = $"select value(c) from c where c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and array_length(c.groupIds)>0 ";
+                //var queryClass = $"select value(c) from c where c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and array_length(c.groupIds)>0 ";
+                var queryClass = $"select value(c) from c where c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and 1=1 ";
                 string tId = string.Empty;
                 if (request.TryGetProperty("tmdId", out JsonElement tmdId))
                 {
-                    queryClass = $"select value(c) from c where c.tmdid = '{tmdId}' and c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and array_length(c.groupIds)>0 ";
+                    // queryClass = $"select value(c) from c where c.tmdid = '{tmdId}' and c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and array_length(c.groupIds)>0 ";
+                    queryClass = $"select value(c) from c where c.tmdid = '{tmdId}' and c.periodId = '{pId}' and (c.status<>404 or IS_DEFINED(c.status) = false) and  c.expire <= 0 and 1=1 ";
                     tId = tmdId.GetString();
                 }
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIteratorSql<LessonRecord>(queryText: queryClass, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"LessonRecord-{code}") }))

+ 2 - 1
TEAMModelOS/Controllers/Both/KnowledgeController.cs

@@ -322,8 +322,9 @@ namespace TEAMModelOS.Controllers.Both
 #if !DEBUG
 
         [Authorize(Roles = "IES")]
+       [AuthToken(Roles = "admin,teacher", Permissions = "knowledge-upd")]
 #endif
-        [AuthToken(Roles = "admin,teacher", Permissions = "knowledge-upd")]
+
 
         public async Task<IActionResult> ClearOld(JsonElement json) 
         {

+ 18 - 9
TEAMModelOS/Controllers/Both/LessonRecordController.cs

@@ -411,7 +411,8 @@ namespace TEAMModelOS.Controllers
             if (managePage) {
                 sql_status_managePage = "";
             }
-            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} 1=1 {sqlPrivate} {sqlShow}  and  ");
             if (request.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
             {
                 cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");
@@ -535,7 +536,8 @@ namespace TEAMModelOS.Controllers
                         }
                         sqlShow = $" and (array_contains(c.show,'student') or array_contains(c.show,'all')  {autoSql} ) ";
                     }
-                    cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                    //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                    cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  1=1 {sqlPrivate}  {sqlShow} and  ");
                     await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
                        .GetItemQueryStreamIteratorQuery(queryDefinition: cosmosDbQuery.CosmosQueryDefinition,   
                        requestOptions: new QueryRequestOptions() {  PartitionKey = new PartitionKey(code) }))
@@ -583,7 +585,8 @@ namespace TEAMModelOS.Controllers
                         }
                         sqlShow = $" and (array_contains(c.show,'student') or array_contains(c.show,'all')  {autoSql} ) ";
                     }
-                    cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                    //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                    cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false ) and  1=1  {sqlPrivate}  {sqlShow} and  ");
                     await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
                        .GetItemQueryStreamIteratorQuery(queryDefinition: cosmosDbQuery.CosmosQueryDefinition,  
                        requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))
@@ -696,6 +699,7 @@ namespace TEAMModelOS.Controllers
                 }
             }
             AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
+            cosmosDbQuery.QueryText=cosmosDbQuery.QueryText.Replace("distinct", "");
             string tbname = "";
             string code = "";
             string school = null;
@@ -788,8 +792,8 @@ namespace TEAMModelOS.Controllers
                 {
                     sql_status_managePage = "";
                 }
-                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
-
+                //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  1=1 {sqlPrivate}  {sqlShow} and  ");
                 if (request.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
                 {
                     cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");
@@ -822,7 +826,9 @@ namespace TEAMModelOS.Controllers
 
                                 rcd.learningCategory.cooperation = 1;
                             }
-                            lessonRecords.Add(rcd);
+                            if (!lessonRecords.Exists(x => x.id.Equals(rcd.id))) 
+                            { lessonRecords.Add(rcd); }
+                            
                         }
                         continuationToken = item.ContinuationToken;
                         break;
@@ -901,8 +907,9 @@ namespace TEAMModelOS.Controllers
                 }
                 return Ok(new { currCount = lessonRecords.Count, continuationToken, lessonRecords });
             }
-            catch (Exception )
+            catch (Exception ex)
             {
+                await _dingDing.SendBotMsg($"{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
                 continuationToken = null;
                 return Ok(new { currCount = 0, continuationToken = continuationToken, lessonRecords });
             }
@@ -1029,7 +1036,8 @@ namespace TEAMModelOS.Controllers
             try
             {
                 string sql_status_managePage = "(c.status<>404 or IS_DEFINED(c.status) = false ) and  ";
-                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}   and  ");
+                // cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}   and  ");
+                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  1=1 {sqlPrivate}   and  ");
                 if (request.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
                 {
                     cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");
@@ -1327,7 +1335,8 @@ namespace TEAMModelOS.Controllers
             Dictionary<string, object> dict = LessonService.GetLessonCond(request);
             AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
             List<LessonRecord> lessonRecords = new List<LessonRecord>();
-            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false )  and  array_length(c.groupIds)>0 {sqlPrivate}  and  ");
+            // cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false )  and  array_length(c.groupIds)>0 {sqlPrivate}  and  ");
+            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where (c.status<>404 or IS_DEFINED(c.status) = false )  and  1=1 {sqlPrivate}  and  ");
             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
                .GetItemQueryStreamIteratorQuery(queryDefinition: cosmosDbQuery.CosmosQueryDefinition,
                requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(code) }))

+ 2 - 1
TEAMModelOS/Controllers/Both/ScoreCalcController.cs

@@ -2034,7 +2034,8 @@ namespace TEAMModelOS.Controllers
             {
                 string sql_status_managePage = "(c.status<>404 or IS_DEFINED(c.status) = false ) and  ";
 
-                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0  and  ");
+               // cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0  and  ");
+                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  1=1  and  ");
 
                 await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
                    .GetItemQueryStreamIteratorQuery(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, continuationToken: continuationToken,

+ 4 - 3
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -4149,8 +4149,8 @@ namespace TEAMModelOS.Controllers.Client
                 {
                     sql_status_managePage = "";
                 }
-                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
-
+                // cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  array_length(c.groupIds)>0 {sqlPrivate}  {sqlShow} and  ");
+                cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  {sql_status_managePage}  1=1 {sqlPrivate}  {sqlShow} and  ");
                 if (request.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
                 {
                     cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");
@@ -4389,7 +4389,8 @@ namespace TEAMModelOS.Controllers.Client
             {
                 sql_status_managePage = "";
             }
-            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            //cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} array_length(c.groupIds)>0 {sqlPrivate} {sqlShow}  and  ");
+            cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where {sql_status_managePage} 1=1 {sqlPrivate} {sqlShow}  and  ");
             if (request.TryGetProperty("singleGreen", out JsonElement doubleGreen) && doubleGreen.GetBoolean())
             {
                 cosmosDbQuery.QueryText = cosmosDbQuery.QueryText.Replace("where", $" where  ((c.tLevel=2 and c.pLevel<2 )or(c.tLevel<2 and c.pLevel=2 )) and   ");

+ 0 - 1
TEAMModelOS/TEAMModelOS.csproj

@@ -6,7 +6,6 @@
 	<ItemGroup>
 		<PackageReference Include="DotNetZip" Version="1.16.0" />
 		<PackageReference Include="DinkToPdf" Version="1.0.8" />
-		<PackageReference Include="EPPlus" Version="7.2.0" />
 		<PackageReference Include="IP2Region.Net" Version="2.0.2" />
 		<!--<PackageReference Include="JsonPath.Net" Version="1.1.2" />-->
 		<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />

文件差异内容过多而无法显示
+ 28 - 28
TEAMModelOS/appsettings.Development.json