Selaa lähdekoodia

1.統測活動生成老師個人評量邏輯修正 2.取得活動評量API修正 3.決賽名單生成邏輯

jeff 9 kuukautta sitten
vanhempi
commit
b33644ee16

+ 1 - 1
TEAMModelOS.Function/CosmosDBTriggers/TriggerJointExam.cs

@@ -81,7 +81,7 @@ namespace TEAMModelOS.CosmosDBTriggers
                                 await table.Save<ChangeRecord>(changeRecord);
                                 await table.Save<ChangeRecord>(changeRecord);
                             }
                             }
                             //生成所有報名教師個人評量
                             //生成所有報名教師個人評量
-                            await JointService.GenerateExamFromJointExamAsync(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, info);
+                            await JointService.GenerateExamFromJointExamAsync(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, info, string.Empty);
                         }
                         }
                         catch (Exception e)
                         catch (Exception e)
                         {
                         {

+ 6 - 0
TEAMModelOS.Function/IESServiceBusTrigger.cs

@@ -2613,6 +2613,12 @@ namespace TEAMModelOS.Function
                                 string pk = string.Format("{0}{1}{2}{3}{4}{5}{6}", "JointEvent", "-", $"{jointEventId}", "-", "schedule", "-", "going");
                                 string pk = string.Format("{0}{1}{2}{3}{4}{5}{6}", "JointEvent", "-", $"{jointEventId}", "-", "schedule", "-", "going");
                                 await table.DeleteSingle<ChangeRecord>(pk, jointEventSchedule.id);
                                 await table.DeleteSingle<ChangeRecord>(pk, jointEventSchedule.id);
                                 //寄發Email
                                 //寄發Email
+                                //熱身賽行程結束、決賽名單計算
+                                if(jointEventSchedule.type.Equals("exam") && jointEventSchedule.examType.Equals("regular"))
+                                {
+
+                                }
+
                                 break;
                                 break;
                         }
                         }
                     }
                     }

+ 3 - 1
TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs

@@ -122,8 +122,10 @@ namespace TEAMModelOS.SDK.Models
         public List<FMember> staffIds { get; set; } = new List<FMember>();
         public List<FMember> staffIds { get; set; } = new List<FMember>();
         //是否啟動cloudas計算 true:啟動
         //是否啟動cloudas計算 true:啟動
         public bool cloudas { get; set;}
         public bool cloudas { get; set;}
-        //統測ID [測試中]
+        //統測ID
         public string jointExamId { get; set; }
         public string jointExamId { get; set; }
+        //統測決賽可見狀態 true:可見 false:不可見
+        public bool jointVisiable { get; set; }
         public string moofenCode { get; set; }
         public string moofenCode { get; set; }
         //不可重複作答
         //不可重複作答
         public bool overwriteDisable { get; set; }
         public bool overwriteDisable { get; set; }

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/Teacher/JointEvent.cs

@@ -65,7 +65,6 @@ namespace TEAMModelOS.SDK.Models
         public string schoolId { get; set; } //老師教育雲綁定的學校ID
         public string schoolId { get; set; } //老師教育雲綁定的學校ID
         public string schoolName { get; set; } //老師教育雲綁定的學校名稱
         public string schoolName { get; set; } //老師教育雲綁定的學校名稱
         public string periodId { get; set; } //老師個人課程時為null
         public string periodId { get; set; } //老師個人課程時為null
-        public string jointScheduleId { get; set; } //活動進程ID 決賽用名單才有值,其餘為null
         public List<JointEventGroupCourse> courseLists { get; set; } = new();
         public List<JointEventGroupCourse> courseLists { get; set; } = new();
         public class JointEventGroupCourse
         public class JointEventGroupCourse
         {
         {
@@ -95,6 +94,7 @@ namespace TEAMModelOS.SDK.Models
     {
     {
         public string jointEventId { get; set; }
         public string jointEventId { get; set; }
         public string jointGroupId { get; set; }
         public string jointGroupId { get; set; }
+        public string jointScheduleId { get; set; } //活動進程ID 決賽用名單才有值,其餘為null
         public string id { get; set; }
         public string id { get; set; }
         public string code { get; set; }
         public string code { get; set; }
         public string pk { get; set; }
         public string pk { get; set; }

+ 306 - 5
TEAMModelOS.SDK/Models/Service/JointService.cs

@@ -16,13 +16,14 @@ using TEAMModelOS.SDK.Services;
 using static TEAMModelOS.SDK.Models.JointEventGroupBase;
 using static TEAMModelOS.SDK.Models.JointEventGroupBase;
 using Azure.Core;
 using Azure.Core;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
+using static TEAMModelOS.SDK.Models.JointEvent;
 
 
 namespace TEAMModelOS.SDK.Models.Service
 namespace TEAMModelOS.SDK.Models.Service
 {
 {
     public static class JointService
     public static class JointService
     {
     {
         //取得JointExam生成Exam
         //取得JointExam生成Exam
-        public static async Task GenerateExamFromJointExamAsync(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam)
+        public static async Task GenerateExamFromJointExamAsync(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam, string creatorId)
         {
         {
             try
             try
             {
             {
@@ -35,7 +36,8 @@ namespace TEAMModelOS.SDK.Models.Service
                 if (!jointExam.examType.Equals("custom")) //老師報名名單
                 if (!jointExam.examType.Equals("custom")) //老師報名名單
                 {
                 {
                     string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}' AND ( IS_DEFINED(c.type) = false OR c.type = 'regular' )";
                     string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}' AND ( IS_DEFINED(c.type) = false OR c.type = 'regular' )";
-                    await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql<JointEventGroupDb>(queryText: jointCourseSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"JointCourse") }))
+                    if (!string.IsNullOrWhiteSpace(creatorId)) jointCourseSql += $" AND c.creatorId = '{creatorId}' ";
+                    await foreach (var item in client.GetContainer("TEAMModelOS", Constant.Teacher).GetItemQueryIteratorSql<JointEventGroupDb>(queryText: jointCourseSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"JointCourse") }))
                     {
                     {
                         jointCourses.Add(item);
                         jointCourses.Add(item);
                     }
                     }
@@ -55,8 +57,8 @@ namespace TEAMModelOS.SDK.Models.Service
                             //評量資料生成
                             //評量資料生成
                             ExamInfo actExamInfo = new ExamInfo();
                             ExamInfo actExamInfo = new ExamInfo();
                             ///取得已生成的Exam ※
                             ///取得已生成的Exam ※
-                            string examSql = $"SELECT DISTINCT c.id, c.source, c.name, c.jointExamId, c.subjects, c.stuLists, c.targets, c.papers, c.year, c.startTime, c.endTime FROM c JOIN s IN c.subjects WHERE c.jointExamId = '{jointExam.id}' AND s.id = '{actExamCourseId}'";
-                            await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql<ExamInfo>(queryText: examSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{actExamCreatorId}") }))
+                            string examSql = $"SELECT DISTINCT c.id, c.source, c.name, c.jointExamId, c.subjects, c.stuLists, c.targets, c.papers, c.year, c.startTime, c.endTime, c.code, c.owner, c.scope, c.creatorId FROM c JOIN s IN c.subjects WHERE c.jointExamId = '{jointExam.id}' AND s.id = '{actExamCourseId}'";
+                            await foreach (var item in client.GetContainer("TEAMModelOS", Constant.Common).GetItemQueryIteratorSql<ExamInfo>(queryText: examSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{actExamCreatorId}") }))
                             {
                             {
                                 actExamInfo = item;
                                 actExamInfo = item;
                             }
                             }
@@ -82,7 +84,16 @@ namespace TEAMModelOS.SDK.Models.Service
                                 }
                                 }
                                 List<string> targetRow = new List<string>() { actExamCourseId, actGroup.id };
                                 List<string> targetRow = new List<string>() { actExamCourseId, actGroup.id };
                                 var targetRowJson = JsonSerializer.SerializeToElement(targetRow);
                                 var targetRowJson = JsonSerializer.SerializeToElement(targetRow);
-                                if(!actExamInfo.targets.Contains(targetRowJson))
+                                bool add = true;
+                                foreach(JsonElement target in actExamInfo.targets)
+                                {
+                                    if(target.ToJsonString().Equals(targetRowJson.ToJsonString()))
+                                    {
+                                        add = false;
+                                        break;
+                                    }
+                                }
+                                if(add)
                                 {
                                 {
                                     actExamInfo.targets.Add(targetRowJson);
                                     actExamInfo.targets.Add(targetRowJson);
                                 }
                                 }
@@ -261,5 +272,295 @@ namespace TEAMModelOS.SDK.Models.Service
 
 
             return Result;
             return Result;
         }
         }
+
+        //以JointSchedule為單位,判斷班級/課程名單是否完成並生成決賽名單
+        public static async Task<List<JointEventGroupDb>> CreatePassJointCourseBySchedule(CosmosClient client, string jointEventId, string jointGroupId, string jointScheduleId, string scope)
+        {
+            List<JointEventGroupDb> result = new List<JointEventGroupDb>();
+            //0. 取得jointEvent、JointEventSchedule
+            JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
+            if (jointEvent == null)
+            {
+                return result;
+            }
+            JointEventSchedule jointEventSchedule = jointEvent.schedule.Where(s => s.id.Equals(jointScheduleId)).FirstOrDefault();
+            if (jointEventSchedule == null)
+            {
+                return result;
+            }
+            //1. 用jointEventId、jointGroupId 取得所有老師報名的 班級/課程名單
+            List<JointEventGroupDb> jointEventCourse = new List<JointEventGroupDb>();
+            StringBuilder stringBuilderJointCourse = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.scope = '{scope}' AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) ");
+            string container = (scope.Equals("school")) ? Constant.School : Constant.Teacher;
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilderJointCourse.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.Content);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        jointEventCourse.Add(obj.ToObject<JointEventGroupDb>());
+                    }
+                }
+            }
+            //2. 取得本Schedule的所有JointExam
+            List<string> jointExamIdList = new List<string>();
+            List<JointExam> jointEventExam = new List<JointExam>();
+            StringBuilder stringBuilderJointExam = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.jointScheduleId = '{jointScheduleId}' ");
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryStreamIteratorSql(queryText: stringBuilderJointExam.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointExam") }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.Content);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        JointExam jointExamRow = obj.ToObject<JointExam>();
+                        jointEventExam.Add(jointExamRow);
+                        if (!jointExamIdList.Contains(jointExamRow.id))
+                        {
+                            jointExamIdList.Add(jointExamRow.id);
+                        }
+                    }
+                }
+            }
+            //3. 取得所有JointExam關聯的Exam
+            List<ExamInfo> exam = new List<ExamInfo>();
+            StringBuilder stringBuilderExam = new($"SELECT * FROM c WHERE c.pk = 'Exam' AND ARRAY_CONTAINS({JsonSerializer.Serialize(jointExamIdList)}, c.jointExamId) ");
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryStreamIteratorSql(queryText: stringBuilderExam.ToString(), requestOptions: null))
+            {
+                using var json = await JsonDocument.ParseAsync(item.Content);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        exam.Add(obj.ToObject<ExamInfo>());
+                    }
+                }
+            }
+            //4. 用 IfJointExamComplete 列出完成評量的 班級/課程名單
+            List<JointEventGroupPassDto> passGroupsBindJointexam = new List<JointEventGroupPassDto>(); //已完成的課程名單和已完成的JointExam對照表
+            JointEventSchedule finalSchedule = jointEvent.schedule.Where(s => s.type.Equals("exam") && s.examType.Equals("custom")).FirstOrDefault(); //取schedule中的決賽為jointScheduleId ※應該只會有一個決賽
+            if (finalSchedule != null) //可取得挑戰賽才繼續
+            {
+                foreach (JointExam jointExamRow in jointEventExam)
+                {
+                    ///第1層 JointExam
+                    List<ExamInfo> examsNow = exam.Where(e => e.jointExamId.Equals(jointExamRow.id)).ToList();
+                    foreach (JointEventGroupDb jointEventCourseRow in jointEventCourse)
+                    {
+                        ///第2層 jointEventCourse
+                        string creatorIdRow = jointEventCourseRow.creatorId;
+                        foreach (JointEventGroupBase.JointEventGroupCourse jointCourseIn in jointEventCourseRow.courseLists)
+                        {
+                            ///第3層 老師報名的course
+                            string courseId = jointCourseIn.courseId;
+                            string courseName = jointCourseIn.courseName;
+                            List<string> groupIds = jointCourseIn.groupLists.Select(c => c.id).ToList();
+                            ExamInfo examNow = examsNow.Where(e => e.creatorId.Equals(creatorIdRow) && e.subjects[0].id.Equals(courseId)).FirstOrDefault();
+                            if (examNow != null)
+                            {
+                                string examId = examNow.id;
+                                string schoolId = string.Empty;
+                                string classId = string.Empty;
+                                foreach (string groupId in groupIds)
+                                {
+                                    JointEventGroupBase.JointEventGroupCourseGroup passGroupInfo = await IfExamComplete(client, examId, scope, creatorIdRow, schoolId, classId, groupId);
+                                    if (passGroupInfo != null)
+                                    {
+                                        //生成決賽通過的老師課程名單用中間model
+                                        JointEventGroupPassDto passGroupRow = passGroupsBindJointexam.Where(p => p.creatorId.Equals(jointEventCourseRow.creatorId) && p.courseId.Equals(courseId) && p.groupId.Equals(groupId)).FirstOrDefault();
+                                        if (passGroupRow == null)
+                                        {
+                                            passGroupsBindJointexam.Add(new JointEventGroupPassDto()
+                                            {
+                                                creatorId = jointEventCourseRow.creatorId,
+                                                courseId = courseId,
+                                                groupId = groupId
+                                            });
+                                            passGroupRow = passGroupsBindJointexam.Where(p => p.creatorId.Equals(jointEventCourseRow.creatorId) && p.courseId.Equals(courseId) && p.groupId.Equals(groupId)).FirstOrDefault();
+                                        }
+                                        if (!passGroupRow.jointExamId.Contains(jointExamRow.id))
+                                        {
+                                            passGroupRow.jointExamId.Add(jointExamRow.id);
+                                        }
+                                        if (passGroupRow.jointExamId.Count.Equals(jointExamIdList.Count))
+                                        {
+                                            passGroupRow.pass = true;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            //5. 決賽課程生成
+            ///生成資料製作
+            List<JointEventGroupDb> jointCourseCreates = new List<JointEventGroupDb>(); //要生成的老師課程名單
+            foreach (JointEventGroupDb jointEventCourseRow in jointEventCourse)
+            {
+                foreach (JointEventGroupBase.JointEventGroupCourse course in jointEventCourseRow.courseLists)
+                {
+                    foreach (JointEventGroupBase.JointEventGroupCourseGroup group in course.groupLists)
+                    {
+                        JointEventGroupPassDto passGroupRow = passGroupsBindJointexam.Where(g => g.creatorId.Equals(jointEventCourseRow.creatorId) && g.courseId.Equals(course.courseId) && g.groupId.Equals(group.id) && g.pass.Equals(true)).FirstOrDefault();
+                        if (passGroupRow != null)
+                        {
+                            string courseId = course.courseId;
+                            string courseName = course.courseName;
+                            JointEventGroupDb jointCourseCreateRow = jointCourseCreates.Where(c => c.jointEventId.Equals(jointEventCourseRow.jointEventId) && c.jointGroupId.Equals(jointEventCourseRow.jointGroupId) && c.creatorId.Equals(jointEventCourseRow.creatorId)).FirstOrDefault();
+                            if (jointCourseCreateRow == null)
+                            {
+                                JointEventGroupDb finalEventCourse = new JointEventGroupDb();
+                                finalEventCourse.jointEventId = jointEventCourseRow.jointEventId;
+                                finalEventCourse.jointGroupId = jointEventCourseRow.jointGroupId;
+                                finalEventCourse.code = jointEventCourseRow.code;
+                                finalEventCourse.pk = jointEventCourseRow.pk;
+                                finalEventCourse.scope = jointEventCourseRow.scope;
+                                finalEventCourse.type = "custom"; //決賽
+                                finalEventCourse.creatorId = jointEventCourseRow.creatorId;
+                                finalEventCourse.creatorName = jointEventCourseRow.creatorName;
+                                finalEventCourse.creatorEmail = jointEventCourseRow.creatorEmail;
+                                finalEventCourse.schoolId = jointEventCourseRow.schoolId;
+                                finalEventCourse.schoolName = jointEventCourseRow.schoolName;
+                                finalEventCourse.jointScheduleId = finalSchedule.id;
+                                finalEventCourse.courseLists.Add(new JointEventGroupBase.JointEventGroupCourse()
+                                {
+                                    courseId = courseId,
+                                    courseName = courseName,
+                                    groupLists = new List<JointEventGroupBase.JointEventGroupCourseGroup>() {
+                                        new() { id = group.id, name = group.name }
+                                    }
+                                });
+                                jointCourseCreates.Add(finalEventCourse);
+                            }
+                            else
+                            {
+                                JointEventGroupBase.JointEventGroupCourse courseRowNow = jointCourseCreateRow.courseLists.Where(c => c.courseId.Equals(courseId)).FirstOrDefault();
+                                if (courseRowNow == null)
+                                {
+                                    jointCourseCreateRow.courseLists.Add(
+                                        new JointEventGroupBase.JointEventGroupCourse
+                                        {
+                                            courseId = courseId,
+                                            courseName = courseName,
+                                            groupLists = new List<JointEventGroupBase.JointEventGroupCourseGroup>() {
+                                                            new() { id = group.id, name = group.name }
+                                            }
+                                        }
+                                    );
+                                }
+                                else
+                                {
+                                    JointEventGroupBase.JointEventGroupCourseGroup groupRowNow = courseRowNow.groupLists.Where(g => g.id.Equals(group.id)).FirstOrDefault();
+                                    if (groupRowNow == null)
+                                    {
+                                        courseRowNow.groupLists.Add(
+                                            new() { id = group.id, name = group.name }
+                                        );
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            ///DB
+            if (jointCourseCreates.Count > 0)
+            {
+                //取得所有已存在的決賽課程名單
+                List<JointEventGroupDb> jointCourseFinalExist = new List<JointEventGroupDb>();
+                StringBuilder stringBuilderJointCourseCreates = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.scope = '{scope}' AND c.type = 'custom' AND c.jointScheduleId = '{finalSchedule.id}' ");
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilderJointCourseCreates.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.Content);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            jointCourseFinalExist.Add(obj.ToObject<JointEventGroupDb>());
+                        }
+                    }
+                }
+                //生成決賽課程名單
+                foreach (JointEventGroupDb jointCourseCreateRow in jointCourseCreates)
+                {
+                    JointEventGroupDb jointCourseFinalExistRow = jointCourseFinalExist.Where(j => j.creatorId.Equals(jointCourseCreateRow.creatorId)).FirstOrDefault();
+                    jointCourseCreateRow.id = (jointCourseFinalExistRow != null) ? jointCourseFinalExistRow.id : Guid.NewGuid().ToString();
+                    await client.GetContainer(Constant.TEAMModelOS, container).UpsertItemAsync(jointCourseCreateRow);
+                    result.Add(jointCourseCreateRow);
+                }
+            }
+
+            return result;
+        }
+        //判斷某課程名單是否已完成評量
+        //回傳值: 班級ID 或 課程名單ID 列表
+        private static async Task<JointEventGroupCourseGroup> IfExamComplete(CosmosClient client, string examId, string scope, string creatorId, string school, string classId, string groupId)
+        {
+            JointEventGroupCourseGroup result = null;
+            try
+            {
+                if (string.IsNullOrWhiteSpace(classId) && string.IsNullOrWhiteSpace(groupId)) return result;
+                if ((scope.Equals("private") && string.IsNullOrWhiteSpace(creatorId)) || (scope.Equals("school") && string.IsNullOrWhiteSpace(school))) return result;
+                string examClassResultCode = (scope.Equals("school") && !string.IsNullOrWhiteSpace(school)) ? $"ExamClassResult-{school}" : $"ExamClassResult-{creatorId}";
+                StringBuilder stringBuilder = new($"SELECT c.info.id AS infoId, c.info.name AS infoName, c.studentIds, c.studentAnswers FROM c WHERE c.examId = '{examId}' ");
+                if (!string.IsNullOrWhiteSpace(classId))
+                {
+                    stringBuilder.Append($" AND c.info.id = '{classId}' ");
+                }
+                else
+                {
+                    stringBuilder.Append($" AND c.info.id = '{groupId}' ");
+                }
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{examClassResultCode}") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.Content);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            string infoId = obj.GetProperty("infoId").GetString();
+                            string infoName = obj.GetProperty("infoName").GetString();
+                            List<string> studentIds = obj.GetProperty("studentIds").ToObject<List<string>>();
+                            List<List<string>> studentAnswers = obj.GetProperty("studentAnswers").ToObject<List<List<string>>>();
+                            bool hasAnswer = false;
+                            foreach (List<string> studentAnswer in studentAnswers)
+                            {
+                                if (studentAnswer.Count > 0)
+                                {
+                                    hasAnswer = true;
+                                    break;
+                                }
+                            }
+                            if (hasAnswer)
+                            {
+                                result = new JointEventGroupBase.JointEventGroupCourseGroup() { id = infoId, name = infoName };
+                            }
+                        }
+                    }
+                }
+                return result;
+            }
+            catch (Exception e)
+            {
+                return result;
+            }
+
+        }
+
+        /// <summary>
+        /// 計算決賽通過的老師課程名單用中間model
+        /// </summary>
+        public class JointEventGroupPassDto
+        {
+            public string creatorId { get; set; }
+            public string courseId { get; set; }
+            public string groupId { get; set; }
+            public bool pass { get; set; }
+            public List<string> jointExamId { get; set; } = new(); //已完成的活動評量ID
+
+        }
     }
     }
 }
 }

+ 66 - 151
TEAMModelOS/Controllers/Teacher/JointEventController.cs

@@ -22,7 +22,6 @@ using System.Linq;
 using TEAMModelOS.SDK.Models.Service;
 using TEAMModelOS.SDK.Models.Service;
 using Azure.Messaging.ServiceBus;
 using Azure.Messaging.ServiceBus;
 using Azure.Storage.Sas;
 using Azure.Storage.Sas;
-using Microsoft.Azure.Cosmos.Linq;
 
 
 namespace TEAMModelOS.Controllers.Common
 namespace TEAMModelOS.Controllers.Common
 {
 {
@@ -657,8 +656,8 @@ namespace TEAMModelOS.Controllers.Common
                 {
                 {
                     foreach (var info in jointExams)
                     foreach (var info in jointExams)
                     {
                     {
-                        //生成所有報名教師個人評量
-                        await JointService.GenerateExamFromJointExamAsync(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, info);
+                        //生成該報名教師的統測活動個人評量
+                        await JointService.GenerateExamFromJointExamAsync(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, info, creatorId);
                     }
                     }
                 }
                 }
 
 
@@ -841,7 +840,9 @@ namespace TEAMModelOS.Controllers.Common
                     }
                     }
                 }
                 }
                 //取得老師報名的JointCourse
                 //取得老師報名的JointCourse
-                List<JointEventGroupDb> jointEventGroup = new List<JointEventGroupDb>(); //個人課程
+                List<JointEventGroupDb> jointEventGroup = new List<JointEventGroupDb>(); //個人課程 ※報名、決賽 一起取,最後整理時再分
+                Dictionary<string, Dictionary<string,string>> courseToExamIdRegularDic = new Dictionary<string, Dictionary<string, string>>(); //活動評量ID => 課程ID、個人評量ID對應表 (報名)
+                Dictionary<string, Dictionary<string, string>> courseToExamIdCustomDic = new Dictionary<string, Dictionary<string, string>>(); //活動評量ID => 課程ID、個人評量ID對應表 (決賽)
                 if (jointExamIdCollectorsPrivate.Count > 0)
                 if (jointExamIdCollectorsPrivate.Count > 0)
                 {
                 {
                     StringBuilder stringBuilderEventGroup = new($"SELECT * FROM c ");
                     StringBuilder stringBuilderEventGroup = new($"SELECT * FROM c ");
@@ -866,8 +867,38 @@ namespace TEAMModelOS.Controllers.Common
                                 foreach(var course in jointEventGroupRow.courseLists)
                                 foreach(var course in jointEventGroupRow.courseLists)
                                 {
                                 {
                                     if(jointEventGroupRow.scope.Equals("private") && string.IsNullOrWhiteSpace(course.subjectId) && !string.IsNullOrWhiteSpace(course.courseId)) course.subjectId = course.courseId;
                                     if(jointEventGroupRow.scope.Equals("private") && string.IsNullOrWhiteSpace(course.subjectId) && !string.IsNullOrWhiteSpace(course.courseId)) course.subjectId = course.courseId;
-                                    ExamInfo examRelated = exams.Where(e => e.subjects.Select(s => s.id).ToList().Contains(course.courseId)).FirstOrDefault();
-                                    if(examRelated != null) course.examId = examRelated.id;
+                                    
+                                    //課程ID => 活動ID、個人評量ID對應表製作
+                                    List<ExamInfo> examByCreator = exams.Where(e => e.subjects.Select(s => s.id).ToList().Contains(course.courseId) && e.creatorId.Equals(jointEventGroupRow.creatorId)).ToList();
+                                    if(jointEventGroupRow.type.Equals("custom")) //決賽
+                                    {
+                                        foreach (ExamInfo exam in examByCreator)
+                                        {
+                                            if(!courseToExamIdCustomDic.ContainsKey(exam.jointExamId))
+                                            {
+                                                courseToExamIdCustomDic.Add(exam.jointExamId, new Dictionary<string, string>());
+                                            }
+                                            if (!courseToExamIdCustomDic[exam.jointExamId].ContainsKey(course.courseId))
+                                            {
+                                                courseToExamIdCustomDic[exam.jointExamId].Add(course.courseId, exam.id);
+                                            }
+                                        }
+                                    }
+                                    else //報名
+                                    {
+
+                                        foreach (ExamInfo exam in examByCreator)
+                                        {
+                                            if (!courseToExamIdRegularDic.ContainsKey(exam.jointExamId))
+                                            {
+                                                courseToExamIdRegularDic.Add(exam.jointExamId, new Dictionary<string, string>());
+                                            }
+                                            if (!courseToExamIdRegularDic[exam.jointExamId].ContainsKey(course.courseId))
+                                            {
+                                                courseToExamIdRegularDic[exam.jointExamId].Add(course.courseId, exam.id);
+                                            }
+                                        }
+                                    }
                                 }
                                 }
                                 jointEventGroup.Add(jointEventGroupRow);
                                 jointEventGroup.Add(jointEventGroupRow);
                             }
                             }
@@ -880,7 +911,6 @@ namespace TEAMModelOS.Controllers.Common
 
 
                 }
                 }
                 
                 
-
                 //整理 jointExamInfo
                 //整理 jointExamInfo
                 if (jointExamInfo.Count > 0)
                 if (jointExamInfo.Count > 0)
                 {
                 {
@@ -905,9 +935,18 @@ namespace TEAMModelOS.Controllers.Common
                             {
                             {
                                 JointEventGroupBase stuListRow = new JointEventGroupBase();
                                 JointEventGroupBase stuListRow = new JointEventGroupBase();
                                 stuListRow.scope = "private";
                                 stuListRow.scope = "private";
+                                stuListRow.type = stu.type;
                                 stuListRow.creatorId = stu.creatorId;
                                 stuListRow.creatorId = stu.creatorId;
                                 stuListRow.creatorName = stu.creatorName;
                                 stuListRow.creatorName = stu.creatorName;
-                                stuListRow.courseLists = stu.courseLists;
+                                stuListRow.creatorEmail = stu.creatorEmail;
+                                stuListRow.schoolId = stu.schoolId;
+                                stuListRow.schoolName = stu.schoolName;
+                                stuListRow.courseLists = Newtonsoft.Json.JsonConvert.DeserializeObject<List<JointEventGroupBase.JointEventGroupCourse>>(Newtonsoft.Json.JsonConvert.SerializeObject(stu.courseLists));
+                                Dictionary<string, Dictionary<string, string>> courseToExamIdDic = (stu.type.Equals("custom")) ? courseToExamIdCustomDic : courseToExamIdRegularDic;
+                                foreach (JointEventGroupBase.JointEventGroupCourse course in stuListRow.courseLists)
+                                {
+                                    course.examId = (courseToExamIdDic.ContainsKey(jointExam.id) && courseToExamIdDic[jointExam.id].ContainsKey(course.courseId)) ? courseToExamIdDic[jointExam.id][course.courseId] : null;
+                                }
                                 jointExam.stuLists.Add(stuListRow);
                                 jointExam.stuLists.Add(stuListRow);
                             }
                             }
                         }
                         }
@@ -1180,128 +1219,36 @@ namespace TEAMModelOS.Controllers.Common
             return result;
             return result;
         }
         }
 
 
-        //以JointSchedule為單位,判斷班級/課程名單是否完成 ※回傳已完成的courseId、groupId
-        private async Task<List<string>> IfJointScheduleComplete(string jointEventId, string jointGroupId, string jointScheduleId, string scope, string creatorId, string school, List<string> classIds, List<string> groupListIds)
+
+        [ProducesDefaultResponseType]
+#if !DEBUG
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher")]
+#endif
+        [HttpPost("exam/create-final-teachercourse")]
+        public async Task<IActionResult> createFinalJointTeacherCourse(JsonElement request)
         {
         {
+            List<JointEventGroupDb> result = new List<JointEventGroupDb>();
             var client = _azureCosmos.GetCosmosClient();
             var client = _azureCosmos.GetCosmosClient();
-            List<string> result = new List<string>();
-            //0. 取得jointEvent、JointEventSchedule
+            string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
+            string jointScheduleId = (request.TryGetProperty("jointScheduleId", out JsonElement _jointScheduleId)) ? _jointScheduleId.ToString() : string.Empty; //要計算的行程
+            string scope = "private";
             JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
             JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
             if (jointEvent == null)
             if (jointEvent == null)
             {
             {
-                return result;
-            }
-            JointEventSchedule jointEventSchedule = jointEvent.schedule.Where(s => s.id.Equals(jointScheduleId)).FirstOrDefault();
-            if(jointEventSchedule == null)
-            {
-                return result;
-            }
-            //1. 用jointEventId、jointGroupId 取得所有老師的 班級/課程名單
-            List<JointEventGroupDb> jointEventCourse = new List<JointEventGroupDb>();
-            StringBuilder stringBuilderJointCourse = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' ");
-            string container = (scope.Equals("school")) ? Constant.School : Constant.Teacher;
-            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilderJointCourse.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
-            {
-                using var json = await JsonDocument.ParseAsync(item.Content);
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    {
-                        jointEventCourse.Add(obj.ToObject<JointEventGroupDb>());
-                    }
-                }
-            }
-            //2. 取得本Schedule的所有JointExam
-            List<string> jointExamIdList = new List<string>();
-            List<JointExam> jointEventExam = new List<JointExam>();
-            StringBuilder stringBuilderJointExam = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.jointScheduleId = '{jointScheduleId}' ");
-            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilderJointExam.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointExam") }))
-            {
-                using var json = await JsonDocument.ParseAsync(item.Content);
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    {
-                        JointExam jointExamRow = obj.ToObject<JointExam>();
-                        jointEventExam.Add(jointExamRow);
-                        if (!jointExamIdList.Contains(jointExamRow.id))
-                        {
-                            jointExamIdList.Add(jointExamRow.id);
-                        }
-                    }
-                }
-            }
-            //3. 取得所有JointExam關聯的Exam
-            List<ExamInfo> exam = new List<ExamInfo>();
-            StringBuilder stringBuilderExam = new($"SELECT * FROM c WHERE c.pk = 'Exam' AND ARRAY_CONTAINS({JsonSerializer.Serialize(jointExamIdList)}, c.jointExamId) ");
-            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilderExam.ToString(), requestOptions: null ))
-            {
-                using var json = await JsonDocument.ParseAsync(item.Content);
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    {
-                        exam.Add(obj.ToObject<ExamInfo>());
-                    }
-                }
+                return BadRequest();
             }
             }
-            //4. 用 IfJointExamComplete 列出完成評量的 班級/課程名單
-
-
-            //5. 回傳值
-
-            return result;
-        }
-
-        //取得某班級/課程名單是否已完成評量
-        //回傳值: 班級ID 或 課程名單ID 列表
-        private async Task<List<string>> IfJointExamComplete(string examId, string scope, string creatorId, string school, List<string> classIds, List<string> groupListIds)
-        {
-            List<string> result = new List<string>();
-            try
+            List<string> jointGroupIds = jointEvent.groups.Select(g => g.id).ToList();
+            if(jointGroupIds.Count > 0)
             {
             {
-                if (classIds.Count.Equals(0) && groupListIds.Count.Equals(0)) return result;
-                var client = _azureCosmos.GetCosmosClient();
-                string examClassResultCode = (scope.Equals("school") && !string.IsNullOrWhiteSpace(school)) ? $"ExamClassResult-{school}" : $"ExamClassResult-{creatorId}";
-                StringBuilder stringBuilder = new($"SELECT c.info.id AS infoId, c.studentIds, c.studentAnswers FROM c WHERE c.examId = '{examId}'  ");
-                if(classIds.Count > 0)
-                {
-                    stringBuilder.Append($" AND ARRAY_CONTAINS({JsonSerializer.Serialize(classIds)}, c.info.id) ");
-                }
-                else
-                {
-                    stringBuilder.Append($" AND ARRAY_CONTAINS({JsonSerializer.Serialize(groupListIds)}, c.info.id) ");
-                }
-                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{examClassResultCode}") })) 
+                foreach(string jointGroupId in jointGroupIds)
                 {
                 {
-                    using var json = await JsonDocument.ParseAsync(item.Content);
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                    {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        {
-                            string infoId = obj.GetProperty("infoId").GetString();
-                            List<string> studentIds = obj.GetProperty("studentIds").ToObject<List<string>>();
-                            List<List<string>> studentAnswers = obj.GetProperty("studentIds").ToObject<List<List<string>>>();
-                            bool hasAnswer = false;
-                            foreach(List<string> studentAnswer in studentAnswers)
-                            {
-                                if (studentAnswer.Count > 0) hasAnswer = true; break;
-                            }
-                            if(hasAnswer)
-                            {
-                                result.Add(infoId);
-                            }
-                        }
-                    }
+                    List<JointEventGroupDb> addResult = await JointService.CreatePassJointCourseBySchedule(client, jointEventId, jointGroupId, jointScheduleId, scope);
+                    result = result.Union(addResult).ToList();
                 }
                 }
-                return result;
             }
             }
-            catch (Exception e)
-            {
 
 
-                return result;
-            }
-            
+            return Ok(result);
         }
         }
 
 
         public class JointExamIdCollector
         public class JointExamIdCollector
@@ -1348,37 +1295,5 @@ namespace TEAMModelOS.Controllers.Common
                 public List<object> schedule { get; set; } = new List<object>(); //已完成的行程
                 public List<object> schedule { get; set; } = new List<object>(); //已完成的行程
             }
             }
         }
         }
-
-        //以JointEvent.schedule為資訊新建更新JointExam
-        //public async Task<ItemResponse<JointExam>> upsertJointExamBySchedule(string jointEventId, string creatorId, List<JointEventTGroupBase> groupLists, List<PaperSimple> papers, JointEvent.JointEventSchedule scheduleRow)
-        //{
-        //    var client = _azureCosmos.GetCosmosClient();
-        //    JointExam jointExam = new JointExam();
-        //    //新建
-        //    if (string.IsNullOrWhiteSpace(scheduleRow.id))
-        //    {
-        //        jointExam.id = Guid.NewGuid().ToString();
-        //        jointExam.pk = "JointEvent";
-        //        jointExam.code = "JointEvent";
-        //        jointExam.scope = "private";
-        //        jointExam.jointEventId = jointEventId;
-        //        jointExam.creatorId = creatorId;
-        //    }
-        //    //更新
-        //    else
-        //    {
-        //        jointExam = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointExam>(scheduleRow.id, new PartitionKey("JointExam"));
-        //        if (jointExam == null)
-        //        {
-        //            return null;
-        //        }
-        //    }
-        //    jointExam.groupLists = groupLists;
-        //    jointExam.papers = papers;
-        //    jointExam.startTime = scheduleRow.startTime;
-        //    jointExam.endTime = scheduleRow.endTime;
-
-        //    return await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<JointExam>(jointExam, new PartitionKey("JointExam"));
-        //} 
     }
     }
 }
 }