HiTeachController.cs 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using Microsoft.AspNetCore.Authorization;
  5. using Microsoft.AspNetCore.Http;
  6. using Microsoft.AspNetCore.Mvc;
  7. using Microsoft.Extensions.Options;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IdentityModel.Tokens.Jwt;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Text.Json;
  15. using System.Threading.Tasks;
  16. using TEAMModelOS.Models;
  17. using TEAMModelOS.Filter;
  18. using TEAMModelOS.SDK.DI;
  19. using TEAMModelOS.SDK.Extension;
  20. using Microsoft.Azure.Cosmos.Table;
  21. using TEAMModelOS.SDK.Models;
  22. using System.Dynamic;
  23. using HTEXLib.COMM.Helpers;
  24. using StackExchange.Redis;
  25. namespace TEAMModelOS.Controllers.Client
  26. {
  27. [ProducesResponseType(StatusCodes.Status200OK)]
  28. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  29. //[Authorize(Roles = "HiTeach")]
  30. [Route("hiteach")]
  31. [ApiController]
  32. public class HiTeachController : ControllerBase
  33. {
  34. private readonly AzureStorageFactory _azureStorage;
  35. private readonly AzureRedisFactory _azureRedis;
  36. private readonly AzureCosmosFactory _azureCosmos;
  37. private readonly DingDing _dingDing;
  38. private readonly Option _option;
  39. private readonly SnowflakeId _snowflakeId;
  40. public HiTeachController(
  41. AzureStorageFactory azureStorage,
  42. AzureRedisFactory azureRedis,
  43. AzureCosmosFactory azureCosmos,
  44. DingDing dingDing,
  45. SnowflakeId snowflakeId,
  46. IOptionsSnapshot<Option> option)
  47. {
  48. _azureStorage = azureStorage;
  49. _azureRedis = azureRedis;
  50. _azureCosmos = azureCosmos;
  51. _dingDing = dingDing;
  52. _snowflakeId = snowflakeId;
  53. _option = option?.Value;
  54. }
  55. [ProducesDefaultResponseType]
  56. [HttpPost("get-teacher-info")]
  57. public async Task<IActionResult> GetTeacherInfo()
  58. {
  59. //Debug
  60. //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
  61. try
  62. {
  63. string id_token = HttpContext.GetXAuth("IdToken");
  64. if(string.IsNullOrEmpty(id_token)) return BadRequest();
  65. var jwt = new JwtSecurityToken(id_token);
  66. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  67. var id = jwt.Payload.Sub;
  68. jwt.Payload.TryGetValue("name", out object name);
  69. jwt.Payload.TryGetValue("picture", out object picture);
  70. List<object> schools = new List<object>();
  71. string defaultschool = null;
  72. //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  73. var client = _azureCosmos.GetCosmosClient();
  74. var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  75. if (response.Status == 200)
  76. {
  77. var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
  78. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  79. {
  80. foreach (var obj in value.EnumerateArray())
  81. {
  82. string statusNow = obj.GetProperty("status").ToString();
  83. if(statusNow.Equals("join")) //成為老師的才放入
  84. {
  85. dynamic schoolExtobj = new ExpandoObject();
  86. var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  87. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  88. schoolExtobj.schoolId = obj.GetProperty("schoolId");
  89. schoolExtobj.name = school.RootElement.GetProperty("name");
  90. schoolExtobj.status = obj.GetProperty("status");
  91. schoolExtobj.picture = school.RootElement.GetProperty("picture");
  92. schools.Add(schoolExtobj);
  93. }
  94. }
  95. }
  96. //預設學校ID
  97. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
  98. {
  99. defaultschool = valueD.ToString();
  100. }
  101. }
  102. else
  103. {
  104. //如果沒有,則初始化Teacher基本資料到Cosmos
  105. using var stream = new MemoryStream();
  106. using var writer = new Utf8JsonWriter(stream); //new JsonWriterOptions() { Indented = true }
  107. writer.WriteStartObject();
  108. writer.WriteString("code", "Base");
  109. writer.WriteString("id", id);
  110. writer.WriteString("name", name?.ToString());
  111. writer.WriteString("picture", picture?.ToString());
  112. writer.WriteStartArray("schools");
  113. writer.WriteEndArray();
  114. writer.WriteEndObject();
  115. writer.Flush();
  116. //Debug
  117. //string teacher = Encoding.UTF8.GetString(stream.ToArray());
  118. response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
  119. }
  120. //老師個人課程清單
  121. List<object> courses = new List<object>();
  122. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.schedule, c.scope FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  123. {
  124. var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
  125. if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  126. {
  127. foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
  128. {
  129. dynamic courseExtobj = new ExpandoObject();
  130. courseExtobj.id = Convert.ToString(obj.GetProperty("id"));
  131. courseExtobj.name = Convert.ToString(obj.GetProperty("name"));
  132. courseExtobj.scope = Convert.ToString(obj.GetProperty("scope"));
  133. List<object> classes = new List<object>();
  134. if(obj.TryGetProperty("schedule", out JsonElement schedule))
  135. {
  136. foreach (var scheduleobj in schedule.EnumerateArray())
  137. {
  138. dynamic classExtobj = new ExpandoObject();
  139. classExtobj.id = null;
  140. classExtobj.code = null;
  141. classExtobj.teacher = null;
  142. if (scheduleobj.TryGetProperty("teacherId", out JsonElement teacherId) && !string.IsNullOrWhiteSpace(Convert.ToString(teacherId)))
  143. {
  144. await foreach (var teaitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name FROM c WHERE c.id = '{teacherId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  145. {
  146. var jsontea = await JsonDocument.ParseAsync(teaitem.ContentStream);
  147. foreach (var teaobj in jsontea.RootElement.GetProperty("Documents").EnumerateArray())
  148. {
  149. classExtobj.teacher = new ExpandoObject();
  150. classExtobj.teacher = teaobj.ToObject<object>();
  151. }
  152. }
  153. }
  154. classExtobj.scope = "private";
  155. int stuCount = 0;
  156. string stuListId = Convert.ToString(scheduleobj.GetProperty("stulist"));
  157. classExtobj.stuListId = stuListId;
  158. await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, ARRAY_LENGTH(c.students) as stucnt, c.tmids FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("StuList") }))
  159. {
  160. //取得班級學生數
  161. List<string> tmidList = new List<string>();
  162. var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  163. foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  164. {
  165. classExtobj.name = stuobj.GetProperty("name");
  166. stuCount += stuobj.GetProperty("stucnt").GetInt32();
  167. tmidList = stuobj.GetProperty("tmids").ToObject<List<string>>();
  168. }
  169. //取得掃碼學生數
  170. var querytmct = $"SELECT Count(1) AS tmidCount FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(tmidList)}, c.id)";
  171. await foreach (var itemtmct in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: querytmct, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  172. {
  173. var jsontmct = await JsonDocument.ParseAsync(itemtmct.ContentStream);
  174. foreach (var objstc in jsontmct.RootElement.GetProperty("Documents").EnumerateArray())
  175. {
  176. stuCount += objstc.GetProperty("tmidCount").GetInt32();
  177. }
  178. }
  179. }
  180. classExtobj.stuCnt = stuCount;
  181. classExtobj.grpCnt = 0;
  182. classExtobj.gradeId = null;
  183. classExtobj.year = 0;
  184. classes.Add(classExtobj);
  185. }
  186. }
  187. courseExtobj.classes = classes;
  188. courses.Add(courseExtobj);
  189. }
  190. }
  191. }
  192. //取得老師個人評測
  193. List<object> exams = new List<object>();
  194. //取得評測ID List (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中))
  195. List<string> examIdList = new List<string>();
  196. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c WHERE c.source = '1' AND c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  197. {
  198. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  199. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  200. {
  201. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  202. {
  203. examIdList.Add(obj.GetProperty("id").GetString());
  204. }
  205. }
  206. }
  207. //取得有作答的評測班級
  208. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  209. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { }))
  210. {
  211. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  212. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  213. {
  214. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  215. {
  216. string examId = obj.GetProperty("examId").ToString();
  217. string classId = obj.GetProperty("classId").ToString();
  218. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  219. {
  220. examClassFinDic[examId].Add(classId);
  221. }
  222. else
  223. {
  224. List<string> classIdList = new List<string>();
  225. classIdList.Add(classId);
  226. examClassFinDic.Add(examId, classIdList);
  227. }
  228. }
  229. }
  230. }
  231. //取得評測資料
  232. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.createTime, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope, c.owner, c.period, c.grades, c.subjects, c.classes, c.stuLists, ARRAY(SELECT p.id, p.code, p.name, p.blob, p.scope FROM p IN c.papers) AS papers FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.id)", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  233. {
  234. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  235. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  236. {
  237. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  238. {
  239. dynamic examExtobj = new ExpandoObject();
  240. string examId = obj.GetProperty("id").GetString();
  241. examExtobj.code = obj.GetProperty("code");
  242. examExtobj.id = examId;
  243. examExtobj.name = obj.GetProperty("name");
  244. examExtobj.createTime = obj.GetProperty("createTime");
  245. examExtobj.startTime = obj.GetProperty("startTime");
  246. examExtobj.endTime = obj.GetProperty("endTime");
  247. examExtobj.year = obj.GetProperty("year");
  248. examExtobj.source = obj.GetProperty("source");
  249. examExtobj.type = obj.GetProperty("type");
  250. examExtobj.progress = obj.GetProperty("progress");
  251. examExtobj.stuCount = obj.GetProperty("stuCount");
  252. examExtobj.scope = obj.GetProperty("scope");
  253. examExtobj.owner = obj.GetProperty("owner");
  254. examExtobj.period = obj.GetProperty("period");
  255. examExtobj.grades = obj.GetProperty("grades");
  256. examExtobj.subjects = obj.GetProperty("subjects");
  257. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  258. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  259. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  260. examExtobj.papers = obj.GetProperty("papers");
  261. //examExtobj = obj.ToObject<object>();
  262. exams.Add(examExtobj);
  263. }
  264. }
  265. }
  266. //取得Teacher Blob 容器位置及SAS
  267. var container = _azureStorage.GetBlobContainerClient(id);
  268. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  269. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  270. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Read);
  271. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write);
  272. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, schools, defaultschool, courses, exams });
  273. }
  274. catch (Exception ex)
  275. {
  276. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  277. return BadRequest();
  278. }
  279. }
  280. [ProducesDefaultResponseType]
  281. [HttpPost("get-school-info")]
  282. public async Task<IActionResult> GetSchoolInfo(JsonElement requert)
  283. {
  284. try
  285. {
  286. string id_token = HttpContext.GetXAuth("IdToken");
  287. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  288. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  289. var jwt = new JwtSecurityToken(id_token);
  290. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  291. var id = jwt.Payload.Sub;
  292. var client = _azureCosmos.GetCosmosClient();
  293. //取得學校學段、年級、科目、考試類型
  294. List<object> periods = new List<object>();
  295. List<object> grades = new List<object>();
  296. List<object> subjects = new List<object>();
  297. List<object> examTypes = new List<object>();
  298. var responsesch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
  299. if (responsesch.Status == 200)
  300. {
  301. var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
  302. if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
  303. {
  304. foreach (var periodinfo in periodJobj.EnumerateArray())
  305. {
  306. dynamic periodExtobj = new ExpandoObject();
  307. periodExtobj.id = periodinfo.GetProperty("id");
  308. periodExtobj.name = periodinfo.GetProperty("name");
  309. periods.Add(periodExtobj);
  310. if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
  311. {
  312. int gradeIndex = 0;
  313. foreach (var gradeName in gradesJobj.EnumerateArray()) //2021-06-09 [式樣]grades改為array(string)
  314. {
  315. dynamic gradeExtobj = new ExpandoObject();
  316. gradeExtobj.id = gradeIndex.ToString();
  317. gradeExtobj.name = gradeName;
  318. gradeExtobj.periodId = periodinfo.GetProperty("id");
  319. grades.Add(gradeExtobj);
  320. gradeIndex++;
  321. }
  322. }
  323. if (periodinfo.TryGetProperty("subjects", out JsonElement subjectsJobj))
  324. {
  325. foreach (var subjectinfo in subjectsJobj.EnumerateArray())
  326. {
  327. dynamic subjectExtobj = new ExpandoObject();
  328. subjectExtobj.id = subjectinfo.GetProperty("id");
  329. subjectExtobj.name = subjectinfo.GetProperty("name");
  330. subjectExtobj.periodId = periodinfo.GetProperty("id");
  331. subjects.Add(subjectExtobj);
  332. }
  333. }
  334. if (periodinfo.TryGetProperty("analysis", out JsonElement periodanalysisJobj))
  335. {
  336. if (periodanalysisJobj.TryGetProperty("type", out JsonElement examTypeJobj))
  337. {
  338. foreach (var examType in examTypeJobj.EnumerateArray())
  339. {
  340. examTypes.Add(examType.ToObject<object>());
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. else //無此學校資料
  348. {
  349. return BadRequest();
  350. }
  351. //該老師排定的學校課程
  352. List<object> courses = new List<object>();
  353. List<string> classIds = new List<string>(); //老師排定的班級ID列表 篩選校園評測用
  354. var query = $"SELECT c.id, c.name, c.scope, c.subject, schedule.classId AS scheduleClassId, schedule.teacher AS scheduleTeacher, schedule.stulist AS scheduleStulist, schedule.time AS scheduleTime, schedule.notice AS scheduleNotice FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
  355. //var query = $"SELECT c.id, c.name, c.teacher, cc.course, c.scope FROM c JOIN cc IN c.courses JOIN cct IN cc.teachers WHERE cct.id = '{id}'";
  356. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  357. {
  358. var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
  359. if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  360. {
  361. foreach (var obj in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
  362. {
  363. //班級
  364. string classIdNow = string.Empty;
  365. if(obj.TryGetProperty("scheduleClassId", out JsonElement scheduleClassId))
  366. {
  367. classIdNow = Convert.ToString(scheduleClassId);
  368. }
  369. dynamic classExtobj = new ExpandoObject();
  370. classExtobj.id = null;
  371. classExtobj.code = null;
  372. classExtobj.name = null;
  373. classExtobj.scope = null;
  374. classExtobj.gradeId = null;
  375. classExtobj.year =0;
  376. classExtobj.teacher = null;
  377. classExtobj.stuListId = null;
  378. classExtobj.stuCnt = 0;
  379. classExtobj.grpCnt = 0;
  380. if (!string.IsNullOrWhiteSpace(classIdNow))
  381. {
  382. var querycl = $"SELECT c.code, c.id, c.name, c.teacher, c.gradeId, c.year FROM c WHERE c.id = '{classIdNow}'";
  383. await foreach (var itemcl in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: querycl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  384. {
  385. var jsoncl = await JsonDocument.ParseAsync(itemcl.ContentStream);
  386. foreach (var objcl in jsoncl.RootElement.GetProperty("Documents").EnumerateArray())
  387. {
  388. classIds.Add(Convert.ToString(objcl.GetProperty("id")));
  389. classExtobj.id = Convert.ToString(objcl.GetProperty("id"));
  390. classExtobj.code = Convert.ToString(objcl.GetProperty("code"));
  391. classExtobj.name = Convert.ToString(objcl.GetProperty("name"));
  392. //classExtobj.scope = Convert.ToString(objcl.GetProperty("scope")); //2021-07-16 廢除class.scope
  393. classExtobj.gradeId = (objcl.TryGetProperty("gradeId", out JsonElement gradeIdJson)) ? Convert.ToString(gradeIdJson) : null; //2021-06-01 [式樣]class去除gradeId,改以year代替
  394. classExtobj.year = (objcl.TryGetProperty("year", out JsonElement yearJson)) ? yearJson.GetInt32() : 0;
  395. classExtobj.teacher = objcl.GetProperty("teacher");
  396. classExtobj.stuCnt = 0;
  397. classExtobj.grpCnt = 0;
  398. var queryclstc = $"SELECT Count(1) AS stuCnt FROM c WHERE c.classId = '{classIdNow}'";
  399. await foreach (var itemclstc in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclstc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  400. {
  401. var jsonclstc = await JsonDocument.ParseAsync(itemclstc.ContentStream);
  402. foreach (var objstc in jsonclstc.RootElement.GetProperty("Documents").EnumerateArray())
  403. {
  404. classExtobj.stuCnt = objstc.GetProperty("stuCnt").GetUInt32();
  405. }
  406. }
  407. var queryclgp = $"SELECT c.groupId FROM c WHERE c.classId = '{classIdNow}' AND IS_NULL(c.groupId)=false GROUP BY c.groupId";
  408. await foreach (var itemgp in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclgp, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  409. {
  410. var jsongp = await JsonDocument.ParseAsync(itemgp.ContentStream);
  411. if (jsongp.RootElement.TryGetProperty("_count", out JsonElement gpcount) && gpcount.GetUInt32() > 0)
  412. {
  413. classExtobj.grpCnt = gpcount.GetUInt32();
  414. }
  415. }
  416. }
  417. }
  418. }
  419. var stuListId = obj.GetProperty("scheduleStulist");
  420. if (!stuListId.ValueKind.Equals(JsonValueKind.Null))
  421. {
  422. classExtobj.stuListId = Convert.ToString(stuListId);
  423. await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT ARRAY_LENGTH(c.students) as stucnt FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"StuList-{school_code}") }))
  424. {
  425. var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  426. foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  427. {
  428. classExtobj.stuCnt = stuobj.GetProperty("stucnt").GetInt32();
  429. }
  430. }
  431. }
  432. //課程
  433. //var scheduleTeacherInfo = obj.GetProperty("scheduleTeacher");
  434. string courseIdNow = obj.GetProperty("id").ToString();
  435. var courseExist = courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  436. if (courseExist == null)
  437. {
  438. dynamic courseExtobj = new ExpandoObject();
  439. courseExtobj.id = courseIdNow;
  440. courseExtobj.name = obj.GetProperty("name").ToString();
  441. courseExtobj.scope = obj.GetProperty("scope").ToString();
  442. courseExtobj.classes = new List<object>();
  443. courseExtobj.subject = obj.GetProperty("subject");
  444. //classExtobj.teacher = scheduleTeacherInfo;
  445. courseExtobj.classes.Add(classExtobj);
  446. courses.Add(courseExtobj);
  447. }
  448. else
  449. {
  450. //classExtobj.teacher = scheduleTeacherInfo;
  451. courseExist.classes.Add(classExtobj);
  452. }
  453. }
  454. }
  455. }
  456. //取得校園評測 (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中)) && 排定的學校課程班級
  457. List<object> exams = new List<object>();
  458. string classSqlString = "";
  459. if (classIds.Count > 0)
  460. {
  461. foreach (string classId in classIds)
  462. {
  463. if (!string.IsNullOrWhiteSpace(classSqlString))
  464. {
  465. classSqlString += " OR ";
  466. }
  467. classSqlString += $"ARRAY_CONTAINS(c.classes, '{classId}')";
  468. }
  469. }
  470. else //無分配任何課程教室,校園評測不取
  471. {
  472. classSqlString += "c.id = '0'";
  473. }
  474. classSqlString = $"AND ({classSqlString})";
  475. //取得評測ID List
  476. List<string> examIdList = new List<string>();
  477. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c WHERE c.source = '1' AND c.progress = 'going' {classSqlString}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  478. {
  479. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  480. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  481. {
  482. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  483. {
  484. examIdList.Add(obj.GetProperty("id").GetString());
  485. }
  486. }
  487. }
  488. //取得有作答的評測班級
  489. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  490. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school_code}") }))
  491. {
  492. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  493. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  494. {
  495. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  496. {
  497. string examId = obj.GetProperty("examId").ToString();
  498. string classId = obj.GetProperty("classId").ToString();
  499. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  500. {
  501. examClassFinDic[examId].Add(classId);
  502. }
  503. else
  504. {
  505. List<string> classIdList = new List<string>();
  506. classIdList.Add(classId);
  507. examClassFinDic.Add(examId, classIdList);
  508. }
  509. }
  510. }
  511. }
  512. //取得評測資料
  513. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.createTime, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope, c.owner, c.period, c.grades, c.subjects, c.classes, c.stuLists, ARRAY(SELECT p.id, p.code, p.name, p.blob, p.scope FROM p IN c.papers) AS papers FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.id)", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  514. {
  515. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  516. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  517. {
  518. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  519. {
  520. dynamic examExtobj = new ExpandoObject();
  521. string examId = obj.GetProperty("id").GetString();
  522. examExtobj.code = obj.GetProperty("code");
  523. examExtobj.id = examId;
  524. examExtobj.name = obj.GetProperty("name");
  525. examExtobj.createTime = obj.GetProperty("createTime");
  526. examExtobj.startTime = obj.GetProperty("startTime");
  527. examExtobj.endTime = obj.GetProperty("endTime");
  528. examExtobj.year = obj.GetProperty("year");
  529. examExtobj.source = obj.GetProperty("source");
  530. examExtobj.type = obj.GetProperty("type");
  531. examExtobj.progress = obj.GetProperty("progress");
  532. examExtobj.stuCount = obj.GetProperty("stuCount");
  533. examExtobj.scope = obj.GetProperty("scope");
  534. examExtobj.owner = obj.GetProperty("owner");
  535. examExtobj.period = obj.GetProperty("period");
  536. examExtobj.grades = obj.GetProperty("grades");
  537. examExtobj.subjects = obj.GetProperty("subjects");
  538. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  539. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  540. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  541. examExtobj.papers = obj.GetProperty("papers");
  542. //examExtobj = obj.ToObject<object>();
  543. exams.Add(examExtobj);
  544. }
  545. }
  546. }
  547. //取得School Blob 容器位置及SAS
  548. string school_code_blob = school_code.GetString().ToLower();
  549. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列
  550. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
  551. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write); //寫
  552. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, periods, grades, subjects, courses, examTypes, exams });
  553. }
  554. catch (Exception ex)
  555. {
  556. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  557. return BadRequest();
  558. }
  559. }
  560. //取得試卷
  561. [ProducesDefaultResponseType]
  562. [HttpPost("get-paper")]
  563. public async Task<IActionResult> GetPaperList(JsonElement request)
  564. {
  565. try
  566. {
  567. //Header驗證
  568. string id_token = HttpContext.GetXAuth("IdToken");
  569. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  570. var jwt = new JwtSecurityToken(id_token);
  571. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  572. var id = jwt.Payload.Sub;
  573. var client = _azureCosmos.GetCosmosClient();
  574. //參數
  575. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  576. string partitionid = string.Empty;
  577. string container = string.Empty;
  578. if (grant_type.ToString() .Equals("school"))
  579. {
  580. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  581. {
  582. return BadRequest();
  583. }
  584. else
  585. {
  586. partitionid = school_code_json.ToString();
  587. container = "School";
  588. }
  589. }
  590. else
  591. {
  592. partitionid = id.ToString();
  593. container = "Teacher";
  594. }
  595. //SQL文
  596. List<object> papers = new List<object>();
  597. string queryWhere = " WHERE 1=1 ";
  598. string queryOption = string.Empty;
  599. //學段
  600. if (request.TryGetProperty("periodId", out JsonElement periodId) && container .Equals("School") )
  601. {
  602. queryWhere += $" AND c.periodId = '{periodId}'";
  603. }
  604. //年級
  605. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container .Equals("School"))
  606. {
  607. string queryOptionForGrade = string.Empty;
  608. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  609. {
  610. if(!string.IsNullOrWhiteSpace(queryOptionForGrade))
  611. {
  612. queryOptionForGrade += " OR ";
  613. }
  614. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  615. }
  616. queryWhere += $" AND ( {queryOptionForGrade} )";
  617. }
  618. //科目ID
  619. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container .Equals("School"))
  620. {
  621. queryWhere += $" AND c.subjectId = '{subjectId}'";
  622. }
  623. //科目名稱
  624. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  625. {
  626. queryWhere += $" AND c.subjectName = '{subjectName}'";
  627. }
  628. int perpage = 0; //每頁幾條
  629. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  630. int page = 0; //目前第幾頁
  631. if (request.TryGetProperty("page", out JsonElement page_json))
  632. {
  633. if(page_json.GetInt32() > 0)
  634. {
  635. page = page_json.GetInt32() - 1;
  636. }
  637. }
  638. string order = "createTime"; //排序項目
  639. if (request.TryGetProperty("order", out JsonElement order_json))
  640. {
  641. if(order_json.ToString().Equals("useCount"))
  642. {
  643. order = order_json.ToString();
  644. }
  645. }
  646. queryOption += $" Order By c." + order + " DESC ";
  647. if(perpage > 0 )
  648. {
  649. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  650. }
  651. //資料取得
  652. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.name, REPLACE(c.blob, 'index.json', '') AS blob, c.score, c.useCount, ARRAY_LENGTH(c.scoring) AS itemCount, c.createTime From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  653. {
  654. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  655. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  656. {
  657. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  658. {
  659. papers.Add(obj.ToObject<object>());
  660. }
  661. }
  662. }
  663. //總件數
  664. int totalCount = 0;
  665. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  666. {
  667. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  668. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  669. {
  670. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  671. {
  672. totalCount = obj.GetInt32();
  673. }
  674. }
  675. }
  676. return Ok(new { papers, totalCount });
  677. }
  678. catch (Exception ex)
  679. {
  680. return BadRequest();
  681. }
  682. }
  683. //取得試題
  684. [ProducesDefaultResponseType]
  685. [HttpPost("get-item")]
  686. public async Task<IActionResult> GetItemList(JsonElement request)
  687. {
  688. try
  689. {
  690. //Header驗證
  691. string id_token = HttpContext.GetXAuth("IdToken");
  692. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  693. var jwt = new JwtSecurityToken(id_token);
  694. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  695. var id = jwt.Payload.Sub;
  696. var client = _azureCosmos.GetCosmosClient();
  697. //參數
  698. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  699. string partitionid = string.Empty;
  700. string container = string.Empty;
  701. if (grant_type.ToString().Equals("school"))
  702. {
  703. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  704. {
  705. return BadRequest();
  706. }
  707. else
  708. {
  709. partitionid = school_code_json.ToString();
  710. container = "School";
  711. }
  712. }
  713. else
  714. {
  715. partitionid = id.ToString();
  716. container = "Teacher";
  717. }
  718. //SQL文
  719. List<object> items = new List<object>();
  720. string queryWhere = " WHERE 1=1 ";
  721. string queryOption = string.Empty;
  722. //學段
  723. if (request.TryGetProperty("periodId", out JsonElement periodId) && container .Equals("School"))
  724. {
  725. queryWhere += $" AND c.periodId = '{periodId}'";
  726. }
  727. //年級
  728. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container .Equals("School"))
  729. {
  730. string queryOptionForGrade = string.Empty;
  731. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  732. {
  733. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  734. {
  735. queryOptionForGrade += " OR ";
  736. }
  737. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  738. }
  739. queryWhere += $" AND ( {queryOptionForGrade} )";
  740. }
  741. //科目ID
  742. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container .Equals("school"))
  743. {
  744. queryWhere += $" AND c.subjectId = '{subjectId}'";
  745. }
  746. //科目名稱
  747. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  748. {
  749. queryWhere += $" AND c.subjectName = '{subjectName}'";
  750. }
  751. //題型
  752. int dummy = 0;
  753. if (request.TryGetProperty("type", out JsonElement type))
  754. {
  755. queryWhere += $" AND c.type = '{type}'";
  756. }
  757. //難度
  758. dummy = 0;
  759. if (request.TryGetProperty("level", out JsonElement level) && int.TryParse(level.ToString(), out dummy))
  760. {
  761. queryWhere += $" AND c.level = {level}";
  762. }
  763. //層次
  764. if (request.TryGetProperty("field", out JsonElement field) && int.TryParse(field.ToString(), out dummy))
  765. {
  766. queryWhere += $" AND c.field = {field}";
  767. }
  768. int perpage = 0; //每頁幾條
  769. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  770. int page = 0; //目前第幾頁
  771. if (request.TryGetProperty("page", out JsonElement page_json))
  772. {
  773. if (page_json.GetInt32() > 0)
  774. {
  775. page = page_json.GetInt32() - 1;
  776. }
  777. }
  778. string order = "createTime"; //排序項目
  779. if (request.TryGetProperty("order", out JsonElement order_json))
  780. {
  781. if (order_json.ToString() .Equals("useCount"))
  782. {
  783. order = order_json.ToString();
  784. }
  785. }
  786. queryOption += $" Order By c." + order + " DESC ";
  787. if (perpage > 0)
  788. {
  789. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  790. }
  791. //資料取得
  792. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.blob, c.field, c.level, c.type, c.useCount, c.createTime From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  793. {
  794. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  795. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  796. {
  797. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  798. {
  799. items.Add(obj.ToObject<object>());
  800. }
  801. }
  802. }
  803. //總件數
  804. int totalCount = 0;
  805. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  806. {
  807. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  808. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  809. {
  810. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  811. {
  812. totalCount = obj.GetInt32();
  813. }
  814. }
  815. }
  816. return Ok(new { items, totalCount });
  817. }
  818. catch (Exception ex)
  819. {
  820. return BadRequest();
  821. }
  822. }
  823. //取得知識點
  824. [ProducesDefaultResponseType]
  825. [HttpPost("get-knowledge")]
  826. public async Task<IActionResult> GetKnowledgePointList(JsonElement request)
  827. {
  828. var client = _azureCosmos.GetCosmosClient();
  829. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  830. //知識點
  831. List<object> points = new List<object>();
  832. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.subjectId FROM c WHERE c.type = 'point'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
  833. {
  834. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  835. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  836. {
  837. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  838. {
  839. points.Add(obj.ToObject<object>());
  840. }
  841. }
  842. }
  843. return Ok(points);
  844. }
  845. //取得課綱
  846. [ProducesDefaultResponseType]
  847. [HttpPost("get-syllabus")]
  848. public async Task<IActionResult> GetSyllabusList(JsonElement request)
  849. {
  850. var client = _azureCosmos.GetCosmosClient();
  851. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  852. //校本課綱
  853. List<SyllabusRole> syllabus = new List<SyllabusRole>();
  854. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
  855. {
  856. var jsons = await JsonDocument.ParseAsync(item.ContentStream);
  857. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  858. {
  859. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  860. {
  861. SyllabusRole syllabusRole = new SyllabusRole();
  862. syllabusRole.id = obj.GetProperty("id").ToString();
  863. syllabusRole.name = obj.GetProperty("name").ToString();
  864. syllabusRole.period = obj.GetProperty("period");
  865. syllabusRole.grade = obj.GetProperty("grade");
  866. syllabusRole.semester = obj.GetProperty("semester");
  867. syllabusRole.subject = obj.GetProperty("subject");
  868. syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
  869. syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
  870. List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
  871. syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
  872. syllabusList = syllabusList.OrderBy(x => x.order).ToList();
  873. syllabusRole.structure = CreateSyllabusTree(syllabusList);
  874. syllabus.Add(syllabusRole);
  875. }
  876. //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
  877. }
  878. }
  879. return Ok(syllabus);
  880. }
  881. //取得某班級的學生成員
  882. [ProducesDefaultResponseType]
  883. [HttpPost("get-students-list")]
  884. public async Task<IActionResult> GetStudentsList(JsonElement request)
  885. {
  886. string id_token = HttpContext.GetXAuth("IdToken");
  887. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  888. var jwt = new JwtSecurityToken(id_token);
  889. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  890. var id = jwt.Payload.Sub;
  891. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  892. request.TryGetProperty("class_code", out JsonElement class_code);
  893. request.TryGetProperty("stulist_id", out JsonElement stulist_id);
  894. string classId = Convert.ToString(class_code);
  895. string stulist = Convert.ToString(stulist_id);
  896. if(string.IsNullOrWhiteSpace(classId) && string.IsNullOrWhiteSpace(stulist)) return BadRequest();
  897. request.TryGetProperty("school_code", out JsonElement school_code);
  898. if (grant_type.GetString() .Equals("school") && string.IsNullOrWhiteSpace(Convert.ToString(school_code))) return BadRequest();
  899. var client = _azureCosmos.GetCosmosClient();
  900. List<object> students = new List<object>();
  901. string container = (grant_type.GetString() .Equals("school")) ? "School" : "Teacher";
  902. //Case 1 取得stulist成員 (有stulist_id則優先取)
  903. if (grant_type.GetString().Equals("private") && string.IsNullOrWhiteSpace(stulist) && !string.IsNullOrWhiteSpace(classId)) //若private,且classId不為空,stulist為空,有可能HiTeach無法判別是若classId還是stulist
  904. {
  905. stulist = classId;
  906. }
  907. if (!string.IsNullOrWhiteSpace(stulist))
  908. {
  909. string pk = (grant_type.GetString() .Equals("school")) ? $"StuList-{school_code}" : $"StuList";
  910. var querystl = $"SELECT c.students, c.tmids FROM c WHERE c.id = '{stulist}'";
  911. Dictionary<string, List<string>> stuDic = new Dictionary<string, List<string>>();
  912. List<string> tmidList = new List<string>();
  913. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: querystl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
  914. {
  915. var json = await JsonDocument.ParseAsync(item.ContentStream);
  916. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  917. {
  918. foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  919. {
  920. //取得學校班級學生ID表
  921. foreach (var studentObj in stuObj.GetProperty("students").EnumerateArray())
  922. {
  923. string stuCode = studentObj.GetProperty("code").ToString();
  924. string stuId = studentObj.GetProperty("id").ToString();
  925. if (stuDic.ContainsKey(stuCode) && !stuDic[stuCode].Contains(stuId))
  926. {
  927. if (!stuDic[stuCode].Contains(stuId))
  928. {
  929. stuDic[stuCode].Add(stuId);
  930. }
  931. }
  932. else
  933. {
  934. List<string> stuIdList = new List<string>();
  935. stuIdList.Add(stuId);
  936. stuDic.Add(stuCode, stuIdList);
  937. }
  938. }
  939. //取得TMID加入學生ID表
  940. tmidList = stuObj.GetProperty("tmids").ToObject<List<string>>();
  941. }
  942. }
  943. }
  944. //取得學校班級學生資料
  945. if (stuDic.Count > 0)
  946. {
  947. foreach (KeyValuePair<string, List<string>> stuDicRow in stuDic)
  948. {
  949. string stuCode = stuDicRow.Key;
  950. List<string> stuIdList = stuDicRow.Value;
  951. var queryst = $"SELECT c.id, c.name, null AS no, c.schoolId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(stuIdList)}, c.id)";
  952. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryst, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(stuCode) }))
  953. {
  954. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  955. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  956. {
  957. foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  958. {
  959. students.Add(stuObj.ToObject<object>());
  960. }
  961. }
  962. }
  963. }
  964. }
  965. //取得TMID用戶資料
  966. if(tmidList.Count > 0)
  967. {
  968. var querytmct = $"SELECT c.id, c.name, null AS no, null AS schoolId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(tmidList)}, c.id)";
  969. await foreach (var itemtm in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: querytmct, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  970. {
  971. using var json = await JsonDocument.ParseAsync(itemtm.ContentStream);
  972. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  973. {
  974. foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  975. {
  976. students.Add(stuObj.ToObject<object>());
  977. }
  978. }
  979. }
  980. }
  981. }
  982. //Case 2 取得班級固定成員 條件:有school_code、有classId、無stulist
  983. if (!string.IsNullOrWhiteSpace(Convert.ToString(school_code)) && students.Count == 0 && !string.IsNullOrWhiteSpace(classId))
  984. {
  985. var query = $"SELECT c.id, c.name, c.no, c.schoolId, c.groupId, c.groupName FROM c WHERE c.classId = '{classId}'";
  986. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  987. {
  988. using var jsonst = await JsonDocument.ParseAsync(item.ContentStream);
  989. if (jsonst.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  990. {
  991. foreach (var stuObj in jsonst.RootElement.GetProperty("Documents").EnumerateArray())
  992. {
  993. students.Add(stuObj.ToObject<object>());
  994. }
  995. }
  996. }
  997. }
  998. //Case 3 不論學校、個人 有classId,
  999. return Ok(new { students });
  1000. }
  1001. [ProducesDefaultResponseType]
  1002. [HttpPost("start-lesson")]
  1003. public async Task<IActionResult> StartLesson(JsonElement request)
  1004. {
  1005. //取得授課ID
  1006. string lesson_code = (request.TryGetProperty("lesson_id", out JsonElement lesson_id) && !string.IsNullOrWhiteSpace(Convert.ToString(lesson_id))) ? Convert.ToString(lesson_id) : _snowflakeId.NextId().ToString();
  1007. bool get_lesson_id = (string.IsNullOrWhiteSpace(Convert.ToString(lesson_id)) || Convert.ToString(lesson_id) != lesson_code) ? false : true;
  1008. //取得醍摩豆ID
  1009. string teacherId = string.Empty;
  1010. string id_token = HttpContext.GetXAuth("IdToken");
  1011. if (!string.IsNullOrWhiteSpace(id_token))
  1012. {
  1013. var jwt = new JwtSecurityToken(id_token);
  1014. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1015. teacherId = jwt.Payload.Sub;
  1016. }
  1017. if (string.IsNullOrWhiteSpace(teacherId) && get_lesson_id) return BadRequest(); //有授課ID無醍摩豆ID,BadRequest
  1018. //DB操作
  1019. if (!string.IsNullOrWhiteSpace(teacherId))
  1020. {
  1021. string tableName = "TeacherLesson";
  1022. CloudTable table = _azureStorage.GetCloudTableClient().GetTableReference(tableName);
  1023. Lesson lessonEntity = new Lesson(teacherId, lesson_code);
  1024. TableOperation operation = TableOperation.InsertOrReplace(lessonEntity);
  1025. TableResult result = await table.ExecuteAsync(operation);
  1026. }
  1027. return Ok(new { lesson_code });
  1028. }
  1029. //上傳評測結果
  1030. //錯誤代碼:error = 1001 message = "Paper blob copy failure."
  1031. // error = 1002 message = "Student answers blob upload failure."
  1032. [ProducesDefaultResponseType]
  1033. [HttpPost("upd-exam-result")]
  1034. public async Task<IActionResult> UploadExamResult(JsonElement request)
  1035. {
  1036. try
  1037. {
  1038. string message = "";
  1039. int error = 0;
  1040. string id_token = HttpContext.GetXAuth("IdToken");
  1041. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  1042. var jwt = new JwtSecurityToken(id_token);
  1043. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1044. var id = jwt.Payload.Sub;
  1045. if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
  1046. if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
  1047. bool blobUploaded = (request.TryGetProperty("blobUploaded", out JsonElement blobUploadedJson)) ? blobUploadedJson.GetBoolean() : false;
  1048. ExamInfo ExamInfoFromReq = exam.ToObject<ExamInfo>();
  1049. string examId = ExamInfoFromReq.id;
  1050. string excode = ExamInfoFromReq.code;
  1051. ExamInfo dbExamInfo = exam.ToObject<ExamInfo>();
  1052. var queryex = $"SELECT * FROM c WHERE c.id = '{examId}'";
  1053. await foreach (var itemex in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: queryex, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{excode}") }))
  1054. {
  1055. var jsonex = await JsonDocument.ParseAsync(itemex.ContentStream);
  1056. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1057. {
  1058. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  1059. {
  1060. dbExamInfo = obj.ToObject<ExamInfo>();
  1061. }
  1062. }
  1063. }
  1064. if(string.IsNullOrWhiteSpace(dbExamInfo.id))
  1065. {
  1066. dbExamInfo = ExamInfoFromReq;
  1067. }
  1068. //Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
  1069. //ExamInfo內容取得、調整 [2021-7-13 廢除,給予HiTeach學校Blob寫入權限,API不再對Blob做搬運]
  1070. //※規則 owner:"school" => 校園評測 "teacher" => 個人評測
  1071. //※規則 scope:"school" => 校本班級 "private" => 個人班級
  1072. //※規則 BLOB容器: scope:"school" => {學校ID}下 scope:"private" => {個人ID}下
  1073. string blobContainer = (!string.IsNullOrWhiteSpace(dbExamInfo.school) && dbExamInfo.scope .Equals("school")) ? dbExamInfo.school : id; //blob容器
  1074. //dbExamInfo.source = "1"; //評測來源固定為 1:課中評量(不應由API擅自變更)
  1075. //試卷List
  1076. List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
  1077. int paperIndex = 0;
  1078. foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
  1079. {
  1080. string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
  1081. paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
  1082. string subjectId = dbExamInfo.subjects[paperIndex].id;
  1083. recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "subjectId", subjectId }, { "itemcount", paperInfo.point.Count.ToString() } });
  1084. paperIndex++;
  1085. }
  1086. //取得課堂紀錄下的試卷資料(blob)、複製到評測紀錄下
  1087. bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 (測試站)不再對Blob做搬運 永為false]
  1088. //Blob搬運 (待HiTeach完善後刪除)
  1089. if (!blobUploaded)
  1090. {
  1091. foreach (Dictionary<string, string> recordPaperInfoDic in recordPaperInfo)
  1092. {
  1093. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  1094. var blobPrivateContainer = _azureStorage.GetBlobContainerClient(id);
  1095. string sourceBlobPath = recordPaperInfoDic["blob"];
  1096. Azure.Pageable<BlobItem> sourceBlobs = blobPrivateContainer.GetBlobs(prefix: sourceBlobPath);
  1097. string destBlobPath = $"exam/{dbExamInfo.id}/paper/{recordPaperInfoDic["id"]}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{試卷ID}/
  1098. if (targetScope .Equals("school")) //校本
  1099. {
  1100. string schoolId = dbExamInfo.school;
  1101. var blobSchoolContainer = _azureStorage.GetBlobContainerClient(schoolId);
  1102. if (sourceBlobs.Count() > 0)
  1103. {
  1104. foreach (var blob in sourceBlobs)
  1105. {
  1106. var sourceFileBlob = blobPrivateContainer.GetBlobClient(blob.Name);
  1107. if (sourceFileBlob.Exists())
  1108. {
  1109. var sourceFileUri = blobPrivateContainer.GetBlobClient(blob.Name).Uri;
  1110. string fileName = blob.Name.Replace(sourceBlobPath, "");
  1111. string destBlobFilePath = $"{destBlobPath}{fileName}";
  1112. await blobSchoolContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  1113. }
  1114. else
  1115. {
  1116. paperDataCopyErrFlg = true;
  1117. }
  1118. }
  1119. }
  1120. }
  1121. else //私人
  1122. {
  1123. if (sourceBlobs.Count() > 0)
  1124. {
  1125. foreach (var blob in sourceBlobs)
  1126. {
  1127. var sourceFileBlob = blobPrivateContainer.GetBlobClient(blob.Name);
  1128. if (sourceFileBlob.Exists())
  1129. {
  1130. var sourceFileUri = blobPrivateContainer.GetBlobClient(blob.Name).Uri;
  1131. string fileName = blob.Name.Replace(sourceBlobPath, "");
  1132. string destBlobFilePath = $"{destBlobPath}{fileName}";
  1133. await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  1134. }
  1135. else
  1136. {
  1137. paperDataCopyErrFlg = true;
  1138. }
  1139. }
  1140. }
  1141. }
  1142. //替換 Exam.papers.blob
  1143. PaperSimple paperInfoNow = dbExamInfo.papers.Where((PaperSimple p) => p.id == recordPaperInfoDic["id"]).FirstOrDefault();
  1144. string destBlobPathForDocument = (destBlobPath.EndsWith("/")) ? destBlobPath.Remove(destBlobPath.Length - 1, 1) : destBlobPath;
  1145. destBlobPathForDocument = (!destBlobPathForDocument.StartsWith("/")) ? "/" + destBlobPathForDocument : destBlobPathForDocument;
  1146. paperInfoNow.blob = destBlobPathForDocument;
  1147. }
  1148. }
  1149. //ExamClassResult內容調整
  1150. bool studentAnswerCopyErrFlg = false; //學生作答資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 不再對Blob做搬運 永為false]
  1151. //Blob搬運 (待HiTeach完善後刪除)
  1152. if (!blobUploaded)
  1153. {
  1154. List<ExamClassResultStudentAnswerArrayOld> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArrayOld>>();
  1155. foreach (ExamClassResultStudentAnswerArrayOld examClassResultRow in dbExamClassResultList)
  1156. {
  1157. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  1158. ExamClassResult examClassResultUpd = new ExamClassResult();
  1159. string exclcode = examClassResultRow.code;
  1160. string classId = examClassResultRow.info.id;
  1161. string subjectId = examClassResultRow.subjectId;
  1162. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  1163. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  1164. {
  1165. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  1166. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1167. {
  1168. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  1169. {
  1170. examClassResultUpd = obj.ToObject<ExamClassResult>();
  1171. examClassResultUpd.progress = examClassResultRow.progress;
  1172. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1173. examClassResultUpd.studentAnswers = new List<List<string>>();
  1174. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1175. examClassResultUpd.sum = examClassResultRow.sum;
  1176. }
  1177. }
  1178. }
  1179. //無法取得既有ExamClassResult,新建
  1180. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  1181. {
  1182. examClassResultUpd.pk = examClassResultRow.pk;
  1183. examClassResultUpd.code = examClassResultRow.code;
  1184. examClassResultUpd.id = examClassResultRow.id;
  1185. examClassResultUpd.school = examClassResultRow.school;
  1186. examClassResultUpd.examId = examClassResultRow.examId;
  1187. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  1188. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  1189. examClassResultUpd.year = examClassResultRow.year;
  1190. examClassResultUpd.info = examClassResultRow.info;
  1191. examClassResultUpd.progress = examClassResultRow.progress;
  1192. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1193. examClassResultUpd.studentAnswers = new List<List<string>>();
  1194. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1195. examClassResultUpd.scope = examClassResultRow.scope;
  1196. examClassResultUpd.sum = examClassResultRow.sum;
  1197. }
  1198. //examClassResult.studentAnswers 將學生答案上傳blob後轉換內容為blob路徑 //[2021-7-13 不再對Blob做搬運]
  1199. if (examClassResultRow.studentIds != null && examClassResultRow.studentIds.Count > 0 && examClassResultRow.studentAnswersArray != null && examClassResultRow.studentAnswersArray.Count > 0)
  1200. {
  1201. for (int i = 0; i < examClassResultRow.studentAnswersArray.Count; i++)
  1202. {
  1203. string studentId = examClassResultRow.studentIds[i];
  1204. string fileName = examId + "/" + subjectId + "/" + studentId;
  1205. string blob = fileName + "/" + "ans.json";
  1206. var uploadFileResult = await _azureStorage.UploadFileByContainer(blobContainer, examClassResultRow.studentAnswersArray[i].ToJsonString(), "exam", blob, false);
  1207. if (string.IsNullOrWhiteSpace(uploadFileResult))
  1208. {
  1209. studentAnswerCopyErrFlg = true;
  1210. }
  1211. List<string> studenrAnswerRow = new List<string>();
  1212. studenrAnswerRow.Add(blob);
  1213. examClassResultUpd.studentAnswers.Add(studenrAnswerRow);
  1214. }
  1215. }
  1216. //批註欄位處理
  1217. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string paperStatusSubjectId) && paperStatusSubjectId.Equals(subjectId)).FirstOrDefault();
  1218. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  1219. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  1220. {
  1221. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  1222. }
  1223. //UPDATE ExamClassResult
  1224. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  1225. }
  1226. }
  1227. else
  1228. {
  1229. List<ExamClassResultStudentAnswerArray> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArray>>();
  1230. foreach (ExamClassResultStudentAnswerArray examClassResultRow in dbExamClassResultList)
  1231. {
  1232. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  1233. ExamClassResult examClassResultUpd = new ExamClassResult();
  1234. string exclcode = examClassResultRow.code;
  1235. string classId = examClassResultRow.info.id;
  1236. string subjectId = examClassResultRow.subjectId;
  1237. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  1238. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  1239. {
  1240. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  1241. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1242. {
  1243. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  1244. {
  1245. examClassResultUpd = obj.ToObject<ExamClassResult>();
  1246. examClassResultUpd.progress = examClassResultRow.progress;
  1247. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1248. examClassResultUpd.studentAnswers = new List<List<string>>();
  1249. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1250. examClassResultUpd.sum = examClassResultRow.sum;
  1251. }
  1252. }
  1253. }
  1254. //無法取得既有ExamClassResult,新建
  1255. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  1256. {
  1257. examClassResultUpd.pk = examClassResultRow.pk;
  1258. examClassResultUpd.code = examClassResultRow.code;
  1259. examClassResultUpd.id = examClassResultRow.id;
  1260. examClassResultUpd.school = examClassResultRow.school;
  1261. examClassResultUpd.examId = examClassResultRow.examId;
  1262. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  1263. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  1264. examClassResultUpd.year = examClassResultRow.year;
  1265. examClassResultUpd.info = examClassResultRow.info;
  1266. examClassResultUpd.progress = examClassResultRow.progress;
  1267. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1268. examClassResultUpd.studentAnswers = new List<List<string>>();
  1269. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1270. examClassResultUpd.scope = examClassResultRow.scope;
  1271. examClassResultUpd.sum = examClassResultRow.sum;
  1272. }
  1273. examClassResultUpd.studentAnswers = examClassResultRow.studentAnswersArray; //[2021-7-13 不再對Blob做搬運 學生答案直接寫入DB]
  1274. //批註欄位處理
  1275. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string subjectId) && subjectId.Equals(examClassResultUpd.subjectId)).FirstOrDefault();
  1276. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  1277. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  1278. {
  1279. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  1280. }
  1281. //UPDATE ExamClassResult
  1282. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  1283. }
  1284. }
  1285. //UPDATE Exam
  1286. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(dbExamInfo, new PartitionKey(dbExamInfo.code));
  1287. //錯誤處理
  1288. if(paperDataCopyErrFlg) //試卷Blob拷貝失敗
  1289. {
  1290. error = 1001;
  1291. message = "Paper blob copy failure.";
  1292. } else if(studentAnswerCopyErrFlg) //學生作答資料上傳blob失敗
  1293. {
  1294. error = 1002;
  1295. message = "Student answers blob upload failure.";
  1296. }
  1297. return Ok(new { error, message });
  1298. }
  1299. catch (CosmosException cex)
  1300. {
  1301. return BadRequest(cex.Message);
  1302. }
  1303. catch (StorageException bex)
  1304. {
  1305. return BadRequest(bex.Message);
  1306. }
  1307. catch (Exception ex)
  1308. {
  1309. return BadRequest(ex.Message);
  1310. }
  1311. }
  1312. private List<List<List<Details>>> createEmptyMark(int studentNum, int itemNum)
  1313. {
  1314. List<List<List<Details>>> mark = new List<List<List<Details>>>();
  1315. for (int i = 0; i < studentNum; i++)
  1316. {
  1317. List<List<Details>> markRow = new List<List<Details>>();
  1318. for (int j = 0; j < itemNum; j++)
  1319. {
  1320. markRow.Add(new List<Details>());
  1321. }
  1322. mark.Add(markRow);
  1323. }
  1324. return mark;
  1325. }
  1326. //課綱的model先記在下面,待式樣確定後再轉換
  1327. private List<SyllabusNode> CreateSyllabusTree(List<Syllabus> syllabuses)
  1328. {
  1329. List<SyllabusNode> nodes = new List<SyllabusNode>();
  1330. foreach (var syllabus in syllabuses)
  1331. {
  1332. if (syllabus.pid.Equals(""))
  1333. nodes.Add(new SyllabusNode { id = syllabus.id, name = syllabus.name });
  1334. else
  1335. {
  1336. CreateNode(nodes, syllabus);
  1337. }
  1338. }
  1339. return nodes;
  1340. }
  1341. private void CreateNode(List<SyllabusNode> nodes, Syllabus parent)
  1342. {
  1343. foreach (var node in nodes)
  1344. {
  1345. if (node.id == parent.pid)
  1346. {
  1347. node.children.Add(new SyllabusNode { id = parent.id, name = parent.name });
  1348. }
  1349. else
  1350. {
  1351. CreateNode(node.children, parent);
  1352. }
  1353. }
  1354. }
  1355. public class SyllabusRole
  1356. {
  1357. public string id { get; set; }
  1358. public string name { get; set; }
  1359. public object period { get; set; }
  1360. public object semester { get; set; }
  1361. public object grade { get; set; }
  1362. public object subject { get; set; }
  1363. public int resourceCount { get; set; }
  1364. public int itemCount { get; set; }
  1365. public object structure { get; set; }
  1366. }
  1367. public class Syllabus
  1368. {
  1369. public string id { get; set; }
  1370. public string name { get; set; }
  1371. public string pid { get; set; }
  1372. public int order { get; set; }
  1373. }
  1374. public class SyllabusNode
  1375. {
  1376. public string id { get; set; }
  1377. public string name { get; set; }
  1378. public List<SyllabusNode> children { get; set; }
  1379. public SyllabusNode()
  1380. {
  1381. children = new List<SyllabusNode>();
  1382. }
  1383. }
  1384. //ExamClassResult 學生作答紀錄(舊式 正式站暫時用這個)
  1385. public class ExamClassResultStudentAnswerArrayOld : ExamClassResult
  1386. {
  1387. public List<List<List<string>>> studentAnswersArray { get; set; }
  1388. }
  1389. //ExamClassResult 學生作答紀錄
  1390. public class ExamClassResultStudentAnswerArray : ExamClassResult
  1391. {
  1392. public List<List<string>> studentAnswersArray { get; set; }
  1393. }
  1394. }
  1395. }