HiTeachController.cs 176 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962
  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. using TEAMModelOS.SDK.Models.Cosmos.Common;
  26. using TEAMModelOS.SDK;
  27. using Microsoft.Extensions.Configuration;
  28. using Azure.Messaging.ServiceBus;
  29. using TEAMModelOS.SDK.Services;
  30. using TEAMModelOS.SDK.Models.Service;
  31. using TEAMModelOS.Models.Request;
  32. using Azure;
  33. using TEAMModelOS.Controllers.Both;
  34. using TEAMModelOS.SDK.Models.Cosmos.School;
  35. using TEAMModelOS.SDK.Helper.Common.DateTimeHelper;
  36. using Azure.Storage.Blobs;
  37. namespace TEAMModelOS.Controllers.Client
  38. {
  39. [ProducesResponseType(StatusCodes.Status200OK)]
  40. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  41. //[Authorize(Roles = "HiTeach")]
  42. [Route("hiteach")]
  43. [ApiController]
  44. public class HiTeachController : ControllerBase
  45. {
  46. private readonly AzureStorageFactory _azureStorage;
  47. private readonly AzureRedisFactory _azureRedis;
  48. private readonly AzureCosmosFactory _azureCosmos;
  49. private readonly DingDing _dingDing;
  50. private readonly Option _option;
  51. private readonly SnowflakeId _snowflakeId;
  52. private readonly AzureServiceBusFactory _serviceBus;
  53. private readonly IConfiguration _configuration;
  54. private readonly CoreAPIHttpService _coreAPIHttpService;
  55. private readonly IPSearcher _searcher;
  56. private readonly HttpTrigger _httpTrigger;
  57. public HiTeachController(
  58. AzureStorageFactory azureStorage,
  59. AzureRedisFactory azureRedis,
  60. AzureCosmosFactory azureCosmos,
  61. DingDing dingDing,
  62. SnowflakeId snowflakeId,
  63. IOptionsSnapshot<Option> option,
  64. AzureServiceBusFactory serviceBus,
  65. IConfiguration configuration,
  66. CoreAPIHttpService coreAPIHttpService, IPSearcher searcher, HttpTrigger httpTrigger)
  67. {
  68. _azureStorage = azureStorage;
  69. _azureRedis = azureRedis;
  70. _azureCosmos = azureCosmos;
  71. _dingDing = dingDing;
  72. _snowflakeId = snowflakeId;
  73. _option = option?.Value;
  74. _serviceBus = serviceBus;
  75. _configuration = configuration;
  76. _coreAPIHttpService = coreAPIHttpService;
  77. _searcher = searcher;
  78. _httpTrigger = httpTrigger;
  79. }
  80. /// <summary>
  81. /// 更新课堂记录
  82. /// </summary>
  83. /// <param name="request"></param>
  84. /// <returns></returns>
  85. //[Authorize(Roles = "HiTeach")]
  86. [ProducesDefaultResponseType]
  87. [HttpPost("update-lesson-record")]
  88. public async Task<IActionResult> UpdateLessonRecord(JsonElement request)
  89. {
  90. var client = _azureCosmos.GetCosmosClient();
  91. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  92. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  93. request.TryGetProperty("name", out JsonElement name);
  94. request.TryGetProperty("school", out JsonElement _school);
  95. if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
  96. request.TryGetProperty("grant_types", out JsonElement _grant_types);
  97. string tbname;
  98. string code;
  99. if (_scope.GetString().Equals("school") && !string.IsNullOrWhiteSpace(_school.GetString()))
  100. {
  101. code = $"LessonRecord-{_school}";
  102. tbname = "School";
  103. }
  104. else if ($"{_scope}".Equals("private"))
  105. {
  106. code = $"LessonRecord";
  107. tbname = "Teacher";
  108. }
  109. else
  110. {
  111. return BadRequest();
  112. }
  113. try
  114. {
  115. LessonRecord lessonRecord = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<LessonRecord>($"{_lessonId}", new PartitionKey(code));
  116. List<LessonUpdate> updates = new List<LessonUpdate>() { new LessonUpdate { grant_type = "up-base" } };
  117. if (_grant_types.ValueKind.Equals(JsonValueKind.Array))
  118. {
  119. updates = _grant_types.ToObject<List<LessonUpdate>>();
  120. if (!updates.Select(x => x.grant_type).Contains("up-base"))
  121. {
  122. updates.Add(new LessonUpdate { grant_type = "up-base" });
  123. }
  124. }
  125. if (!string.IsNullOrWhiteSpace($"{name}"))
  126. {
  127. lessonRecord.name = $"{name}";
  128. await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, $"{_lessonId}", new PartitionKey(code));
  129. }
  130. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  131. string msg = null;
  132. msg = new { lesson_id = $"{_lessonId}", tmdid = $"{_tmdid}", grant_types = updates, school = $"{_school}", scope = $"{_scope}" }.ToJsonString();
  133. //if (!string.IsNullOrWhiteSpace(_school.GetString()))
  134. //{
  135. // SaveLearnRecordBlob(_school.GetString(), _lessonId.GetString());
  136. //}
  137. var messageChange = new ServiceBusMessage(msg);
  138. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  139. //await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新事件,{msg}", GroupNames.醍摩豆服務運維群組);
  140. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  141. SetLearnRecordContent(_school, _tmdid, _lessonId, _scope);
  142. return Ok(new { status = 200 });
  143. }
  144. catch (CosmosException ex) when (ex.Status == 404)
  145. {
  146. await _dingDing.SendBotMsg($"更新课堂记录出错\n{ex.StackTrace}{ex.Message}", GroupNames.成都开发測試群組);
  147. return BadRequest("课堂记录不存在");
  148. }
  149. catch (Exception ex)
  150. {
  151. await _dingDing.SendBotMsg($"更新课堂记录出错\n{ex.StackTrace}{ex.Message}", GroupNames.成都开发測試群組);
  152. return BadRequest();
  153. }
  154. }
  155. /// <summary>
  156. /// 更新课堂记录
  157. /// </summary>
  158. /// <param name="request"></param>
  159. /// <returns></returns>
  160. //[Authorize(Roles = "HiTeach")]
  161. [ProducesDefaultResponseType]
  162. [HttpPost("update-lesson-recordLearnRecord")]
  163. public async Task<IActionResult> UpdateLessonRecordLearnRecord(JsonElement request)
  164. {
  165. List<LearnRecordItem> learnRecordItems = new List<LearnRecordItem>();
  166. try
  167. {
  168. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  169. if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
  170. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  171. request.TryGetProperty("school", out JsonElement _school);
  172. SetLearnRecordContent(_school, _tmdid, _lessonId, _scope);
  173. return Ok(new { status = 200, learnRecordItems });
  174. }
  175. catch (Exception ex)
  176. {
  177. string sss = ex.Message;
  178. return BadRequest();
  179. }
  180. }
  181. /// <summary>
  182. /// 設定及儲存學習紀錄
  183. /// </summary>
  184. /// <param name="_school"></param>
  185. /// <param name="_tmdid"></param>
  186. /// <param name="_lessonId"></param>
  187. /// <param name="_scope"></param>
  188. /// <returns></returns>
  189. private async Task SetLearnRecordContent(JsonElement _school, JsonElement _tmdid, JsonElement _lessonId, JsonElement _scope)
  190. {
  191. try
  192. {
  193. string school = $"{_school}";
  194. string tmdid = _tmdid.GetString();
  195. string lessonId = _lessonId.GetString();
  196. string rootName = "";
  197. List<LearnRecordItem> learnRecordItems = new List<LearnRecordItem>();
  198. if (_scope.GetString().Equals("school") && !string.IsNullOrWhiteSpace(_school.GetString()))
  199. {
  200. rootName = school;
  201. }
  202. else if ($"{_scope}".Equals("private"))
  203. {
  204. rootName = tmdid;
  205. }
  206. // 檢查 TimeLine 是否存在blob
  207. if (_azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/TimeLine.json").Exists() &&
  208. _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/IRS.json").Exists() &&
  209. _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/base.json").Exists())
  210. {
  211. // 開啟 TimeLine.json
  212. BlobDownloadResult TimeLineblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/TimeLine.json").DownloadContentAsync();
  213. TimeLineEvents timeLineEvents = TimeLineblobDownload.Content.ToObjectFromJson<TimeLineEvents>();
  214. // 開啟 IRS.json
  215. BlobDownloadResult IRSblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/IRS.json").DownloadContentAsync();
  216. List<IRSItem> iRSItems = IRSblobDownload.Content.ToObjectFromJson<List<IRSItem>>();
  217. // 開啟 base.json
  218. BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/base.json").DownloadContentAsync();
  219. ScoreLessonBase lessonBase = baseblobDownload.Content.ToObjectFromJson<ScoreLessonBase>();
  220. // 開啟 Task.json
  221. BlobDownloadResult taskblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/records/{lessonId}/IES/Task.json").DownloadContentAsync();
  222. List<TaskItem> taskItems = taskblobDownload.Content.ToObjectFromJson<List<TaskItem>>();
  223. if (timeLineEvents.events.Count > 0)
  224. {
  225. for (int i = 0; i < timeLineEvents.events.Count; i++)
  226. {
  227. switch (timeLineEvents.events[i].Event)
  228. {
  229. // 互動
  230. case "PopQuesLoad":
  231. for (int j = 0; j < iRSItems.Count; j++)
  232. {
  233. if (timeLineEvents.events[i].Pgid == iRSItems[j].pageID) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  234. {
  235. switch (iRSItems[j].question.exercise.type)
  236. {
  237. // 單選
  238. case "single":
  239. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsSingle", learnRecordItems);
  240. break;
  241. // 複選
  242. case "multiple":
  243. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsMultiple", learnRecordItems);
  244. break;
  245. // 是非
  246. case "judge":
  247. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsJudge", learnRecordItems);
  248. break;
  249. // 填充
  250. case "complete":
  251. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsComplete", learnRecordItems);
  252. break;
  253. // 問答
  254. case "subjective":
  255. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsSubjective", learnRecordItems);
  256. break;
  257. }
  258. }
  259. }
  260. break;
  261. // 搶權
  262. case "BuzrAns":
  263. for (int j = 0; j < iRSItems.Count; j++)
  264. {
  265. if (timeLineEvents.events[i].Pgid == iRSItems[j].pageID && iRSItems[j].isBuzz) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  266. {
  267. for (int k = 0; k < iRSItems[j].buzzClients.Count; k++)
  268. {
  269. LearnRecordItem learnRecordItem = new();
  270. for (int m = 0; m < lessonBase.student.Count; m++)
  271. {// 比對學生座號 取學號
  272. if (lessonBase.student[m].seatID.ToString() == iRSItems[j].buzzClients[k])
  273. {
  274. if (lessonBase.student[k].type == 2)
  275. {
  276. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem);
  277. }
  278. else
  279. {// 非校內帳號用直接用學號
  280. learnRecordItem.actor = lessonBase.student[k].id;
  281. }
  282. }
  283. }
  284. string[] arrymd = lessonBase.summary.date.Split('.');
  285. string[] arrhms = lessonBase.summary.startTime.Split(':');
  286. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  287. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  288. learnRecordItem.verb = "AnsBuzzin";
  289. learnRecordItem.time = dt.ToUnixTimestamp();
  290. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  291. if (iRSItems[j].question.item[0].question == "_popquiz_" || iRSItems[j].question.item[0].question == "")
  292. {
  293. learnRecordItem.Desc = "隨堂問答";
  294. }
  295. else
  296. {
  297. learnRecordItem.Desc = iRSItems[j].question.item[0].question;
  298. }
  299. learnRecordItem.Points = iRSItems[j].question.exercise.knowledges;
  300. await setCorrectChoices(iRSItems[j], learnRecordItem);
  301. learnRecordItem.ExamQuesQty = null;
  302. learnRecordItem.TotalScore = null;
  303. learnRecordItem.Success = null;
  304. learnRecordItems.Add(learnRecordItem);
  305. }
  306. }
  307. }
  308. break;
  309. // 上傳作品
  310. case "WrkSpaceLoad":
  311. for (int j = 0; j < taskItems.Count; j++)
  312. {
  313. if (timeLineEvents.events[i].Pgid == taskItems[j].pageID) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  314. {
  315. for (int k = 0; k < taskItems[j].clientWorks.Count; k++)
  316. {
  317. LearnRecordItem learnRecordItem = new();
  318. for (int m = 0; m < lessonBase.student.Count; m++)
  319. {// 比對學生座號 取學號
  320. if (lessonBase.student[m].seatID == taskItems[j].clientWorks[k].seatID)
  321. {
  322. if (lessonBase.student[k].type == 2)
  323. {
  324. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem);
  325. }
  326. else
  327. {// 非校內帳號用直接用學號
  328. learnRecordItem.actor = lessonBase.student[k].id;
  329. }
  330. }
  331. }
  332. string[] arrymd = lessonBase.summary.date.Split('.');
  333. string[] arrhms = taskItems[j].clientWorks[k].reciveTime.Split(':');
  334. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  335. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  336. learnRecordItem.verb = "SubmitTask";
  337. learnRecordItem.time = dt.ToUnixTimestamp();
  338. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  339. learnRecordItem.Desc = "隨堂作品";
  340. learnRecordItem.Correct = null;
  341. learnRecordItem.Choices = null;
  342. learnRecordItem.ExamQuesQty = null;
  343. learnRecordItem.TotalScore = null;
  344. learnRecordItem.Success = null;
  345. learnRecordItems.Add(learnRecordItem);
  346. }
  347. }
  348. }
  349. break;
  350. case "FastPgPush":
  351. for (int k = 0; k < lessonBase.student.Count; k++)
  352. {
  353. LearnRecordItem learnRecordItem = new();
  354. for (int m = 0; m < lessonBase.student.Count; m++)
  355. {// 比對學生座號 取學號
  356. if (lessonBase.student[k].type == 2)
  357. {
  358. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem);
  359. }
  360. else
  361. {// 非校內帳號用直接用學號
  362. learnRecordItem.actor = lessonBase.student[k].id;
  363. }
  364. }
  365. string[] arrymd = lessonBase.summary.date.Split('.');
  366. string[] arrhms = lessonBase.summary.startTime.Split(':');
  367. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  368. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  369. learnRecordItem.verb = "ViewPage";
  370. learnRecordItem.time = dt.ToUnixTimestamp();
  371. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  372. learnRecordItem.Desc = null;
  373. learnRecordItem.Correct = null;
  374. learnRecordItem.Choices = null;
  375. learnRecordItem.ExamQuesQty = null;
  376. learnRecordItem.TotalScore = null;
  377. learnRecordItem.Success = null;
  378. learnRecordItems.Add(learnRecordItem);
  379. }
  380. break;
  381. case "ActStart":
  382. case "ActEnd":
  383. for (int k = 0; k < lessonBase.student.Count; k++)
  384. {
  385. LearnRecordItem learnRecordItem = new();
  386. for (int m = 0; m < lessonBase.student.Count; m++)
  387. {// 比對學生座號 取學號
  388. if (lessonBase.student[k].type == 2)
  389. {
  390. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem);
  391. }
  392. else
  393. {// 非校內帳號用直接用學號
  394. learnRecordItem.actor = lessonBase.student[k].id;
  395. }
  396. }
  397. string[] arrymd = lessonBase.summary.date.Split('.');
  398. string[] arrhms = lessonBase.summary.startTime.Split(':');
  399. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  400. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  401. if (timeLineEvents.events[i].Event == "ActStart") { learnRecordItem.verb = "StartExam"; }
  402. if (timeLineEvents.events[i].Event == "ActEnd") { learnRecordItem.verb = "EndExam"; }
  403. learnRecordItem.time = dt.ToUnixTimestamp();
  404. learnRecordItem.ID = lessonId;
  405. learnRecordItem.Desc = lessonBase.summary.activityName;
  406. learnRecordItem.Correct = null;
  407. learnRecordItem.Choices = null;
  408. learnRecordItem.ExamQuesQty = 0;
  409. foreach (var item in timeLineEvents.events)
  410. {
  411. if (item.Event == "PopQuesLoad" || item.Event == "QuesLoad" || item.Event == "BuzrAns")
  412. {
  413. learnRecordItem.ExamQuesQty = learnRecordItem.ExamQuesQty + 1;
  414. }
  415. }
  416. learnRecordItem.TotalScore = learnRecordItem.ExamQuesQty * 10;
  417. learnRecordItem.Success = null;
  418. learnRecordItems.Add(learnRecordItem);
  419. }
  420. break;
  421. // 測驗
  422. case "SPQStrt":
  423. //// 開啟 .json
  424. //BlobDownloadResult taskblobDownload = await _azureStorage.GetBlobContainerClient(school).GetBlobClient($"/records/{lessonId}/IES/Task.json").DownloadContentAsync();
  425. //List<TaskItem> taskItems = taskblobDownload.Content.ToObjectFromJson<List<TaskItem>>();
  426. break;
  427. }
  428. }
  429. }
  430. #region 寫入blob
  431. // 容器名称
  432. string containerName = "twmoeld";
  433. // 新文件的名称
  434. string blobName = $"/{DateTime.Now.ToString("yyyyMMdd")}/{lessonId}.json";
  435. // 要上传的文件内容
  436. string fileContent = Newtonsoft.Json.JsonConvert.SerializeObject(learnRecordItems);
  437. // 获取容器引用
  438. BlobContainerClient containerClient = _azureStorage.GetBlobContainerClient(containerName);
  439. // 获取 Blob 客户端
  440. BlobClient blobClient = containerClient.GetBlobClient(blobName);
  441. // 将文件内容上传到 Blob
  442. using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(fileContent)))
  443. {
  444. await blobClient.UploadAsync(stream, true);
  445. }
  446. #endregion
  447. }
  448. }
  449. catch (Exception ex)
  450. {
  451. }
  452. }
  453. /// <summary>
  454. /// 設定學習記錄互動的內容
  455. /// </summary>
  456. /// <param name="lessonBase"></param>
  457. /// <param name="school"></param>
  458. /// <param name="lessonId"></param>
  459. /// <param name="iRSItem"></param>
  460. /// <param name="timeLineEvent"></param>
  461. /// <param name="verb"></param>
  462. /// <param name="learnRecordItems"></param>
  463. /// <returns></returns>
  464. private async Task SetPopQuesLoadContent(ScoreLessonBase lessonBase, string school, string lessonId, IRSItem iRSItem, TimeLineEventPg timeLineEvent, string verb, List<LearnRecordItem> learnRecordItems)
  465. {
  466. for (int k = 0; k < lessonBase.student.Count; k++)
  467. {
  468. LearnRecordItem learnRecordItem = new();
  469. if (lessonBase.student[k].type == 2)
  470. {
  471. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem);
  472. }
  473. else
  474. {// 非校內帳號用直接用學號
  475. learnRecordItem.actor = lessonBase.student[k].id;
  476. }
  477. string[] arrymd = lessonBase.summary.date.Split('.');
  478. string[] arrhms = lessonBase.summary.startTime.Split(':');
  479. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  480. dt = dt.AddSeconds(timeLineEvent.Time);
  481. learnRecordItem.verb = verb;
  482. learnRecordItem.time = dt.ToUnixTimestamp();
  483. learnRecordItem.ID = timeLineEvent.Pgid;
  484. if (iRSItem.question.item[0].question == "_popquiz_" || iRSItem.question.item[0].question == "")
  485. {
  486. learnRecordItem.Desc = "隨堂問答";
  487. }
  488. else
  489. {
  490. learnRecordItem.Desc = iRSItem.question.item[0].question;
  491. }
  492. learnRecordItem.Points = iRSItem.question.exercise.knowledges;
  493. await setCorrectChoices(iRSItem, learnRecordItem);
  494. learnRecordItem.ExamQuesQty = null;
  495. learnRecordItem.TotalScore = null;
  496. iRSItem.clientAnswers.TryGetProperty("0", out JsonElement anwersArr);
  497. List<List<string>> clientAnswersarr = anwersArr.ToJsonString().ToObject<List<List<string>>>();
  498. if (iRSItem.question.exercise.answer.SequenceEqual(clientAnswersarr[k]))
  499. {
  500. learnRecordItem.Success = true;
  501. }
  502. else
  503. {
  504. learnRecordItem.Success = false;
  505. }
  506. learnRecordItems.Add(learnRecordItem);
  507. }
  508. }
  509. /// <summary>
  510. /// 取得學校簡碼
  511. /// </summary>
  512. /// <param name="school"></param>
  513. /// <param name="lessonId"></param>
  514. /// <param name="studentId"></param>
  515. /// <param name="learnRecordItem"></param>
  516. /// <returns></returns>
  517. private async Task getSchoolCode(string school, string lessonId, string studentId, LearnRecordItem learnRecordItem)
  518. {
  519. if (string.IsNullOrWhiteSpace(school))
  520. {// 如果沒有學校代碼 需要用lessonId去DB取學校代碼
  521. GroupIdsFromLesson groupIdsFromLesson = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<GroupIdsFromLesson>(lessonId, new PartitionKey($"LessonRecord"));
  522. SchoolFromgroupIds schoolFromgroupIds = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<SchoolFromgroupIds>(groupIdsFromLesson.groupIds[0], new PartitionKey($"GroupList"));
  523. learnRecordItem.actor = $"Base-{schoolFromgroupIds.school.Trim()},{studentId}";
  524. }
  525. else
  526. {// 如果有學校代碼 校內帳號用組合的 "Base-hbgl,473891247381"
  527. learnRecordItem.actor = $"Base-{school.Trim()},{studentId}";
  528. }
  529. }
  530. /// <summary>
  531. /// 設定CorrectChoices的邏輯
  532. /// </summary>
  533. /// <param name="iRSItems"></param>
  534. /// <param name="learnRecordItem"></param>
  535. /// <returns></returns>
  536. private async Task setCorrectChoices(IRSItem iRSItem, LearnRecordItem learnRecordItem)
  537. {
  538. #region === Correct Choices ===
  539. #region === 是非題邏輯 ===
  540. if (iRSItem.question.exercise.type == "judge" && iRSItem.question.exercise.answer.Count > 0)
  541. {// 如果是是非題 正確答案要用true false的方式設定
  542. if (iRSItem.question.exercise.answer[0] == "A")
  543. {
  544. learnRecordItem.Correct = new string[] { "true" };
  545. }
  546. else
  547. {
  548. learnRecordItem.Correct = new string[] { "false" };
  549. }
  550. }
  551. else
  552. {
  553. learnRecordItem.Correct = iRSItem.question.exercise.answer;
  554. }
  555. #endregion
  556. if (iRSItem.question.item[0].option.Count > 0)
  557. {// 如果有選項資料 記錄起來
  558. foreach (var option in iRSItem.question.item[0].option)
  559. {
  560. ChoicesItem item = new();
  561. item.id = option.code;
  562. if (iRSItem.question.exercise.type == "judge")
  563. {// 如果是是非題 固定選項
  564. if (option.code == "A")
  565. {
  566. item.description.zhTW = "是";
  567. }
  568. else
  569. {
  570. item.description.zhTW = "否";
  571. }
  572. }
  573. else
  574. {
  575. if (string.IsNullOrWhiteSpace(option.value))
  576. {
  577. item.description.zhTW = $"選項{option.code}";
  578. }
  579. else
  580. {
  581. item.description.zhTW = option.value;
  582. }
  583. }
  584. learnRecordItem.Choices.Add(item);
  585. }
  586. }
  587. #endregion
  588. }
  589. /// <summary>
  590. /// 创建课堂开课记录(新版)
  591. /// </summary>
  592. /// <param name="request"></param>
  593. /// <returns></returns>
  594. [Authorize(Roles = "HiTeach")]
  595. [ProducesDefaultResponseType]
  596. [HttpPost("create-lesson")]
  597. public async Task<IActionResult> CreateLesson(CreateLessondRequest request)
  598. {
  599. try
  600. {
  601. string id_token = HttpContext.GetXAuth("IdToken");
  602. //判斷ID Token
  603. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  604. var jwt = new JwtSecurityToken(id_token);
  605. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  606. var tid = jwt.Payload.Sub;
  607. //初始化
  608. var r8 = _azureRedis.GetRedisClient(8);
  609. var db = _azureCosmos.GetCosmosClient();
  610. var sbm = new List<ServiceBusMessage>();
  611. var sp = request.sp.Equals("school", StringComparison.OrdinalIgnoreCase);
  612. int size = 0; //學校或個人總空間
  613. int tsize = 0; //學校分配給老師的總空間
  614. double usize = 0; //學校或個人已使用空間
  615. string timezone = string.Empty;
  616. if (sp)
  617. {
  618. //取得學校資訊
  619. if (string.IsNullOrWhiteSpace(request.school)) return BadRequest();
  620. await foreach (Response item in db.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryStreamIterator(
  621. queryText: $"SELECT TOP 1 c.id, c.size, c.tsize, c.timeZone FROM c WHERE c.id = '{request.school}'",
  622. requestOptions: new() { PartitionKey = new("Base") }))
  623. {
  624. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  625. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  626. {
  627. var school = json.RootElement.GetProperty("Documents").EnumerateArray().First();
  628. timezone = school.GetProperty("timeZone").GetProperty("value").GetString();
  629. size = school.GetProperty("size").GetInt32();
  630. tsize = school.TryGetProperty("tsize", out var tsvalue) ? tsvalue.GetInt32() : 0;
  631. }
  632. else return BadRequest();
  633. }
  634. }
  635. else
  636. {
  637. Teacher teacher = await db.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tid, new PartitionKey("Base"));
  638. size = teacher.size;
  639. foreach (var school in teacher.schools)
  640. {
  641. try
  642. {
  643. SchoolTeacher st = await db.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<SchoolTeacher>(tid, new PartitionKey($"Teacher-{school.schoolId}"));
  644. size += st.size;
  645. }
  646. catch (CosmosException)
  647. {
  648. }
  649. }
  650. }
  651. (long usedSize, long teach, long total, long surplus, Dictionary<string, double?> catalog) space = await BlobService.GetSurplusSpace($"{(sp ? request.school : tid)}", request.sp, _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing, _httpTrigger);
  652. //計算學校或個人的使用空間
  653. // RedisValue redisValue = r8.HashGet($"Blob:Record", $"{(sp ? request.school : tid)}");
  654. if (space.usedSize > 0)
  655. {
  656. usize = Math.Round(space.usedSize / 1073741824.0 - (sp ? tsize : 0), 2, MidpointRounding.AwayFromZero); //1073741824 1G
  657. }
  658. else //如果檢測不到緩存,觸發刷新計算空間
  659. {
  660. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = "records", name = $"{(sp ? request.school : tid)}" }, _serviceBus, _configuration, _azureRedis);
  661. }
  662. //取得學校或個人名單
  663. (List<RMember> students, _) = await GroupListService.GetMemberByListids(_coreAPIHttpService, db, _dingDing, new List<string>() { request.sid }, request.school);
  664. //觸發IMEI更新消息
  665. var stus = students.Select(x => x.id).ToList();
  666. var imeimsg = new ServiceBusMessage(new { request.channel, userid = request.did, request.school, stus }.ToJsonString());
  667. imeimsg.ApplicationProperties.Add("name", "Imei");
  668. sbm.Add(imeimsg);
  669. //開課記錄保存
  670. LessonRecord lr = new()
  671. {
  672. school = request.sp.Equals("school") ? request?.school : null,
  673. code = request.sp.Equals("school") ? $"LessonRecord-{request.school}" : $"LessonRecord",
  674. id = _snowflakeId.NextId().ToString(), //取得授課ID
  675. courseId = request.cid,
  676. groupIds = new() { request.sid },
  677. tmdid = tid,
  678. scope = request.sp,
  679. pk = "LessonRecord",
  680. name = request.cname,
  681. startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
  682. };
  683. await db.GetContainer(Constant.TEAMModelOS, request.sp.Equals("school") ? "School" : "Teacher").CreateItemAsync(lr, new PartitionKey(lr.code));
  684. //觸發開課統計
  685. var messageChange = new ServiceBusMessage(lr.ToJsonString());
  686. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  687. sbm.Add(messageChange);
  688. //批量發送消息
  689. await _serviceBus.GetServiceBusClient().SendBatchMessageAsync(_configuration.GetValue<string>("Azure:ServiceBus:ActiveTask"), sbm);
  690. if (sp && usize > size)
  691. {
  692. ////处理学校开课,空间不足时。检查是否有 当前教师tid,强制保存save<>1,没有标记未删除status<>404,没有被收藏favorite<=0 ,时间最旧的一条记录startTime
  693. LessonRecord lessonRecord = null;
  694. string sql = $"SELECT top 1 value(c) FROM c where ( c.expire<=0 or IS_DEFINED(c.expire) = false ) and c.tmdid='{tid}' and c.save<>1 and c.status<>404 and c.favorite<=0 order by c.startTime ";
  695. await foreach (var item in db.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIterator<LessonRecord>(
  696. queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"LessonRecord-{request.school}") }))
  697. {
  698. lessonRecord = item;
  699. break;
  700. }
  701. if (lessonRecord != null)
  702. {
  703. lessonRecord.status = 404;
  704. await db.GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(lessonRecord, lessonRecord.id, new PartitionKey(lessonRecord.code));
  705. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  706. var messageChangeEvent = new ServiceBusMessage(request.ToJsonString());
  707. messageChangeEvent.ApplicationProperties.Add("name", "LessonRecordEvent");
  708. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChangeEvent);
  709. //保证客户端可以正常开课。
  710. usize -= 1;
  711. }
  712. else
  713. { //没有找到匹配当前 教师tid,save<>1,status<>404,没有被收藏,时间最旧的一条记录。无法手动再继续 usize -= 1;,则不能继续开课。
  714. }
  715. }
  716. return Ok(new { status = 200, lr.id, students, size, usize });
  717. }
  718. catch (Exception ex)
  719. {
  720. await _dingDing.SendBotMsg($"IES5,{_option.Location}, hiteach/create-lesson:()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  721. return BadRequest();
  722. }
  723. }
  724. /// <summary>
  725. /// 创建课堂开课记录(舊版,已被CreateLesson取代)
  726. /// </summary>
  727. /// <param name="request"></param>
  728. /// <returns></returns>
  729. [Authorize(Roles = "HiTeach")]
  730. [ProducesDefaultResponseType]
  731. [HttpPost("create-lesson-record")]
  732. public async Task<IActionResult> CreateLessonRecord(JsonElement request)
  733. {
  734. if (!request.TryGetProperty("lesson", out JsonElement _lesson)) return BadRequest();
  735. var client = _azureCosmos.GetCosmosClient();
  736. LessonRecord lessonRecord = _lesson.ToObject<LessonRecord>();
  737. int blobTotal = 0;
  738. long teach = 0;
  739. string blobName = "";
  740. if (!string.IsNullOrEmpty(lessonRecord.scope) && !string.IsNullOrEmpty(lessonRecord.tmdid) && !string.IsNullOrEmpty(lessonRecord.id))
  741. {
  742. string tbname = null;
  743. if (lessonRecord.scope.Equals("school") && !string.IsNullOrEmpty(lessonRecord.school))
  744. {
  745. School school = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(lessonRecord.school, new PartitionKey("Base"));
  746. lessonRecord.code = $"LessonRecord-{lessonRecord.school}";
  747. tbname = "School";
  748. blobTotal = school.size;
  749. //计算分配给学校教师的空间
  750. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT sum(c.size) as size FROM c ",
  751. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{lessonRecord.school}") }))
  752. {
  753. var json = await JsonDocument.ParseAsync(item.ContentStream);
  754. foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
  755. {
  756. if (elmt.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  757. {
  758. teach = _size.GetInt32();
  759. break;
  760. }
  761. }
  762. }
  763. blobName = lessonRecord.school;
  764. }
  765. if (lessonRecord.scope.Equals("private"))
  766. {
  767. lessonRecord.code = $"LessonRecord";
  768. tbname = "Teacher";
  769. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(lessonRecord.tmdid, new PartitionKey("Base"));
  770. blobTotal = teacher.size;
  771. foreach (var school in teacher.schools)
  772. {
  773. SchoolTeacher st = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<SchoolTeacher>(lessonRecord.tmdid, new PartitionKey($"Teacher-{school.schoolId}"));
  774. blobTotal += st.size;
  775. }
  776. blobName = lessonRecord.tmdid;
  777. }
  778. RedisValue redisValue = _azureRedis.GetRedisClient(8).HashGet($"Blob:Record", $"{blobName}");
  779. long blobsize = 0;
  780. if (redisValue != default && !redisValue.IsNullOrEmpty)
  781. {
  782. JsonElement record = redisValue.ToString().ToObject<JsonElement>();
  783. if (record.TryGetInt64(out blobsize))
  784. {
  785. }
  786. }
  787. else
  788. {
  789. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = "records", name = $"{blobName}" }, _serviceBus, _configuration, _azureRedis);
  790. }
  791. double blobUsed = blobsize / 1073741824.0 + teach; //1073741824 1G
  792. if (tbname == null)
  793. {
  794. return BadRequest("参数异常:scope应为school或private,scope=school ,school字段不能为空");
  795. }
  796. if (lessonRecord.startTime == 0)
  797. {
  798. lessonRecord.startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  799. }
  800. lessonRecord.pk = "LessonRecord";
  801. try
  802. {
  803. await client.GetContainer(Constant.TEAMModelOS, tbname).CreateItemAsync(lessonRecord, new PartitionKey(lessonRecord.code));
  804. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  805. var messageChange = new ServiceBusMessage(lessonRecord.ToJsonString());
  806. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  807. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  808. return Ok(new { status = 200, blobTotal, blobUsed });
  809. }
  810. catch (Exception ex)
  811. {
  812. await _dingDing.SendBotMsg($"IES5,{_option.Location}, hiteach/create-lesson-record:()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  813. return BadRequest();
  814. }
  815. }
  816. else
  817. {
  818. return BadRequest();
  819. }
  820. }
  821. [Authorize(Roles = "HiTeach")]
  822. [ProducesDefaultResponseType]
  823. [HttpPost("get-teacher-info")]
  824. public async Task<IActionResult> GetTeacherInfo()
  825. {
  826. //Debug
  827. //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
  828. try
  829. {
  830. string id_token = HttpContext.GetXAuth("IdToken");
  831. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  832. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  833. var jwt = new JwtSecurityToken(id_token);
  834. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  835. var id = jwt.Payload.Sub;
  836. jwt.Payload.TryGetValue("name", out object name);
  837. jwt.Payload.TryGetValue("picture", out object picture);
  838. List<object> schools = new List<object>();
  839. string defaultschool = null;
  840. //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  841. var client = _azureCosmos.GetCosmosClient();
  842. var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  843. if (response.Status == 200)
  844. {
  845. var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
  846. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  847. {
  848. GetTeacherInfoApiSchool schoolExtobj;
  849. foreach (var obj in value.EnumerateArray())
  850. {
  851. string statusNow = obj.GetProperty("status").ToString();
  852. if (statusNow.Equals("join")) //成為老師的才放入
  853. {
  854. var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  855. if (schoolJson.Status.Equals(StatusCodes.Status200OK))
  856. {
  857. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  858. schoolExtobj = new GetTeacherInfoApiSchool();
  859. schoolExtobj.schoolId = Convert.ToString(obj.GetProperty("schoolId"));
  860. schoolExtobj.name = Convert.ToString(school.RootElement.GetProperty("name"));
  861. schoolExtobj.status = Convert.ToString(obj.GetProperty("status"));
  862. schoolExtobj.picture = Convert.ToString(school.RootElement.GetProperty("picture"));
  863. schools.Add(schoolExtobj);
  864. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{obj.GetProperty("schoolId")}"));
  865. if (sctch.Status.Equals(StatusCodes.Status200OK) && sctch != null && sctch.ContentStream != null)
  866. {
  867. var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
  868. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  869. }
  870. }
  871. }
  872. }
  873. }
  874. //預設學校ID
  875. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
  876. {
  877. defaultschool = valueD.ToString();
  878. }
  879. }
  880. else
  881. {
  882. //如果沒有,則初始化Teacher基本資料到Cosmos
  883. Teacher teacher = new Teacher
  884. {
  885. createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
  886. id = id,
  887. pk = "Base",
  888. code = "Base",
  889. name = name?.ToString(),
  890. picture = picture?.ToString(),
  891. //创建账号并第一次登录IES5则默认赠送1G
  892. size = 1,
  893. defaultSchool = null,
  894. schools = new List<Teacher.TeacherSchool>(),
  895. };
  896. teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
  897. }
  898. //老師個人課程清單 [新架構 Todo]
  899. List<GetTeacherInfoApiCourse> courses = new List<GetTeacherInfoApiCourse>();
  900. List<KeyValuePair<string, CourseTask>> privateTeacherTask = new List<KeyValuePair<string, CourseTask>>(); //老師的課程安排Dictionary key:courseId val:CourseTask
  901. HashSet<string> courseIds = new HashSet<string>(); //有被安排的課程ID
  902. List<string> groupIds = new List<string>(); //班級團體ID
  903. List<Both.CourseDto> teahcerCourses = new List<Both.CourseDto>();
  904. string sqlCoursePrivate = $"select value c.id from c where c.creatorId='{id}'";
  905. var resultCourseBasePrivate = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(sqlCoursePrivate, $"CourseBase"); //CourseID列表
  906. string sqlprivate = $"SELECT distinct value c FROM c join b in c.schedules where c.pk='CourseTask' and b.teacherId ='{id}'";
  907. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sqlprivate, $"CourseTask");
  908. if (resultTeacher.list.IsNotEmpty())
  909. {
  910. resultTeacher.list.ForEach(x =>
  911. {
  912. var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId) && z.teacherId.Equals(id));
  913. if (schedulesTeacher.Any())
  914. {
  915. courseIds.Add(x.courseId);
  916. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  917. courseTask.schedules = schedulesTeacher.ToList();
  918. privateTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  919. groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  920. }
  921. });
  922. }
  923. //班級團體人數Dic
  924. Dictionary<string, GroupListMemberCnt> groupCntDic = new Dictionary<string, GroupListMemberCnt>();
  925. if (groupIds.Any())
  926. {
  927. string sqlGroup = $"SELECT c.code, c.id, c.name, c.scope, ARRAY_LENGTH(c.members) as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  928. var resultGroup = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<GroupListMemberCnt>(sqlGroup, $"GroupList");
  929. if (resultGroup.list.IsNotEmpty())
  930. {
  931. foreach (GroupListMemberCnt groupData in resultGroup.list)
  932. {
  933. groupCntDic.Add(groupData.id, groupData);
  934. }
  935. }
  936. }
  937. if (courseIds.Any())
  938. {
  939. GetTeacherInfoApiCourse courseExtobj;
  940. List<GetTeacherInfoApiCourseClass> classes;
  941. string sqlCourse = $"select value c from c where c.creatorId='{id}' and c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
  942. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseBase>(sqlCourse, $"CourseBase");
  943. if (result.list.IsNotEmpty())
  944. {
  945. foreach (var item in result.list)
  946. {
  947. courseExtobj = new GetTeacherInfoApiCourse();
  948. courseExtobj.id = item.id;
  949. courseExtobj.name = item.name;
  950. courseExtobj.scope = item.scope;
  951. classes = new List<GetTeacherInfoApiCourseClass>();
  952. List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
  953. var teacher = privateTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "teacher" });
  954. if (teacher.Any())
  955. {
  956. List<CourseTaskDto> teacherList = teacher.ToList();
  957. foreach (CourseTaskDto teacherTaskDto in teacherList)
  958. {
  959. List<ScheduleTask> schedules = teacherTaskDto.courseTask.schedules;
  960. foreach (ScheduleTask schedule in schedules)
  961. {
  962. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  963. classExtobj.code = null;
  964. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty; ;
  965. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  966. classExtobj.stuListId = schedule.groupId;
  967. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase() { id = id, name = name?.ToString() };
  968. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  969. classExtobj.grpCnt = 0;
  970. classExtobj.gradeId = null;
  971. classExtobj.year = 0;
  972. classes.Add(classExtobj);
  973. }
  974. }
  975. }
  976. courseExtobj.classes = classes;
  977. courses.Add(courseExtobj);
  978. }
  979. }
  980. }
  981. //老師個人課程清單 [舊架構]
  982. //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}") }))
  983. //{
  984. // var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
  985. // if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  986. // {
  987. // GetTeacherInfoApiCourse courseExtobj;
  988. // List<GetTeacherInfoApiCourseClass> classes;
  989. // foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
  990. // {
  991. // courseExtobj = new GetTeacherInfoApiCourse();
  992. // courseExtobj.id = Convert.ToString(obj.GetProperty("id"));
  993. // courseExtobj.name = Convert.ToString(obj.GetProperty("name"));
  994. // courseExtobj.scope = Convert.ToString(obj.GetProperty("scope"));
  995. // classes = new List<GetTeacherInfoApiCourseClass>();
  996. // if (obj.TryGetProperty("schedule", out JsonElement schedule))
  997. // {
  998. // GetTeacherInfoApiCourseClass classExtobj;
  999. // foreach (var scheduleobj in schedule.EnumerateArray())
  1000. // {
  1001. // classExtobj = new GetTeacherInfoApiCourseClass();
  1002. // classExtobj.id = null;
  1003. // classExtobj.code = null;
  1004. // classExtobj.teacher = null;
  1005. // if (scheduleobj.TryGetProperty("teacherId", out JsonElement teacherId) && !string.IsNullOrWhiteSpace(Convert.ToString(teacherId)))
  1006. // {
  1007. // 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") }))
  1008. // {
  1009. // var jsontea = await JsonDocument.ParseAsync(teaitem.ContentStream);
  1010. // foreach (var teaobj in jsontea.RootElement.GetProperty("Documents").EnumerateArray())
  1011. // {
  1012. // classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  1013. // classExtobj.teacher = teaobj.ToObject<GetTeacherInfoApiSimlpeBase>();
  1014. // }
  1015. // }
  1016. // }
  1017. // classExtobj.scope = "private";
  1018. // int stuCount = 0;
  1019. // string stuListId = Convert.ToString(scheduleobj.GetProperty("stulist"));
  1020. // classExtobj.stuListId = stuListId;
  1021. // await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("GroupList") }))
  1022. // {
  1023. // //取得學生總數
  1024. // var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  1025. // foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  1026. // {
  1027. // classExtobj.name = Convert.ToString(stuobj.GetProperty("name"));
  1028. // stuCount += stuobj.GetProperty("scount").GetInt32();
  1029. // stuCount += stuobj.GetProperty("tcount").GetInt32();
  1030. // }
  1031. // }
  1032. // classExtobj.stuCnt = stuCount;
  1033. // classExtobj.grpCnt = 0;
  1034. // classExtobj.gradeId = null;
  1035. // classExtobj.year = 0;
  1036. // classes.Add(classExtobj);
  1037. // }
  1038. // }
  1039. // courseExtobj.classes = classes;
  1040. // courses.Add(courseExtobj);
  1041. // }
  1042. // }
  1043. //}
  1044. //取得老師個人評測
  1045. List<object> exams = new List<object>();
  1046. //取得評測ID List (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中))
  1047. List<string> examIdList = new List<string>();
  1048. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and c.source = '1' AND c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  1049. {
  1050. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  1051. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1052. {
  1053. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1054. {
  1055. examIdList.Add(obj.GetProperty("id").GetString());
  1056. }
  1057. }
  1058. }
  1059. //取得有作答的評測班級
  1060. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  1061. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { }))
  1062. {
  1063. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  1064. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1065. {
  1066. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  1067. {
  1068. string examId = obj.GetProperty("examId").ToString();
  1069. string classId = obj.GetProperty("classId").ToString();
  1070. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  1071. {
  1072. examClassFinDic[examId].Add(classId);
  1073. }
  1074. else
  1075. {
  1076. List<string> classIdList = new List<string>();
  1077. classIdList.Add(classId);
  1078. examClassFinDic.Add(examId, classIdList);
  1079. }
  1080. }
  1081. }
  1082. }
  1083. //取得評測資料
  1084. 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}") }))
  1085. {
  1086. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  1087. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1088. {
  1089. dynamic examExtobj;
  1090. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  1091. {
  1092. examExtobj = new ExpandoObject();
  1093. string examId = obj.GetProperty("id").GetString();
  1094. examExtobj.code = obj.GetProperty("code");
  1095. examExtobj.id = examId;
  1096. examExtobj.name = obj.GetProperty("name");
  1097. examExtobj.createTime = obj.GetProperty("createTime");
  1098. examExtobj.startTime = obj.GetProperty("startTime");
  1099. examExtobj.endTime = obj.GetProperty("endTime");
  1100. examExtobj.year = obj.GetProperty("year");
  1101. examExtobj.source = obj.GetProperty("source");
  1102. examExtobj.type = obj.GetProperty("type");
  1103. examExtobj.progress = obj.GetProperty("progress");
  1104. examExtobj.stuCount = obj.GetProperty("stuCount");
  1105. examExtobj.scope = obj.GetProperty("scope");
  1106. examExtobj.owner = obj.GetProperty("owner");
  1107. examExtobj.period = obj.GetProperty("period");
  1108. examExtobj.grades = obj.GetProperty("grades");
  1109. examExtobj.subjects = obj.GetProperty("subjects");
  1110. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  1111. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  1112. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  1113. examExtobj.papers = obj.GetProperty("papers");
  1114. //examExtobj = obj.ToObject<object>();
  1115. exams.Add(examExtobj);
  1116. }
  1117. }
  1118. }
  1119. //用户在线记录
  1120. try
  1121. {
  1122. _ = _httpTrigger.RequestHttpTrigger(new { school = defaultschool, scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  1123. }
  1124. catch { }
  1125. //取得Teacher Blob 容器位置及SAS
  1126. var container = _azureStorage.GetBlobContainerClient(id);
  1127. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  1128. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  1129. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Read);
  1130. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write);
  1131. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, schools, defaultschool, courses, exams });
  1132. }
  1133. catch (Exception ex)
  1134. {
  1135. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1136. return BadRequest();
  1137. }
  1138. }
  1139. [Authorize(Roles = "HiTeach")]
  1140. [ProducesDefaultResponseType]
  1141. [HttpPost("get-school-info")]
  1142. public async Task<IActionResult> GetSchoolInfo(JsonElement request)
  1143. {
  1144. try
  1145. {
  1146. DateTime nowDate = DateTime.Now;
  1147. string id_token = HttpContext.GetXAuth("IdToken");
  1148. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  1149. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1150. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  1151. var jwt = new JwtSecurityToken(id_token);
  1152. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1153. var id = jwt.Payload.Sub;
  1154. string inputPeriodId = (request.TryGetProperty("periodId", out JsonElement _periodId)) ? _periodId.ToString() : string.Empty; //若未輸入periodId,則所有的學段課程都取
  1155. var client = _azureCosmos.GetCosmosClient();
  1156. //取得學校學段、年級、科目、考試類型
  1157. List<object> periods = new List<object>();
  1158. List<object> grades = new List<object>();
  1159. List<object> subjects = new List<object>();
  1160. List<object> examTypes = new List<object>();
  1161. List<string> periodIds = new List<string>(); //學段ID 取得課程時限制用
  1162. string lang = "en-us";
  1163. School school_base = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
  1164. if (!string.IsNullOrWhiteSpace(school_base.id))
  1165. {
  1166. //語系取得
  1167. if (!string.IsNullOrEmpty(school_base.region))
  1168. {
  1169. if (school_base.region.Equals("中国")) lang = "zh-cn";
  1170. else if (school_base.region.Equals("臺灣")) lang = "zh-tw";
  1171. }
  1172. else
  1173. {
  1174. if (_option.Location.Equals("China")) lang = "zh-cn";
  1175. }
  1176. //年級、科目、評測類型取得
  1177. foreach (Period periodinfo in school_base.period)
  1178. {
  1179. if (!string.IsNullOrWhiteSpace(inputPeriodId))
  1180. {
  1181. if (!periodinfo.id.Equals(inputPeriodId)) continue; //若有給periodId,則過濾學段;未給則全取
  1182. }
  1183. periods.Add(new { id = periodinfo.id, name = periodinfo.name, subjects = periodinfo.subjects });
  1184. periodIds.Add(periodinfo.id);
  1185. int gradeIndex = 0;
  1186. foreach (string gradeName in periodinfo.grades)
  1187. {
  1188. grades.Add(new { id = gradeIndex.ToString(), name = gradeName, periodId = periodinfo.id });
  1189. gradeIndex++;
  1190. }
  1191. foreach (Subject subjectinfo in periodinfo.subjects)
  1192. {
  1193. subjects.Add(new { id = subjectinfo.id, name = subjectinfo.name, periodId = periodinfo.id });
  1194. }
  1195. foreach (var examType in periodinfo.analysis.type)
  1196. {
  1197. examTypes.Add(examType);
  1198. }
  1199. }
  1200. }
  1201. else //無此學校資料
  1202. {
  1203. return BadRequest();
  1204. }
  1205. //該老師排定的學校課程
  1206. List<object> courses = new List<object>();
  1207. List<string> classIds = new List<string>(); //班級ID列表 篩選校園評測用
  1208. List<string> groupIds = new List<string>(); //團體ID列表 篩選校園評測用
  1209. //取得所有班级
  1210. (List<Class> school_classes, _) = await SchoolService.DoGraduateClasses(_httpTrigger, _azureCosmos, null, school_base, _option, _dingDing);
  1211. //取得學校安排老師課程
  1212. HashSet<string> courseIds = new HashSet<string>();
  1213. List<KeyValuePair<string, CourseTask>> schoolTeacherTask = new List<KeyValuePair<string, CourseTask>>(); //key:courseId value:CourseTask
  1214. List<KeyValuePair<string, CourseTask>> schoolAssistantTask = new List<KeyValuePair<string, CourseTask>>();
  1215. List<string> periodIdList = new List<string>();
  1216. if (string.IsNullOrWhiteSpace(inputPeriodId)) periodIdList = periodIds;
  1217. else periodIdList.Add(inputPeriodId);
  1218. Dictionary<string, GroupListMemberCnt> groupCntDic = new Dictionary<string, GroupListMemberCnt>(); //班級團體+人數Dic
  1219. foreach (string periodId in periodIdList)
  1220. {
  1221. var period = school_base.period.Find(x => x.id.Equals(periodId));
  1222. (Semester currSemester, int studyYear, DateTimeOffset currSemesterDate, DateTimeOffset date, DateTimeOffset nextSemester) info = SchoolService.GetSemester(period, 0, nowDate.ToString());
  1223. int studyYear = (request.TryGetProperty("year", out JsonElement _year)) ? _year.GetInt32() : info.studyYear;
  1224. string semesterId = (request.TryGetProperty("semesterId", out JsonElement _semesterId)) ? _semesterId.GetString() : info.currSemester.id;
  1225. //string date = SchoolService.GetOpensByStudyYearAndSemester(period.semesters, int.Parse($"{_year}"), $"{_semesterId}");
  1226. string sql = $"SELECT distinct value c FROM c join b in c.schedules where c.pk='CourseTask' and ( b.teacherId ='{id}' OR ARRAY_CONTAINS(b.assistants,'{id}'))";
  1227. sql = $"{sql} and c.year={studyYear} and c.semesterId='{semesterId}'";
  1228. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseTask>(sql, $"CourseTask-{school_code}");
  1229. if (resultSchool.list.IsNotEmpty())
  1230. {
  1231. resultSchool.list.ForEach(x =>
  1232. {
  1233. var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId) && z.teacherId.Equals(id));
  1234. if (schedulesTeacher.Any())
  1235. {
  1236. courseIds.Add(x.courseId);
  1237. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  1238. courseTask.schedules = schedulesTeacher.ToList();
  1239. schoolTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  1240. groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  1241. }
  1242. var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(id));
  1243. if (schedulesAssistant.Any())
  1244. {
  1245. courseIds.Add(x.courseId);
  1246. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  1247. courseTask.schedules = schedulesAssistant.ToList();
  1248. schoolAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  1249. groupIds.AddRange(schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  1250. }
  1251. });
  1252. }
  1253. //班級團體人數Dic
  1254. if (groupIds.Any())
  1255. {
  1256. string sqlGroup = $"SELECT c.code, c.id, c.name, c.scope, c.year, 'teach' as type, ARRAY_LENGTH(c.members) as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  1257. var resultGroup = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<GroupListMemberCnt>(sqlGroup, $"GroupList-{school_code}");
  1258. if (resultGroup.list.IsNotEmpty())
  1259. {
  1260. foreach (GroupListMemberCnt groupData in resultGroup.list)
  1261. {
  1262. if (!groupCntDic.ContainsKey(groupData.id))
  1263. {
  1264. groupCntDic.Add(groupData.id, groupData);
  1265. }
  1266. }
  1267. }
  1268. string sqlClass = $"SELECT c.code, c.id, c.name, c.scope, c.year, 'class' as type, 0 as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  1269. var resultClass = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<GroupListMemberCnt>(sqlClass, $"Class-{school_code}");
  1270. if (resultClass.list.IsNotEmpty())
  1271. {
  1272. foreach (GroupListMemberCnt groupData in resultClass.list)
  1273. {
  1274. if (!groupCntDic.ContainsKey(groupData.id))
  1275. {
  1276. groupCntDic.Add(groupData.id, groupData);
  1277. }
  1278. }
  1279. }
  1280. //取得學生數
  1281. foreach (KeyValuePair<string, GroupListMemberCnt> groupCntItem in groupCntDic)
  1282. {
  1283. string classId = groupCntItem.Key;
  1284. var sqlStudent = $"SELECT VALUE Count(1) FROM c WHERE c.classId = '{classId}'";
  1285. await foreach (int itemclstc in client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetItemQueryIterator<int>(queryText: sqlStudent, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  1286. {
  1287. if (itemclstc > 0)
  1288. {
  1289. groupCntDic[classId].memberCount = itemclstc;
  1290. }
  1291. }
  1292. }
  1293. }
  1294. }
  1295. if (courseIds.Any())
  1296. {
  1297. GetSchoolInfoApiCourse courseExtobj;
  1298. List<GetTeacherInfoApiCourseClass> classes;
  1299. string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
  1300. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase-{school_code}");
  1301. if (result.list.IsNotEmpty())
  1302. {
  1303. foreach (var item in result.list)
  1304. {
  1305. courseExtobj = new GetSchoolInfoApiCourse();
  1306. courseExtobj.id = item.id;
  1307. courseExtobj.name = item.name;
  1308. courseExtobj.scope = item.scope;
  1309. courseExtobj.subject = new GetTeacherInfoApiSimlpeBase();
  1310. courseExtobj.subject.id = item.subject.id;
  1311. courseExtobj.subject.name = item.subject.name;
  1312. var period = school_base.period.Find(p => p.subjects.Exists(s => s.id.Equals(item.subject.id)));
  1313. classes = new List<GetTeacherInfoApiCourseClass>();
  1314. //任教教師
  1315. //List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
  1316. var teacher = schoolTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "teacher" });
  1317. if (teacher.Any() && period != null)
  1318. {
  1319. List<CourseTaskDto> teacherList = teacher.ToList();
  1320. foreach (CourseTaskDto teacherTaskDto in teacherList)
  1321. {
  1322. List<ScheduleTask> schedules = teacherTaskDto.courseTask.schedules;
  1323. foreach (ScheduleTask schedule in schedules)
  1324. {
  1325. Class classInfo = school_classes.Where((Class x) => x.id == schedule.groupId).FirstOrDefault();
  1326. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  1327. classExtobj.code = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].code : string.Empty;
  1328. classExtobj.id = schedule.groupId;
  1329. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty;
  1330. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  1331. string classType = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].type : schedule.type; /// class:編制班 teach:選課班
  1332. classExtobj.stuListId = (classType.Equals("teach")) ? schedule.groupId : string.Empty; ///選課班 stuListId才填值
  1333. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  1334. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.id)) classExtobj.teacher.id = classInfo.teacher.id;
  1335. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.name)) classExtobj.teacher.name = classInfo.teacher.name;
  1336. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  1337. classExtobj.gradeId = null;
  1338. classExtobj.gradeName = null;
  1339. classExtobj.year = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].year : teacherTaskDto.courseTask.year;
  1340. var grpCnt = schedules.GroupBy(c => c.groupId).Select(x => new { Count = x.Count() });
  1341. classExtobj.grpCnt = grpCnt.FirstOrDefault().Count;
  1342. var gradeInfo = getGradeInfoByYear(classExtobj.year, period);
  1343. if (!string.IsNullOrWhiteSpace(gradeInfo.id))
  1344. {
  1345. classExtobj.gradeId = gradeInfo.id;
  1346. if (gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  1347. else if (gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  1348. else classExtobj.gradeName = gradeInfo.name;
  1349. }
  1350. classes.Add(classExtobj);
  1351. }
  1352. }
  1353. }
  1354. //協同教師
  1355. var assistant = schoolAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "assistant" });
  1356. if (assistant.Any() && period != null)
  1357. {
  1358. List<CourseTaskDto> assistantList = assistant.ToList();
  1359. foreach (CourseTaskDto assistantTaskDto in assistantList)
  1360. {
  1361. List<ScheduleTask> schedules = assistantTaskDto.courseTask.schedules;
  1362. foreach (ScheduleTask schedule in schedules)
  1363. {
  1364. Class classInfo = school_classes.Where((Class x) => x.id == schedule.groupId).FirstOrDefault();
  1365. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  1366. classExtobj.code = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].code : string.Empty;
  1367. classExtobj.id = schedule.groupId;
  1368. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty;
  1369. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  1370. string classType = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].type : schedule.type; /// class:編制班 teach:選課班
  1371. classExtobj.stuListId = (classType.Equals("teach")) ? schedule.groupId : string.Empty; ///選課班 stuListId才填值
  1372. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  1373. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.id)) classExtobj.teacher.id = classInfo.teacher.id;
  1374. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.name)) classExtobj.teacher.name = classInfo.teacher.name;
  1375. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  1376. classExtobj.gradeId = null;
  1377. classExtobj.gradeName = null;
  1378. classExtobj.year = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].year : assistantTaskDto.courseTask.year;
  1379. var grpCnt = schedules.GroupBy(c => c.groupId).Select(x => new { Count = x.Count() });
  1380. classExtobj.grpCnt = grpCnt.FirstOrDefault().Count;
  1381. var gradeInfo = getGradeInfoByYear(classExtobj.year, period);
  1382. if (!string.IsNullOrWhiteSpace(gradeInfo.id))
  1383. {
  1384. classExtobj.gradeId = gradeInfo.id;
  1385. if (gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  1386. else if (gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  1387. else classExtobj.gradeName = gradeInfo.name;
  1388. }
  1389. classes.Add(classExtobj);
  1390. }
  1391. }
  1392. }
  1393. courseExtobj.classes = classes;
  1394. courses.Add(courseExtobj);
  1395. }
  1396. }
  1397. }
  1398. #region ====舊課程架構===
  1399. //如果没传,则以当前时间获取学年和学期信息
  1400. //[取得當前學年方法 備用] (Semester currSemester, int studyYear, DateTimeOffset date, DateTimeOffset nextSemester) info = SchoolService.GetSemester(period);
  1401. //var gradeInfo = getGradeInfoByYear(classInfo.year, curPeriod);
  1402. //var query = $"SELECT DISTINCT c.id, c.name, c.scope, c.subject, c.period.id AS periodId, schedule.classId AS scheduleClassId, schedule.stulist AS scheduleStulist, schedule.notice AS scheduleNotice FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
  1403. //if(periodIds.Count > 0)
  1404. //{
  1405. // string periodIdsJsonStr = JsonSerializer.Serialize(periodIds);
  1406. // query += $" AND ARRAY_CONTAINS({periodIdsJsonStr}, c.period.id, true)";
  1407. //}
  1408. ////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}'";
  1409. //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  1410. //{
  1411. // var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
  1412. // if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1413. // {
  1414. // foreach (var obj in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
  1415. // {
  1416. // dynamic classExtobj = new ExpandoObject();
  1417. // classExtobj.id = null;
  1418. // classExtobj.code = null;
  1419. // classExtobj.name = null;
  1420. // classExtobj.scope = null;
  1421. // classExtobj.gradeId = null;
  1422. // classExtobj.gradeName = null;
  1423. // classExtobj.year = 0;
  1424. // classExtobj.teacher = null;
  1425. // classExtobj.stuListId = null;
  1426. // classExtobj.stuCnt = 0;
  1427. // classExtobj.grpCnt = 0;
  1428. // //編制班
  1429. // string classIdNow = string.Empty;
  1430. // if (obj.TryGetProperty("scheduleClassId", out JsonElement scheduleClassId))
  1431. // {
  1432. // classIdNow = Convert.ToString(scheduleClassId);
  1433. // }
  1434. // if (!string.IsNullOrWhiteSpace(classIdNow))
  1435. // {
  1436. // Class classInfo = school_classes.Where((Class x) => x.id == classIdNow).FirstOrDefault();
  1437. // if(classInfo != null && !string.IsNullOrWhiteSpace(classInfo.id))
  1438. // {
  1439. // classIds.Add(classInfo.id);
  1440. // classExtobj.id = classInfo.id;
  1441. // classExtobj.code = classInfo.code;
  1442. // classExtobj.name = classInfo.name;
  1443. // classExtobj.year = classInfo.year;
  1444. // classExtobj.teacher = classInfo.teacher;
  1445. // //取得學生數
  1446. // var queryclstc = $"SELECT Count(1) AS stuCnt FROM c WHERE c.classId = '{classIdNow}'";
  1447. // await foreach (var itemclstc in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclstc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  1448. // {
  1449. // var jsonclstc = await JsonDocument.ParseAsync(itemclstc.ContentStream);
  1450. // foreach (var objstc in jsonclstc.RootElement.GetProperty("Documents").EnumerateArray())
  1451. // {
  1452. // classExtobj.stuCnt = objstc.GetProperty("stuCnt").GetInt32();
  1453. // }
  1454. // }
  1455. // //取得分組數
  1456. // var queryclgp = $"SELECT c.groupId FROM c WHERE c.classId = '{classIdNow}' AND IS_NULL(c.groupId)=false GROUP BY c.groupId";
  1457. // await foreach (var itemgp in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclgp, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  1458. // {
  1459. // var jsongp = await JsonDocument.ParseAsync(itemgp.ContentStream);
  1460. // if (jsongp.RootElement.TryGetProperty("_count", out JsonElement gpcount) && gpcount.GetInt32() > 0)
  1461. // {
  1462. // classExtobj.grpCnt = gpcount.GetInt32();
  1463. // }
  1464. // }
  1465. // //取得年級
  1466. // if (obj.TryGetProperty("periodId", out JsonElement curPeriodIdJson))
  1467. // {
  1468. // string curPeriodId = curPeriodIdJson.GetString();
  1469. // Period curPeriod = school_base.period.Where((Period x) => x.id == curPeriodId).FirstOrDefault();
  1470. // if(curPeriodId != null)
  1471. // {
  1472. // var gradeInfo = getGradeInfoByYear(classInfo.year, curPeriod);
  1473. // if(!string.IsNullOrWhiteSpace(gradeInfo.id))
  1474. // {
  1475. // classExtobj.gradeId = gradeInfo.id;
  1476. // if(gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  1477. // else if(gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  1478. // else classExtobj.gradeName = gradeInfo.name;
  1479. // }
  1480. // }
  1481. // }
  1482. // }
  1483. // }
  1484. // //選課班 (過期者不選)
  1485. // long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1486. // var stuListId = obj.GetProperty("scheduleStulist");
  1487. // if (!stuListId.ValueKind.Equals(JsonValueKind.Null) && !string.IsNullOrWhiteSpace(stuListId.GetString()))
  1488. // {
  1489. // classExtobj.stuListId = Convert.ToString(stuListId);
  1490. // await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.year, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}' AND ( c.expire = 0 OR IS_DEFINED(c.expire) = false OR c.expire >={nowtime} )", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList-{school_code}") }))
  1491. // {
  1492. // var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  1493. // foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  1494. // {
  1495. // groupIds.Add(Convert.ToString(stuobj.GetProperty("id")));
  1496. // classExtobj.id = Convert.ToString(stuobj.GetProperty("id"));
  1497. // classExtobj.code = Convert.ToString(stuobj.GetProperty("code"));
  1498. // classExtobj.name = Convert.ToString(stuobj.GetProperty("name"));
  1499. // classExtobj.gradeId = null;
  1500. // classExtobj.year = (stuobj.TryGetProperty("year", out JsonElement yearJson)) ? yearJson.GetInt32() : 0;
  1501. // classExtobj.teacher = null;
  1502. // classExtobj.stuCnt += stuobj.GetProperty("tcount").GetInt32();
  1503. // classExtobj.stuCnt += stuobj.GetProperty("scount").GetInt32();
  1504. // }
  1505. // }
  1506. // }
  1507. // //課程
  1508. // string courseIdNow = obj.GetProperty("id").ToString();
  1509. // var courseExist = courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  1510. // if (courseExist == null)
  1511. // {
  1512. // dynamic courseExtobj = new ExpandoObject();
  1513. // courseExtobj.id = courseIdNow;
  1514. // courseExtobj.name = obj.GetProperty("name").ToString();
  1515. // courseExtobj.scope = obj.GetProperty("scope").ToString();
  1516. // courseExtobj.classes = new List<object>();
  1517. // courseExtobj.subject = obj.GetProperty("subject");
  1518. // //classExtobj.teacher = scheduleTeacherInfo;
  1519. // if (!string.IsNullOrWhiteSpace(classExtobj.id))
  1520. // {
  1521. // courseExtobj.classes.Add(classExtobj);
  1522. // }
  1523. // courses.Add(courseExtobj);
  1524. // }
  1525. // else
  1526. // {
  1527. // //classExtobj.teacher = scheduleTeacherInfo;
  1528. // if (!string.IsNullOrWhiteSpace(classExtobj.id))
  1529. // {
  1530. // courseExist.classes.Add(classExtobj);
  1531. // }
  1532. // }
  1533. // }
  1534. // }
  1535. //}
  1536. #endregion
  1537. //取得校園評測 (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中)) && 排定的學校課程班級
  1538. List<object> exams = new List<object>();
  1539. string classSqlString = "";
  1540. if (classIds.Count > 0 || groupIds.Count > 0)
  1541. {
  1542. if (classIds.Count > 0)
  1543. {
  1544. foreach (string classId in classIds)
  1545. {
  1546. if (!string.IsNullOrWhiteSpace(classSqlString))
  1547. {
  1548. classSqlString += " OR ";
  1549. }
  1550. classSqlString += $"ARRAY_CONTAINS(c.classes, '{classId}')";
  1551. }
  1552. }
  1553. if (groupIds.Count > 0)
  1554. {
  1555. foreach (string groupId in groupIds)
  1556. {
  1557. if (!string.IsNullOrWhiteSpace(classSqlString))
  1558. {
  1559. classSqlString += " OR ";
  1560. }
  1561. classSqlString += $"ARRAY_CONTAINS(c.classes, '{groupId}')";
  1562. }
  1563. }
  1564. }
  1565. else //無分配任何課程教室,校園評測不取
  1566. {
  1567. classSqlString += "c.id = '0'";
  1568. }
  1569. classSqlString = $"AND ({classSqlString})";
  1570. //取得評測ID List
  1571. List<string> examIdList = new List<string>();
  1572. 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}") }))
  1573. {
  1574. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  1575. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1576. {
  1577. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1578. {
  1579. examIdList.Add(obj.GetProperty("id").GetString());
  1580. }
  1581. }
  1582. }
  1583. //取得有作答的評測班級、已完成的 評測ID、班級ID、科目ID 列表製作
  1584. List<ExamFinishClassesSubList> examFinClassSubList = new List<ExamFinishClassesSubList>(); //已完成的 評測ID、班級ID、科目ID 列表
  1585. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId, c.subjectId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school_code}") }))
  1586. {
  1587. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  1588. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1589. {
  1590. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  1591. {
  1592. string examId = obj.GetProperty("examId").ToString();
  1593. string classId = obj.GetProperty("classId").ToString();
  1594. string subjectId = obj.GetProperty("subjectId").ToString();
  1595. ExamFinishClassesSubList existExamFinishClassesSubRow = examFinClassSubList.Where(e => e.examId == examId && e.classId == classId && e.subjectId == subjectId).FirstOrDefault();
  1596. if (existExamFinishClassesSubRow == null)
  1597. {
  1598. ExamFinishClassesSubList ExamFinishClassesSubRow = new ExamFinishClassesSubList();
  1599. ExamFinishClassesSubRow.examId = examId;
  1600. ExamFinishClassesSubRow.classId = classId;
  1601. ExamFinishClassesSubRow.subjectId = subjectId;
  1602. examFinClassSubList.Add(ExamFinishClassesSubRow);
  1603. }
  1604. }
  1605. }
  1606. }
  1607. //取得評測資料
  1608. 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}") }))
  1609. {
  1610. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  1611. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1612. {
  1613. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  1614. {
  1615. dynamic examExtobj = new ExpandoObject();
  1616. string examId = obj.GetProperty("id").GetString();
  1617. examExtobj.code = obj.GetProperty("code");
  1618. examExtobj.id = examId;
  1619. examExtobj.name = obj.GetProperty("name");
  1620. examExtobj.createTime = obj.GetProperty("createTime");
  1621. examExtobj.startTime = obj.GetProperty("startTime");
  1622. examExtobj.endTime = obj.GetProperty("endTime");
  1623. examExtobj.year = obj.GetProperty("year");
  1624. examExtobj.source = obj.GetProperty("source");
  1625. examExtobj.type = obj.GetProperty("type");
  1626. examExtobj.progress = obj.GetProperty("progress");
  1627. examExtobj.stuCount = obj.GetProperty("stuCount");
  1628. examExtobj.scope = obj.GetProperty("scope");
  1629. examExtobj.owner = obj.GetProperty("owner");
  1630. examExtobj.period = obj.GetProperty("period");
  1631. examExtobj.grades = obj.GetProperty("grades");
  1632. examExtobj.subjects = (obj.TryGetProperty("subjects", out JsonElement subjectsJobj)) ? subjectsJobj.ToObject<List<GetSchInfoExamSubject>>() : new List<GetSchInfoExamSubject>();
  1633. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  1634. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  1635. examExtobj.papers = obj.GetProperty("papers");
  1636. //examExtobj = obj.ToObject<object>();
  1637. //須完成列表、已完成班級列表生成
  1638. examExtobj.finishClasses = new List<string>();
  1639. examExtobj.finishClassesSub = new List<GetSchInfoExamFinishClassesSub>();
  1640. List<GetSchInfoExamFinishClassesSub> examClassSubjectList = new List<GetSchInfoExamFinishClassesSub>();
  1641. foreach (string classIdNow in examExtobj.classes)
  1642. {
  1643. GetSchInfoExamFinishClassesSub examClassSubjectRow = examClassSubjectList.Where(x => x.classId == classIdNow).FirstOrDefault();
  1644. if (examClassSubjectRow == null)
  1645. {
  1646. examClassSubjectRow = new GetSchInfoExamFinishClassesSub();
  1647. examClassSubjectRow.classId = classIdNow;
  1648. examClassSubjectRow.subjectIds = new List<string>();
  1649. examClassSubjectRow.finishSubjectIds = new List<string>();
  1650. }
  1651. foreach (GetSchInfoExamSubject subjectNow in examExtobj.subjects)
  1652. {
  1653. string subjectIdNow = subjectNow.id;
  1654. if (!examClassSubjectRow.subjectIds.Contains(subjectIdNow))
  1655. {
  1656. examClassSubjectRow.subjectIds.Add(subjectIdNow);
  1657. }
  1658. ExamFinishClassesSubList existFinClassesSub = examFinClassSubList.Where(e => e.examId == examId && e.classId == classIdNow && e.subjectId == subjectIdNow).FirstOrDefault();
  1659. if (existFinClassesSub != null)
  1660. {
  1661. examClassSubjectRow.finishSubjectIds.Add(subjectIdNow);
  1662. }
  1663. }
  1664. examExtobj.finishClassesSub.Add(examClassSubjectRow);
  1665. if (examClassSubjectRow.subjectIds.Count.Equals(examClassSubjectRow.finishSubjectIds.Count))
  1666. {
  1667. if (!examExtobj.finishClasses.Contains(classIdNow))
  1668. {
  1669. examExtobj.finishClasses.Add(classIdNow);
  1670. }
  1671. }
  1672. }
  1673. exams.Add(examExtobj);
  1674. }
  1675. }
  1676. }
  1677. //用户在线记录
  1678. try
  1679. {
  1680. _ = _httpTrigger.RequestHttpTrigger(new { school = school_code.GetString(), scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  1681. }
  1682. catch { }
  1683. //取得School Blob 容器位置及SAS
  1684. string school_code_blob = school_code.GetString().ToLower();
  1685. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列
  1686. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
  1687. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write); //寫
  1688. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, periods, grades, subjects, courses, examTypes, exams });
  1689. }
  1690. catch (Exception ex)
  1691. {
  1692. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach/GetSchoolInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1693. return BadRequest();
  1694. }
  1695. }
  1696. //取得試卷
  1697. [Authorize(Roles = "HiTeach")]
  1698. [ProducesDefaultResponseType]
  1699. [HttpPost("get-paper")]
  1700. public async Task<IActionResult> GetPaperList(JsonElement request)
  1701. {
  1702. try
  1703. {
  1704. //Header驗證
  1705. string id_token = HttpContext.GetXAuth("IdToken");
  1706. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1707. var jwt = new JwtSecurityToken(id_token);
  1708. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1709. var id = jwt.Payload.Sub;
  1710. var client = _azureCosmos.GetCosmosClient();
  1711. //參數
  1712. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  1713. string partitionid = string.Empty;
  1714. string container = string.Empty;
  1715. if (grant_type.ToString().Equals("school"))
  1716. {
  1717. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  1718. {
  1719. return BadRequest();
  1720. }
  1721. else
  1722. {
  1723. partitionid = school_code_json.ToString();
  1724. container = "School";
  1725. }
  1726. }
  1727. else
  1728. {
  1729. partitionid = id.ToString();
  1730. container = "Teacher";
  1731. }
  1732. //SQL文
  1733. List<object> papers = new List<object>();
  1734. string queryWhere = " WHERE 1=1 ";
  1735. string queryOption = string.Empty;
  1736. //學段
  1737. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  1738. {
  1739. queryWhere += $" AND c.periodId = '{periodId}'";
  1740. }
  1741. //年級
  1742. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  1743. {
  1744. string queryOptionForGrade = string.Empty;
  1745. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  1746. {
  1747. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  1748. {
  1749. queryOptionForGrade += " OR ";
  1750. }
  1751. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  1752. }
  1753. queryWhere += $" AND ( {queryOptionForGrade} )";
  1754. }
  1755. //科目ID
  1756. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("School"))
  1757. {
  1758. queryWhere += $" AND c.subjectId = '{subjectId}'";
  1759. }
  1760. //科目名稱
  1761. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  1762. {
  1763. queryWhere += $" AND c.subjectName = '{subjectName}'";
  1764. }
  1765. //試卷ID
  1766. if (request.TryGetProperty("id", out JsonElement paperId))
  1767. {
  1768. queryWhere += $" AND c.id = '{paperId}'";
  1769. }
  1770. int perpage = 0; //每頁幾條
  1771. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  1772. int page = 0; //目前第幾頁
  1773. if (request.TryGetProperty("page", out JsonElement page_json))
  1774. {
  1775. if (page_json.GetInt32() > 0)
  1776. {
  1777. page = page_json.GetInt32() - 1;
  1778. }
  1779. }
  1780. string order = "createTime"; //排序項目
  1781. if (request.TryGetProperty("order", out JsonElement order_json))
  1782. {
  1783. if (order_json.ToString().Equals("useCount"))
  1784. {
  1785. order = order_json.ToString();
  1786. }
  1787. }
  1788. queryOption += $" Order By c." + order + " DESC ";
  1789. if (perpage > 0)
  1790. {
  1791. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  1792. }
  1793. //資料取得
  1794. 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, c.scope From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  1795. {
  1796. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1797. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1798. {
  1799. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1800. {
  1801. papers.Add(obj.ToObject<object>());
  1802. }
  1803. }
  1804. }
  1805. //總件數
  1806. int totalCount = 0;
  1807. 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}") }))
  1808. {
  1809. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1810. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1811. {
  1812. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1813. {
  1814. totalCount = obj.GetInt32();
  1815. }
  1816. }
  1817. }
  1818. return Ok(new { papers, totalCount });
  1819. }
  1820. catch (Exception ex)
  1821. {
  1822. return BadRequest();
  1823. }
  1824. }
  1825. //取得試題
  1826. [Authorize(Roles = "HiTeach")]
  1827. [ProducesDefaultResponseType]
  1828. [HttpPost("get-item")]
  1829. public async Task<IActionResult> GetItemList(JsonElement request)
  1830. {
  1831. try
  1832. {
  1833. //Header驗證
  1834. string id_token = HttpContext.GetXAuth("IdToken");
  1835. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1836. var jwt = new JwtSecurityToken(id_token);
  1837. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1838. var id = jwt.Payload.Sub;
  1839. var client = _azureCosmos.GetCosmosClient();
  1840. //參數
  1841. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  1842. string partitionid = string.Empty;
  1843. string container = string.Empty;
  1844. if (grant_type.ToString().Equals("school"))
  1845. {
  1846. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  1847. {
  1848. return BadRequest();
  1849. }
  1850. else
  1851. {
  1852. partitionid = school_code_json.ToString();
  1853. container = "School";
  1854. }
  1855. }
  1856. else
  1857. {
  1858. partitionid = id.ToString();
  1859. container = "Teacher";
  1860. }
  1861. //SQL文
  1862. List<object> items = new List<object>();
  1863. string queryWhere = " WHERE 1=1 ";
  1864. string queryOption = string.Empty;
  1865. //學段
  1866. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  1867. {
  1868. queryWhere += $" AND c.periodId = '{periodId}'";
  1869. }
  1870. //年級
  1871. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  1872. {
  1873. string queryOptionForGrade = string.Empty;
  1874. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  1875. {
  1876. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  1877. {
  1878. queryOptionForGrade += " OR ";
  1879. }
  1880. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  1881. }
  1882. queryWhere += $" AND ( {queryOptionForGrade} )";
  1883. }
  1884. //科目ID
  1885. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("school"))
  1886. {
  1887. queryWhere += $" AND c.subjectId = '{subjectId}'";
  1888. }
  1889. //科目名稱
  1890. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  1891. {
  1892. queryWhere += $" AND c.subjectName = '{subjectName}'";
  1893. }
  1894. //題型
  1895. int dummy = 0;
  1896. if (request.TryGetProperty("type", out JsonElement type))
  1897. {
  1898. queryWhere += $" AND c.type = '{type}'";
  1899. }
  1900. //難度
  1901. dummy = 0;
  1902. if (request.TryGetProperty("level", out JsonElement level) && int.TryParse(level.ToString(), out dummy))
  1903. {
  1904. queryWhere += $" AND c.level = {level}";
  1905. }
  1906. //層次
  1907. if (request.TryGetProperty("field", out JsonElement field) && int.TryParse(field.ToString(), out dummy))
  1908. {
  1909. queryWhere += $" AND c.field = {field}";
  1910. }
  1911. int perpage = 0; //每頁幾條
  1912. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  1913. int page = 0; //目前第幾頁
  1914. if (request.TryGetProperty("page", out JsonElement page_json))
  1915. {
  1916. if (page_json.GetInt32() > 0)
  1917. {
  1918. page = page_json.GetInt32() - 1;
  1919. }
  1920. }
  1921. string order = "createTime"; //排序項目
  1922. if (request.TryGetProperty("order", out JsonElement order_json))
  1923. {
  1924. if (order_json.ToString().Equals("useCount"))
  1925. {
  1926. order = order_json.ToString();
  1927. }
  1928. }
  1929. queryOption += $" Order By c." + order + " DESC ";
  1930. if (perpage > 0)
  1931. {
  1932. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  1933. }
  1934. //資料取得
  1935. 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}") }))
  1936. {
  1937. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1938. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1939. {
  1940. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1941. {
  1942. items.Add(obj.ToObject<object>());
  1943. }
  1944. }
  1945. }
  1946. //總件數
  1947. int totalCount = 0;
  1948. 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}") }))
  1949. {
  1950. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1951. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1952. {
  1953. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1954. {
  1955. totalCount = obj.GetInt32();
  1956. }
  1957. }
  1958. }
  1959. return Ok(new { items, totalCount });
  1960. }
  1961. catch (Exception ex)
  1962. {
  1963. return BadRequest();
  1964. }
  1965. }
  1966. //取得知識點
  1967. [Authorize(Roles = "HiTeach")]
  1968. [ProducesDefaultResponseType]
  1969. [HttpPost("get-knowledge")]
  1970. public async Task<IActionResult> GetKnowledgePointList(JsonElement request)
  1971. {
  1972. var client = _azureCosmos.GetCosmosClient();
  1973. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  1974. //知識點
  1975. List<object> points = new List<object>();
  1976. 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}") }))
  1977. {
  1978. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1979. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1980. {
  1981. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1982. {
  1983. points.Add(obj.ToObject<object>());
  1984. }
  1985. }
  1986. }
  1987. return Ok(points);
  1988. }
  1989. //取得課綱
  1990. [Authorize(Roles = "HiTeach")]
  1991. [ProducesDefaultResponseType]
  1992. [HttpPost("get-syllabus")]
  1993. public async Task<IActionResult> GetSyllabusList(JsonElement request)
  1994. {
  1995. //Header驗證
  1996. string id_token = HttpContext.GetXAuth("IdToken");
  1997. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1998. var jwt = new JwtSecurityToken(id_token);
  1999. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  2000. var id = jwt.Payload.Sub;
  2001. //參數驗證
  2002. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  2003. string grantType = (grant_type.GetString().Equals("school")) ? "school" : "private";
  2004. JsonElement school_code = new();
  2005. if (grantType == "school" && !request.TryGetProperty("school_code", out school_code)) return BadRequest();
  2006. string dataId = (grantType.Equals("school")) ? school_code.GetString() : id;
  2007. string container = (grantType.Equals("school")) ? "School" : "Teacher";
  2008. string periodId = (request.TryGetProperty("periodId", out JsonElement periodIdJobj)) ? periodIdJobj.GetString() : String.Empty;
  2009. string subjectId = (request.TryGetProperty("subjectId", out JsonElement subjectIdJobj)) ? subjectIdJobj.GetString() : String.Empty;
  2010. string volumeId = (request.TryGetProperty("volumeId", out JsonElement volumeIdJobj)) ? volumeIdJobj.GetString() : String.Empty;
  2011. string syllabusId = (request.TryGetProperty("syllabusId", out JsonElement syllabusIdJobj)) ? syllabusIdJobj.GetString() : String.Empty;
  2012. int display = (request.TryGetProperty("display", out JsonElement displayJobj)) ? displayJobj.GetInt32() : 0;
  2013. var client = _azureCosmos.GetCosmosClient();
  2014. //取得卷前置作業:有給syllabusId,取得該課綱的卷ID
  2015. string volumeIdFromSyllabusId = String.Empty;
  2016. if (!string.IsNullOrWhiteSpace(syllabusId))
  2017. {
  2018. await foreach (string volid in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIterator<string>(queryText: $"SELECT value(c.volumeId) FROM c WHERE c.id = '{syllabusId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  2019. {
  2020. volumeIdFromSyllabusId = volid;
  2021. }
  2022. }
  2023. //取得卷
  2024. List<object> volumes = new();
  2025. List<Volume> volumeList = new();
  2026. List<string> volumeIdList = new();
  2027. string queryTextVol = string.Empty;
  2028. List<string> WhereVol = new();
  2029. if (!string.IsNullOrWhiteSpace(periodId)) WhereVol.Add($" c.periodId = '{periodId}' ");
  2030. if (!string.IsNullOrWhiteSpace(subjectId)) WhereVol.Add($" c.subjectId = '{subjectId}' ");
  2031. if (!string.IsNullOrWhiteSpace(volumeId)) WhereVol.Add($" c.id = '{volumeId}' ");
  2032. if (!string.IsNullOrWhiteSpace(volumeIdFromSyllabusId)) WhereVol.Add($" c.id = '{volumeIdFromSyllabusId}' ");
  2033. foreach (string Where in WhereVol)
  2034. {
  2035. queryTextVol += (String.IsNullOrWhiteSpace(queryTextVol)) ? $" WHERE {Where} " : $" AND {Where} ";
  2036. }
  2037. queryTextVol = "SELECT * FROM c " + queryTextVol;
  2038. await foreach (var itemv in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: queryTextVol, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{dataId}") }))
  2039. {
  2040. var jsons = await JsonDocument.ParseAsync(itemv.ContentStream);
  2041. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2042. {
  2043. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  2044. {
  2045. Volume volExtobj = obj.ToObject<Volume>();
  2046. volumeList.Add(volExtobj);
  2047. volumeIdList.Add(volExtobj.id);
  2048. }
  2049. }
  2050. }
  2051. //取得課綱 ※display=1時不取任何課綱
  2052. List<SyllabusTreeNode> treeNodes = new();
  2053. if (!display.Equals(1))
  2054. {
  2055. string queryTextSyl = $"SELECT value(c) FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(volumeIdList)}, c.volumeId, true)";
  2056. if (!string.IsNullOrWhiteSpace(syllabusId)) queryTextSyl += $" AND c.id = '{syllabusId}'";
  2057. await foreach (Syllabus items in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIterator<Syllabus>(queryText: queryTextSyl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  2058. {
  2059. List<SyllabusTree> trees = SyllabusService.ListToTree(items.children);
  2060. SyllabusTreeNode tree = new() { id = items.id, scope = items.scope, trees = trees, volumeId = items.volumeId, auth = items.auth, codeval = $"{id}" };
  2061. treeNodes.Add(tree);
  2062. }
  2063. }
  2064. //輸出結果
  2065. foreach (Volume vr in volumeList)
  2066. {
  2067. volumes.Add(new
  2068. {
  2069. vr.periodId,
  2070. vr.subjectId,
  2071. vr.id,
  2072. vr.gradeId,
  2073. vr.semesterId,
  2074. vr.name,
  2075. vr.creatorId,
  2076. vr.creatorName,
  2077. vr.school,
  2078. vr.scope,
  2079. vr.syllabusIds,
  2080. vr.auth,
  2081. vr.order,
  2082. syllabus = treeNodes.Where(t => t.volumeId == vr.id).ToList()
  2083. });
  2084. }
  2085. return Ok(volumes);
  2086. }
  2087. //取得被分享的課綱
  2088. [ProducesDefaultResponseType]
  2089. [Authorize(Roles = "HiTeach")]
  2090. [HttpPost("get-share-syllabus")]
  2091. public async Task<IActionResult> GetShareSyllabusList(JsonElement request)
  2092. {
  2093. //Header驗證
  2094. string id_token = HttpContext.GetXAuth("IdToken");
  2095. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  2096. var jwt = new JwtSecurityToken(id_token);
  2097. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  2098. var id = jwt.Payload.Sub;
  2099. //參數驗證
  2100. string volumeId = (request.TryGetProperty("volumeId", out JsonElement volumeIdJobj)) ? volumeIdJobj.GetString() : String.Empty;
  2101. string syllabusId = (request.TryGetProperty("syllabusId", out JsonElement syllabusIdJobj)) ? syllabusIdJobj.GetString() : String.Empty;
  2102. int display = (request.TryGetProperty("display", out JsonElement displayJobj)) ? displayJobj.GetInt32() : 0;
  2103. var client = _azureCosmos.GetCosmosClient();
  2104. //取得分享內容
  2105. Dictionary<string, string> tmidDic = new Dictionary<string, string>();
  2106. List<Volume> volumeList = new List<Volume>();
  2107. StringBuilder queryText = new StringBuilder("SELECT value(c) FROM c WHERE c.type='share' ");
  2108. if (!string.IsNullOrWhiteSpace(volumeId))
  2109. {
  2110. queryText.Append($"AND c.volumeId='{volumeId}'");
  2111. }
  2112. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Share>(queryText: queryText.ToString(),
  2113. requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Share-share-{id}") }))
  2114. {
  2115. if (!string.IsNullOrWhiteSpace(syllabusId))
  2116. {
  2117. if (item.id.Equals(syllabusId))
  2118. {
  2119. Volume volumeExist = volumeList.Where((Volume v) => v.id == item.volumeId).FirstOrDefault();
  2120. if (volumeExist == null)
  2121. {
  2122. Volume volume = new Volume();
  2123. volume.id = item.volumeId;
  2124. volume.name = item.volumeName;
  2125. volume.creatorId = item.issuer;
  2126. volume.creatorName = item.issuerName;
  2127. volume.syllabusIds.Add(item.id);
  2128. volumeList.Add(volume);
  2129. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(item.issuer, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  2130. if (!tmidDic.ContainsKey(item.issuer)) tmidDic.Add(item.issuer, blob_sas);
  2131. }
  2132. else
  2133. {
  2134. volumeExist.syllabusIds.Add(item.id);
  2135. }
  2136. }
  2137. }
  2138. else
  2139. {
  2140. Volume volumeExist = volumeList.Where((Volume v) => v.id == item.volumeId).FirstOrDefault();
  2141. if (volumeExist == null)
  2142. {
  2143. Volume volume = new Volume();
  2144. volume.id = item.volumeId;
  2145. volume.name = item.volumeName;
  2146. volume.creatorId = item.issuer;
  2147. volume.creatorName = item.issuerName;
  2148. volume.syllabusIds.Add(item.id);
  2149. volumeList.Add(volume);
  2150. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(item.issuer, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  2151. if (!tmidDic.ContainsKey(item.issuer)) tmidDic.Add(item.issuer, blob_sas);
  2152. }
  2153. else
  2154. {
  2155. volumeExist.syllabusIds.Add(item.id);
  2156. }
  2157. }
  2158. }
  2159. //取得課綱並輸出回傳值
  2160. List<object> result = new List<object>();
  2161. foreach (Volume volTmp in volumeList)
  2162. {
  2163. List<SyllabusTreeNode> treeNodes = new();
  2164. if (!display.Equals(1))
  2165. {
  2166. string queryTextSyl = $"SELECT value(c) FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(volTmp.syllabusIds)}, c.id, true)";
  2167. await foreach (Syllabus items in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Syllabus>(queryText: queryTextSyl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{volTmp.creatorId}") }))
  2168. {
  2169. List<SyllabusTree> trees = SyllabusService.ListToTree(items.children);
  2170. SyllabusTreeNode tree = new() { id = items.id, scope = items.scope, trees = trees, volumeId = items.volumeId, auth = items.auth, codeval = $"{volTmp.creatorId}" };
  2171. treeNodes.Add(tree);
  2172. }
  2173. }
  2174. result.Add(new
  2175. {
  2176. id = volTmp.id,
  2177. name = volTmp.name,
  2178. creatorId = volTmp.creatorId,
  2179. creatorName = volTmp.creatorName,
  2180. creatorSas = (tmidDic.ContainsKey(volTmp.creatorId)) ? tmidDic[volTmp.creatorId] : null,
  2181. syllabusIds = volTmp.syllabusIds,
  2182. syllabus = treeNodes.ToList(),
  2183. });
  2184. }
  2185. return Ok(result);
  2186. }
  2187. //取得某班級的學生成員
  2188. [Authorize(Roles = "HiTeach")]
  2189. [ProducesDefaultResponseType]
  2190. [HttpPost("get-students-list")]
  2191. public async Task<IActionResult> GetStudentsList(JsonElement request)
  2192. {
  2193. string id_token = HttpContext.GetXAuth("IdToken");
  2194. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  2195. var jwt = new JwtSecurityToken(id_token);
  2196. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  2197. var id = jwt.Payload.Sub;
  2198. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  2199. request.TryGetProperty("class_code", out JsonElement class_code);
  2200. request.TryGetProperty("stulist_id", out JsonElement stulist_id);
  2201. string classId = Convert.ToString(class_code);
  2202. string stulist = Convert.ToString(stulist_id);
  2203. if (string.IsNullOrWhiteSpace(classId) && string.IsNullOrWhiteSpace(stulist)) return BadRequest();
  2204. request.TryGetProperty("school_code", out JsonElement school_code);
  2205. if (grant_type.GetString().Equals("school") && string.IsNullOrWhiteSpace(Convert.ToString(school_code))) return BadRequest();
  2206. var client = _azureCosmos.GetCosmosClient();
  2207. Dictionary<string, string> irsDic = new Dictionary<string, string>(); //key:學生ID或TMID value:irs號碼
  2208. string container = (grant_type.GetString().Equals("school")) ? "School" : "Teacher";
  2209. List<string> listids = new List<string>();
  2210. if (!string.IsNullOrWhiteSpace(stulist))
  2211. {
  2212. listids.Add(stulist);
  2213. }
  2214. else if (!string.IsNullOrWhiteSpace(classId))
  2215. {
  2216. listids.Add(classId);
  2217. }
  2218. (List<RMember> students, List<RGroupList> groupList) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, listids, $"{school_code}");
  2219. return Ok(new { students });
  2220. }
  2221. /// <summary>
  2222. /// 開始課堂(舊版,已被CreateLesson取代)
  2223. /// </summary>
  2224. /// <param name="request"></param>
  2225. /// <returns></returns>
  2226. [Authorize(Roles = "HiTeach")]
  2227. [ProducesDefaultResponseType]
  2228. [HttpPost("start-lesson")]
  2229. public async Task<IActionResult> StartLesson(JsonElement request)
  2230. {
  2231. //醍摩豆ID驗證
  2232. string teacherId = string.Empty;
  2233. string id_token = HttpContext.GetXAuth("IdToken");
  2234. if (!string.IsNullOrWhiteSpace(id_token))
  2235. {
  2236. var jwt = new JwtSecurityToken(id_token);
  2237. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  2238. teacherId = jwt.Payload.Sub;
  2239. }
  2240. if (string.IsNullOrWhiteSpace(teacherId)) return BadRequest(); //無醍摩豆ID,BadRequest
  2241. //取得授課ID
  2242. string lesson_code = _snowflakeId.NextId().ToString();
  2243. //bool get_lesson_id = (string.IsNullOrWhiteSpace(Convert.ToString(lesson_id)) || Convert.ToString(lesson_id) != lesson_code) ? false : true;
  2244. //string tableName = "TeacherLesson";
  2245. return Ok(new { lesson_code });
  2246. }
  2247. //上傳評測結果
  2248. //錯誤代碼:error = 1001 message = "Paper blob copy failure."
  2249. // error = 1002 message = "Student answers blob upload failure."
  2250. [Authorize(Roles = "HiTeach")]
  2251. [ProducesDefaultResponseType]
  2252. [HttpPost("upd-exam-result")]
  2253. public async Task<IActionResult> UploadExamResult(JsonElement request)
  2254. {
  2255. try
  2256. {
  2257. string message = "";
  2258. int error = 0;
  2259. string id_token = HttpContext.GetXAuth("IdToken");
  2260. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  2261. var jwt = new JwtSecurityToken(id_token);
  2262. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  2263. var id = jwt.Payload.Sub;
  2264. if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
  2265. if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
  2266. bool blobUploaded = (request.TryGetProperty("blobUploaded", out JsonElement blobUploadedJson)) ? blobUploadedJson.GetBoolean() : false;
  2267. bool recordSwitch = (request.TryGetProperty("recordSwitch", out JsonElement recordSwitchJson)) ? recordSwitchJson.GetBoolean() : false;
  2268. bool cloudas = (request.TryGetProperty("cloudas", out JsonElement cloudasJson)) ? cloudasJson.GetBoolean() : false; //是否啟動cloudas運算
  2269. //ExamInfo ExamInfoFromReq = exam.ToObject<ExamInfo>();
  2270. string strExam = JsonSerializer.Serialize(exam);
  2271. if (strExam.Contains("\"publish\":\"0\""))
  2272. {
  2273. strExam = strExam.Replace("\"publish\":\"0\"", "\"publish\":0");
  2274. }
  2275. ExamInfo ExamInfoFromReq = JsonSerializer.Deserialize<ExamInfo>(strExam);
  2276. string examId = ExamInfoFromReq.id;
  2277. string excode = ExamInfoFromReq.code;
  2278. string examSubjectId = ExamInfoFromReq.subjects[0].id;
  2279. //ExamInfo dbExamInfo = exam.ToObject<ExamInfo>();
  2280. ExamInfo dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExam);
  2281. var queryex = $"SELECT * FROM c WHERE c.id = '{examId}'";
  2282. await foreach (var itemex in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: queryex, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{excode}") }))
  2283. {
  2284. var jsonex = await JsonDocument.ParseAsync(itemex.ContentStream);
  2285. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2286. {
  2287. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  2288. {
  2289. string strExamDb = JsonSerializer.Serialize(obj);
  2290. if (strExamDb.Contains("\"publish\":\"0\""))
  2291. {
  2292. strExamDb = strExamDb.Replace("\"publish\":\"0\"", "\"publish\":0");
  2293. }
  2294. dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExamDb);
  2295. //dbExamInfo = obj.ToObject<ExamInfo>();
  2296. }
  2297. }
  2298. }
  2299. if (string.IsNullOrWhiteSpace(dbExamInfo.id))
  2300. {
  2301. dbExamInfo = ExamInfoFromReq;
  2302. }
  2303. if (cloudas) dbExamInfo.cloudas = cloudas;
  2304. //Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
  2305. //ExamInfo內容取得、調整 [2021-7-13 廢除,給予HiTeach學校Blob寫入權限,API不再對Blob做搬運]
  2306. //※規則 owner:"school" => 校園評測 "teacher" => 個人評測
  2307. //※規則 scope:"school" => 校本班級 "private" => 個人班級
  2308. //※規則 BLOB容器: scope:"school" => {學校ID}下 scope:"private" => {個人ID}下
  2309. string blobContainer = (!string.IsNullOrWhiteSpace(dbExamInfo.school) && dbExamInfo.scope.Equals("school")) ? dbExamInfo.school : id; //blob容器
  2310. //dbExamInfo.source = "1"; //評測來源固定為 1:課中評量(不應由API擅自變更)
  2311. //試卷List
  2312. List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
  2313. int paperIndex = 0;
  2314. foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
  2315. {
  2316. string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
  2317. paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
  2318. //string subjectId = dbExamInfo.subjects[paperIndex].id;
  2319. string subjectId = examSubjectId;
  2320. recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "subjectId", subjectId }, { "itemcount", paperInfo.point.Count.ToString() } });
  2321. paperIndex++;
  2322. }
  2323. //取得課堂紀錄下的試卷資料(blob)、複製到評測紀錄下
  2324. bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 (測試站)不再對Blob做搬運 永為false]
  2325. //Blob搬運 (待HiTeach完善後刪除)
  2326. if (!blobUploaded)
  2327. {
  2328. foreach (Dictionary<string, string> recordPaperInfoDic in recordPaperInfo)
  2329. {
  2330. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  2331. var blobPrivateContainer = _azureStorage.GetBlobContainerClient(id);
  2332. string sourceBlobPath = recordPaperInfoDic["blob"];
  2333. string destBlobPath = $"exam/{dbExamInfo.id}/paper/{recordPaperInfoDic["subjectId"]}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/ ※2022-1-6 式樣變更[原]/paper/{試卷ID}/ [新]/paper/{subjectID}/
  2334. if (targetScope.Equals("school")) //校本
  2335. {
  2336. string schoolId = dbExamInfo.school;
  2337. var blobSchoolContainer = _azureStorage.GetBlobContainerClient(schoolId);
  2338. var sourceBlobContainer = (recordSwitch) ? blobSchoolContainer : blobPrivateContainer;
  2339. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  2340. if (sourceBlobs.Count() > 0)
  2341. {
  2342. foreach (var blob in sourceBlobs)
  2343. {
  2344. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  2345. if (sourceFileBlob.Exists())
  2346. {
  2347. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  2348. string fileName = blob.Name.Replace(sourceBlobPath, "");
  2349. string destBlobFilePath = $"{destBlobPath}{fileName}";
  2350. await blobSchoolContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  2351. }
  2352. else
  2353. {
  2354. paperDataCopyErrFlg = true;
  2355. }
  2356. }
  2357. }
  2358. }
  2359. else //私人
  2360. {
  2361. var sourceBlobContainer = blobPrivateContainer;
  2362. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  2363. if (sourceBlobs.Count() > 0)
  2364. {
  2365. foreach (var blob in sourceBlobs)
  2366. {
  2367. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  2368. if (sourceFileBlob.Exists())
  2369. {
  2370. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  2371. string fileName = blob.Name.Replace(sourceBlobPath, "");
  2372. string destBlobFilePath = $"{destBlobPath}{fileName}";
  2373. await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  2374. }
  2375. else
  2376. {
  2377. paperDataCopyErrFlg = true;
  2378. }
  2379. }
  2380. }
  2381. }
  2382. //替換 Exam.papers.blob
  2383. PaperSimple paperInfoNow = dbExamInfo.papers.Where((PaperSimple p) => p.id == recordPaperInfoDic["id"]).FirstOrDefault();
  2384. string destBlobPathForDocument = (destBlobPath.EndsWith("/")) ? destBlobPath.Remove(destBlobPath.Length - 1, 1) : destBlobPath;
  2385. destBlobPathForDocument = (!destBlobPathForDocument.StartsWith("/")) ? "/" + destBlobPathForDocument : destBlobPathForDocument;
  2386. paperInfoNow.blob = destBlobPathForDocument;
  2387. }
  2388. }
  2389. //ExamClassResult內容調整
  2390. bool studentAnswerCopyErrFlg = false; //學生作答資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 不再對Blob做搬運 永為false]
  2391. //Blob搬運 (待HiTeach完善後刪除)
  2392. if (!blobUploaded)
  2393. {
  2394. List<ExamClassResultStudentAnswerArrayOld> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArrayOld>>();
  2395. foreach (ExamClassResultStudentAnswerArrayOld examClassResultRow in dbExamClassResultList)
  2396. {
  2397. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  2398. ExamClassResult examClassResultUpd = new ExamClassResult();
  2399. string exclcode = examClassResultRow.code;
  2400. string classId = examClassResultRow.info.id;
  2401. string subjectId = examClassResultRow.subjectId;
  2402. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  2403. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  2404. {
  2405. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  2406. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2407. {
  2408. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  2409. {
  2410. examClassResultUpd = obj.ToObject<ExamClassResult>();
  2411. examClassResultUpd.progress = examClassResultRow.progress;
  2412. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  2413. examClassResultUpd.studentAnswers = new List<List<string>>();
  2414. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  2415. examClassResultUpd.status = examClassResultRow.status;
  2416. examClassResultUpd.sum = examClassResultRow.sum;
  2417. }
  2418. }
  2419. }
  2420. //無法取得既有ExamClassResult,新建
  2421. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  2422. {
  2423. examClassResultUpd.pk = examClassResultRow.pk;
  2424. examClassResultUpd.code = examClassResultRow.code;
  2425. examClassResultUpd.id = examClassResultRow.id;
  2426. examClassResultUpd.school = examClassResultRow.school;
  2427. examClassResultUpd.examId = examClassResultRow.examId;
  2428. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  2429. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  2430. examClassResultUpd.year = examClassResultRow.year;
  2431. examClassResultUpd.info = examClassResultRow.info;
  2432. examClassResultUpd.progress = examClassResultRow.progress;
  2433. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  2434. examClassResultUpd.studentAnswers = new List<List<string>>(); //學生作答Blob位置
  2435. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  2436. examClassResultUpd.status = examClassResultRow.status;
  2437. examClassResultUpd.scope = examClassResultRow.scope;
  2438. examClassResultUpd.sum = examClassResultRow.sum;
  2439. }
  2440. //examClassResult.studentAnswers (1)將學生答案上傳blob後轉換內容為blob路徑 //[2021-7-13 不再對Blob做搬運] (2)將學生答案放入examClassResult.ans
  2441. if (examClassResultRow.studentIds != null && examClassResultRow.studentIds.Count > 0 && examClassResultRow.studentAnswersArray != null && examClassResultRow.studentAnswersArray.Count > 0)
  2442. {
  2443. for (int i = 0; i < examClassResultRow.studentAnswersArray.Count; i++)
  2444. {
  2445. string studentId = examClassResultRow.studentIds[i];
  2446. string fileName = examId + "/" + subjectId + "/" + studentId;
  2447. string blob = fileName + "/" + "ans.json";
  2448. var uploadFileResult = await _azureStorage.GetBlobContainerClient(blobContainer).UploadFileByContainer(examClassResultRow.studentAnswersArray[i].ToJsonString(), "exam", blob, false);
  2449. if (string.IsNullOrWhiteSpace(uploadFileResult))
  2450. {
  2451. studentAnswerCopyErrFlg = true;
  2452. }
  2453. //studentAnswers
  2454. List<string> studenrAnswerRow = new List<string>();
  2455. studenrAnswerRow.Add(blob);
  2456. examClassResultUpd.studentAnswers.Add(studenrAnswerRow);
  2457. }
  2458. //examClassResult.ans 將學生答案放入
  2459. examClassResultUpd.ans = examClassResultRow.studentAnswersArray;
  2460. }
  2461. //批註欄位處理
  2462. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string paperStatusSubjectId) && paperStatusSubjectId.Equals(subjectId)).FirstOrDefault();
  2463. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  2464. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  2465. {
  2466. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  2467. }
  2468. //UPDATE ExamClassResult
  2469. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  2470. }
  2471. }
  2472. else
  2473. {
  2474. List<ExamClassResultStudentAnswerArray> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArray>>();
  2475. foreach (ExamClassResultStudentAnswerArray examClassResultRow in dbExamClassResultList)
  2476. {
  2477. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  2478. ExamClassResult examClassResultUpd = new ExamClassResult();
  2479. string exclcode = examClassResultRow.code;
  2480. string classId = examClassResultRow.info.id;
  2481. string subjectId = examClassResultRow.subjectId;
  2482. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  2483. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  2484. {
  2485. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  2486. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2487. {
  2488. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  2489. {
  2490. examClassResultUpd = obj.ToObject<ExamClassResult>();
  2491. examClassResultUpd.progress = examClassResultRow.progress;
  2492. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  2493. examClassResultUpd.studentAnswers = new List<List<string>>();
  2494. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  2495. examClassResultUpd.status = examClassResultRow.status;
  2496. examClassResultUpd.sum = examClassResultRow.sum;
  2497. }
  2498. }
  2499. }
  2500. //無法取得既有ExamClassResult,新建
  2501. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  2502. {
  2503. examClassResultUpd.pk = examClassResultRow.pk;
  2504. examClassResultUpd.code = examClassResultRow.code;
  2505. examClassResultUpd.id = examClassResultRow.id;
  2506. examClassResultUpd.school = examClassResultRow.school;
  2507. examClassResultUpd.examId = examClassResultRow.examId;
  2508. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  2509. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  2510. examClassResultUpd.year = examClassResultRow.year;
  2511. examClassResultUpd.info = examClassResultRow.info;
  2512. examClassResultUpd.progress = examClassResultRow.progress;
  2513. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  2514. examClassResultUpd.studentAnswers = new List<List<string>>();
  2515. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  2516. examClassResultUpd.status = examClassResultRow.status;
  2517. examClassResultUpd.scope = examClassResultRow.scope;
  2518. examClassResultUpd.sum = examClassResultRow.sum;
  2519. }
  2520. //學生作答Blob位置[2021-7-13 不再對Blob做搬運 學生答案直接寫入DB]
  2521. examClassResultUpd.studentAnswers = examClassResultRow.studentAnswersArray;
  2522. //學生作答答案 (從blob取得學生答案、放入ans欄位)
  2523. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  2524. var targetBlobContainer = _azureStorage.GetBlobContainerClient(id);
  2525. string blobCntr = id;
  2526. if (targetScope.Equals("school")) //校本
  2527. {
  2528. string schoolId = dbExamInfo.school;
  2529. blobCntr = schoolId;
  2530. }
  2531. targetBlobContainer = _azureStorage.GetBlobContainerClient(blobCntr);
  2532. if (examClassResultRow.studentAnswersArray.Count > 0)
  2533. {
  2534. foreach (List<string> studentAnswerBlobPath in examClassResultRow.studentAnswersArray)
  2535. {
  2536. string stuAnswerBlobPath = studentAnswerBlobPath.FirstOrDefault();
  2537. if (stuAnswerBlobPath != null)
  2538. {
  2539. StringBuilder builder = new StringBuilder();
  2540. builder.Append("exam").Append("/").Append(stuAnswerBlobPath);
  2541. var Download = await targetBlobContainer.GetBlobClient(builder.ToString()).DownloadAsync();
  2542. var json = await JsonDocument.ParseAsync(Download.Value.Content);
  2543. var Record = json.RootElement.ToObject<List<List<string>>>();
  2544. examClassResultUpd.ans.Add(Record);
  2545. }
  2546. }
  2547. }
  2548. //批註欄位處理
  2549. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string subjectId) && subjectId.Equals(examClassResultUpd.subjectId)).FirstOrDefault();
  2550. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  2551. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  2552. {
  2553. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  2554. }
  2555. //UPDATE ExamClassResult
  2556. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  2557. }
  2558. }
  2559. //UPDATE Exam
  2560. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(dbExamInfo, new PartitionKey(dbExamInfo.code));
  2561. //錯誤處理
  2562. if (paperDataCopyErrFlg) //試卷Blob拷貝失敗
  2563. {
  2564. error = 1001;
  2565. message = "Paper blob copy failure.";
  2566. }
  2567. else if (studentAnswerCopyErrFlg) //學生作答資料上傳blob失敗
  2568. {
  2569. error = 1002;
  2570. message = "Student answers blob upload failure.";
  2571. }
  2572. return Ok(new { error, message });
  2573. }
  2574. catch (CosmosException cex)
  2575. {
  2576. return BadRequest(cex.Message);
  2577. }
  2578. catch (StorageException bex)
  2579. {
  2580. return BadRequest(bex.Message);
  2581. }
  2582. catch (Exception ex)
  2583. {
  2584. return BadRequest(ex.Message);
  2585. }
  2586. }
  2587. /**
  2588. * 根据学年获取年级信息
  2589. * @param year 学年
  2590. * @param Period 学段資料
  2591. */
  2592. private ExamSimple getGradeInfoByYear(int year, Period curPeriod)
  2593. {
  2594. ExamSimple result = new ExamSimple();
  2595. if (year > 0)
  2596. {
  2597. DateTime date = DateTime.UtcNow;
  2598. int curYear = date.Year;
  2599. int month = date.Month;
  2600. Semester semesterStart = curPeriod.semesters.Where((Semester x) => x.start.Equals(1)).FirstOrDefault();
  2601. if (semesterStart != null)
  2602. {
  2603. if (month < semesterStart.month)
  2604. {
  2605. curYear--;
  2606. }
  2607. int gradeIndex = curYear - year;
  2608. result.id = gradeIndex.ToString();
  2609. result.name = (gradeIndex >= curPeriod.grades.Count) ? "graduated" : (gradeIndex >= 0) ? curPeriod.grades[gradeIndex] : "not-enrollment";
  2610. }
  2611. }
  2612. return result;
  2613. }
  2614. private List<List<List<Details>>> createEmptyMark(int studentNum, int itemNum)
  2615. {
  2616. List<List<List<Details>>> mark = new List<List<List<Details>>>();
  2617. for (int i = 0; i < studentNum; i++)
  2618. {
  2619. List<List<Details>> markRow = new List<List<Details>>();
  2620. for (int j = 0; j < itemNum; j++)
  2621. {
  2622. markRow.Add(new List<Details>());
  2623. }
  2624. mark.Add(markRow);
  2625. }
  2626. return mark;
  2627. }
  2628. public class ClassStudents
  2629. {
  2630. public string id { get; set; }
  2631. public string name { get; set; }
  2632. public string no { get; set; }
  2633. public string schoolId { get; set; }
  2634. public string groupId { get; set; }
  2635. public string groupName { get; set; }
  2636. public string irs { get; set; }
  2637. public int type { get; set; } //类型 1 tmdid,2 student
  2638. public string nickname { get; set; }
  2639. }
  2640. public class ClassGroups
  2641. {
  2642. public ClassGroups()
  2643. {
  2644. studentIds = new List<string>();
  2645. }
  2646. public string id { get; set; }
  2647. public string name { get; set; }
  2648. public List<string> studentIds { get; set; }
  2649. }
  2650. //ExamClassResult 學生作答紀錄(舊式 正式站暫時用這個)
  2651. public class ExamClassResultStudentAnswerArrayOld : ExamClassResult
  2652. {
  2653. public List<List<List<string>>> studentAnswersArray { get; set; }
  2654. }
  2655. //ExamClassResult 學生作答紀錄
  2656. public class ExamClassResultStudentAnswerArray : ExamClassResult
  2657. {
  2658. public List<List<string>> studentAnswersArray { get; set; }
  2659. }
  2660. //get-teacher-info API輸出 schools
  2661. private class GetTeacherInfoApiSchool
  2662. {
  2663. public string schoolId { get; set; }
  2664. public string name { get; set; }
  2665. public string status { get; set; }
  2666. public string picture { get; set; }
  2667. }
  2668. //get-teacher-info輸出 courses
  2669. private class GetTeacherInfoApiCourse : GetTeacherInfoApiSimlpeBase
  2670. {
  2671. public string scope { get; set; }
  2672. public List<GetTeacherInfoApiCourseClass> classes { get; set; }
  2673. }
  2674. //get-teacher-info輸出 courses.classes
  2675. private class GetTeacherInfoApiCourseClass : GetTeacherInfoApiSimlpeBase
  2676. {
  2677. public string code { get; set; }
  2678. public GetTeacherInfoApiSimlpeBase teacher { get; set; }
  2679. public string scope { get; set; }
  2680. public string stuListId { get; set; }
  2681. public int stuCnt { get; set; }
  2682. public int grpCnt { get; set; }
  2683. public string gradeId { get; set; }
  2684. public string gradeName { get; set; }
  2685. public int year { get; set; }
  2686. }
  2687. //get-teacher-info輸出 基本class(各class繼承用)
  2688. internal class GetTeacherInfoApiSimlpeBase
  2689. {
  2690. public string id { get; set; }
  2691. public string name { get; set; }
  2692. }
  2693. //get-school-info輸出 exams.finishClassesSub(各班級已完成評測的科目列表)
  2694. private class GetSchInfoExamFinishClassesSub
  2695. {
  2696. public string classId { get; set; } //班級ID
  2697. public List<string> subjectIds { get; set; } //需完成科目ID
  2698. public List<string> finishSubjectIds { get; set; } //已完成科目ID
  2699. }
  2700. //get-school-info輸出 courses
  2701. private class GetSchoolInfoApiCourse : GetTeacherInfoApiSimlpeBase
  2702. {
  2703. public string scope { get; set; }
  2704. public GetTeacherInfoApiSimlpeBase subject { get; set; }
  2705. public List<GetTeacherInfoApiCourseClass> classes { get; set; }
  2706. }
  2707. //get-school-info (中間資料)記錄學生已完成的評測、班級、科目
  2708. private class ExamFinishClassesSubList
  2709. {
  2710. public string examId { get; set; } //評測ID
  2711. public string classId { get; set; } //班級ID
  2712. public string subjectId { get; set; } //科目ID
  2713. }
  2714. //get-school-info輸出 exams.subjects
  2715. private class GetSchInfoExamSubject
  2716. {
  2717. public string id { get; set; }
  2718. public string name { get; set; }
  2719. public int classCount { get; set; }
  2720. }
  2721. private class GroupListMemberCnt
  2722. {
  2723. public string id { get; set; }
  2724. public string code { get; set; }
  2725. public string name { get; set; }
  2726. public string scope { get; set; }
  2727. public string type { get; set; } /// class:編制班 teach:選課班
  2728. public int year { get; set; }
  2729. public int memberCount { get; set; }
  2730. }
  2731. #region ===學習記錄用類別===
  2732. #region ===TimeLine.json===
  2733. private class TimeLineEvents
  2734. {
  2735. public TimeLineEvents()
  2736. {
  2737. events = new List<TimeLineEventPg>();
  2738. }
  2739. public List<TimeLineEventPg> events { get; set; }
  2740. }
  2741. private class TimeLineEventPg
  2742. {
  2743. public double Time { get; set; }
  2744. public string Pgid { get; set; }
  2745. public string Event { get; set; }
  2746. }
  2747. #endregion
  2748. #region ===IRS.json===
  2749. /// <summary>
  2750. /// IRS架構第一層
  2751. /// </summary>
  2752. private class IRSItem
  2753. {
  2754. public IRSItem()
  2755. {
  2756. buzzClients = new List<string>();
  2757. }
  2758. public string pageID { get; set; }
  2759. public IRSQuestion question { get; set; }
  2760. public JsonElement clientAnswers { get; set; }
  2761. public List<string> buzzClients { get; set; }
  2762. public bool isBuzz { get; set; }
  2763. }
  2764. /// <summary>
  2765. /// IRS架構第二層
  2766. /// </summary>
  2767. private class IRSQuestion
  2768. {
  2769. public IRSQuestion()
  2770. {
  2771. item = new List<QuestionItem>();
  2772. }
  2773. public QuestionExercise exercise { get; set; }
  2774. public List<QuestionItem> item { get; set; }
  2775. }
  2776. private class QuestionItem
  2777. {
  2778. public QuestionItem()
  2779. {
  2780. option = new List<OptionItem>();
  2781. }
  2782. public string question { get; set; }
  2783. public List<OptionItem> option { get; set; }
  2784. }
  2785. private class OptionItem
  2786. {
  2787. public string code { get; set; }
  2788. public string value { get; set; }
  2789. }
  2790. /// <summary>
  2791. /// IRS架構第三層
  2792. /// </summary>
  2793. private class QuestionExercise
  2794. {
  2795. public string type { get; set; }
  2796. public List<string> knowledges { get; set; }
  2797. public List<string> answer { get; set; }
  2798. }
  2799. #endregion
  2800. #region ===Task.json===
  2801. private class TaskItem
  2802. {
  2803. public List<ClientWork> clientWorks { get; set; }
  2804. public string pageID { get; set; }
  2805. }
  2806. private class ClientWork
  2807. {
  2808. public int seatID { get; set; }
  2809. public string reciveTime { get; set; }
  2810. }
  2811. #endregion
  2812. #region ===取學校代碼===
  2813. private class GroupIdsFromLesson
  2814. {
  2815. public List<string> groupIds { get; set; }
  2816. }
  2817. private class SchoolFromgroupIds
  2818. {
  2819. public string school { get; set; }
  2820. }
  2821. #endregion
  2822. #endregion
  2823. }
  2824. }