BIProdAnalysis.cs 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. using Microsoft.Azure.Cosmos;
  2. using Newtonsoft.Json;
  3. using StackExchange.Redis;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text;
  9. using System.Text.Json;
  10. using System.Text.RegularExpressions;
  11. using System.Threading.Tasks;
  12. using TEAMModelOS.SDK.DI;
  13. using TEAMModelOS.SDK.Extension;
  14. using TEAMModelOS.SDK.Models.Cosmos;
  15. using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
  16. using TEAMModelOS.SDK.Models.Cosmos;
  17. namespace TEAMModelOS.SDK.Models.Service.BI
  18. {
  19. public static class BIProdAnalysis
  20. {
  21. /// <summary>
  22. /// 1.取得某日CS IOT 資料
  23. /// 2.生成IES5各校產品分析統計資料
  24. /// 3.生成TMID產品分析統計資料
  25. /// 4.生成DeviceID產品分析統計資料
  26. /// </summary>
  27. /// <param name="_azureRedis"></param>
  28. /// <param name="_dingDing"></param>
  29. /// <param name="y">年</param>
  30. /// <param name="m">月(2位數)</param>
  31. /// <param name="d">日(2位數)</param>
  32. /// <returns></returns>
  33. public static async Task BICreatDailyAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, CosmosClient _azureCosmosClientCsv2, CosmosClient _azureCosmosClientCsv2CnRead, DingDing _dingDing, string y, string m, string d)
  34. {
  35. List<IotTeachingData> IotTeachingDataList = await BIGetDailyRedisProdAnalData(_azureRedis, _azureCosmosClientCsv2, _dingDing, y, m, d); //取得Redis IOT 每日資訊
  36. await CreatIes5ProdAnalData(_azureRedis, _azureCosmosClient, _azureCosmosClientCsv2CnRead, _dingDing, y, m, d, IotTeachingDataList); //生成學校CosmosDB年月日統計資料
  37. await CreatTmidProdAnalData(_azureRedis, _azureCosmosClient, _dingDing, y, m, d, IotTeachingDataList); //生成TMID CosmosDB年月日統計資料
  38. }
  39. /// <summary>
  40. /// 取得某日CS IOT 資料
  41. /// </summary>
  42. /// <param name="_azureRedis"></param>
  43. /// <param name="_dingDing"></param>
  44. /// <param name="y">年</param>
  45. /// <param name="m">月(2位數)</param>
  46. /// <param name="d">日(2位數)</param>
  47. /// <returns></returns>
  48. public static async Task<List<IotTeachingData>> BIGetDailyRedisProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClientCsv2, DingDing _dingDing, string y, string m, string d)
  49. {
  50. List<IotTeachingData> IotTeachingDataList = new List<IotTeachingData>();
  51. try
  52. {
  53. var redisClinet2 = _azureRedis.GetRedisClient(2);
  54. var datetime = DateTimeOffset.UtcNow;
  55. var ynow = datetime.Year;
  56. HashSet<string> tmids = new HashSet<string>();
  57. Dictionary<string, string> tmidSchidDic = new Dictionary<string, string>();
  58. //取得CS Redis TeachingData (IOT紀錄只有三個月分)
  59. if (y.Equals(ynow.ToString()))
  60. {
  61. bool TeachingDataExist = await redisClinet2.KeyExistsAsync($"TeachingData:{m}{d}");
  62. if (TeachingDataExist)
  63. {
  64. RedisValue[] TeachingData = redisClinet2.ListRange($"TeachingData:{m}{d}");
  65. foreach (RedisValue tdataRow in TeachingData)
  66. {
  67. string[] tdata = tdataRow.ToString().Split(',');
  68. IotTeachingData IotTeachingData = new IotTeachingData();
  69. IotTeachingData.timestamp = Convert.ToInt64(tdata[0]);
  70. IotTeachingData.deviceId = tdata[1];
  71. IotTeachingData.channel = tdata[2];
  72. IotTeachingData.tmid = tdata[3];
  73. IotTeachingData.schoolId = tdata[4];
  74. IotTeachingData.useIES = tdata[5];
  75. IotTeachingData.useIES5Resource = (!string.IsNullOrWhiteSpace(tdata[6])) ? Convert.ToInt32(tdata[6]) : 0;
  76. IotTeachingData.useWebIrs = tdata[7];
  77. IotTeachingData.useDeviceIrs = tdata[8];
  78. IotTeachingData.useHaboard = tdata[9];
  79. IotTeachingData.useHita = tdata[10];
  80. IotTeachingData.lessonLengMin = (!string.IsNullOrWhiteSpace(tdata[11])) ? Convert.ToInt32(tdata[11]) : 0;
  81. IotTeachingData.stuShow = (!string.IsNullOrWhiteSpace(tdata[12])) ? Convert.ToInt32(tdata[12]) : 0;
  82. IotTeachingData.tPoint = (!string.IsNullOrWhiteSpace(tdata[13])) ? Convert.ToInt32(tdata[13]) : 0;
  83. IotTeachingData.lTypeCoop = tdata[14];
  84. IotTeachingData.lTypeIact = tdata[15];
  85. IotTeachingData.lTypeMis = tdata[16];
  86. IotTeachingData.lTypeTst = tdata[17];
  87. IotTeachingData.lTypeDif = tdata[18];
  88. IotTeachingData.authType = tdata[19];
  89. IotTeachingData.mission = (!string.IsNullOrWhiteSpace(tdata[20])) ? Convert.ToInt32(tdata[20]) : 0;
  90. IotTeachingData.missionFin = (!string.IsNullOrWhiteSpace(tdata[21])) ? Convert.ToInt32(tdata[21]) : 0;
  91. IotTeachingData.item = (!string.IsNullOrWhiteSpace(tdata[22])) ? Convert.ToInt32(tdata[22]) : 0;
  92. IotTeachingData.interact = (!string.IsNullOrWhiteSpace(tdata[23])) ? Convert.ToInt32(tdata[23]) : 0;
  93. IotTeachingData.ip = (tdata.Length > 24) ? tdata[24] : "";
  94. IotTeachingData.version = (tdata.Length > 25) ? tdata[25] : "";
  95. IotTeachingData.sendSok = (tdata.Length > 26 && !string.IsNullOrWhiteSpace(tdata[26])) ? tdata[26] : "0";
  96. IotTeachingData.learnPeer = (tdata.Length > 27 && !string.IsNullOrWhiteSpace(tdata[27])) ? tdata[27] : "0";
  97. IotTeachingData.learnCoop = (tdata.Length > 28 && !string.IsNullOrWhiteSpace(tdata[28])) ? tdata[28] : "0";
  98. IotTeachingData.useWordCloud = (tdata.Length > 29 && !string.IsNullOrWhiteSpace(tdata[29])) ? tdata[29] : "0";
  99. IotTeachingData.useClouDAS = (tdata.Length > 30 && !string.IsNullOrWhiteSpace(tdata[30])) ? tdata[30] : "0";
  100. IotTeachingData.useGPT = (tdata.Length > 31 && !string.IsNullOrWhiteSpace(tdata[31])) ? tdata[31] : "0";
  101. IotTeachingData.useIes5Test = (tdata.Length > 32 && !string.IsNullOrWhiteSpace(tdata[32])) ? tdata[32] : "0";
  102. IotTeachingData.usePaperTest = (tdata.Length > 33 && !string.IsNullOrWhiteSpace(tdata[33])) ? tdata[33] : "0";
  103. IotTeachingData.useExcelTest = (tdata.Length > 34 && !string.IsNullOrWhiteSpace(tdata[34])) ? tdata[34] : "0";
  104. IotTeachingData.useScoreBoard = (tdata.Length > 35 && !string.IsNullOrWhiteSpace(tdata[35])) ? tdata[35] : "0"; //課堂中使用記分板
  105. IotTeachingData.learnParticipation = (tdata.Length > 36 && !string.IsNullOrWhiteSpace(tdata[36])) ? Convert.ToInt32(tdata[36]) : 0; //學習參與度指數
  106. IotTeachingData.learnParticipationCnt = (IotTeachingData.learnParticipation > 0) ? "1" : "0"; //是否計算學習參與度
  107. IotTeachingData.coopMission = (tdata.Length > 37 && !string.IsNullOrWhiteSpace(tdata[37])) ? Convert.ToInt32(tdata[37]) : 0; //協作任務數
  108. IotTeachingData.coopWork = (tdata.Length > 38 && !string.IsNullOrWhiteSpace(tdata[38])) ? Convert.ToInt32(tdata[38]) : 0; //協作作品數
  109. IotTeachingData.coopContributionT = (tdata.Length > 39 && !string.IsNullOrWhiteSpace(tdata[39])) ? Convert.ToInt32(tdata[39]) : 0; //協作總貢獻度
  110. IotTeachingData.peerAct = (tdata.Length > 40 && !string.IsNullOrWhiteSpace(tdata[40])) ? Convert.ToInt32(tdata[40]) : 0; //互評活動次數
  111. IotTeachingData.peerStuParticipationT = (tdata.Length > 41 && !string.IsNullOrWhiteSpace(tdata[41])) ? Convert.ToInt32(tdata[41]) : 0; //互評學生參與總次數
  112. IotTeachingData.useTransMode = (tdata.Length > 42 && !string.IsNullOrWhiteSpace(tdata[42])) ? tdata[42] : "0"; //有使用透明模式
  113. IotTeachingData.useMiniMode = (tdata.Length > 43 && !string.IsNullOrWhiteSpace(tdata[43])) ? tdata[43] : "0"; //有使用最小化模式
  114. IotTeachingDataList.Add(IotTeachingData);
  115. if(!string.IsNullOrWhiteSpace(IotTeachingData.tmid))
  116. {
  117. tmids.Add(IotTeachingData.tmid);
  118. }
  119. }
  120. }
  121. }
  122. //取得TMID進階資料,以弱強歸戶學校補足學校ID
  123. QueryDefinition querye = new QueryDefinition(@"SELECT c.id, c.schoolCode, c.schoolCodeW FROM c WHERE (ARRAY_CONTAINS(@key, c.id) AND ( (IS_DEFINED(c.schoolCode) AND IS_STRING(c.schoolCode)) OR (IS_DEFINED(c.schoolCodeW) AND IS_STRING(c.schoolCodeW)) ))").WithParameter("@key", tmids);
  124. await foreach (var item in _azureCosmosClientCsv2.GetContainer("Core", "ID2")
  125. .GetItemQueryStreamIteratorQuery(querye, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base-ex")}))
  126. {
  127. using var json = await JsonDocument.ParseAsync(item.Content);
  128. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  129. {
  130. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  131. {
  132. string id = doc.GetProperty("id").GetString();
  133. string schoolCode = (doc.TryGetProperty("schoolCode", out JsonElement _tmidSchoolCode)) ? _tmidSchoolCode.GetString() : string.Empty;
  134. string schoolCodeW = (doc.TryGetProperty("schoolCodeW", out JsonElement _tmidSchoolCodeW)) ? _tmidSchoolCodeW.GetString() : string.Empty;
  135. List<IotTeachingData> IotTeachingDatas = IotTeachingDataList.Where(x => x.tmid.Equals(id) && string.IsNullOrWhiteSpace(x.schoolId)).ToList();
  136. if(IotTeachingDatas.Count > 0)
  137. {
  138. foreach(IotTeachingData IotTeachingDataRow in IotTeachingDatas)
  139. {
  140. if (!string.IsNullOrWhiteSpace(schoolCode))
  141. {
  142. IotTeachingDataRow.schoolId = schoolCode;
  143. }
  144. else if (!string.IsNullOrWhiteSpace(schoolCodeW))
  145. {
  146. IotTeachingDataRow.schoolId = schoolCodeW;
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. return IotTeachingDataList;
  154. }
  155. catch (Exception ex)
  156. {
  157. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},BIGetDailyRedisProdAnalData() 取得每日CoreServiceIOT資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  158. return IotTeachingDataList;
  159. }
  160. }
  161. //年月日 IES5學校IOT產品分析數據生成
  162. ///參數:某日IOT 教學資料列
  163. ///1.生成 Redis ProdAnalysis:Month
  164. ///2.生成 CosmosDB Month
  165. ///3.生成 CosmosDB 系統所有學校數值加總
  166. private static async Task CreatIes5ProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, CosmosClient _azureCosmosClientCsv2CnRead, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  167. {
  168. try
  169. {
  170. var redisClinet8 = _azureRedis.GetRedisClient(8);
  171. List<string> crtVirtualSchoolId = new List<string>(); //虛擬學校創建ID列表
  172. List<string> calPropList = new List<string>() { "lessonRecord", "useIES", "useIES5Resource", "useWebIrs", "useDeviceIrs", "useHaboard", "useHita", "lessonLengMin", "lessonLeng0", "stuShow", "stuLessonLengMin", "tGreen", "tLesson", "lTypeCoop", "lTypeIact", "lTypeMis", "lTypeTst", "lTypeDif", "lTypeNone", "lessonCnt928", "lessonCntId", "lessonCntDevice", "lessonCntIdDevice", "mission", "missionFin", "item", "interact", "sendSok", "learnPeer", "learnCoop", "useWordCloud", "useClouDAS", "useGPT", "useIes5Test", "usePaperTest", "useExcelTest", "useScoreBoard", "learnParticipationCnt", "learnParticipationT", "coopMission", "coopWork", "coopContributionT", "peerAct", "peerStuParticipationT", "useTransMode", "useMiniMode" }; //要計算的ProdAnalysis欄位列表
  173. List<ProdAnalysis> ProdAnalysisList = new List<ProdAnalysis>();
  174. if (IotTeachingDataList.Count > 0)
  175. {
  176. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  177. {
  178. string schoolId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.schoolId)) ? IotTeachingDatRow.schoolId.ToLower() : "noschoolid"; //noschoolid:無任何學校ID
  179. if (!schoolId.Equals("noschoolid") && !schoolId.Equals("allschool") && !crtVirtualSchoolId.Contains(schoolId))
  180. {
  181. crtVirtualSchoolId.Add(schoolId);
  182. }
  183. string toolType = string.Empty;
  184. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  185. {
  186. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  187. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  188. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  189. }
  190. //學校數據生成
  191. if (!string.IsNullOrWhiteSpace(schoolId) && !string.IsNullOrWhiteSpace(toolType))
  192. {
  193. bool addFlg = false;
  194. ProdAnalysis prodAnalysisRow = ProdAnalysisList.Where(s => s.schoolId.Equals(schoolId) && s.toolType.Equals(toolType)).FirstOrDefault();
  195. //無此校數據=>創建
  196. if (prodAnalysisRow == null)
  197. {
  198. prodAnalysisRow = new ProdAnalysis();
  199. prodAnalysisRow.schoolId = schoolId;
  200. prodAnalysisRow.toolType = toolType;
  201. addFlg = true;
  202. }
  203. //欄位加總
  204. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId) && !prodAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  205. {
  206. prodAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  207. }
  208. prodAnalysisRow.deviceCnt = prodAnalysisRow.deviceList.Count;
  209. switch (IotTeachingDatRow.authType)
  210. {
  211. case "0": //928授權
  212. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  213. prodAnalysisRow.lessonCnt928++;
  214. break;
  215. case "1": //ID授權
  216. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  217. prodAnalysisRow.lessonCntId++;
  218. break;
  219. case "2": //機器授權
  220. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  221. prodAnalysisRow.lessonCntDevice++;
  222. break;
  223. case "3": //ID+機器授權
  224. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  225. prodAnalysisRow.lessonCntIdDevice++;
  226. break;
  227. }
  228. prodAnalysisRow.deviceNoAuth = prodAnalysisRow.deviceNoAuthList.Count;
  229. prodAnalysisRow.deviceAuth = prodAnalysisRow.deviceAuthList.Count;
  230. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid) && !prodAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  231. {
  232. prodAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  233. }
  234. prodAnalysisRow.tmidCnt = prodAnalysisRow.tmidList.Count;
  235. prodAnalysisRow.lessonRecord++;
  236. if (IotTeachingDatRow.useIES.Equals("1")) prodAnalysisRow.useIES++;
  237. prodAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  238. if (IotTeachingDatRow.useWebIrs.Equals("1")) prodAnalysisRow.useWebIrs++;
  239. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) prodAnalysisRow.useDeviceIrs++;
  240. if (IotTeachingDatRow.useHaboard.Equals("1")) prodAnalysisRow.useHaboard++;
  241. if (IotTeachingDatRow.useHita.Equals("1")) prodAnalysisRow.useHita++;
  242. prodAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  243. if (IotTeachingDatRow.lessonLengMin.Equals(0)) prodAnalysisRow.lessonLeng0++;
  244. prodAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  245. prodAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  246. if (IotTeachingDatRow.tPoint >= 70) prodAnalysisRow.tGreen++;
  247. if (IotTeachingDatRow.lessonLengMin >= 10 && IotTeachingDatRow.tPoint > 0) prodAnalysisRow.tLesson++;
  248. if (IotTeachingDatRow.lTypeCoop.Equals("1")) prodAnalysisRow.lTypeCoop++;
  249. if (IotTeachingDatRow.lTypeIact.Equals("1")) prodAnalysisRow.lTypeIact++;
  250. if (IotTeachingDatRow.lTypeMis.Equals("1")) prodAnalysisRow.lTypeMis++;
  251. if (IotTeachingDatRow.lTypeTst.Equals("1")) prodAnalysisRow.lTypeTst++;
  252. if (IotTeachingDatRow.lTypeDif.Equals("1")) prodAnalysisRow.lTypeDif++;
  253. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) prodAnalysisRow.lTypeNone++;
  254. prodAnalysisRow.mission += IotTeachingDatRow.mission;
  255. prodAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  256. prodAnalysisRow.item += IotTeachingDatRow.item;
  257. prodAnalysisRow.interact += IotTeachingDatRow.interact;
  258. if (IotTeachingDatRow.sendSok.Equals("1")) prodAnalysisRow.sendSok++;
  259. if (IotTeachingDatRow.learnPeer.Equals("1")) prodAnalysisRow.learnPeer++;
  260. if (IotTeachingDatRow.learnCoop.Equals("1")) prodAnalysisRow.learnCoop++;
  261. if (IotTeachingDatRow.useWordCloud.Equals("1")) prodAnalysisRow.useWordCloud++;
  262. if (IotTeachingDatRow.useClouDAS.Equals("1")) prodAnalysisRow.useClouDAS++;
  263. if (IotTeachingDatRow.useGPT.Equals("1")) prodAnalysisRow.useGPT++;
  264. if (IotTeachingDatRow.useIes5Test.Equals("1")) prodAnalysisRow.useIes5Test++;
  265. if (IotTeachingDatRow.usePaperTest.Equals("1")) prodAnalysisRow.usePaperTest++;
  266. if (IotTeachingDatRow.useExcelTest.Equals("1")) prodAnalysisRow.useExcelTest++;
  267. if (IotTeachingDatRow.useScoreBoard.Equals("1")) prodAnalysisRow.useScoreBoard++; //課堂中使用記分板
  268. if (IotTeachingDatRow.learnParticipationCnt.Equals("1")) prodAnalysisRow.learnParticipationCnt++; //學習參與度次數
  269. prodAnalysisRow.learnParticipationT += IotTeachingDatRow.learnParticipation; //學習參與度指數(總和)
  270. decimal learnParticipationTmp = (prodAnalysisRow.learnParticipationCnt > 0) ? (decimal)prodAnalysisRow.learnParticipationT / (decimal)prodAnalysisRow.learnParticipationCnt : 0;
  271. prodAnalysisRow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  272. prodAnalysisRow.coopMission += IotTeachingDatRow.coopMission; //協作任務數
  273. prodAnalysisRow.coopWork += IotTeachingDatRow.coopWork; //協作作品數
  274. prodAnalysisRow.coopContributionT += IotTeachingDatRow.coopContributionT; //協作總貢獻度
  275. prodAnalysisRow.peerAct += IotTeachingDatRow.peerAct; //互評活動次數
  276. prodAnalysisRow.peerStuParticipationT += IotTeachingDatRow.peerStuParticipationT; //互評學生參與總次數
  277. if (IotTeachingDatRow.useTransMode.Equals("1")) prodAnalysisRow.useTransMode++; //有使用透明模式
  278. if (IotTeachingDatRow.useMiniMode.Equals("1")) prodAnalysisRow.useMiniMode++; //有使用最小化模式
  279. if (addFlg)
  280. {
  281. ProdAnalysisList.Add(prodAnalysisRow);
  282. }
  283. }
  284. }
  285. }
  286. //1.記入IES5 Redis ProdAnalysis:Day
  287. //2.記入IES5 CosmosDB Day
  288. if (ProdAnalysisList.Count > 0)
  289. {
  290. //資料整形
  291. Dictionary<string, Dictionary<string, string>> redisSchFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => schId => schJsonContent
  292. List<ProdAnalysisCosmos> cosmosSchList = new List<ProdAnalysisCosmos>();
  293. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumDayDic = new Dictionary<string, ProdAnalysisCosmos>();
  294. foreach (ProdAnalysis prodAnalysisRow in ProdAnalysisList)
  295. {
  296. //Redis整形
  297. string toolType = prodAnalysisRow.toolType;
  298. string schoolId = prodAnalysisRow.schoolId;
  299. string hkey = $"ProdAnalysis:Day:{toolType}:{y}{m}{d}";
  300. string fieldContent = prodAnalysisRow.ToJsonString();
  301. if (redisSchFieldDic.ContainsKey(hkey)) redisSchFieldDic[hkey].Add(schoolId, fieldContent);
  302. else redisSchFieldDic.Add(hkey, new Dictionary<string, string>() { { schoolId, fieldContent } });
  303. //CosmosDB整形
  304. ProdAnalysisCosmos ProdAnalysisCosmosRow = prodAnalysisRow.ToJsonString().ToObject<ProdAnalysisCosmos>();
  305. ProdAnalysisCosmosRow.date = $"{y}{m}{d}";
  306. ProdAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  307. ProdAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  308. ProdAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  309. DateTimeOffset dateTime = new DateTimeOffset(ProdAnalysisCosmosRow.year, ProdAnalysisCosmosRow.month, ProdAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  310. ProdAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  311. ProdAnalysisCosmosRow.id = $"{ProdAnalysisCosmosRow.toolType}-{ProdAnalysisCosmosRow.date}-{schoolId}";
  312. ProdAnalysisCosmosRow.dateUnit = "day";
  313. ProdAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  314. cosmosSchList.Add(ProdAnalysisCosmosRow);
  315. }
  316. //記入Redis
  317. if (redisSchFieldDic.Count > 0)
  318. {
  319. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisSchFieldDic)
  320. {
  321. //記入Redis
  322. string hkey = hkeyItem.Key;
  323. List<HashEntry> hvalList = new List<HashEntry>();
  324. Dictionary<string, string> fieldDic = hkeyItem.Value;
  325. foreach (KeyValuePair<string, string> schItem in fieldDic)
  326. {
  327. string schoolId = schItem.Key;
  328. string fieldContent = schItem.Value;
  329. hvalList.Add(new HashEntry($"{schoolId}", fieldContent));
  330. }
  331. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  332. }
  333. }
  334. //記入CosmosDB
  335. if (cosmosSchList.Count > 0)
  336. {
  337. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchList)
  338. {
  339. //各校每日CosmosDB記入
  340. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  341. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  342. cosmosAllSchSumDayDic = GenAnalysisRowSumData(cosmosAllSchSumDayDic, cosmosSchRow, calPropList, "day");
  343. }
  344. //每日所有學校數據總計CosmosDB記入
  345. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumDayDic)
  346. {
  347. string toolType = schItem.Key;
  348. ProdAnalysisCosmos cosmosAllSchSumDay = schItem.Value;
  349. cosmosAllSchSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  350. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumDay);
  351. }
  352. }
  353. }
  354. //取 Redis 該月所有 ProdAnalysis:Day
  355. //1.生成 Redis ProdAnalysis:Month
  356. //2.生成 CosmosDB Month
  357. Dictionary<string, Dictionary<string, ProdAnalysis>> ProdAnalysisListMonth = new Dictionary<string, Dictionary<string, ProdAnalysis>>();
  358. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumMonthDic = new Dictionary<string, ProdAnalysisCosmos>();
  359. List<ProdAnalysisCosmos> cosmosSchListMonth = new List<ProdAnalysisCosmos>();
  360. string patternD = $"ProdAnalysis:Day:HiT*:{y}{m}*";
  361. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  362. if (keysDayList.Count > 0)
  363. {
  364. foreach (string keyDay in keysDayList)
  365. {
  366. string[] keyItemList = keyDay.Split(':'); //ProdAnalysis:Day:HiTeach:20230326
  367. string toolType = keyItemList[2];
  368. string dateStrD = keyItemList[3];
  369. string dateStrD_y = string.Empty;
  370. string dateStrD_m = string.Empty;
  371. string dateStrD_d = string.Empty;
  372. Regex rgx = new Regex(@"[0-9]{8}");
  373. if (rgx.IsMatch(dateStrD))
  374. {
  375. dateStrD_y = dateStrD.Substring(0, 4);
  376. dateStrD_m = dateStrD.Substring(4, 2);
  377. dateStrD_d = dateStrD.Substring(6, 2);
  378. }
  379. string prodAnalMonthKey = $"ProdAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  380. bool ProdAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  381. if (ProdAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  382. {
  383. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有學校的統計項目
  384. foreach (HashEntry hset in hsetDay)
  385. {
  386. string keySchId = hset.Name;
  387. string valSchDataJson = hset.Value;
  388. ProdAnalysis SchDataTodo = valSchDataJson.ToObject<ProdAnalysis>();
  389. //Redis Month 資料生成
  390. if (ProdAnalysisListMonth.ContainsKey(prodAnalMonthKey)) //月Dic已有此key => 分校累加
  391. {
  392. if (ProdAnalysisListMonth[$"{prodAnalMonthKey}"].ContainsKey($"{keySchId}"))
  393. {
  394. ProdAnalysis SchDataNow = ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"];
  395. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  396. {
  397. if (calPropList.Contains(propertyInfo.Name))
  398. {
  399. var propType = propertyInfo.PropertyType;
  400. var valNow = propertyInfo.GetValue(SchDataNow);
  401. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  402. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  403. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  404. }
  405. }
  406. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  407. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  408. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  409. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  410. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  411. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  412. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  413. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  414. }
  415. //無此校資料 => 該校資料放入
  416. else
  417. {
  418. ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"] = SchDataTodo;
  419. }
  420. }
  421. else //無此月資料 => 所有學校資料放入
  422. {
  423. ProdAnalysisListMonth.Add(prodAnalMonthKey, new Dictionary<string, ProdAnalysis>() { { keySchId, SchDataTodo } });
  424. }
  425. }
  426. }
  427. }
  428. }
  429. if (ProdAnalysisListMonth.Count > 0)
  430. {
  431. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysis>> item in ProdAnalysisListMonth)
  432. {
  433. string monthRedisKey = item.Key;
  434. List<HashEntry> hvalList = new List<HashEntry>();
  435. Dictionary<string, ProdAnalysis> monthRedisSchDIc = item.Value;
  436. foreach (KeyValuePair<string, ProdAnalysis> monthRedisSchItem in monthRedisSchDIc)
  437. {
  438. string monthRedisSchId = monthRedisSchItem.Key;
  439. ProdAnalysis monthRedisSchData = monthRedisSchItem.Value;
  440. //Redis資料製作
  441. hvalList.Add(new HashEntry(monthRedisSchId, monthRedisSchData.ToJsonString()));
  442. //CosmosDB資料製作
  443. ProdAnalysisCosmos cosmosSchRow = monthRedisSchData.ToJsonString().ToObject<ProdAnalysisCosmos>();
  444. cosmosSchRow.date = $"{y}{m}";
  445. cosmosSchRow.year = Convert.ToInt32(y, 10);
  446. cosmosSchRow.month = Convert.ToInt32(m, 10);
  447. DateTimeOffset dateTime = new DateTimeOffset(cosmosSchRow.year, cosmosSchRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  448. cosmosSchRow.dateTime = dateTime.ToUnixTimeSeconds();
  449. cosmosSchRow.id = $"{monthRedisSchData.toolType}-{cosmosSchRow.date}-{monthRedisSchId}";
  450. cosmosSchRow.dateUnit = "month";
  451. cosmosSchRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  452. cosmosSchListMonth.Add(cosmosSchRow);
  453. }
  454. //記入Redis
  455. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  456. }
  457. //記入CosmosDB
  458. if (cosmosSchListMonth.Count > 0)
  459. {
  460. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchListMonth)
  461. {
  462. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  463. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  464. cosmosAllSchSumMonthDic = GenAnalysisRowSumData(cosmosAllSchSumMonthDic, cosmosSchRow, calPropList, "month");
  465. }
  466. //每月所有學校數據總計CosmosDB記入
  467. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumMonthDic)
  468. {
  469. string toolType = schItem.Key;
  470. ProdAnalysisCosmos cosmosAllSchSumMonth = schItem.Value;
  471. cosmosAllSchSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  472. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumMonth);
  473. }
  474. }
  475. }
  476. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  477. Dictionary<string, Dictionary<string, ProdAnalysisCosmos>> ProdAnalysisListYear = new Dictionary<string, Dictionary<string, ProdAnalysisCosmos>>();
  478. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumYearDic = new Dictionary<string, ProdAnalysisCosmos>();
  479. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.schoolId != 'allschool'";
  480. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProdAnalysis") }))
  481. {
  482. var json = await JsonDocument.ParseAsync(itemcr.Content);
  483. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  484. {
  485. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  486. {
  487. ProdAnalysisCosmos SchDataTodo = obj.ToObject<ProdAnalysisCosmos>();
  488. string toolType = SchDataTodo.toolType;
  489. string schId = SchDataTodo.schoolId;
  490. //年Dic已有此key => 分校累加
  491. if (ProdAnalysisListYear.ContainsKey(toolType))
  492. {
  493. if (ProdAnalysisListYear[$"{toolType}"].ContainsKey($"{schId}"))
  494. {
  495. ProdAnalysisCosmos SchDataNow = ProdAnalysisListYear[$"{toolType}"][$"{schId}"];
  496. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  497. {
  498. if (calPropList.Contains(propertyInfo.Name))
  499. {
  500. var propType = propertyInfo.PropertyType;
  501. var valNow = propertyInfo.GetValue(SchDataNow);
  502. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  503. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  504. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  505. }
  506. }
  507. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  508. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  509. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  510. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  511. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  512. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  513. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  514. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  515. }
  516. //無此校資料 => 該校資料放入
  517. else
  518. {
  519. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  520. SchDataNow.date = $"{y}";
  521. SchDataNow.year = Convert.ToInt32(y, 10);
  522. SchDataNow.month = 0;
  523. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  524. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  525. SchDataNow.id = $"{toolType}-{y}-{schId}";
  526. SchDataNow.dateUnit = "year";
  527. ProdAnalysisListYear[$"{toolType}"][$"{schId}"] = SchDataNow;
  528. }
  529. }
  530. //無此年資料 => 所有學校資料放入
  531. else
  532. {
  533. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  534. SchDataNow.date = $"{y}";
  535. SchDataNow.year = Convert.ToInt32(y, 10);
  536. SchDataNow.month = 0;
  537. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  538. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  539. SchDataNow.id = $"{toolType}-{y}-{schId}";
  540. SchDataNow.dateUnit = "year";
  541. ProdAnalysisListYear.Add(toolType, new Dictionary<string, ProdAnalysisCosmos>() { { schId, SchDataNow } });
  542. }
  543. }
  544. }
  545. }
  546. if (ProdAnalysisListYear.Count > 0)
  547. {
  548. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysisCosmos>> item in ProdAnalysisListYear)
  549. {
  550. //string toolType = item.Key;
  551. ProdAnalysisCosmos SchDataNow = new ProdAnalysisCosmos(); //當年某產品所有學校總和
  552. Dictionary<string, ProdAnalysisCosmos> yearCosmosSchDIc = item.Value;
  553. foreach (KeyValuePair<string, ProdAnalysisCosmos> yearCosmosSchItem in yearCosmosSchDIc)
  554. {
  555. string schId = yearCosmosSchItem.Key;
  556. ProdAnalysisCosmos cosmosSchRow = yearCosmosSchItem.Value;
  557. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  558. //每年所有學校數據總計CosmosDB記入
  559. cosmosAllSchSumYearDic = GenAnalysisRowSumData(cosmosAllSchSumYearDic, cosmosSchRow, calPropList, "year");
  560. }
  561. //每年所有學校數據總計CosmosDB記入
  562. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumYearDic)
  563. {
  564. string toolType = schItem.Key;
  565. ProdAnalysisCosmos cosmosAllSchSumYear = schItem.Value;
  566. cosmosAllSchSumYear.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  567. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumYear);
  568. }
  569. //await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(SchDataNow);
  570. }
  571. }
  572. //虛擬學校創建
  573. if (crtVirtualSchoolId.Count > 0)
  574. {
  575. //取得IES5 school Base
  576. string schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  577. string schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  578. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  579. {
  580. using var json = await JsonDocument.ParseAsync(itemsr.Content);
  581. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  582. {
  583. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  584. {
  585. string existSchid = obj.GetProperty("id").GetString();
  586. if (crtVirtualSchoolId.Contains(existSchid))
  587. {
  588. crtVirtualSchoolId.Remove(existSchid);
  589. }
  590. }
  591. }
  592. }
  593. //取得IES5 school VirtualBase
  594. schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  595. schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  596. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"VirtualBase") }))
  597. {
  598. using var json = await JsonDocument.ParseAsync(itemsr.Content);
  599. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  600. {
  601. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  602. {
  603. string existSchid = obj.GetProperty("id").GetString();
  604. if (crtVirtualSchoolId.Contains(existSchid))
  605. {
  606. crtVirtualSchoolId.Remove(existSchid);
  607. }
  608. }
  609. }
  610. }
  611. //VirtualBase school 建立
  612. if (crtVirtualSchoolId.Count > 0)
  613. {
  614. //取得CSV2 School,存在者建立IES5 VirtualBase school
  615. List<School> crtVSchoolList = new List<School>();
  616. string csv2SchIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  617. string schCsv2QueryText = $"SELECT c.shortCode, c.name, c.countryName, c.provinceName, c.cityName, c.distName, c.address FROM c where ARRAY_CONTAINS({csv2SchIdListStr}, c.shortCode, true)";
  618. await foreach (var item in _azureCosmosClientCsv2CnRead.GetContainer("Core", "School").GetItemQueryStreamIteratorSql(queryText: schCsv2QueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
  619. {
  620. using var json = await JsonDocument.ParseAsync(item.Content);
  621. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  622. {
  623. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  624. {
  625. VirtualBase crtVSchool = new VirtualBase();
  626. crtVSchool.code = "VirtualBase";
  627. crtVSchool.id = obj.GetProperty("shortCode").GetString();
  628. crtVSchool.pk = "School";
  629. crtVSchool.schoolCode = obj.GetProperty("shortCode").GetString();
  630. crtVSchool.name = obj.GetProperty("name").GetString();
  631. crtVSchool.region = (obj.TryGetProperty("countryName", out JsonElement countryNameJ)) ? Convert.ToString(countryNameJ) : null;
  632. crtVSchool.province = (obj.TryGetProperty("provinceName", out JsonElement provinceNameJ)) ? Convert.ToString(provinceNameJ) : null;
  633. crtVSchool.city = (obj.TryGetProperty("cityName", out JsonElement cityNameJ)) ? Convert.ToString(cityNameJ) : null;
  634. crtVSchool.dist = (obj.TryGetProperty("distName", out JsonElement distNameJ)) ? Convert.ToString(distNameJ) : null;
  635. crtVSchool.address = (obj.TryGetProperty("address", out JsonElement addressJ)) ? Convert.ToString(addressJ) : null;
  636. crtVSchool.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  637. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<VirtualBase>(crtVSchool);
  638. }
  639. }
  640. }
  641. }
  642. }
  643. }
  644. catch (Exception ex)
  645. {
  646. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatIes5ProdAnalData() 生成學校年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  647. }
  648. }
  649. //年月日 TMID IOT產品分析數據生成
  650. ///1.生成 Redis TmidAnalysis:Month
  651. ///2.生成 CosmosDB Month
  652. ///3.生成 CosmosDB 系統所有TMID數值加總
  653. private static async Task CreatTmidProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  654. {
  655. try
  656. {
  657. var redisClinet8 = _azureRedis.GetRedisClient(8);
  658. List<string> calPropList = new List<string>() { "lessonRecord", "useIES", "useIES5Resource", "useWebIrs", "useDeviceIrs", "useHaboard", "useHita", "lessonLengMin", "lessonLeng0", "stuShow", "stuLessonLengMin", "tGreen", "tLesson", "lTypeCoop", "lTypeIact", "lTypeMis", "lTypeTst", "lTypeDif", "lTypeNone", "lessonCnt928", "lessonCntId", "lessonCntDevice", "lessonCntIdDevice", "mission", "missionFin", "item", "interact", "sendSok", "learnPeer", "learnCoop", "useWordCloud", "useClouDAS", "useGPT", "useIes5Test", "usePaperTest", "useExcelTest", "useScoreBoard", "learnParticipationCnt", "learnParticipationT", "coopMission", "coopWork", "coopContributionT", "peerAct", "peerStuParticipationT", "useTransMode", "useMiniMode" }; //要計算的ProdAnalysis欄位列表
  659. List<TmidAnalysis> TmidAnalysisList = new List<TmidAnalysis>();
  660. if (IotTeachingDataList.Count > 0)
  661. {
  662. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  663. {
  664. bool addTmidFlg = false;
  665. string tmid = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid)) ? IotTeachingDatRow.tmid.ToString() : string.Empty;
  666. string deviceId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId)) ? IotTeachingDatRow.deviceId.ToString() : string.Empty;
  667. string ver = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.version)) ? IotTeachingDatRow.version : string.Empty;
  668. string toolType = string.Empty;
  669. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  670. {
  671. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  672. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  673. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  674. }
  675. if (!string.IsNullOrWhiteSpace(tmid) && !string.IsNullOrWhiteSpace(deviceId) && !string.IsNullOrWhiteSpace(toolType))
  676. {
  677. bool addFlg = false;
  678. TmidAnalysis tmidAnalysisRow = TmidAnalysisList.Where(s => s.tmid.Equals(tmid) && s.toolType.Equals(toolType)).FirstOrDefault();
  679. //無此tmid數據=>創建
  680. if (tmidAnalysisRow == null)
  681. {
  682. tmidAnalysisRow = new TmidAnalysis();
  683. tmidAnalysisRow.tmid = tmid;
  684. tmidAnalysisRow.toolType = toolType;
  685. addFlg = true;
  686. }
  687. //欄位加總
  688. if (!tmidAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  689. {
  690. tmidAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  691. }
  692. tmidAnalysisRow.deviceCnt = tmidAnalysisRow.deviceList.Count;
  693. switch (IotTeachingDatRow.authType)
  694. {
  695. case "0": //928授權
  696. if (!tmidAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  697. tmidAnalysisRow.lessonCnt928++;
  698. break;
  699. case "1": //ID授權
  700. if (!tmidAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  701. tmidAnalysisRow.lessonCntId++;
  702. break;
  703. case "2": //機器授權
  704. if (!tmidAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  705. tmidAnalysisRow.lessonCntDevice++;
  706. break;
  707. case "3": //ID+機器授權
  708. if (!tmidAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  709. tmidAnalysisRow.lessonCntIdDevice++;
  710. break;
  711. }
  712. tmidAnalysisRow.deviceNoAuth = tmidAnalysisRow.deviceNoAuthList.Count;
  713. tmidAnalysisRow.deviceAuth = tmidAnalysisRow.deviceAuthList.Count;
  714. if (!tmidAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  715. {
  716. tmidAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  717. }
  718. tmidAnalysisRow.tmidCnt = tmidAnalysisRow.tmidList.Count;
  719. tmidAnalysisRow.lessonRecord++;
  720. if (IotTeachingDatRow.useIES.Equals("1")) tmidAnalysisRow.useIES++;
  721. tmidAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  722. if (IotTeachingDatRow.useWebIrs.Equals("1")) tmidAnalysisRow.useWebIrs++;
  723. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) tmidAnalysisRow.useDeviceIrs++;
  724. if (IotTeachingDatRow.useHaboard.Equals("1")) tmidAnalysisRow.useHaboard++;
  725. if (IotTeachingDatRow.useHita.Equals("1")) tmidAnalysisRow.useHita++;
  726. tmidAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  727. if (IotTeachingDatRow.lessonLengMin.Equals(0)) tmidAnalysisRow.lessonLeng0++;
  728. tmidAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  729. tmidAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  730. if (IotTeachingDatRow.tPoint >= 70) tmidAnalysisRow.tGreen++;
  731. if (IotTeachingDatRow.lessonLengMin >= 10 && IotTeachingDatRow.tPoint > 0) tmidAnalysisRow.tLesson++;
  732. if (IotTeachingDatRow.lTypeCoop.Equals("1")) tmidAnalysisRow.lTypeCoop++;
  733. if (IotTeachingDatRow.lTypeIact.Equals("1")) tmidAnalysisRow.lTypeIact++;
  734. if (IotTeachingDatRow.lTypeMis.Equals("1")) tmidAnalysisRow.lTypeMis++;
  735. if (IotTeachingDatRow.lTypeTst.Equals("1")) tmidAnalysisRow.lTypeTst++;
  736. if (IotTeachingDatRow.lTypeDif.Equals("1")) tmidAnalysisRow.lTypeDif++;
  737. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) tmidAnalysisRow.lTypeNone++;
  738. tmidAnalysisRow.mission += IotTeachingDatRow.mission;
  739. tmidAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  740. tmidAnalysisRow.item += IotTeachingDatRow.item;
  741. tmidAnalysisRow.interact += IotTeachingDatRow.interact;
  742. if (IotTeachingDatRow.sendSok.Equals("1")) tmidAnalysisRow.sendSok++;
  743. if (IotTeachingDatRow.learnPeer.Equals("1")) tmidAnalysisRow.learnPeer++;
  744. if (IotTeachingDatRow.learnCoop.Equals("1")) tmidAnalysisRow.learnCoop++;
  745. if (IotTeachingDatRow.useWordCloud.Equals("1")) tmidAnalysisRow.useWordCloud++;
  746. if (IotTeachingDatRow.useClouDAS.Equals("1")) tmidAnalysisRow.useClouDAS++;
  747. if (IotTeachingDatRow.useGPT.Equals("1")) tmidAnalysisRow.useGPT++;
  748. if (IotTeachingDatRow.useIes5Test.Equals("1")) tmidAnalysisRow.useIes5Test++;
  749. if (IotTeachingDatRow.usePaperTest.Equals("1")) tmidAnalysisRow.usePaperTest++;
  750. if (IotTeachingDatRow.useExcelTest.Equals("1")) tmidAnalysisRow.useExcelTest++;
  751. if (IotTeachingDatRow.useScoreBoard.Equals("1")) tmidAnalysisRow.useScoreBoard++; //課堂中使用記分板
  752. if (IotTeachingDatRow.learnParticipationCnt.Equals("1")) tmidAnalysisRow.learnParticipationCnt++; //學習參與度次數
  753. tmidAnalysisRow.learnParticipationT += IotTeachingDatRow.learnParticipation; //學習參與度指數(總和)
  754. decimal learnParticipationTmp = (tmidAnalysisRow.learnParticipationCnt > 0) ? (decimal)tmidAnalysisRow.learnParticipationT / (decimal)tmidAnalysisRow.learnParticipationCnt : 0;
  755. tmidAnalysisRow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  756. tmidAnalysisRow.coopMission += IotTeachingDatRow.coopMission; //協作任務數
  757. tmidAnalysisRow.coopWork += IotTeachingDatRow.coopWork; //協作作品數
  758. tmidAnalysisRow.coopContributionT += IotTeachingDatRow.coopContributionT; //協作總貢獻度
  759. tmidAnalysisRow.peerAct += IotTeachingDatRow.peerAct; //互評活動次數
  760. tmidAnalysisRow.peerStuParticipationT += IotTeachingDatRow.peerStuParticipationT; //互評學生參與總次數
  761. if (IotTeachingDatRow.useTransMode.Equals("1")) tmidAnalysisRow.useTransMode++; //有使用透明模式
  762. if (IotTeachingDatRow.useMiniMode.Equals("1")) tmidAnalysisRow.useMiniMode++; //有使用最小化模式
  763. if (!string.IsNullOrWhiteSpace(ver) && !tmidAnalysisRow.verList.Contains(ver))
  764. {
  765. tmidAnalysisRow.verList.Add(ver);
  766. }
  767. if (addFlg)
  768. {
  769. TmidAnalysisList.Add(tmidAnalysisRow);
  770. }
  771. }
  772. }
  773. }
  774. //1.記入CSV2 Redis TmidAnalysis:Day
  775. //2.記入CSV2 CosmosDB Day
  776. if (TmidAnalysisList.Count > 0)
  777. {
  778. //資料整形
  779. Dictionary<string, Dictionary<string, string>> redisTmidFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => tmid => JsonContent
  780. List<TmidAnalysisCosmos> cosmosTmidList = new List<TmidAnalysisCosmos>();
  781. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumDayDic = new Dictionary<string, TmidAnalysisCosmos>();
  782. foreach (TmidAnalysis tmidAnalysisRow in TmidAnalysisList)
  783. {
  784. //Redis整形
  785. string toolType = tmidAnalysisRow.toolType;
  786. string tmid = tmidAnalysisRow.tmid;
  787. string hkey = $"TmidAnalysis:Day:{toolType}:{y}{m}{d}";
  788. string fieldContent = tmidAnalysisRow.ToJsonString();
  789. if (redisTmidFieldDic.ContainsKey(hkey)) redisTmidFieldDic[hkey].Add(tmid, fieldContent);
  790. else redisTmidFieldDic.Add(hkey, new Dictionary<string, string>() { { tmid, fieldContent } });
  791. //CosmosDB整形
  792. TmidAnalysisCosmos TmidAnalysisCosmosRow = tmidAnalysisRow.ToJsonString().ToObject<TmidAnalysisCosmos>();
  793. TmidAnalysisCosmosRow.date = $"{y}{m}{d}";
  794. TmidAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  795. TmidAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  796. TmidAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  797. DateTimeOffset dateTime = new DateTimeOffset(TmidAnalysisCosmosRow.year, TmidAnalysisCosmosRow.month, TmidAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  798. TmidAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  799. TmidAnalysisCosmosRow.id = $"{TmidAnalysisCosmosRow.toolType}-{TmidAnalysisCosmosRow.date}-{tmid}";
  800. TmidAnalysisCosmosRow.dateUnit = "day";
  801. TmidAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  802. cosmosTmidList.Add(TmidAnalysisCosmosRow);
  803. }
  804. //記入Redis
  805. if (redisTmidFieldDic.Count > 0)
  806. {
  807. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisTmidFieldDic)
  808. {
  809. string hkey = hkeyItem.Key;
  810. List<HashEntry> hvalList = new List<HashEntry>();
  811. Dictionary<string, string> fieldDic = hkeyItem.Value;
  812. foreach (KeyValuePair<string, string> tmidItem in fieldDic)
  813. {
  814. string tmid = tmidItem.Key;
  815. string fieldContent = tmidItem.Value;
  816. hvalList.Add(new HashEntry($"{tmid}", fieldContent));
  817. }
  818. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  819. }
  820. }
  821. //記入CosmosDB
  822. if (cosmosTmidList.Count > 0)
  823. {
  824. foreach (TmidAnalysisCosmos cosmosTmidRow in cosmosTmidList)
  825. {
  826. //各TMID每日CosmosDB記入
  827. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  828. //所有TMID每日數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  829. cosmosAllTmidSumDayDic = GenAnalysisRowSumData(cosmosAllTmidSumDayDic, cosmosTmidRow, calPropList, "day");
  830. }
  831. //每日所有TMID數據總計CosmosDB記入
  832. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumDayDic)
  833. {
  834. string toolType = tmidItem.Key;
  835. TmidAnalysisCosmos cosmosAllTmidSumDay = tmidItem.Value;
  836. cosmosAllTmidSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  837. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumDay);
  838. }
  839. }
  840. }
  841. //取 Redis 該月所有 TmidAnalysis:Day
  842. //1.生成 Redis TmidAnalysis:Month
  843. //2.生成 CosmosDB Month
  844. Dictionary<string, Dictionary<string, TmidAnalysis>> TmidAnalysisListMonth = new Dictionary<string, Dictionary<string, TmidAnalysis>>();
  845. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumMonthDic = new Dictionary<string, TmidAnalysisCosmos>();
  846. List<TmidAnalysisCosmos> cosmosTmidListMonth = new List<TmidAnalysisCosmos>();
  847. string patternD = $"TmidAnalysis:Day:HiT*:{y}{m}*";
  848. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  849. if (keysDayList.Count > 0)
  850. {
  851. foreach (string keyDay in keysDayList)
  852. {
  853. string[] keyItemList = keyDay.Split(':'); //TmidAnalysis:Day:HiTeach:20230326
  854. string toolType = keyItemList[2];
  855. string dateStrD = keyItemList[3];
  856. string dateStrD_y = string.Empty;
  857. string dateStrD_m = string.Empty;
  858. string dateStrD_d = string.Empty;
  859. Regex rgx = new Regex(@"[0-9]{8}");
  860. if (rgx.IsMatch(dateStrD))
  861. {
  862. dateStrD_y = dateStrD.Substring(0, 4);
  863. dateStrD_m = dateStrD.Substring(4, 2);
  864. dateStrD_d = dateStrD.Substring(6, 2);
  865. }
  866. string tmidAnalMonthKey = $"TmidAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  867. bool tmidAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  868. if (tmidAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  869. {
  870. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有學校的統計項目
  871. foreach (HashEntry hset in hsetDay)
  872. {
  873. string keyTMID = hset.Name;
  874. string valTmidDataJson = hset.Value;
  875. TmidAnalysis tmidDataTodo = valTmidDataJson.ToObject<TmidAnalysis>();
  876. //Redis Month 資料生成
  877. if (TmidAnalysisListMonth.ContainsKey(tmidAnalMonthKey)) //月Dic已有此key => TMID累加
  878. {
  879. if (TmidAnalysisListMonth[$"{tmidAnalMonthKey}"].ContainsKey($"{keyTMID}"))
  880. {
  881. TmidAnalysis TmidDataNow = TmidAnalysisListMonth[$"{tmidAnalMonthKey}"][$"{keyTMID}"];
  882. foreach (PropertyInfo propertyInfo in TmidDataNow.GetType().GetProperties())
  883. {
  884. if (calPropList.Contains(propertyInfo.Name))
  885. {
  886. var propType = propertyInfo.PropertyType;
  887. var valNow = propertyInfo.GetValue(TmidDataNow);
  888. var valTodo = tmidDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(tmidDataTodo);
  889. if (propType.Equals(typeof(long))) propertyInfo.SetValue(TmidDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  890. else propertyInfo.SetValue(TmidDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  891. }
  892. }
  893. TmidDataNow.deviceList = TmidDataNow.deviceList.Union(tmidDataTodo.deviceList).ToList();
  894. TmidDataNow.deviceCnt = TmidDataNow.deviceList.Count;
  895. TmidDataNow.deviceAuthList = TmidDataNow.deviceAuthList.Union(tmidDataTodo.deviceAuthList).ToList();
  896. TmidDataNow.deviceAuth = TmidDataNow.deviceAuthList.Count;
  897. TmidDataNow.deviceNoAuthList = TmidDataNow.deviceNoAuthList.Union(tmidDataTodo.deviceNoAuthList).ToList();
  898. TmidDataNow.deviceNoAuth = TmidDataNow.deviceNoAuthList.Count;
  899. TmidDataNow.tmidList = TmidDataNow.tmidList.Union(tmidDataTodo.tmidList).ToList();
  900. TmidDataNow.tmidCnt = TmidDataNow.tmidList.Count;
  901. TmidDataNow.verList = TmidDataNow.verList.Union(tmidDataTodo.verList).ToList();
  902. }
  903. //無TMID資料 => 該TMID資料放入
  904. else
  905. {
  906. TmidAnalysisListMonth[$"{tmidAnalMonthKey}"][$"{keyTMID}"] = tmidDataTodo;
  907. }
  908. }
  909. else //無此月資料 => 所有TMID資料放入
  910. {
  911. TmidAnalysisListMonth.Add(tmidAnalMonthKey, new Dictionary<string, TmidAnalysis>() { { keyTMID, tmidDataTodo } });
  912. }
  913. }
  914. }
  915. }
  916. }
  917. if (TmidAnalysisListMonth.Count > 0)
  918. {
  919. foreach (KeyValuePair<string, Dictionary<string, TmidAnalysis>> item in TmidAnalysisListMonth)
  920. {
  921. string monthRedisKey = item.Key;
  922. List<HashEntry> hvalList = new List<HashEntry>();
  923. Dictionary<string, TmidAnalysis> monthRedisTmidDIc = item.Value;
  924. foreach (KeyValuePair<string, TmidAnalysis> monthRedisTmidItem in monthRedisTmidDIc)
  925. {
  926. string monthRedisTmid = monthRedisTmidItem.Key;
  927. TmidAnalysis monthRedisTmidData = monthRedisTmidItem.Value;
  928. //Redis資料製作
  929. hvalList.Add(new HashEntry(monthRedisTmid, monthRedisTmidData.ToJsonString()));
  930. //CosmosDB資料製作
  931. TmidAnalysisCosmos cosmosTmidRow = monthRedisTmidData.ToJsonString().ToObject<TmidAnalysisCosmos>();
  932. cosmosTmidRow.date = $"{y}{m}";
  933. cosmosTmidRow.year = Convert.ToInt32(y, 10);
  934. cosmosTmidRow.month = Convert.ToInt32(m, 10);
  935. DateTimeOffset dateTime = new DateTimeOffset(cosmosTmidRow.year, cosmosTmidRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  936. cosmosTmidRow.dateTime = dateTime.ToUnixTimeSeconds();
  937. cosmosTmidRow.id = $"{monthRedisTmidData.toolType}-{cosmosTmidRow.date}-{monthRedisTmid}";
  938. cosmosTmidRow.dateUnit = "month";
  939. cosmosTmidRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  940. cosmosTmidListMonth.Add(cosmosTmidRow);
  941. }
  942. //記入Redis
  943. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  944. }
  945. //記入CosmosDB
  946. if (cosmosTmidListMonth.Count > 0)
  947. {
  948. foreach (TmidAnalysisCosmos cosmosTmidRow in cosmosTmidListMonth)
  949. {
  950. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  951. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  952. cosmosAllTmidSumMonthDic = GenAnalysisRowSumData(cosmosAllTmidSumMonthDic, cosmosTmidRow, calPropList, "day");
  953. }
  954. //每月所有TMID數據總計CosmosDB記入
  955. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumMonthDic)
  956. {
  957. string toolType = tmidItem.Key;
  958. TmidAnalysisCosmos cosmosAllTmidSumMonth = tmidItem.Value;
  959. cosmosAllTmidSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  960. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumMonth);
  961. }
  962. }
  963. }
  964. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  965. Dictionary<string, Dictionary<string, TmidAnalysisCosmos>> TmidAnalysisListYear = new Dictionary<string, Dictionary<string, TmidAnalysisCosmos>>();
  966. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumYearDic = new Dictionary<string, TmidAnalysisCosmos>();
  967. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.tmid != 'alltmid'";
  968. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("TmidAnalysis") }))
  969. {
  970. var json = await JsonDocument.ParseAsync(itemcr.Content);
  971. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  972. {
  973. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  974. {
  975. TmidAnalysisCosmos TmidDataTodo = obj.ToObject<TmidAnalysisCosmos>();
  976. string toolType = TmidDataTodo.toolType;
  977. string tmid = TmidDataTodo.tmid;
  978. //年Dic已有此key => 分校累加
  979. if (TmidAnalysisListYear.ContainsKey(toolType))
  980. {
  981. if (TmidAnalysisListYear[$"{toolType}"].ContainsKey($"{tmid}"))
  982. {
  983. TmidAnalysisCosmos TmidDataNow = TmidAnalysisListYear[$"{toolType}"][$"{tmid}"];
  984. foreach (PropertyInfo propertyInfo in TmidDataNow.GetType().GetProperties())
  985. {
  986. if (calPropList.Contains(propertyInfo.Name))
  987. {
  988. var propType = propertyInfo.PropertyType;
  989. var valNow = propertyInfo.GetValue(TmidDataNow);
  990. var valTodo = TmidDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(TmidDataTodo);
  991. if (propType.Equals(typeof(long))) propertyInfo.SetValue(TmidDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  992. else propertyInfo.SetValue(TmidDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  993. }
  994. }
  995. TmidDataNow.deviceList = TmidDataNow.deviceList.Union(TmidDataTodo.deviceList).ToList();
  996. TmidDataNow.deviceCnt = TmidDataNow.deviceList.Count;
  997. TmidDataNow.deviceAuthList = TmidDataNow.deviceAuthList.Union(TmidDataTodo.deviceAuthList).ToList();
  998. TmidDataNow.deviceAuth = TmidDataNow.deviceAuthList.Count;
  999. TmidDataNow.deviceNoAuthList = TmidDataNow.deviceNoAuthList.Union(TmidDataTodo.deviceNoAuthList).ToList();
  1000. TmidDataNow.deviceNoAuth = TmidDataNow.deviceNoAuthList.Count;
  1001. TmidDataNow.tmidList = TmidDataNow.tmidList.Union(TmidDataTodo.tmidList).ToList();
  1002. TmidDataNow.tmidCnt = TmidDataNow.tmidList.Count;
  1003. TmidDataNow.verList = TmidDataNow.verList.Union(TmidDataTodo.verList).ToList();
  1004. }
  1005. //無此校資料 => 該校資料放入
  1006. else
  1007. {
  1008. TmidAnalysisCosmos TmidDataNow = TmidDataTodo;
  1009. TmidDataNow.date = $"{y}";
  1010. TmidDataNow.year = Convert.ToInt32(y, 10);
  1011. TmidDataNow.month = 0;
  1012. DateTimeOffset dateTime = new DateTimeOffset(TmidDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1013. TmidDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1014. TmidDataNow.id = $"{toolType}-{y}-{tmid}";
  1015. TmidDataNow.dateUnit = "year";
  1016. TmidAnalysisListYear[$"{toolType}"][$"{tmid}"] = TmidDataNow;
  1017. }
  1018. }
  1019. //無此年資料 => 所有學校資料放入
  1020. else
  1021. {
  1022. TmidAnalysisCosmos TmidDataNow = TmidDataTodo;
  1023. TmidDataNow.date = $"{y}";
  1024. TmidDataNow.year = Convert.ToInt32(y, 10);
  1025. TmidDataNow.month = 0;
  1026. DateTimeOffset dateTime = new DateTimeOffset(TmidDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1027. TmidDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1028. TmidDataNow.id = $"{toolType}-{y}-{tmid}";
  1029. TmidDataNow.dateUnit = "year";
  1030. TmidAnalysisListYear.Add(toolType, new Dictionary<string, TmidAnalysisCosmos>() { { tmid, TmidDataNow } });
  1031. }
  1032. }
  1033. }
  1034. }
  1035. if (TmidAnalysisListYear.Count > 0)
  1036. {
  1037. foreach (KeyValuePair<string, Dictionary<string, TmidAnalysisCosmos>> item in TmidAnalysisListYear)
  1038. {
  1039. //string toolType = item.Key;
  1040. TmidAnalysisCosmos TmidDataNow = new TmidAnalysisCosmos(); //當年某產品所有學校總和
  1041. Dictionary<string, TmidAnalysisCosmos> yearCosmosTmidDIc = item.Value;
  1042. foreach (KeyValuePair<string, TmidAnalysisCosmos> yearCosmosTmidItem in yearCosmosTmidDIc)
  1043. {
  1044. string tmid = yearCosmosTmidItem.Key;
  1045. TmidAnalysisCosmos cosmosTmidRow = yearCosmosTmidItem.Value;
  1046. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  1047. //每年所有學校數據總計CosmosDB記入
  1048. cosmosAllTmidSumYearDic = GenAnalysisRowSumData(cosmosAllTmidSumYearDic, cosmosTmidRow, calPropList, "year");
  1049. }
  1050. //每年所有學校數據總計CosmosDB記入
  1051. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumYearDic)
  1052. {
  1053. string toolType = tmidItem.Key;
  1054. TmidAnalysisCosmos cosmosAllTmidSumYear = tmidItem.Value;
  1055. cosmosAllTmidSumYear.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1056. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumYear);
  1057. }
  1058. //await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(TmidDataNow);
  1059. }
  1060. }
  1061. }
  1062. catch (Exception ex)
  1063. {
  1064. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatTmidProdAnalData() 生成TMID年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.研發C組);
  1065. }
  1066. }
  1067. //生成年月日 Device IOT產品分析數據 [待做]
  1068. private static async Task CreatDeviceAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  1069. {
  1070. try
  1071. {
  1072. var redisClinet8 = _azureRedis.GetRedisClient(8);
  1073. //1. 從IOT 取得DeviceID 或 IP
  1074. //2. 從DeviceID 或 IP 算出地理位置(國省市區)
  1075. //3. 依照各地理位置進行項目統計;統計項目和學校及TMID統計項目相同
  1076. }
  1077. catch (Exception ex)
  1078. {
  1079. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatDeviceAnalData() 生成TMID年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.研發C組);
  1080. }
  1081. }
  1082. //累加各校每日/月總計資料,生成所有學校每日/月總計資料
  1083. private static Dictionary<string, ProdAnalysisCosmos> GenAnalysisRowSumData(Dictionary<string, ProdAnalysisCosmos> cosmosAllSumDic, ProdAnalysisCosmos cosmosRow, List<string> calPropList, string dataType) //dataType:"day","month","year"
  1084. {
  1085. try
  1086. {
  1087. string toolType = cosmosRow.toolType;
  1088. string schoolId = "allschool";
  1089. int year = 0;
  1090. int month = 1;
  1091. int day = 1;
  1092. switch (dataType)
  1093. {
  1094. case "day":
  1095. year = cosmosRow.year;
  1096. month = cosmosRow.month;
  1097. day = cosmosRow.day;
  1098. break;
  1099. case "month":
  1100. year = cosmosRow.year;
  1101. month = cosmosRow.month;
  1102. break;
  1103. case "year":
  1104. year = cosmosRow.year;
  1105. break;
  1106. }
  1107. ProdAnalysisCosmos cosmosAllSum = new ProdAnalysisCosmos();
  1108. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSum = cosmosAllSumDic[toolType];
  1109. foreach (PropertyInfo propertyInfo in cosmosAllSum.GetType().GetProperties()) //累加項目
  1110. {
  1111. if (calPropList.Contains(propertyInfo.Name))
  1112. {
  1113. var propType = propertyInfo.PropertyType;
  1114. var valNow = propertyInfo.GetValue(cosmosAllSum);
  1115. var valTodo = cosmosRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosRow);
  1116. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSum, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1117. else propertyInfo.SetValue(cosmosAllSum, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1118. }
  1119. }
  1120. cosmosAllSum.schoolId = schoolId;
  1121. cosmosAllSum.toolType = cosmosRow.toolType;
  1122. cosmosAllSum.date = cosmosRow.date;
  1123. cosmosAllSum.id = $"{cosmosAllSum.toolType}-{cosmosAllSum.date}-{schoolId}";
  1124. cosmosAllSum.dateUnit = dataType;
  1125. cosmosAllSum.year = year;
  1126. cosmosAllSum.month = month;
  1127. cosmosAllSum.day = day;
  1128. DateTimeOffset dateTime = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
  1129. cosmosAllSum.dateTime = dateTime.ToUnixTimeSeconds();
  1130. cosmosAllSum.deviceList = cosmosAllSum.deviceList.Union(cosmosRow.deviceList).ToList();
  1131. cosmosAllSum.deviceCnt = cosmosAllSum.deviceList.Count;
  1132. cosmosAllSum.deviceAuthList = cosmosAllSum.deviceAuthList.Union(cosmosRow.deviceAuthList).ToList();
  1133. cosmosAllSum.deviceAuth = cosmosAllSum.deviceAuthList.Count;
  1134. cosmosAllSum.deviceNoAuthList = cosmosAllSum.deviceNoAuthList.Union(cosmosRow.deviceNoAuthList).ToList();
  1135. cosmosAllSum.deviceNoAuth = cosmosAllSum.deviceNoAuthList.Count;
  1136. cosmosAllSum.tmidList = cosmosAllSum.tmidList.Union(cosmosRow.tmidList).ToList();
  1137. cosmosAllSum.tmidCnt = cosmosAllSum.tmidList.Count;
  1138. decimal learnParticipationTmp = (cosmosAllSum.learnParticipationCnt > 0) ? (decimal)cosmosAllSum.learnParticipationT / (decimal)cosmosAllSum.learnParticipationCnt : 0;
  1139. cosmosAllSum.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1140. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSumDic[toolType] = cosmosAllSum;
  1141. else cosmosAllSumDic.Add(toolType, cosmosAllSum);
  1142. return cosmosAllSumDic;
  1143. }
  1144. catch (Exception ex)
  1145. {
  1146. return cosmosAllSumDic;
  1147. }
  1148. }
  1149. //累加各TMID每日/月總計資料,生成所有TMID每日/月總計資料
  1150. private static Dictionary<string, TmidAnalysisCosmos> GenAnalysisRowSumData(Dictionary<string, TmidAnalysisCosmos> cosmosAllSumDic, TmidAnalysisCosmos cosmosRow, List<string> calPropList, string dataType) //dataType:"day","month","year"
  1151. {
  1152. try
  1153. {
  1154. string toolType = cosmosRow.toolType;
  1155. string tmid = "alltmid";
  1156. int year = 0;
  1157. int month = 1;
  1158. int day = 1;
  1159. switch (dataType)
  1160. {
  1161. case "day":
  1162. year = cosmosRow.year;
  1163. month = cosmosRow.month;
  1164. day = cosmosRow.day;
  1165. break;
  1166. case "month":
  1167. year = cosmosRow.year;
  1168. month = cosmosRow.month;
  1169. break;
  1170. case "year":
  1171. year = cosmosRow.year;
  1172. break;
  1173. }
  1174. TmidAnalysisCosmos cosmosAllSum = new TmidAnalysisCosmos();
  1175. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSum = cosmosAllSumDic[toolType];
  1176. foreach (PropertyInfo propertyInfo in cosmosAllSum.GetType().GetProperties()) //累加項目
  1177. {
  1178. if (calPropList.Contains(propertyInfo.Name))
  1179. {
  1180. var propType = propertyInfo.PropertyType;
  1181. var valNow = propertyInfo.GetValue(cosmosAllSum);
  1182. var valTodo = cosmosRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosRow);
  1183. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSum, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1184. else propertyInfo.SetValue(cosmosAllSum, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1185. }
  1186. }
  1187. cosmosAllSum.tmid = tmid;
  1188. cosmosAllSum.toolType = cosmosRow.toolType;
  1189. cosmosAllSum.date = cosmosRow.date;
  1190. cosmosAllSum.id = $"{cosmosAllSum.toolType}-{cosmosAllSum.date}-{tmid}";
  1191. cosmosAllSum.dateUnit = dataType;
  1192. cosmosAllSum.year = year;
  1193. cosmosAllSum.month = month;
  1194. cosmosAllSum.day = day;
  1195. DateTimeOffset dateTime = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
  1196. cosmosAllSum.dateTime = dateTime.ToUnixTimeSeconds();
  1197. cosmosAllSum.deviceList = cosmosAllSum.deviceList.Union(cosmosRow.deviceList).ToList();
  1198. cosmosAllSum.deviceCnt = cosmosAllSum.deviceList.Count;
  1199. cosmosAllSum.deviceAuthList = cosmosAllSum.deviceAuthList.Union(cosmosRow.deviceAuthList).ToList();
  1200. cosmosAllSum.deviceAuth = cosmosAllSum.deviceAuthList.Count;
  1201. cosmosAllSum.deviceNoAuthList = cosmosAllSum.deviceNoAuthList.Union(cosmosRow.deviceNoAuthList).ToList();
  1202. cosmosAllSum.deviceNoAuth = cosmosAllSum.deviceNoAuthList.Count;
  1203. cosmosAllSum.tmidList = cosmosAllSum.tmidList.Union(cosmosRow.tmidList).ToList();
  1204. cosmosAllSum.tmidCnt = cosmosAllSum.tmidList.Count;
  1205. cosmosAllSum.verList = cosmosAllSum.verList.Union(cosmosRow.verList).ToList();
  1206. decimal learnParticipationTmp = (cosmosAllSum.learnParticipationCnt > 0) ? (decimal)cosmosAllSum.learnParticipationT / (decimal)cosmosAllSum.learnParticipationCnt : 0;
  1207. cosmosAllSum.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1208. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSumDic[toolType] = cosmosAllSum;
  1209. else cosmosAllSumDic.Add(toolType, cosmosAllSum);
  1210. return cosmosAllSumDic;
  1211. }
  1212. catch (Exception ex)
  1213. {
  1214. return cosmosAllSumDic;
  1215. }
  1216. }
  1217. /// <summary>
  1218. /// 取得Redis(8)符合搜尋模式的key
  1219. /// </summary>
  1220. /// <param name="pattern"></param>
  1221. /// <returns></returns>
  1222. public static List<string> ScanRedisKeysByPattern(AzureRedisFactory _azureRedis, string pattern)
  1223. {
  1224. var redisClinet8 = _azureRedis.GetRedisClient(8);
  1225. var keys = new HashSet<RedisKey>();
  1226. int nextCursor = 0;
  1227. do
  1228. {
  1229. RedisResult redisResult = redisClinet8.Execute("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000");
  1230. var innerResult = (RedisResult[])redisResult;
  1231. nextCursor = int.Parse((string)innerResult[0]);
  1232. List<RedisKey> resultLines = ((RedisKey[])innerResult[1]).ToList();
  1233. keys.UnionWith(resultLines);
  1234. }
  1235. while (nextCursor != 0);
  1236. List<string> result = new List<string>();
  1237. if (keys.Count > 0)
  1238. {
  1239. foreach (RedisKey key in keys)
  1240. {
  1241. result.Add(key.ToString());
  1242. }
  1243. }
  1244. return result;
  1245. }
  1246. }
  1247. }