BIProdAnalysis.cs 136 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897
  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.DI.IPIP;
  17. using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter;
  18. using static TEAMModelOS.SDK.Extension.GeoRegion;
  19. using static TEAMModelOS.SDK.Models.Cosmos.IotTeachingData;
  20. namespace TEAMModelOS.SDK.Models.Service.BI
  21. {
  22. public static class BIProdAnalysis
  23. {
  24. /// <summary>
  25. /// 1.取得某日CS IOT 資料
  26. /// 2.生成IES5各校產品分析統計資料
  27. /// 3.生成TMID產品分析統計資料
  28. /// 4.生成DeviceID產品分析統計資料
  29. /// </summary>
  30. /// <param name="_azureRedis"></param>
  31. /// <param name="_dingDing"></param>
  32. /// <param name="y">年</param>
  33. /// <param name="m">月(2位數)</param>
  34. /// <param name="d">日(2位數)</param>
  35. /// <returns></returns>
  36. public static async Task BICreatDailyAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, CosmosClient _azureCosmosClientCsv2, CosmosClient _azureCosmosClientCsv2CnRead, DingDing _dingDing, City _city, string _location, string y, string m, string d, List<regionrow> _region_gl, List<regionrow> _region_cn)
  37. {
  38. List<IotTeachingData> IotTeachingDataList = await BIGetDailyRedisProdAnalData(_azureRedis, _azureCosmosClientCsv2, _dingDing, _city, _location, y, m, d, _region_gl, _region_cn); //取得Redis IOT 每日資訊
  39. await CreatIes5ProdAnalData(_azureRedis, _azureCosmosClient, _azureCosmosClientCsv2CnRead, _dingDing, y, m, d, IotTeachingDataList); //生成學校CosmosDB年月日統計資料
  40. await CreatTmidProdAnalData(_azureRedis, _azureCosmosClient, _dingDing, y, m, d, IotTeachingDataList); //生成TMID CosmosDB年月日統計資料
  41. await CreatGeoProdAnalData(_azureRedis, _azureCosmosClient, _dingDing, y, m, d, IotTeachingDataList); //生成地理位置 CosmosDB年月日統計資料
  42. }
  43. /// <summary>
  44. /// 取得某日CS IOT 資料
  45. /// </summary>
  46. /// <param name="_azureRedis"></param>
  47. /// <param name="_dingDing"></param>
  48. /// <param name="y">年</param>
  49. /// <param name="m">月(2位數)</param>
  50. /// <param name="d">日(2位數)</param>
  51. /// <returns></returns>
  52. public static async Task<List<IotTeachingData>> BIGetDailyRedisProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClientCsv2, DingDing _dingDing, City _city, string _location, string y, string m, string d, List<regionrow> _region_gl, List<regionrow> _region_cn)
  53. {
  54. List<IotTeachingData> IotTeachingDataList = new List<IotTeachingData>();
  55. try
  56. {
  57. var redisClinet2 = _azureRedis.GetRedisClient(2);
  58. var datetime = DateTimeOffset.UtcNow;
  59. var ynow = datetime.Year;
  60. HashSet<string> tmids = new HashSet<string>();
  61. Dictionary<string, string> tmidSchidDic = new Dictionary<string, string>();
  62. //取得CS Redis TeachingData (IOT紀錄只有三個月分)
  63. if (y.Equals(ynow.ToString()))
  64. {
  65. bool TeachingDataExist = await redisClinet2.KeyExistsAsync($"TeachingData:{m}{d}");
  66. if (TeachingDataExist)
  67. {
  68. RedisValue[] TeachingData = redisClinet2.ListRange($"TeachingData:{m}{d}");
  69. foreach (RedisValue tdataRow in TeachingData)
  70. {
  71. string[] tdata = tdataRow.ToString().Split(',');
  72. IotTeachingData IotTeachingData = new IotTeachingData();
  73. IotTeachingData.timestamp = Convert.ToInt64(tdata[0]);
  74. IotTeachingData.deviceId = tdata[1];
  75. IotTeachingData.channel = tdata[2];
  76. IotTeachingData.tmid = tdata[3];
  77. IotTeachingData.schoolId = tdata[4];
  78. IotTeachingData.useIES = tdata[5];
  79. IotTeachingData.useIES5Resource = (!string.IsNullOrWhiteSpace(tdata[6])) ? Convert.ToInt32(tdata[6]) : 0;
  80. IotTeachingData.useWebIrs = tdata[7];
  81. IotTeachingData.useDeviceIrs = tdata[8];
  82. IotTeachingData.useHaboard = tdata[9];
  83. IotTeachingData.useHita = tdata[10];
  84. IotTeachingData.lessonLengMin = (!string.IsNullOrWhiteSpace(tdata[11])) ? Convert.ToInt32(tdata[11]) : 0;
  85. IotTeachingData.stuShow = (!string.IsNullOrWhiteSpace(tdata[12])) ? Convert.ToInt32(tdata[12]) : 0;
  86. IotTeachingData.tPoint = (!string.IsNullOrWhiteSpace(tdata[13])) ? Convert.ToInt32(tdata[13]) : 0;
  87. IotTeachingData.lTypeCoop = tdata[14];
  88. IotTeachingData.lTypeIact = tdata[15];
  89. IotTeachingData.lTypeMis = tdata[16];
  90. IotTeachingData.lTypeTst = tdata[17];
  91. IotTeachingData.lTypeDif = tdata[18];
  92. IotTeachingData.authType = tdata[19];
  93. IotTeachingData.mission = (!string.IsNullOrWhiteSpace(tdata[20])) ? Convert.ToInt32(tdata[20]) : 0;
  94. IotTeachingData.missionFin = (!string.IsNullOrWhiteSpace(tdata[21])) ? Convert.ToInt32(tdata[21]) : 0;
  95. IotTeachingData.item = (!string.IsNullOrWhiteSpace(tdata[22])) ? Convert.ToInt32(tdata[22]) : 0;
  96. IotTeachingData.interact = (!string.IsNullOrWhiteSpace(tdata[23])) ? Convert.ToInt32(tdata[23]) : 0;
  97. IotTeachingData.ip = (tdata.Length > 24) ? tdata[24] : "";
  98. IotTeachingData.version = (tdata.Length > 25) ? tdata[25] : "";
  99. IotTeachingData.sendSok = (tdata.Length > 26 && !string.IsNullOrWhiteSpace(tdata[26])) ? tdata[26] : "0";
  100. IotTeachingData.learnPeer = (tdata.Length > 27 && !string.IsNullOrWhiteSpace(tdata[27])) ? tdata[27] : "0";
  101. IotTeachingData.learnCoop = (tdata.Length > 28 && !string.IsNullOrWhiteSpace(tdata[28])) ? tdata[28] : "0";
  102. IotTeachingData.useWordCloud = (tdata.Length > 29 && !string.IsNullOrWhiteSpace(tdata[29])) ? tdata[29] : "0";
  103. IotTeachingData.useClouDAS = (tdata.Length > 30 && !string.IsNullOrWhiteSpace(tdata[30])) ? tdata[30] : "0";
  104. IotTeachingData.useGPT = (tdata.Length > 31 && !string.IsNullOrWhiteSpace(tdata[31])) ? tdata[31] : "0";
  105. IotTeachingData.useIes5Test = (tdata.Length > 32 && !string.IsNullOrWhiteSpace(tdata[32])) ? tdata[32] : "0";
  106. IotTeachingData.usePaperTest = (tdata.Length > 33 && !string.IsNullOrWhiteSpace(tdata[33])) ? tdata[33] : "0";
  107. IotTeachingData.useExcelTest = (tdata.Length > 34 && !string.IsNullOrWhiteSpace(tdata[34])) ? tdata[34] : "0";
  108. IotTeachingData.useScoreBoard = (tdata.Length > 35 && !string.IsNullOrWhiteSpace(tdata[35])) ? tdata[35] : "0"; //課堂中使用記分板
  109. IotTeachingData.learnParticipation = (tdata.Length > 36 && !string.IsNullOrWhiteSpace(tdata[36])) ? Convert.ToInt32(tdata[36]) : 0; //學習參與度指數
  110. IotTeachingData.learnParticipationCnt = (IotTeachingData.learnParticipation > 0) ? "1" : "0"; //是否計算學習參與度
  111. IotTeachingData.coopMission = (tdata.Length > 37 && !string.IsNullOrWhiteSpace(tdata[37])) ? Convert.ToInt32(tdata[37]) : 0; //協作任務數
  112. IotTeachingData.coopWork = (tdata.Length > 38 && !string.IsNullOrWhiteSpace(tdata[38])) ? Convert.ToInt32(tdata[38]) : 0; //協作作品數
  113. IotTeachingData.coopContributionT = (tdata.Length > 39 && !string.IsNullOrWhiteSpace(tdata[39])) ? Convert.ToInt32(tdata[39]) : 0; //協作總貢獻度
  114. IotTeachingData.peerAct = (tdata.Length > 40 && !string.IsNullOrWhiteSpace(tdata[40])) ? Convert.ToInt32(tdata[40]) : 0; //互評活動次數
  115. IotTeachingData.peerStuParticipationT = (tdata.Length > 41 && !string.IsNullOrWhiteSpace(tdata[41])) ? Convert.ToInt32(tdata[41]) : 0; //互評學生參與總次數
  116. IotTeachingData.useTransMode = (tdata.Length > 42 && !string.IsNullOrWhiteSpace(tdata[42])) ? tdata[42] : "0"; //有使用透明模式
  117. IotTeachingData.useMiniMode = (tdata.Length > 43 && !string.IsNullOrWhiteSpace(tdata[43])) ? tdata[43] : "0"; //有使用最小化模式
  118. if (!string.IsNullOrWhiteSpace(IotTeachingData.ip)) ///IP分析地理位置
  119. (IotTeachingData.geo, IotTeachingData.geoFromIp) = BIProdAnalysis.getGeoFromIp(_city, IotTeachingData.ip, _region_gl, _region_cn);
  120. IotTeachingDataList.Add(IotTeachingData);
  121. if(!string.IsNullOrWhiteSpace(IotTeachingData.tmid))
  122. {
  123. tmids.Add(IotTeachingData.tmid);
  124. }
  125. }
  126. }
  127. }
  128. //取得TMID進階資料,以弱強歸戶學校補足學校ID
  129. 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);
  130. await foreach (var item in _azureCosmosClientCsv2.GetContainer("Core", "ID2")
  131. .GetItemQueryStreamIteratorQuery(querye, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base-ex")}))
  132. {
  133. using var json = await JsonDocument.ParseAsync(item.Content);
  134. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  135. {
  136. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  137. {
  138. string id = doc.GetProperty("id").GetString();
  139. string schoolCode = (doc.TryGetProperty("schoolCode", out JsonElement _tmidSchoolCode)) ? _tmidSchoolCode.GetString() : string.Empty;
  140. string schoolCodeW = (doc.TryGetProperty("schoolCodeW", out JsonElement _tmidSchoolCodeW)) ? _tmidSchoolCodeW.GetString() : string.Empty;
  141. List<IotTeachingData> IotTeachingDatas = IotTeachingDataList.Where(x => x.tmid.Equals(id) && string.IsNullOrWhiteSpace(x.schoolId)).ToList();
  142. if(IotTeachingDatas.Count > 0)
  143. {
  144. foreach(IotTeachingData IotTeachingDataRow in IotTeachingDatas)
  145. {
  146. if (!string.IsNullOrWhiteSpace(schoolCode))
  147. {
  148. IotTeachingDataRow.schoolId = schoolCode;
  149. }
  150. else if (!string.IsNullOrWhiteSpace(schoolCodeW))
  151. {
  152. IotTeachingDataRow.schoolId = schoolCodeW;
  153. }
  154. }
  155. }
  156. }
  157. }
  158. }
  159. return IotTeachingDataList;
  160. }
  161. catch (Exception ex)
  162. {
  163. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},BIGetDailyRedisProdAnalData() 取得每日CoreServiceIOT資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  164. return IotTeachingDataList;
  165. }
  166. }
  167. //年月日 IES5學校IOT產品分析數據生成
  168. ///參數:某日IOT 教學資料列
  169. ///1.生成 Redis ProdAnalysis:Month
  170. ///2.生成 CosmosDB Month
  171. ///3.生成 CosmosDB 系統所有學校數值加總
  172. private static async Task CreatIes5ProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, CosmosClient _azureCosmosClientCsv2CnRead, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  173. {
  174. try
  175. {
  176. var redisClinet8 = _azureRedis.GetRedisClient(8);
  177. List<string> crtVirtualSchoolId = new List<string>(); //虛擬學校創建ID列表
  178. 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欄位列表
  179. List<ProdAnalysis> ProdAnalysisList = new List<ProdAnalysis>();
  180. if (IotTeachingDataList.Count > 0)
  181. {
  182. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  183. {
  184. string schoolId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.schoolId)) ? IotTeachingDatRow.schoolId.ToLower() : "noschoolid"; //noschoolid:無任何學校ID
  185. if (!schoolId.Equals("noschoolid") && !schoolId.Equals("allschool") && !crtVirtualSchoolId.Contains(schoolId))
  186. {
  187. crtVirtualSchoolId.Add(schoolId);
  188. }
  189. string toolType = string.Empty;
  190. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  191. {
  192. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  193. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  194. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  195. }
  196. //學校數據生成
  197. if (!string.IsNullOrWhiteSpace(schoolId) && !string.IsNullOrWhiteSpace(toolType))
  198. {
  199. bool addFlg = false;
  200. ProdAnalysis prodAnalysisRow = ProdAnalysisList.Where(s => s.schoolId.Equals(schoolId) && s.toolType.Equals(toolType)).FirstOrDefault();
  201. //無此校數據=>創建
  202. if (prodAnalysisRow == null)
  203. {
  204. prodAnalysisRow = new ProdAnalysis();
  205. prodAnalysisRow.schoolId = schoolId;
  206. prodAnalysisRow.toolType = toolType;
  207. addFlg = true;
  208. }
  209. //欄位加總
  210. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId) && !prodAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  211. {
  212. prodAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  213. }
  214. prodAnalysisRow.deviceCnt = prodAnalysisRow.deviceList.Count;
  215. switch (IotTeachingDatRow.authType)
  216. {
  217. case "0": //928授權
  218. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  219. prodAnalysisRow.lessonCnt928++;
  220. break;
  221. case "1": //ID授權
  222. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  223. prodAnalysisRow.lessonCntId++;
  224. break;
  225. case "2": //機器授權
  226. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  227. prodAnalysisRow.lessonCntDevice++;
  228. break;
  229. case "3": //ID+機器授權
  230. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  231. prodAnalysisRow.lessonCntIdDevice++;
  232. break;
  233. }
  234. prodAnalysisRow.deviceNoAuth = prodAnalysisRow.deviceNoAuthList.Count;
  235. prodAnalysisRow.deviceAuth = prodAnalysisRow.deviceAuthList.Count;
  236. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid) && !prodAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  237. {
  238. prodAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  239. }
  240. prodAnalysisRow.tmidCnt = prodAnalysisRow.tmidList.Count;
  241. prodAnalysisRow.lessonRecord++;
  242. if (IotTeachingDatRow.useIES.Equals("1")) prodAnalysisRow.useIES++;
  243. prodAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  244. if (IotTeachingDatRow.useWebIrs.Equals("1")) prodAnalysisRow.useWebIrs++;
  245. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) prodAnalysisRow.useDeviceIrs++;
  246. if (IotTeachingDatRow.useHaboard.Equals("1")) prodAnalysisRow.useHaboard++;
  247. if (IotTeachingDatRow.useHita.Equals("1")) prodAnalysisRow.useHita++;
  248. prodAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  249. if (IotTeachingDatRow.lessonLengMin.Equals(0)) prodAnalysisRow.lessonLeng0++;
  250. prodAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  251. prodAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  252. if (IotTeachingDatRow.tPoint >= 70) prodAnalysisRow.tGreen++;
  253. if (IotTeachingDatRow.lessonLengMin >= 10 && IotTeachingDatRow.tPoint > 0) prodAnalysisRow.tLesson++;
  254. if (IotTeachingDatRow.lTypeCoop.Equals("1")) prodAnalysisRow.lTypeCoop++;
  255. if (IotTeachingDatRow.lTypeIact.Equals("1")) prodAnalysisRow.lTypeIact++;
  256. if (IotTeachingDatRow.lTypeMis.Equals("1")) prodAnalysisRow.lTypeMis++;
  257. if (IotTeachingDatRow.lTypeTst.Equals("1")) prodAnalysisRow.lTypeTst++;
  258. if (IotTeachingDatRow.lTypeDif.Equals("1")) prodAnalysisRow.lTypeDif++;
  259. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) prodAnalysisRow.lTypeNone++;
  260. prodAnalysisRow.mission += IotTeachingDatRow.mission;
  261. prodAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  262. prodAnalysisRow.item += IotTeachingDatRow.item;
  263. prodAnalysisRow.interact += IotTeachingDatRow.interact;
  264. if (IotTeachingDatRow.sendSok.Equals("1")) prodAnalysisRow.sendSok++;
  265. if (IotTeachingDatRow.learnPeer.Equals("1")) prodAnalysisRow.learnPeer++;
  266. if (IotTeachingDatRow.learnCoop.Equals("1")) prodAnalysisRow.learnCoop++;
  267. if (IotTeachingDatRow.useWordCloud.Equals("1")) prodAnalysisRow.useWordCloud++;
  268. if (IotTeachingDatRow.useClouDAS.Equals("1")) prodAnalysisRow.useClouDAS++;
  269. if (IotTeachingDatRow.useGPT.Equals("1")) prodAnalysisRow.useGPT++;
  270. if (IotTeachingDatRow.useIes5Test.Equals("1")) prodAnalysisRow.useIes5Test++;
  271. if (IotTeachingDatRow.usePaperTest.Equals("1")) prodAnalysisRow.usePaperTest++;
  272. if (IotTeachingDatRow.useExcelTest.Equals("1")) prodAnalysisRow.useExcelTest++;
  273. if (IotTeachingDatRow.useScoreBoard.Equals("1")) prodAnalysisRow.useScoreBoard++; //課堂中使用記分板
  274. if (IotTeachingDatRow.learnParticipationCnt.Equals("1")) prodAnalysisRow.learnParticipationCnt++; //學習參與度次數
  275. prodAnalysisRow.learnParticipationT += IotTeachingDatRow.learnParticipation; //學習參與度指數(總和)
  276. decimal learnParticipationTmp = (prodAnalysisRow.learnParticipationCnt > 0) ? (decimal)prodAnalysisRow.learnParticipationT / (decimal)prodAnalysisRow.learnParticipationCnt : 0;
  277. prodAnalysisRow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  278. prodAnalysisRow.coopMission += IotTeachingDatRow.coopMission; //協作任務數
  279. prodAnalysisRow.coopWork += IotTeachingDatRow.coopWork; //協作作品數
  280. prodAnalysisRow.coopContributionT += IotTeachingDatRow.coopContributionT; //協作總貢獻度
  281. prodAnalysisRow.peerAct += IotTeachingDatRow.peerAct; //互評活動次數
  282. prodAnalysisRow.peerStuParticipationT += IotTeachingDatRow.peerStuParticipationT; //互評學生參與總次數
  283. if (IotTeachingDatRow.useTransMode.Equals("1")) prodAnalysisRow.useTransMode++; //有使用透明模式
  284. if (IotTeachingDatRow.useMiniMode.Equals("1")) prodAnalysisRow.useMiniMode++; //有使用最小化模式
  285. if (addFlg)
  286. {
  287. ProdAnalysisList.Add(prodAnalysisRow);
  288. }
  289. }
  290. }
  291. }
  292. //1.記入IES5 Redis ProdAnalysis:Day
  293. //2.記入IES5 CosmosDB Day
  294. if (ProdAnalysisList.Count > 0)
  295. {
  296. //資料整形
  297. Dictionary<string, Dictionary<string, string>> redisSchFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => schId => schJsonContent
  298. List<ProdAnalysisCosmos> cosmosSchList = new List<ProdAnalysisCosmos>();
  299. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumDayDic = new Dictionary<string, ProdAnalysisCosmos>();
  300. foreach (ProdAnalysis prodAnalysisRow in ProdAnalysisList)
  301. {
  302. //Redis整形
  303. string toolType = prodAnalysisRow.toolType;
  304. string schoolId = prodAnalysisRow.schoolId;
  305. string hkey = $"ProdAnalysis:Day:{toolType}:{y}{m}{d}";
  306. string fieldContent = prodAnalysisRow.ToJsonString();
  307. if (redisSchFieldDic.ContainsKey(hkey)) redisSchFieldDic[hkey].Add(schoolId, fieldContent);
  308. else redisSchFieldDic.Add(hkey, new Dictionary<string, string>() { { schoolId, fieldContent } });
  309. //CosmosDB整形
  310. ProdAnalysisCosmos ProdAnalysisCosmosRow = prodAnalysisRow.ToJsonString().ToObject<ProdAnalysisCosmos>();
  311. ProdAnalysisCosmosRow.date = $"{y}{m}{d}";
  312. ProdAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  313. ProdAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  314. ProdAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  315. DateTimeOffset dateTime = new DateTimeOffset(ProdAnalysisCosmosRow.year, ProdAnalysisCosmosRow.month, ProdAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  316. ProdAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  317. ProdAnalysisCosmosRow.id = $"{ProdAnalysisCosmosRow.toolType}-{ProdAnalysisCosmosRow.date}-{schoolId}";
  318. ProdAnalysisCosmosRow.dateUnit = "day";
  319. ProdAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  320. cosmosSchList.Add(ProdAnalysisCosmosRow);
  321. }
  322. //記入Redis
  323. if (redisSchFieldDic.Count > 0)
  324. {
  325. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisSchFieldDic)
  326. {
  327. //記入Redis
  328. string hkey = hkeyItem.Key;
  329. List<HashEntry> hvalList = new List<HashEntry>();
  330. Dictionary<string, string> fieldDic = hkeyItem.Value;
  331. foreach (KeyValuePair<string, string> schItem in fieldDic)
  332. {
  333. string schoolId = schItem.Key;
  334. string fieldContent = schItem.Value;
  335. hvalList.Add(new HashEntry($"{schoolId}", fieldContent));
  336. }
  337. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  338. }
  339. }
  340. //記入CosmosDB
  341. if (cosmosSchList.Count > 0)
  342. {
  343. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchList)
  344. {
  345. //各校每日CosmosDB記入
  346. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  347. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  348. cosmosAllSchSumDayDic = GenAnalysisRowSumData(cosmosAllSchSumDayDic, cosmosSchRow, calPropList, "day");
  349. }
  350. //每日所有學校數據總計CosmosDB記入
  351. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumDayDic)
  352. {
  353. string toolType = schItem.Key;
  354. ProdAnalysisCosmos cosmosAllSchSumDay = schItem.Value;
  355. cosmosAllSchSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  356. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumDay);
  357. }
  358. }
  359. }
  360. //取 Redis 該月所有 ProdAnalysis:Day
  361. //1.生成 Redis ProdAnalysis:Month
  362. //2.生成 CosmosDB Month
  363. Dictionary<string, Dictionary<string, ProdAnalysis>> ProdAnalysisListMonth = new Dictionary<string, Dictionary<string, ProdAnalysis>>();
  364. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumMonthDic = new Dictionary<string, ProdAnalysisCosmos>();
  365. List<ProdAnalysisCosmos> cosmosSchListMonth = new List<ProdAnalysisCosmos>();
  366. string patternD = $"ProdAnalysis:Day:HiT*:{y}{m}*";
  367. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  368. if (keysDayList.Count > 0)
  369. {
  370. foreach (string keyDay in keysDayList)
  371. {
  372. string[] keyItemList = keyDay.Split(':'); //ProdAnalysis:Day:HiTeach:20230326
  373. string toolType = keyItemList[2];
  374. string dateStrD = keyItemList[3];
  375. string dateStrD_y = string.Empty;
  376. string dateStrD_m = string.Empty;
  377. string dateStrD_d = string.Empty;
  378. Regex rgx = new Regex(@"[0-9]{8}");
  379. if (rgx.IsMatch(dateStrD))
  380. {
  381. dateStrD_y = dateStrD.Substring(0, 4);
  382. dateStrD_m = dateStrD.Substring(4, 2);
  383. dateStrD_d = dateStrD.Substring(6, 2);
  384. }
  385. string prodAnalMonthKey = $"ProdAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  386. bool ProdAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  387. if (ProdAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  388. {
  389. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有學校的統計項目
  390. foreach (HashEntry hset in hsetDay)
  391. {
  392. string keySchId = hset.Name;
  393. string valSchDataJson = hset.Value;
  394. ProdAnalysis SchDataTodo = valSchDataJson.ToObject<ProdAnalysis>();
  395. //Redis Month 資料生成
  396. if (ProdAnalysisListMonth.ContainsKey(prodAnalMonthKey)) //月Dic已有此key => 分校累加
  397. {
  398. if (ProdAnalysisListMonth[$"{prodAnalMonthKey}"].ContainsKey($"{keySchId}"))
  399. {
  400. ProdAnalysis SchDataNow = ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"];
  401. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  402. {
  403. if (calPropList.Contains(propertyInfo.Name))
  404. {
  405. var propType = propertyInfo.PropertyType;
  406. var valNow = propertyInfo.GetValue(SchDataNow);
  407. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  408. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  409. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  410. }
  411. }
  412. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  413. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  414. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  415. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  416. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  417. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  418. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  419. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  420. }
  421. //無此校資料 => 該校資料放入
  422. else
  423. {
  424. ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"] = SchDataTodo;
  425. }
  426. }
  427. else //無此月資料 => 所有學校資料放入
  428. {
  429. ProdAnalysisListMonth.Add(prodAnalMonthKey, new Dictionary<string, ProdAnalysis>() { { keySchId, SchDataTodo } });
  430. }
  431. }
  432. }
  433. }
  434. }
  435. if (ProdAnalysisListMonth.Count > 0)
  436. {
  437. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysis>> item in ProdAnalysisListMonth)
  438. {
  439. string monthRedisKey = item.Key;
  440. List<HashEntry> hvalList = new List<HashEntry>();
  441. Dictionary<string, ProdAnalysis> monthRedisSchDIc = item.Value;
  442. foreach (KeyValuePair<string, ProdAnalysis> monthRedisSchItem in monthRedisSchDIc)
  443. {
  444. string monthRedisSchId = monthRedisSchItem.Key;
  445. ProdAnalysis monthRedisSchData = monthRedisSchItem.Value;
  446. //Redis資料製作
  447. hvalList.Add(new HashEntry(monthRedisSchId, monthRedisSchData.ToJsonString()));
  448. //CosmosDB資料製作
  449. ProdAnalysisCosmos cosmosSchRow = monthRedisSchData.ToJsonString().ToObject<ProdAnalysisCosmos>();
  450. cosmosSchRow.date = $"{y}{m}";
  451. cosmosSchRow.year = Convert.ToInt32(y, 10);
  452. cosmosSchRow.month = Convert.ToInt32(m, 10);
  453. DateTimeOffset dateTime = new DateTimeOffset(cosmosSchRow.year, cosmosSchRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  454. cosmosSchRow.dateTime = dateTime.ToUnixTimeSeconds();
  455. cosmosSchRow.id = $"{monthRedisSchData.toolType}-{cosmosSchRow.date}-{monthRedisSchId}";
  456. cosmosSchRow.dateUnit = "month";
  457. cosmosSchRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  458. cosmosSchListMonth.Add(cosmosSchRow);
  459. }
  460. //記入Redis
  461. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  462. }
  463. //記入CosmosDB
  464. if (cosmosSchListMonth.Count > 0)
  465. {
  466. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchListMonth)
  467. {
  468. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  469. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  470. cosmosAllSchSumMonthDic = GenAnalysisRowSumData(cosmosAllSchSumMonthDic, cosmosSchRow, calPropList, "month");
  471. }
  472. //每月所有學校數據總計CosmosDB記入
  473. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumMonthDic)
  474. {
  475. string toolType = schItem.Key;
  476. ProdAnalysisCosmos cosmosAllSchSumMonth = schItem.Value;
  477. cosmosAllSchSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  478. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumMonth);
  479. }
  480. }
  481. }
  482. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  483. Dictionary<string, Dictionary<string, ProdAnalysisCosmos>> ProdAnalysisListYear = new Dictionary<string, Dictionary<string, ProdAnalysisCosmos>>();
  484. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumYearDic = new Dictionary<string, ProdAnalysisCosmos>();
  485. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.schoolId != 'allschool'";
  486. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProdAnalysis") }))
  487. {
  488. var json = await JsonDocument.ParseAsync(itemcr.Content);
  489. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  490. {
  491. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  492. {
  493. ProdAnalysisCosmos SchDataTodo = obj.ToObject<ProdAnalysisCosmos>();
  494. string toolType = SchDataTodo.toolType;
  495. string schId = SchDataTodo.schoolId;
  496. //年Dic已有此key => 分校累加
  497. if (ProdAnalysisListYear.ContainsKey(toolType))
  498. {
  499. if (ProdAnalysisListYear[$"{toolType}"].ContainsKey($"{schId}"))
  500. {
  501. ProdAnalysisCosmos SchDataNow = ProdAnalysisListYear[$"{toolType}"][$"{schId}"];
  502. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  503. {
  504. if (calPropList.Contains(propertyInfo.Name))
  505. {
  506. var propType = propertyInfo.PropertyType;
  507. var valNow = propertyInfo.GetValue(SchDataNow);
  508. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  509. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  510. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  511. }
  512. }
  513. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  514. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  515. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  516. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  517. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  518. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  519. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  520. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  521. }
  522. //無此校資料 => 該校資料放入
  523. else
  524. {
  525. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  526. SchDataNow.date = $"{y}";
  527. SchDataNow.year = Convert.ToInt32(y, 10);
  528. SchDataNow.month = 0;
  529. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  530. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  531. SchDataNow.id = $"{toolType}-{y}-{schId}";
  532. SchDataNow.dateUnit = "year";
  533. ProdAnalysisListYear[$"{toolType}"][$"{schId}"] = SchDataNow;
  534. }
  535. }
  536. //無此年資料 => 所有學校資料放入
  537. else
  538. {
  539. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  540. SchDataNow.date = $"{y}";
  541. SchDataNow.year = Convert.ToInt32(y, 10);
  542. SchDataNow.month = 0;
  543. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  544. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  545. SchDataNow.id = $"{toolType}-{y}-{schId}";
  546. SchDataNow.dateUnit = "year";
  547. ProdAnalysisListYear.Add(toolType, new Dictionary<string, ProdAnalysisCosmos>() { { schId, SchDataNow } });
  548. }
  549. }
  550. }
  551. }
  552. if (ProdAnalysisListYear.Count > 0)
  553. {
  554. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysisCosmos>> item in ProdAnalysisListYear)
  555. {
  556. //string toolType = item.Key;
  557. ProdAnalysisCosmos SchDataNow = new ProdAnalysisCosmos(); //當年某產品所有學校總和
  558. Dictionary<string, ProdAnalysisCosmos> yearCosmosSchDIc = item.Value;
  559. foreach (KeyValuePair<string, ProdAnalysisCosmos> yearCosmosSchItem in yearCosmosSchDIc)
  560. {
  561. string schId = yearCosmosSchItem.Key;
  562. ProdAnalysisCosmos cosmosSchRow = yearCosmosSchItem.Value;
  563. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  564. //每年所有學校數據總計CosmosDB記入
  565. cosmosAllSchSumYearDic = GenAnalysisRowSumData(cosmosAllSchSumYearDic, cosmosSchRow, calPropList, "year");
  566. }
  567. //每年所有學校數據總計CosmosDB記入
  568. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumYearDic)
  569. {
  570. string toolType = schItem.Key;
  571. ProdAnalysisCosmos cosmosAllSchSumYear = schItem.Value;
  572. cosmosAllSchSumYear.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  573. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumYear);
  574. }
  575. //await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(SchDataNow);
  576. }
  577. }
  578. //虛擬學校創建
  579. if (crtVirtualSchoolId.Count > 0)
  580. {
  581. //取得IES5 school Base
  582. string schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  583. string schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  584. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  585. {
  586. using var json = await JsonDocument.ParseAsync(itemsr.Content);
  587. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  588. {
  589. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  590. {
  591. string existSchid = obj.GetProperty("id").GetString();
  592. if (crtVirtualSchoolId.Contains(existSchid))
  593. {
  594. crtVirtualSchoolId.Remove(existSchid);
  595. }
  596. }
  597. }
  598. }
  599. //取得IES5 school VirtualBase
  600. schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  601. schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  602. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"VirtualBase") }))
  603. {
  604. using var json = await JsonDocument.ParseAsync(itemsr.Content);
  605. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  606. {
  607. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  608. {
  609. string existSchid = obj.GetProperty("id").GetString();
  610. if (crtVirtualSchoolId.Contains(existSchid))
  611. {
  612. crtVirtualSchoolId.Remove(existSchid);
  613. }
  614. }
  615. }
  616. }
  617. //VirtualBase school 建立
  618. if (crtVirtualSchoolId.Count > 0)
  619. {
  620. //取得CSV2 School,存在者建立IES5 VirtualBase school
  621. List<School> crtVSchoolList = new List<School>();
  622. string csv2SchIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  623. 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)";
  624. await foreach (var item in _azureCosmosClientCsv2CnRead.GetContainer("Core", "School").GetItemQueryStreamIteratorSql(queryText: schCsv2QueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
  625. {
  626. using var json = await JsonDocument.ParseAsync(item.Content);
  627. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  628. {
  629. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  630. {
  631. VirtualBase crtVSchool = new VirtualBase();
  632. crtVSchool.code = "VirtualBase";
  633. crtVSchool.id = obj.GetProperty("shortCode").GetString();
  634. crtVSchool.pk = "School";
  635. crtVSchool.schoolCode = obj.GetProperty("shortCode").GetString();
  636. crtVSchool.name = obj.GetProperty("name").GetString();
  637. crtVSchool.region = (obj.TryGetProperty("countryName", out JsonElement countryNameJ)) ? Convert.ToString(countryNameJ) : null;
  638. crtVSchool.province = (obj.TryGetProperty("provinceName", out JsonElement provinceNameJ)) ? Convert.ToString(provinceNameJ) : null;
  639. crtVSchool.city = (obj.TryGetProperty("cityName", out JsonElement cityNameJ)) ? Convert.ToString(cityNameJ) : null;
  640. crtVSchool.dist = (obj.TryGetProperty("distName", out JsonElement distNameJ)) ? Convert.ToString(distNameJ) : null;
  641. crtVSchool.address = (obj.TryGetProperty("address", out JsonElement addressJ)) ? Convert.ToString(addressJ) : null;
  642. crtVSchool.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  643. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<VirtualBase>(crtVSchool);
  644. }
  645. }
  646. }
  647. }
  648. }
  649. }
  650. catch (Exception ex)
  651. {
  652. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatIes5ProdAnalData() 生成學校年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  653. }
  654. }
  655. //年月日 TMID IOT產品分析數據生成
  656. ///1.生成 Redis TmidAnalysis:Month
  657. ///2.生成 CosmosDB Month
  658. ///3.生成 CosmosDB 系統所有TMID數值加總
  659. private static async Task CreatTmidProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  660. {
  661. try
  662. {
  663. var redisClinet8 = _azureRedis.GetRedisClient(8);
  664. 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欄位列表
  665. List<TmidAnalysis> TmidAnalysisList = new List<TmidAnalysis>();
  666. if (IotTeachingDataList.Count > 0)
  667. {
  668. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  669. {
  670. bool addTmidFlg = false;
  671. string tmid = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid)) ? IotTeachingDatRow.tmid.ToString() : string.Empty;
  672. string deviceId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId)) ? IotTeachingDatRow.deviceId.ToString() : string.Empty;
  673. string ver = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.version)) ? IotTeachingDatRow.version : string.Empty;
  674. string toolType = string.Empty;
  675. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  676. {
  677. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  678. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  679. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  680. }
  681. if (!string.IsNullOrWhiteSpace(tmid) && !string.IsNullOrWhiteSpace(deviceId) && !string.IsNullOrWhiteSpace(toolType))
  682. {
  683. bool addFlg = false;
  684. TmidAnalysis tmidAnalysisRow = TmidAnalysisList.Where(s => s.tmid.Equals(tmid) && s.toolType.Equals(toolType)).FirstOrDefault();
  685. //無此tmid數據=>創建
  686. if (tmidAnalysisRow == null)
  687. {
  688. tmidAnalysisRow = new TmidAnalysis();
  689. tmidAnalysisRow.tmid = tmid;
  690. tmidAnalysisRow.toolType = toolType;
  691. addFlg = true;
  692. }
  693. //欄位加總
  694. if (!tmidAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  695. {
  696. tmidAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  697. }
  698. tmidAnalysisRow.deviceCnt = tmidAnalysisRow.deviceList.Count;
  699. switch (IotTeachingDatRow.authType)
  700. {
  701. case "0": //928授權
  702. if (!tmidAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  703. tmidAnalysisRow.lessonCnt928++;
  704. break;
  705. case "1": //ID授權
  706. if (!tmidAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  707. tmidAnalysisRow.lessonCntId++;
  708. break;
  709. case "2": //機器授權
  710. if (!tmidAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  711. tmidAnalysisRow.lessonCntDevice++;
  712. break;
  713. case "3": //ID+機器授權
  714. if (!tmidAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) tmidAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  715. tmidAnalysisRow.lessonCntIdDevice++;
  716. break;
  717. }
  718. tmidAnalysisRow.deviceNoAuth = tmidAnalysisRow.deviceNoAuthList.Count;
  719. tmidAnalysisRow.deviceAuth = tmidAnalysisRow.deviceAuthList.Count;
  720. if (!tmidAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  721. {
  722. tmidAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  723. }
  724. tmidAnalysisRow.tmidCnt = tmidAnalysisRow.tmidList.Count;
  725. tmidAnalysisRow.lessonRecord++;
  726. if (IotTeachingDatRow.useIES.Equals("1")) tmidAnalysisRow.useIES++;
  727. tmidAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  728. if (IotTeachingDatRow.useWebIrs.Equals("1")) tmidAnalysisRow.useWebIrs++;
  729. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) tmidAnalysisRow.useDeviceIrs++;
  730. if (IotTeachingDatRow.useHaboard.Equals("1")) tmidAnalysisRow.useHaboard++;
  731. if (IotTeachingDatRow.useHita.Equals("1")) tmidAnalysisRow.useHita++;
  732. tmidAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  733. if (IotTeachingDatRow.lessonLengMin.Equals(0)) tmidAnalysisRow.lessonLeng0++;
  734. tmidAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  735. tmidAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  736. if (IotTeachingDatRow.tPoint >= 70) tmidAnalysisRow.tGreen++;
  737. if (IotTeachingDatRow.lessonLengMin >= 10 && IotTeachingDatRow.tPoint > 0) tmidAnalysisRow.tLesson++;
  738. if (IotTeachingDatRow.lTypeCoop.Equals("1")) tmidAnalysisRow.lTypeCoop++;
  739. if (IotTeachingDatRow.lTypeIact.Equals("1")) tmidAnalysisRow.lTypeIact++;
  740. if (IotTeachingDatRow.lTypeMis.Equals("1")) tmidAnalysisRow.lTypeMis++;
  741. if (IotTeachingDatRow.lTypeTst.Equals("1")) tmidAnalysisRow.lTypeTst++;
  742. if (IotTeachingDatRow.lTypeDif.Equals("1")) tmidAnalysisRow.lTypeDif++;
  743. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) tmidAnalysisRow.lTypeNone++;
  744. tmidAnalysisRow.mission += IotTeachingDatRow.mission;
  745. tmidAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  746. tmidAnalysisRow.item += IotTeachingDatRow.item;
  747. tmidAnalysisRow.interact += IotTeachingDatRow.interact;
  748. if (IotTeachingDatRow.sendSok.Equals("1")) tmidAnalysisRow.sendSok++;
  749. if (IotTeachingDatRow.learnPeer.Equals("1")) tmidAnalysisRow.learnPeer++;
  750. if (IotTeachingDatRow.learnCoop.Equals("1")) tmidAnalysisRow.learnCoop++;
  751. if (IotTeachingDatRow.useWordCloud.Equals("1")) tmidAnalysisRow.useWordCloud++;
  752. if (IotTeachingDatRow.useClouDAS.Equals("1")) tmidAnalysisRow.useClouDAS++;
  753. if (IotTeachingDatRow.useGPT.Equals("1")) tmidAnalysisRow.useGPT++;
  754. if (IotTeachingDatRow.useIes5Test.Equals("1")) tmidAnalysisRow.useIes5Test++;
  755. if (IotTeachingDatRow.usePaperTest.Equals("1")) tmidAnalysisRow.usePaperTest++;
  756. if (IotTeachingDatRow.useExcelTest.Equals("1")) tmidAnalysisRow.useExcelTest++;
  757. if (IotTeachingDatRow.useScoreBoard.Equals("1")) tmidAnalysisRow.useScoreBoard++; //課堂中使用記分板
  758. if (IotTeachingDatRow.learnParticipationCnt.Equals("1")) tmidAnalysisRow.learnParticipationCnt++; //學習參與度次數
  759. tmidAnalysisRow.learnParticipationT += IotTeachingDatRow.learnParticipation; //學習參與度指數(總和)
  760. decimal learnParticipationTmp = (tmidAnalysisRow.learnParticipationCnt > 0) ? (decimal)tmidAnalysisRow.learnParticipationT / (decimal)tmidAnalysisRow.learnParticipationCnt : 0;
  761. tmidAnalysisRow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  762. tmidAnalysisRow.coopMission += IotTeachingDatRow.coopMission; //協作任務數
  763. tmidAnalysisRow.coopWork += IotTeachingDatRow.coopWork; //協作作品數
  764. tmidAnalysisRow.coopContributionT += IotTeachingDatRow.coopContributionT; //協作總貢獻度
  765. tmidAnalysisRow.peerAct += IotTeachingDatRow.peerAct; //互評活動次數
  766. tmidAnalysisRow.peerStuParticipationT += IotTeachingDatRow.peerStuParticipationT; //互評學生參與總次數
  767. if (IotTeachingDatRow.useTransMode.Equals("1")) tmidAnalysisRow.useTransMode++; //有使用透明模式
  768. if (IotTeachingDatRow.useMiniMode.Equals("1")) tmidAnalysisRow.useMiniMode++; //有使用最小化模式
  769. if (!string.IsNullOrWhiteSpace(ver) && !tmidAnalysisRow.verList.Contains(ver))
  770. {
  771. tmidAnalysisRow.verList.Add(ver);
  772. }
  773. if (addFlg)
  774. {
  775. TmidAnalysisList.Add(tmidAnalysisRow);
  776. }
  777. }
  778. }
  779. }
  780. //1.記入CSV2 Redis TmidAnalysis:Day
  781. //2.記入CSV2 CosmosDB Day
  782. if (TmidAnalysisList.Count > 0)
  783. {
  784. //資料整形
  785. Dictionary<string, Dictionary<string, string>> redisTmidFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => tmid => JsonContent
  786. List<TmidAnalysisCosmos> cosmosTmidList = new List<TmidAnalysisCosmos>();
  787. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumDayDic = new Dictionary<string, TmidAnalysisCosmos>();
  788. foreach (TmidAnalysis tmidAnalysisRow in TmidAnalysisList)
  789. {
  790. //Redis整形
  791. string toolType = tmidAnalysisRow.toolType;
  792. string tmid = tmidAnalysisRow.tmid;
  793. string hkey = $"TmidAnalysis:Day:{toolType}:{y}{m}{d}";
  794. string fieldContent = tmidAnalysisRow.ToJsonString();
  795. if (redisTmidFieldDic.ContainsKey(hkey)) redisTmidFieldDic[hkey].Add(tmid, fieldContent);
  796. else redisTmidFieldDic.Add(hkey, new Dictionary<string, string>() { { tmid, fieldContent } });
  797. //CosmosDB整形
  798. TmidAnalysisCosmos TmidAnalysisCosmosRow = tmidAnalysisRow.ToJsonString().ToObject<TmidAnalysisCosmos>();
  799. TmidAnalysisCosmosRow.date = $"{y}{m}{d}";
  800. TmidAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  801. TmidAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  802. TmidAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  803. DateTimeOffset dateTime = new DateTimeOffset(TmidAnalysisCosmosRow.year, TmidAnalysisCosmosRow.month, TmidAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  804. TmidAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  805. TmidAnalysisCosmosRow.id = $"{TmidAnalysisCosmosRow.toolType}-{TmidAnalysisCosmosRow.date}-{tmid}";
  806. TmidAnalysisCosmosRow.dateUnit = "day";
  807. TmidAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  808. cosmosTmidList.Add(TmidAnalysisCosmosRow);
  809. }
  810. //記入Redis
  811. if (redisTmidFieldDic.Count > 0)
  812. {
  813. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisTmidFieldDic)
  814. {
  815. string hkey = hkeyItem.Key;
  816. List<HashEntry> hvalList = new List<HashEntry>();
  817. Dictionary<string, string> fieldDic = hkeyItem.Value;
  818. foreach (KeyValuePair<string, string> tmidItem in fieldDic)
  819. {
  820. string tmid = tmidItem.Key;
  821. string fieldContent = tmidItem.Value;
  822. hvalList.Add(new HashEntry($"{tmid}", fieldContent));
  823. }
  824. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  825. }
  826. }
  827. //記入CosmosDB
  828. if (cosmosTmidList.Count > 0)
  829. {
  830. foreach (TmidAnalysisCosmos cosmosTmidRow in cosmosTmidList)
  831. {
  832. //各TMID每日CosmosDB記入
  833. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  834. //所有TMID每日數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  835. cosmosAllTmidSumDayDic = GenAnalysisRowSumData(cosmosAllTmidSumDayDic, cosmosTmidRow, calPropList, "day");
  836. }
  837. //每日所有TMID數據總計CosmosDB記入
  838. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumDayDic)
  839. {
  840. string toolType = tmidItem.Key;
  841. TmidAnalysisCosmos cosmosAllTmidSumDay = tmidItem.Value;
  842. cosmosAllTmidSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  843. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumDay);
  844. }
  845. }
  846. }
  847. //取 Redis 該月所有 TmidAnalysis:Day
  848. //1.生成 Redis TmidAnalysis:Month
  849. //2.生成 CosmosDB Month
  850. Dictionary<string, Dictionary<string, TmidAnalysis>> TmidAnalysisListMonth = new Dictionary<string, Dictionary<string, TmidAnalysis>>();
  851. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumMonthDic = new Dictionary<string, TmidAnalysisCosmos>();
  852. List<TmidAnalysisCosmos> cosmosTmidListMonth = new List<TmidAnalysisCosmos>();
  853. string patternD = $"TmidAnalysis:Day:HiT*:{y}{m}*";
  854. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  855. if (keysDayList.Count > 0)
  856. {
  857. foreach (string keyDay in keysDayList)
  858. {
  859. string[] keyItemList = keyDay.Split(':'); //TmidAnalysis:Day:HiTeach:20230326
  860. string toolType = keyItemList[2];
  861. string dateStrD = keyItemList[3];
  862. string dateStrD_y = string.Empty;
  863. string dateStrD_m = string.Empty;
  864. string dateStrD_d = string.Empty;
  865. Regex rgx = new Regex(@"[0-9]{8}");
  866. if (rgx.IsMatch(dateStrD))
  867. {
  868. dateStrD_y = dateStrD.Substring(0, 4);
  869. dateStrD_m = dateStrD.Substring(4, 2);
  870. dateStrD_d = dateStrD.Substring(6, 2);
  871. }
  872. string tmidAnalMonthKey = $"TmidAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  873. bool tmidAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  874. if (tmidAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  875. {
  876. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有學校的統計項目
  877. foreach (HashEntry hset in hsetDay)
  878. {
  879. string keyTMID = hset.Name;
  880. string valTmidDataJson = hset.Value;
  881. TmidAnalysis tmidDataTodo = valTmidDataJson.ToObject<TmidAnalysis>();
  882. //Redis Month 資料生成
  883. if (TmidAnalysisListMonth.ContainsKey(tmidAnalMonthKey)) //月Dic已有此key => TMID累加
  884. {
  885. if (TmidAnalysisListMonth[$"{tmidAnalMonthKey}"].ContainsKey($"{keyTMID}"))
  886. {
  887. TmidAnalysis TmidDataNow = TmidAnalysisListMonth[$"{tmidAnalMonthKey}"][$"{keyTMID}"];
  888. foreach (PropertyInfo propertyInfo in TmidDataNow.GetType().GetProperties())
  889. {
  890. if (calPropList.Contains(propertyInfo.Name))
  891. {
  892. var propType = propertyInfo.PropertyType;
  893. var valNow = propertyInfo.GetValue(TmidDataNow);
  894. var valTodo = tmidDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(tmidDataTodo);
  895. if (propType.Equals(typeof(long))) propertyInfo.SetValue(TmidDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  896. else propertyInfo.SetValue(TmidDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  897. }
  898. }
  899. TmidDataNow.deviceList = TmidDataNow.deviceList.Union(tmidDataTodo.deviceList).ToList();
  900. TmidDataNow.deviceCnt = TmidDataNow.deviceList.Count;
  901. TmidDataNow.deviceAuthList = TmidDataNow.deviceAuthList.Union(tmidDataTodo.deviceAuthList).ToList();
  902. TmidDataNow.deviceAuth = TmidDataNow.deviceAuthList.Count;
  903. TmidDataNow.deviceNoAuthList = TmidDataNow.deviceNoAuthList.Union(tmidDataTodo.deviceNoAuthList).ToList();
  904. TmidDataNow.deviceNoAuth = TmidDataNow.deviceNoAuthList.Count;
  905. TmidDataNow.tmidList = TmidDataNow.tmidList.Union(tmidDataTodo.tmidList).ToList();
  906. TmidDataNow.tmidCnt = TmidDataNow.tmidList.Count;
  907. TmidDataNow.verList = TmidDataNow.verList.Union(tmidDataTodo.verList).ToList();
  908. }
  909. //無TMID資料 => 該TMID資料放入
  910. else
  911. {
  912. TmidAnalysisListMonth[$"{tmidAnalMonthKey}"][$"{keyTMID}"] = tmidDataTodo;
  913. }
  914. }
  915. else //無此月資料 => 所有TMID資料放入
  916. {
  917. TmidAnalysisListMonth.Add(tmidAnalMonthKey, new Dictionary<string, TmidAnalysis>() { { keyTMID, tmidDataTodo } });
  918. }
  919. }
  920. }
  921. }
  922. }
  923. if (TmidAnalysisListMonth.Count > 0)
  924. {
  925. foreach (KeyValuePair<string, Dictionary<string, TmidAnalysis>> item in TmidAnalysisListMonth)
  926. {
  927. string monthRedisKey = item.Key;
  928. List<HashEntry> hvalList = new List<HashEntry>();
  929. Dictionary<string, TmidAnalysis> monthRedisTmidDIc = item.Value;
  930. foreach (KeyValuePair<string, TmidAnalysis> monthRedisTmidItem in monthRedisTmidDIc)
  931. {
  932. string monthRedisTmid = monthRedisTmidItem.Key;
  933. TmidAnalysis monthRedisTmidData = monthRedisTmidItem.Value;
  934. //Redis資料製作
  935. hvalList.Add(new HashEntry(monthRedisTmid, monthRedisTmidData.ToJsonString()));
  936. //CosmosDB資料製作
  937. TmidAnalysisCosmos cosmosTmidRow = monthRedisTmidData.ToJsonString().ToObject<TmidAnalysisCosmos>();
  938. cosmosTmidRow.date = $"{y}{m}";
  939. cosmosTmidRow.year = Convert.ToInt32(y, 10);
  940. cosmosTmidRow.month = Convert.ToInt32(m, 10);
  941. DateTimeOffset dateTime = new DateTimeOffset(cosmosTmidRow.year, cosmosTmidRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  942. cosmosTmidRow.dateTime = dateTime.ToUnixTimeSeconds();
  943. cosmosTmidRow.id = $"{monthRedisTmidData.toolType}-{cosmosTmidRow.date}-{monthRedisTmid}";
  944. cosmosTmidRow.dateUnit = "month";
  945. cosmosTmidRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  946. cosmosTmidListMonth.Add(cosmosTmidRow);
  947. }
  948. //記入Redis
  949. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  950. }
  951. //記入CosmosDB
  952. if (cosmosTmidListMonth.Count > 0)
  953. {
  954. foreach (TmidAnalysisCosmos cosmosTmidRow in cosmosTmidListMonth)
  955. {
  956. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  957. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  958. cosmosAllTmidSumMonthDic = GenAnalysisRowSumData(cosmosAllTmidSumMonthDic, cosmosTmidRow, calPropList, "day");
  959. }
  960. //每月所有TMID數據總計CosmosDB記入
  961. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumMonthDic)
  962. {
  963. string toolType = tmidItem.Key;
  964. TmidAnalysisCosmos cosmosAllTmidSumMonth = tmidItem.Value;
  965. cosmosAllTmidSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  966. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumMonth);
  967. }
  968. }
  969. }
  970. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  971. Dictionary<string, Dictionary<string, TmidAnalysisCosmos>> TmidAnalysisListYear = new Dictionary<string, Dictionary<string, TmidAnalysisCosmos>>();
  972. Dictionary<string, TmidAnalysisCosmos> cosmosAllTmidSumYearDic = new Dictionary<string, TmidAnalysisCosmos>();
  973. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.tmid != 'alltmid'";
  974. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("TmidAnalysis") }))
  975. {
  976. var json = await JsonDocument.ParseAsync(itemcr.Content);
  977. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  978. {
  979. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  980. {
  981. TmidAnalysisCosmos TmidDataTodo = obj.ToObject<TmidAnalysisCosmos>();
  982. string toolType = TmidDataTodo.toolType;
  983. string tmid = TmidDataTodo.tmid;
  984. //年Dic已有此key => 分校累加
  985. if (TmidAnalysisListYear.ContainsKey(toolType))
  986. {
  987. if (TmidAnalysisListYear[$"{toolType}"].ContainsKey($"{tmid}"))
  988. {
  989. TmidAnalysisCosmos TmidDataNow = TmidAnalysisListYear[$"{toolType}"][$"{tmid}"];
  990. foreach (PropertyInfo propertyInfo in TmidDataNow.GetType().GetProperties())
  991. {
  992. if (calPropList.Contains(propertyInfo.Name))
  993. {
  994. var propType = propertyInfo.PropertyType;
  995. var valNow = propertyInfo.GetValue(TmidDataNow);
  996. var valTodo = TmidDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(TmidDataTodo);
  997. if (propType.Equals(typeof(long))) propertyInfo.SetValue(TmidDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  998. else propertyInfo.SetValue(TmidDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  999. }
  1000. }
  1001. TmidDataNow.deviceList = TmidDataNow.deviceList.Union(TmidDataTodo.deviceList).ToList();
  1002. TmidDataNow.deviceCnt = TmidDataNow.deviceList.Count;
  1003. TmidDataNow.deviceAuthList = TmidDataNow.deviceAuthList.Union(TmidDataTodo.deviceAuthList).ToList();
  1004. TmidDataNow.deviceAuth = TmidDataNow.deviceAuthList.Count;
  1005. TmidDataNow.deviceNoAuthList = TmidDataNow.deviceNoAuthList.Union(TmidDataTodo.deviceNoAuthList).ToList();
  1006. TmidDataNow.deviceNoAuth = TmidDataNow.deviceNoAuthList.Count;
  1007. TmidDataNow.tmidList = TmidDataNow.tmidList.Union(TmidDataTodo.tmidList).ToList();
  1008. TmidDataNow.tmidCnt = TmidDataNow.tmidList.Count;
  1009. TmidDataNow.verList = TmidDataNow.verList.Union(TmidDataTodo.verList).ToList();
  1010. }
  1011. //無此校資料 => 該校資料放入
  1012. else
  1013. {
  1014. TmidAnalysisCosmos TmidDataNow = TmidDataTodo;
  1015. TmidDataNow.date = $"{y}";
  1016. TmidDataNow.year = Convert.ToInt32(y, 10);
  1017. TmidDataNow.month = 0;
  1018. DateTimeOffset dateTime = new DateTimeOffset(TmidDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1019. TmidDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1020. TmidDataNow.id = $"{toolType}-{y}-{tmid}";
  1021. TmidDataNow.dateUnit = "year";
  1022. TmidAnalysisListYear[$"{toolType}"][$"{tmid}"] = TmidDataNow;
  1023. }
  1024. }
  1025. //無此年資料 => 所有學校資料放入
  1026. else
  1027. {
  1028. TmidAnalysisCosmos TmidDataNow = TmidDataTodo;
  1029. TmidDataNow.date = $"{y}";
  1030. TmidDataNow.year = Convert.ToInt32(y, 10);
  1031. TmidDataNow.month = 0;
  1032. DateTimeOffset dateTime = new DateTimeOffset(TmidDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1033. TmidDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1034. TmidDataNow.id = $"{toolType}-{y}-{tmid}";
  1035. TmidDataNow.dateUnit = "year";
  1036. TmidAnalysisListYear.Add(toolType, new Dictionary<string, TmidAnalysisCosmos>() { { tmid, TmidDataNow } });
  1037. }
  1038. }
  1039. }
  1040. }
  1041. if (TmidAnalysisListYear.Count > 0)
  1042. {
  1043. foreach (KeyValuePair<string, Dictionary<string, TmidAnalysisCosmos>> item in TmidAnalysisListYear)
  1044. {
  1045. //string toolType = item.Key;
  1046. TmidAnalysisCosmos TmidDataNow = new TmidAnalysisCosmos(); //當年某產品所有學校總和
  1047. Dictionary<string, TmidAnalysisCosmos> yearCosmosTmidDIc = item.Value;
  1048. foreach (KeyValuePair<string, TmidAnalysisCosmos> yearCosmosTmidItem in yearCosmosTmidDIc)
  1049. {
  1050. string tmid = yearCosmosTmidItem.Key;
  1051. TmidAnalysisCosmos cosmosTmidRow = yearCosmosTmidItem.Value;
  1052. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosTmidRow);
  1053. //每年所有學校數據總計CosmosDB記入
  1054. cosmosAllTmidSumYearDic = GenAnalysisRowSumData(cosmosAllTmidSumYearDic, cosmosTmidRow, calPropList, "year");
  1055. }
  1056. //每年所有學校數據總計CosmosDB記入
  1057. foreach (KeyValuePair<string, TmidAnalysisCosmos> tmidItem in cosmosAllTmidSumYearDic)
  1058. {
  1059. string toolType = tmidItem.Key;
  1060. TmidAnalysisCosmos cosmosAllTmidSumYear = tmidItem.Value;
  1061. cosmosAllTmidSumYear.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1062. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(cosmosAllTmidSumYear);
  1063. }
  1064. //await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TmidAnalysisCosmos>(TmidDataNow);
  1065. }
  1066. }
  1067. }
  1068. catch (Exception ex)
  1069. {
  1070. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatTmidProdAnalData() 生成TMID年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.研發C組);
  1071. }
  1072. }
  1073. //年月日 地理位置 IOT產品分析數據生成
  1074. ///1.生成 Redis GeoAnalysis:Month
  1075. ///2.生成 CosmosDB Month
  1076. ///3.生成 CosmosDB 系統所有地理位置數值加總
  1077. private static async Task CreatGeoProdAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  1078. {
  1079. try
  1080. {
  1081. var redisClinet8 = _azureRedis.GetRedisClient(8);
  1082. 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欄位列表
  1083. List<GeoAnalysis> GeoAnalysisList = new List<GeoAnalysis>();
  1084. if (IotTeachingDataList.Count > 0)
  1085. {
  1086. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  1087. {
  1088. bool addTmidFlg = false;
  1089. string tmid = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid)) ? IotTeachingDatRow.tmid.ToString() : string.Empty;
  1090. string deviceId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId)) ? IotTeachingDatRow.deviceId.ToString() : string.Empty;
  1091. string ver = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.version)) ? IotTeachingDatRow.version : string.Empty;
  1092. string toolType = string.Empty;
  1093. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  1094. {
  1095. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  1096. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  1097. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  1098. }
  1099. Geo geo = IotTeachingDatRow.geo;
  1100. if(geo != null)
  1101. {
  1102. string countryId = (!string.IsNullOrWhiteSpace(geo.countryId)) ? geo.countryId : string.Empty;
  1103. string provinceId = (!string.IsNullOrWhiteSpace(geo.provinceId)) ? geo.provinceId : string.Empty;
  1104. string cityId = (!string.IsNullOrWhiteSpace(geo.cityId)) ? geo.cityId : string.Empty;
  1105. string distId = (!string.IsNullOrWhiteSpace(geo.distId)) ? geo.distId : string.Empty;
  1106. string geoId = $"{countryId}-{provinceId}-{cityId}-{distId}";
  1107. if (!string.IsNullOrWhiteSpace(geo.countryId))
  1108. {
  1109. bool addFlg = false;
  1110. GeoAnalysis geoAnalysisRow = GeoAnalysisList.Where(s => s.geoId.Equals(geoId) && s.toolType.Equals(toolType)).FirstOrDefault();
  1111. //無此geoId數據=>創建
  1112. if (geoAnalysisRow == null)
  1113. {
  1114. geoAnalysisRow = new GeoAnalysis();
  1115. geoAnalysisRow.geoId = geoId;
  1116. geoAnalysisRow.geo = geo;
  1117. geoAnalysisRow.toolType = toolType;
  1118. addFlg = true;
  1119. }
  1120. //欄位加總
  1121. if (!geoAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  1122. {
  1123. geoAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  1124. }
  1125. geoAnalysisRow.deviceCnt = geoAnalysisRow.deviceList.Count;
  1126. switch (IotTeachingDatRow.authType)
  1127. {
  1128. case "0": //928授權
  1129. if (!geoAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) geoAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  1130. geoAnalysisRow.lessonCnt928++;
  1131. break;
  1132. case "1": //ID授權
  1133. if (!geoAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) geoAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  1134. geoAnalysisRow.lessonCntId++;
  1135. break;
  1136. case "2": //機器授權
  1137. if (!geoAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) geoAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  1138. geoAnalysisRow.lessonCntDevice++;
  1139. break;
  1140. case "3": //ID+機器授權
  1141. if (!geoAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) geoAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  1142. geoAnalysisRow.lessonCntIdDevice++;
  1143. break;
  1144. }
  1145. geoAnalysisRow.deviceNoAuth = geoAnalysisRow.deviceNoAuthList.Count;
  1146. geoAnalysisRow.deviceAuth = geoAnalysisRow.deviceAuthList.Count;
  1147. if (!geoAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  1148. {
  1149. geoAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  1150. }
  1151. geoAnalysisRow.tmidCnt = geoAnalysisRow.tmidList.Count;
  1152. geoAnalysisRow.lessonRecord++;
  1153. if (IotTeachingDatRow.useIES.Equals("1")) geoAnalysisRow.useIES++;
  1154. geoAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  1155. if (IotTeachingDatRow.useWebIrs.Equals("1")) geoAnalysisRow.useWebIrs++;
  1156. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) geoAnalysisRow.useDeviceIrs++;
  1157. if (IotTeachingDatRow.useHaboard.Equals("1")) geoAnalysisRow.useHaboard++;
  1158. if (IotTeachingDatRow.useHita.Equals("1")) geoAnalysisRow.useHita++;
  1159. geoAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  1160. if (IotTeachingDatRow.lessonLengMin.Equals(0)) geoAnalysisRow.lessonLeng0++;
  1161. geoAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  1162. geoAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  1163. if (IotTeachingDatRow.tPoint >= 70) geoAnalysisRow.tGreen++;
  1164. if (IotTeachingDatRow.lessonLengMin >= 10 && IotTeachingDatRow.tPoint > 0) geoAnalysisRow.tLesson++;
  1165. if (IotTeachingDatRow.lTypeCoop.Equals("1")) geoAnalysisRow.lTypeCoop++;
  1166. if (IotTeachingDatRow.lTypeIact.Equals("1")) geoAnalysisRow.lTypeIact++;
  1167. if (IotTeachingDatRow.lTypeMis.Equals("1")) geoAnalysisRow.lTypeMis++;
  1168. if (IotTeachingDatRow.lTypeTst.Equals("1")) geoAnalysisRow.lTypeTst++;
  1169. if (IotTeachingDatRow.lTypeDif.Equals("1")) geoAnalysisRow.lTypeDif++;
  1170. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) geoAnalysisRow.lTypeNone++;
  1171. geoAnalysisRow.mission += IotTeachingDatRow.mission;
  1172. geoAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  1173. geoAnalysisRow.item += IotTeachingDatRow.item;
  1174. geoAnalysisRow.interact += IotTeachingDatRow.interact;
  1175. if (IotTeachingDatRow.sendSok.Equals("1")) geoAnalysisRow.sendSok++;
  1176. if (IotTeachingDatRow.learnPeer.Equals("1")) geoAnalysisRow.learnPeer++;
  1177. if (IotTeachingDatRow.learnCoop.Equals("1")) geoAnalysisRow.learnCoop++;
  1178. if (IotTeachingDatRow.useWordCloud.Equals("1")) geoAnalysisRow.useWordCloud++;
  1179. if (IotTeachingDatRow.useClouDAS.Equals("1")) geoAnalysisRow.useClouDAS++;
  1180. if (IotTeachingDatRow.useGPT.Equals("1")) geoAnalysisRow.useGPT++;
  1181. if (IotTeachingDatRow.useIes5Test.Equals("1")) geoAnalysisRow.useIes5Test++;
  1182. if (IotTeachingDatRow.usePaperTest.Equals("1")) geoAnalysisRow.usePaperTest++;
  1183. if (IotTeachingDatRow.useExcelTest.Equals("1")) geoAnalysisRow.useExcelTest++;
  1184. if (IotTeachingDatRow.useScoreBoard.Equals("1")) geoAnalysisRow.useScoreBoard++; //課堂中使用記分板
  1185. if (IotTeachingDatRow.learnParticipationCnt.Equals("1")) geoAnalysisRow.learnParticipationCnt++; //學習參與度次數
  1186. geoAnalysisRow.learnParticipationT += IotTeachingDatRow.learnParticipation; //學習參與度指數(總和)
  1187. decimal learnParticipationTmp = (geoAnalysisRow.learnParticipationCnt > 0) ? (decimal)geoAnalysisRow.learnParticipationT / (decimal)geoAnalysisRow.learnParticipationCnt : 0;
  1188. geoAnalysisRow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1189. geoAnalysisRow.coopMission += IotTeachingDatRow.coopMission; //協作任務數
  1190. geoAnalysisRow.coopWork += IotTeachingDatRow.coopWork; //協作作品數
  1191. geoAnalysisRow.coopContributionT += IotTeachingDatRow.coopContributionT; //協作總貢獻度
  1192. geoAnalysisRow.peerAct += IotTeachingDatRow.peerAct; //互評活動次數
  1193. geoAnalysisRow.peerStuParticipationT += IotTeachingDatRow.peerStuParticipationT; //互評學生參與總次數
  1194. if (IotTeachingDatRow.useTransMode.Equals("1")) geoAnalysisRow.useTransMode++; //有使用透明模式
  1195. if (IotTeachingDatRow.useMiniMode.Equals("1")) geoAnalysisRow.useMiniMode++; //有使用最小化模式
  1196. if (!string.IsNullOrWhiteSpace(ver) && !geoAnalysisRow.verList.Contains(ver))
  1197. {
  1198. geoAnalysisRow.verList.Add(ver);
  1199. }
  1200. if (addFlg)
  1201. {
  1202. GeoAnalysisList.Add(geoAnalysisRow);
  1203. }
  1204. }
  1205. }
  1206. }
  1207. }
  1208. //1.記入CSV2 Redis TmidAnalysis:Day
  1209. //2.記入CSV2 CosmosDB Day
  1210. if (GeoAnalysisList.Count > 0) //[待做]
  1211. {
  1212. //資料整形
  1213. Dictionary<string, Dictionary<string, string>> redisGeoFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => geoId => JsonContent key = $"GeoAnalysis:Day:{toolType}:{y}{m}{d}"
  1214. List<GeoAnalysisCosmos> cosmosGeoList = new List<GeoAnalysisCosmos>();
  1215. Dictionary<string, GeoAnalysisCosmos> cosmosAllGeoSumDayDic = new Dictionary<string, GeoAnalysisCosmos>();
  1216. foreach (GeoAnalysis geoAnalysisRow in GeoAnalysisList)
  1217. {
  1218. //Redis整形
  1219. string toolType = geoAnalysisRow.toolType;
  1220. string geoId = geoAnalysisRow.geoId;
  1221. string hkey = $"GeoAnalysis:Day:{toolType}:{y}{m}{d}";
  1222. string fieldContent = geoAnalysisRow.ToJsonString();
  1223. if (redisGeoFieldDic.ContainsKey(hkey)) redisGeoFieldDic[hkey].Add(geoId, fieldContent);
  1224. else redisGeoFieldDic.Add(hkey, new Dictionary<string, string>() { { geoId, fieldContent } });
  1225. //CosmosDB整形
  1226. GeoAnalysisCosmos GeoAnalysisCosmosRow = geoAnalysisRow.ToJsonString().ToObject<GeoAnalysisCosmos>();
  1227. GeoAnalysisCosmosRow.date = $"{y}{m}{d}";
  1228. GeoAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  1229. GeoAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  1230. GeoAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  1231. DateTimeOffset dateTime = new DateTimeOffset(GeoAnalysisCosmosRow.year, GeoAnalysisCosmosRow.month, GeoAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  1232. GeoAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  1233. GeoAnalysisCosmosRow.id = $"{GeoAnalysisCosmosRow.toolType}-{GeoAnalysisCosmosRow.date}-{geoId}";
  1234. GeoAnalysisCosmosRow.dateUnit = "day";
  1235. GeoAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1236. cosmosGeoList.Add(GeoAnalysisCosmosRow);
  1237. }
  1238. //記入Redis
  1239. if (redisGeoFieldDic.Count > 0)
  1240. {
  1241. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisGeoFieldDic)
  1242. {
  1243. string hkey = hkeyItem.Key;
  1244. List<HashEntry> hvalList = new List<HashEntry>();
  1245. Dictionary<string, string> fieldDic = hkeyItem.Value;
  1246. foreach (KeyValuePair<string, string> geoItem in fieldDic)
  1247. {
  1248. string geoId = geoItem.Key;
  1249. string fieldContent = geoItem.Value;
  1250. hvalList.Add(new HashEntry($"{geoId}", fieldContent));
  1251. }
  1252. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  1253. }
  1254. }
  1255. //記入CosmosDB
  1256. if (cosmosGeoList.Count > 0)
  1257. {
  1258. foreach (GeoAnalysisCosmos cosmosGeoRow in cosmosGeoList)
  1259. {
  1260. //各geoId每日CosmosDB記入
  1261. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosGeoRow);
  1262. //所有geoId每日數值加總 數值生成 cosmosAllGeoSumDayDic -> key:toolType val:cosmosAllGeoSumDay
  1263. cosmosAllGeoSumDayDic = GenAnalysisRowSumData(cosmosAllGeoSumDayDic, cosmosGeoRow, calPropList, "day");
  1264. }
  1265. //每日所有geoId數據總計CosmosDB記入
  1266. foreach (KeyValuePair<string, GeoAnalysisCosmos> geoItem in cosmosAllGeoSumDayDic)
  1267. {
  1268. string toolType = geoItem.Key;
  1269. GeoAnalysisCosmos cosmosAllGeoSumDay = geoItem.Value;
  1270. cosmosAllGeoSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1271. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosAllGeoSumDay);
  1272. }
  1273. }
  1274. }
  1275. //取 Redis 該月所有 GeoAnalysis:Day
  1276. //1.生成 Redis GeoAnalysis:Month
  1277. //2.生成 CosmosDB Month
  1278. Dictionary<string, Dictionary<string, GeoAnalysis>> GeoAnalysisListMonth = new Dictionary<string, Dictionary<string, GeoAnalysis>>();
  1279. Dictionary<string, GeoAnalysisCosmos> cosmosAllGeoSumMonthDic = new Dictionary<string, GeoAnalysisCosmos>();
  1280. List<GeoAnalysisCosmos> cosmosGeoListMonth = new List<GeoAnalysisCosmos>();
  1281. string patternD = $"GeoAnalysis:Day:HiT*:{y}{m}*";
  1282. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  1283. if (keysDayList.Count > 0)
  1284. {
  1285. foreach (string keyDay in keysDayList)
  1286. {
  1287. string[] keyItemList = keyDay.Split(':'); //[例] GeoAnalysis:Day:HiTeach:20230326
  1288. string toolType = keyItemList[2];
  1289. string dateStrD = keyItemList[3];
  1290. string dateStrD_y = string.Empty;
  1291. string dateStrD_m = string.Empty;
  1292. string dateStrD_d = string.Empty;
  1293. Regex rgx = new Regex(@"[0-9]{8}");
  1294. if (rgx.IsMatch(dateStrD))
  1295. {
  1296. dateStrD_y = dateStrD.Substring(0, 4);
  1297. dateStrD_m = dateStrD.Substring(4, 2);
  1298. dateStrD_d = dateStrD.Substring(6, 2);
  1299. }
  1300. string geoAnalMonthKey = $"GeoAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  1301. bool geoAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  1302. if (geoAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  1303. {
  1304. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有地理位置的統計項目
  1305. foreach (HashEntry hset in hsetDay)
  1306. {
  1307. string keyGeoId = hset.Name;
  1308. string valGeoDataJson = hset.Value;
  1309. GeoAnalysis geoDataTodo = valGeoDataJson.ToObject<GeoAnalysis>();
  1310. //Redis Month 資料生成
  1311. if (GeoAnalysisListMonth.ContainsKey(geoAnalMonthKey)) //月Dic已有此key => geoId累加
  1312. {
  1313. if (GeoAnalysisListMonth[$"{geoAnalMonthKey}"].ContainsKey($"{keyGeoId}"))
  1314. {
  1315. GeoAnalysis GeoDataNow = GeoAnalysisListMonth[$"{geoAnalMonthKey}"][$"{keyGeoId}"];
  1316. foreach (PropertyInfo propertyInfo in GeoDataNow.GetType().GetProperties())
  1317. {
  1318. if (calPropList.Contains(propertyInfo.Name))
  1319. {
  1320. var propType = propertyInfo.PropertyType;
  1321. var valNow = propertyInfo.GetValue(GeoDataNow);
  1322. var valTodo = geoDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(geoDataTodo);
  1323. if (propType.Equals(typeof(long))) propertyInfo.SetValue(GeoDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1324. else propertyInfo.SetValue(GeoDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1325. }
  1326. }
  1327. GeoDataNow.deviceList = GeoDataNow.deviceList.Union(geoDataTodo.deviceList).ToList();
  1328. GeoDataNow.deviceCnt = GeoDataNow.deviceList.Count;
  1329. GeoDataNow.deviceAuthList = GeoDataNow.deviceAuthList.Union(geoDataTodo.deviceAuthList).ToList();
  1330. GeoDataNow.deviceAuth = GeoDataNow.deviceAuthList.Count;
  1331. GeoDataNow.deviceNoAuthList = GeoDataNow.deviceNoAuthList.Union(geoDataTodo.deviceNoAuthList).ToList();
  1332. GeoDataNow.deviceNoAuth = GeoDataNow.deviceNoAuthList.Count;
  1333. GeoDataNow.tmidList = GeoDataNow.tmidList.Union(geoDataTodo.tmidList).ToList();
  1334. GeoDataNow.tmidCnt = GeoDataNow.tmidList.Count;
  1335. GeoDataNow.verList = GeoDataNow.verList.Union(geoDataTodo.verList).ToList();
  1336. }
  1337. //無TMID資料 => 該TMID資料放入
  1338. else
  1339. {
  1340. GeoAnalysisListMonth[$"{geoAnalMonthKey}"][$"{keyGeoId}"] = geoDataTodo;
  1341. }
  1342. }
  1343. else //無此月資料 => 所有TMID資料放入
  1344. {
  1345. GeoAnalysisListMonth.Add(geoAnalMonthKey, new Dictionary<string, GeoAnalysis>() { { keyGeoId, geoDataTodo } });
  1346. }
  1347. }
  1348. }
  1349. }
  1350. }
  1351. if (GeoAnalysisListMonth.Count > 0)
  1352. {
  1353. foreach (KeyValuePair<string, Dictionary<string, GeoAnalysis>> item in GeoAnalysisListMonth)
  1354. {
  1355. string monthRedisKey = item.Key;
  1356. List<HashEntry> hvalList = new List<HashEntry>();
  1357. Dictionary<string, GeoAnalysis> monthRedisGeoDIc = item.Value;
  1358. foreach (KeyValuePair<string, GeoAnalysis> monthRedisGeoItem in monthRedisGeoDIc)
  1359. {
  1360. string monthRedisGeo = monthRedisGeoItem.Key;
  1361. GeoAnalysis monthRedisGeoData = monthRedisGeoItem.Value;
  1362. //Redis資料製作
  1363. hvalList.Add(new HashEntry(monthRedisGeo, monthRedisGeoData.ToJsonString()));
  1364. //CosmosDB資料製作
  1365. GeoAnalysisCosmos cosmosGeoRow = monthRedisGeoData.ToJsonString().ToObject<GeoAnalysisCosmos>();
  1366. cosmosGeoRow.date = $"{y}{m}";
  1367. cosmosGeoRow.year = Convert.ToInt32(y, 10);
  1368. cosmosGeoRow.month = Convert.ToInt32(m, 10);
  1369. DateTimeOffset dateTime = new DateTimeOffset(cosmosGeoRow.year, cosmosGeoRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  1370. cosmosGeoRow.dateTime = dateTime.ToUnixTimeSeconds();
  1371. cosmosGeoRow.id = $"{monthRedisGeoData.toolType}-{cosmosGeoRow.date}-{monthRedisGeo}";
  1372. cosmosGeoRow.dateUnit = "month";
  1373. cosmosGeoRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1374. cosmosGeoListMonth.Add(cosmosGeoRow);
  1375. }
  1376. //記入Redis
  1377. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  1378. }
  1379. //記入CosmosDB
  1380. if (cosmosGeoListMonth.Count > 0)
  1381. {
  1382. foreach (GeoAnalysisCosmos cosmosGeoRow in cosmosGeoListMonth)
  1383. {
  1384. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosGeoRow);
  1385. //所有地理位置數值加總 數值生成 cosmosAllGeoSumDayDic -> key:toolType val:cosmosAllGeoSumDay
  1386. cosmosAllGeoSumMonthDic = GenAnalysisRowSumData(cosmosAllGeoSumMonthDic, cosmosGeoRow, calPropList, "day");
  1387. }
  1388. //每月所有geoId數據總計CosmosDB記入
  1389. foreach (KeyValuePair<string, GeoAnalysisCosmos> geoItem in cosmosAllGeoSumMonthDic)
  1390. {
  1391. string toolType = geoItem.Key;
  1392. GeoAnalysisCosmos cosmosAllGeoSumMonth = geoItem.Value;
  1393. cosmosAllGeoSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1394. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosAllGeoSumMonth);
  1395. }
  1396. }
  1397. }
  1398. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  1399. Dictionary<string, Dictionary<string, GeoAnalysisCosmos>> GeoAnalysisListYear = new Dictionary<string, Dictionary<string, GeoAnalysisCosmos>>();
  1400. Dictionary<string, GeoAnalysisCosmos> cosmosAllGeoSumYearDic = new Dictionary<string, GeoAnalysisCosmos>();
  1401. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.geoId != 'allgeoid'";
  1402. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("GeoAnalysis") }))
  1403. {
  1404. var json = await JsonDocument.ParseAsync(itemcr.Content);
  1405. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1406. {
  1407. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1408. {
  1409. GeoAnalysisCosmos GeoDataTodo = obj.ToObject<GeoAnalysisCosmos>();
  1410. string toolType = GeoDataTodo.toolType;
  1411. string geoId = GeoDataTodo.geoId;
  1412. //年Dic已有此key => 分校累加
  1413. if (GeoAnalysisListYear.ContainsKey(toolType))
  1414. {
  1415. if (GeoAnalysisListYear[$"{toolType}"].ContainsKey($"{geoId}"))
  1416. {
  1417. GeoAnalysisCosmos GeoDataNow = GeoAnalysisListYear[$"{toolType}"][$"{geoId}"];
  1418. foreach (PropertyInfo propertyInfo in GeoDataNow.GetType().GetProperties())
  1419. {
  1420. if (calPropList.Contains(propertyInfo.Name))
  1421. {
  1422. var propType = propertyInfo.PropertyType;
  1423. var valNow = propertyInfo.GetValue(GeoDataNow);
  1424. var valTodo = GeoDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(GeoDataTodo);
  1425. if (propType.Equals(typeof(long))) propertyInfo.SetValue(GeoDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1426. else propertyInfo.SetValue(GeoDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1427. }
  1428. }
  1429. GeoDataNow.deviceList = GeoDataNow.deviceList.Union(GeoDataTodo.deviceList).ToList();
  1430. GeoDataNow.deviceCnt = GeoDataNow.deviceList.Count;
  1431. GeoDataNow.deviceAuthList = GeoDataNow.deviceAuthList.Union(GeoDataTodo.deviceAuthList).ToList();
  1432. GeoDataNow.deviceAuth = GeoDataNow.deviceAuthList.Count;
  1433. GeoDataNow.deviceNoAuthList = GeoDataNow.deviceNoAuthList.Union(GeoDataTodo.deviceNoAuthList).ToList();
  1434. GeoDataNow.deviceNoAuth = GeoDataNow.deviceNoAuthList.Count;
  1435. GeoDataNow.tmidList = GeoDataNow.tmidList.Union(GeoDataTodo.tmidList).ToList();
  1436. GeoDataNow.tmidCnt = GeoDataNow.tmidList.Count;
  1437. GeoDataNow.verList = GeoDataNow.verList.Union(GeoDataTodo.verList).ToList();
  1438. }
  1439. //無此校資料 => 該校資料放入
  1440. else
  1441. {
  1442. GeoAnalysisCosmos GeoDataNow = GeoDataTodo;
  1443. GeoDataNow.date = $"{y}";
  1444. GeoDataNow.year = Convert.ToInt32(y, 10);
  1445. GeoDataNow.month = 0;
  1446. DateTimeOffset dateTime = new DateTimeOffset(GeoDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1447. GeoDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1448. GeoDataNow.id = $"{toolType}-{y}-{geoId}";
  1449. GeoDataNow.dateUnit = "year";
  1450. GeoAnalysisListYear[$"{toolType}"][$"{geoId}"] = GeoDataNow;
  1451. }
  1452. }
  1453. //無此年資料 => 所有學校資料放入
  1454. else
  1455. {
  1456. GeoAnalysisCosmos GeoDataNow = GeoDataTodo;
  1457. GeoDataNow.date = $"{y}";
  1458. GeoDataNow.year = Convert.ToInt32(y, 10);
  1459. GeoDataNow.month = 0;
  1460. DateTimeOffset dateTime = new DateTimeOffset(GeoDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  1461. GeoDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  1462. GeoDataNow.id = $"{toolType}-{y}-{geoId}";
  1463. GeoDataNow.dateUnit = "year";
  1464. GeoAnalysisListYear.Add(toolType, new Dictionary<string, GeoAnalysisCosmos>() { { geoId, GeoDataNow } });
  1465. }
  1466. }
  1467. }
  1468. }
  1469. if (GeoAnalysisListYear.Count > 0)
  1470. {
  1471. foreach (KeyValuePair<string, Dictionary<string, GeoAnalysisCosmos>> item in GeoAnalysisListYear)
  1472. {
  1473. //string toolType = item.Key;
  1474. GeoAnalysisCosmos GeoDataNow = new GeoAnalysisCosmos(); //當年某產品所有地理位置總和
  1475. Dictionary<string, GeoAnalysisCosmos> yearCosmosGeoDIc = item.Value;
  1476. foreach (KeyValuePair<string, GeoAnalysisCosmos> yearCosmosGeoItem in yearCosmosGeoDIc)
  1477. {
  1478. string tmid = yearCosmosGeoItem.Key;
  1479. GeoAnalysisCosmos cosmosGeoRow = yearCosmosGeoItem.Value;
  1480. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosGeoRow);
  1481. //每年所有學校數據總計CosmosDB記入
  1482. cosmosAllGeoSumYearDic = GenAnalysisRowSumData(cosmosAllGeoSumYearDic, cosmosGeoRow, calPropList, "year");
  1483. }
  1484. //每年所有學校數據總計CosmosDB記入
  1485. foreach (KeyValuePair<string, GeoAnalysisCosmos> geoItem in cosmosAllGeoSumYearDic)
  1486. {
  1487. string toolType = geoItem.Key;
  1488. GeoAnalysisCosmos cosmosAllGeoSumYear = geoItem.Value;
  1489. cosmosAllGeoSumYear.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1490. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<GeoAnalysisCosmos>(cosmosAllGeoSumYear);
  1491. }
  1492. }
  1493. }
  1494. }
  1495. catch (Exception ex)
  1496. {
  1497. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatGeoProdAnalData() 生成地理資訊年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.研發C組);
  1498. }
  1499. }
  1500. public static (Geo, List<string>) getGeoFromIp(City _city, string ip, List<regionrow> _region_gl, List<regionrow> _region_cn)
  1501. {
  1502. var ipInfo = _city.find(ip, "CN");
  1503. //輸出定義
  1504. List<string> ipToGeoList = (ipInfo != null) ? ipInfo.ToList() : new List<string>(); ///直接由IP取得的地理資訊
  1505. IotTeachingData.Geo geo = null; ///地理資訊
  1506. if(ipInfo == null) return (geo, ipToGeoList);
  1507. else geo = new IotTeachingData.Geo();
  1508. //國省市字典製作
  1509. regiondata regionData = GetRegionData(_region_gl, _region_cn); //取得國省市區地理資訊架構
  1510. List<string> comeRemoveStr = GeoRegion.comeRemoveStr;
  1511. ///國字典製作
  1512. var geoCountryIdNameDic = new Dictionary<string, string>();
  1513. foreach (KeyValuePair<string, regionbase> item in regionData.country)
  1514. {
  1515. geoCountryIdNameDic.Add(item.Value.code, item.Value.name);
  1516. }
  1517. ///省字典製作
  1518. var geoProvinceIdNameDic = new Dictionary<string, string>();
  1519. foreach (KeyValuePair<string, regionbase> item in regionData.province["CN"])
  1520. {
  1521. geoProvinceIdNameDic.Add(item.Value.code, item.Value.name);
  1522. }
  1523. ///市字典製作
  1524. var geoCityIdNameDic = new Dictionary<string, Dictionary<string, string>>();
  1525. geoCityIdNameDic.Add("TW", new Dictionary<string, string>());
  1526. geoCityIdNameDic.Add("CN", new Dictionary<string, string>());
  1527. List<string> municipalityId = GeoRegion.municipalityId; //大陸直轄市的省ID
  1528. foreach (var itemCountry in regionData.city)
  1529. {
  1530. if (itemCountry.Key.Equals("TW"))
  1531. {
  1532. foreach (var itemCity in regionData.city["TW"]["tw"])
  1533. {
  1534. geoCityIdNameDic["TW"].Add(itemCity.Value.code, itemCity.Value.name);
  1535. }
  1536. }
  1537. else if (itemCountry.Key.Equals("CN"))
  1538. {
  1539. foreach (var itemProvince in regionData.city["CN"])
  1540. {
  1541. string provinceIdTmp = itemProvince.Key;
  1542. foreach (var itemCity in regionData.city["CN"][provinceIdTmp])
  1543. {
  1544. geoCityIdNameDic["CN"].Add(itemCity.Value.code, itemCity.Value.name);
  1545. }
  1546. }
  1547. }
  1548. }
  1549. //地理資料生成
  1550. if (ipToGeoList[0].Equals("中国") && ipToGeoList[1].Equals("台湾"))
  1551. {
  1552. geo.countryId = "TW";
  1553. geo.countryName = geoCountryIdNameDic["TW"];
  1554. comeRemoveStr.ForEach(c => { geo.countryName = geo.countryName.Replace(c, ""); });
  1555. string cityNameTmp = ipToGeoList[2];
  1556. if (!string.IsNullOrWhiteSpace(cityNameTmp))
  1557. {
  1558. cityNameTmp = ChineseConverter.Convert(cityNameTmp, ChineseConversionDirection.SimplifiedToTraditional);
  1559. comeRemoveStr.ForEach(c => { cityNameTmp = cityNameTmp.Replace(c, ""); });
  1560. var cityDicRow = geoCityIdNameDic["TW"].FirstOrDefault(c => c.Value.Contains(cityNameTmp));
  1561. if (!cityDicRow.Equals(default(KeyValuePair<string, string>)))
  1562. {
  1563. geo.cityId = cityDicRow.Key;
  1564. geo.cityName = cityDicRow.Value;
  1565. comeRemoveStr.ForEach(c => { geo.cityName = geo.cityName.Replace(c, ""); });
  1566. }
  1567. }
  1568. }
  1569. else if (ipToGeoList[0].Equals("中国"))
  1570. {
  1571. geo.countryId = "CN";
  1572. geo.countryName = geoCountryIdNameDic["CN"];
  1573. string provinceNameTmp = ipToGeoList[1];
  1574. comeRemoveStr.ForEach(p => { provinceNameTmp = provinceNameTmp.Replace(p, ""); });
  1575. var provinceDicRow = geoProvinceIdNameDic.FirstOrDefault(p => p.Value.Contains(provinceNameTmp));
  1576. if (!provinceDicRow.Equals(default(KeyValuePair<string, string>)))
  1577. {
  1578. geo.provinceId = provinceDicRow.Key;
  1579. geo.provinceName = provinceDicRow.Value;
  1580. comeRemoveStr.ForEach(c => { geo.provinceName = geo.provinceName.Replace(c, ""); });
  1581. }
  1582. string cityNameTmp = ipToGeoList[2];
  1583. if (!string.IsNullOrWhiteSpace(cityNameTmp))
  1584. {
  1585. comeRemoveStr.ForEach(c => { cityNameTmp = cityNameTmp.Replace(c, ""); });
  1586. if (!string.IsNullOrWhiteSpace(geo.provinceId) && municipalityId.Contains(geo.provinceId)) ///直轄市處理
  1587. {
  1588. var cityDicFromRegion = regionData.city["CN"][geo.provinceId].First();
  1589. string cityId = cityDicFromRegion.Key;
  1590. geo.cityId = cityId;
  1591. geo.cityName = geoCityIdNameDic["CN"][cityId];
  1592. }
  1593. else ///非直轄市
  1594. {
  1595. var cityDicRow = geoCityIdNameDic["CN"].FirstOrDefault(c => c.Value.Contains(cityNameTmp));
  1596. if (!cityDicRow.Equals(default(KeyValuePair<string, string>)))
  1597. {
  1598. geo.cityId = cityDicRow.Key;
  1599. geo.cityName = cityDicRow.Value;
  1600. comeRemoveStr.ForEach(c => { geo.cityName = geo.cityName.Replace(c, ""); });
  1601. }
  1602. }
  1603. }
  1604. }
  1605. else
  1606. {
  1607. string countryNameTmp = ipToGeoList[0];
  1608. countryNameTmp = ChineseConverter.Convert(countryNameTmp, ChineseConversionDirection.SimplifiedToTraditional);
  1609. var countryDicRow = geoCountryIdNameDic.FirstOrDefault(c => c.Value.Contains(countryNameTmp));
  1610. if (!countryDicRow.Equals(default(KeyValuePair<string, string>)))
  1611. {
  1612. geo.countryId = countryDicRow.Key;
  1613. geo.countryName = countryDicRow.Value;
  1614. }
  1615. }
  1616. return ( geo, ipToGeoList );
  1617. }
  1618. //生成年月日 Device IOT產品分析數據 [待做]
  1619. private static async Task CreatDeviceAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, DingDing _dingDing, string y, string m, string d, List<IotTeachingData> IotTeachingDataList)
  1620. {
  1621. try
  1622. {
  1623. var redisClinet8 = _azureRedis.GetRedisClient(8);
  1624. //1. 從IOT 取得DeviceID 或 IP
  1625. //2. 從DeviceID 或 IP 算出地理位置(國省市區)
  1626. //3. 依照各地理位置進行項目統計;統計項目和學校及TMID統計項目相同
  1627. }
  1628. catch (Exception ex)
  1629. {
  1630. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},CreatDeviceAnalData() 生成TMID年月日日IOT統計資料錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.研發C組);
  1631. }
  1632. }
  1633. //累加各校每日/月總計資料,生成所有學校每日/月總計資料
  1634. private static Dictionary<string, ProdAnalysisCosmos> GenAnalysisRowSumData(Dictionary<string, ProdAnalysisCosmos> cosmosAllSumDic, ProdAnalysisCosmos cosmosRow, List<string> calPropList, string dataType) //dataType:"day","month","year"
  1635. {
  1636. try
  1637. {
  1638. string toolType = cosmosRow.toolType;
  1639. string schoolId = "allschool";
  1640. int year = 0;
  1641. int month = 1;
  1642. int day = 1;
  1643. switch (dataType)
  1644. {
  1645. case "day":
  1646. year = cosmosRow.year;
  1647. month = cosmosRow.month;
  1648. day = cosmosRow.day;
  1649. break;
  1650. case "month":
  1651. year = cosmosRow.year;
  1652. month = cosmosRow.month;
  1653. break;
  1654. case "year":
  1655. year = cosmosRow.year;
  1656. break;
  1657. }
  1658. ProdAnalysisCosmos cosmosAllSum = new ProdAnalysisCosmos();
  1659. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSum = cosmosAllSumDic[toolType];
  1660. foreach (PropertyInfo propertyInfo in cosmosAllSum.GetType().GetProperties()) //累加項目
  1661. {
  1662. if (calPropList.Contains(propertyInfo.Name))
  1663. {
  1664. var propType = propertyInfo.PropertyType;
  1665. var valNow = propertyInfo.GetValue(cosmosAllSum);
  1666. var valTodo = cosmosRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosRow);
  1667. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSum, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1668. else propertyInfo.SetValue(cosmosAllSum, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1669. }
  1670. }
  1671. cosmosAllSum.schoolId = schoolId;
  1672. cosmosAllSum.toolType = cosmosRow.toolType;
  1673. cosmosAllSum.date = cosmosRow.date;
  1674. cosmosAllSum.id = $"{cosmosAllSum.toolType}-{cosmosAllSum.date}-{schoolId}";
  1675. cosmosAllSum.dateUnit = dataType;
  1676. cosmosAllSum.year = year;
  1677. cosmosAllSum.month = month;
  1678. cosmosAllSum.day = day;
  1679. DateTimeOffset dateTime = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
  1680. cosmosAllSum.dateTime = dateTime.ToUnixTimeSeconds();
  1681. cosmosAllSum.deviceList = cosmosAllSum.deviceList.Union(cosmosRow.deviceList).ToList();
  1682. cosmosAllSum.deviceCnt = cosmosAllSum.deviceList.Count;
  1683. cosmosAllSum.deviceAuthList = cosmosAllSum.deviceAuthList.Union(cosmosRow.deviceAuthList).ToList();
  1684. cosmosAllSum.deviceAuth = cosmosAllSum.deviceAuthList.Count;
  1685. cosmosAllSum.deviceNoAuthList = cosmosAllSum.deviceNoAuthList.Union(cosmosRow.deviceNoAuthList).ToList();
  1686. cosmosAllSum.deviceNoAuth = cosmosAllSum.deviceNoAuthList.Count;
  1687. cosmosAllSum.tmidList = cosmosAllSum.tmidList.Union(cosmosRow.tmidList).ToList();
  1688. cosmosAllSum.tmidCnt = cosmosAllSum.tmidList.Count;
  1689. decimal learnParticipationTmp = (cosmosAllSum.learnParticipationCnt > 0) ? (decimal)cosmosAllSum.learnParticipationT / (decimal)cosmosAllSum.learnParticipationCnt : 0;
  1690. cosmosAllSum.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1691. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSumDic[toolType] = cosmosAllSum;
  1692. else cosmosAllSumDic.Add(toolType, cosmosAllSum);
  1693. return cosmosAllSumDic;
  1694. }
  1695. catch (Exception ex)
  1696. {
  1697. return cosmosAllSumDic;
  1698. }
  1699. }
  1700. //累加各TMID每日/月總計資料,生成所有TMID每日/月總計資料
  1701. private static Dictionary<string, TmidAnalysisCosmos> GenAnalysisRowSumData(Dictionary<string, TmidAnalysisCosmos> cosmosAllSumDic, TmidAnalysisCosmos cosmosRow, List<string> calPropList, string dataType) //dataType:"day","month","year"
  1702. {
  1703. try
  1704. {
  1705. string toolType = cosmosRow.toolType;
  1706. string tmid = "alltmid";
  1707. int year = 0;
  1708. int month = 1;
  1709. int day = 1;
  1710. switch (dataType)
  1711. {
  1712. case "day":
  1713. year = cosmosRow.year;
  1714. month = cosmosRow.month;
  1715. day = cosmosRow.day;
  1716. break;
  1717. case "month":
  1718. year = cosmosRow.year;
  1719. month = cosmosRow.month;
  1720. break;
  1721. case "year":
  1722. year = cosmosRow.year;
  1723. break;
  1724. }
  1725. TmidAnalysisCosmos cosmosAllSum = new TmidAnalysisCosmos();
  1726. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSum = cosmosAllSumDic[toolType];
  1727. foreach (PropertyInfo propertyInfo in cosmosAllSum.GetType().GetProperties()) //累加項目
  1728. {
  1729. if (calPropList.Contains(propertyInfo.Name))
  1730. {
  1731. var propType = propertyInfo.PropertyType;
  1732. var valNow = propertyInfo.GetValue(cosmosAllSum);
  1733. var valTodo = cosmosRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosRow);
  1734. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSum, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1735. else propertyInfo.SetValue(cosmosAllSum, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1736. }
  1737. }
  1738. cosmosAllSum.tmid = tmid;
  1739. cosmosAllSum.toolType = cosmosRow.toolType;
  1740. cosmosAllSum.date = cosmosRow.date;
  1741. cosmosAllSum.id = $"{cosmosAllSum.toolType}-{cosmosAllSum.date}-{tmid}";
  1742. cosmosAllSum.dateUnit = dataType;
  1743. cosmosAllSum.year = year;
  1744. cosmosAllSum.month = month;
  1745. cosmosAllSum.day = day;
  1746. DateTimeOffset dateTime = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
  1747. cosmosAllSum.dateTime = dateTime.ToUnixTimeSeconds();
  1748. cosmosAllSum.deviceList = cosmosAllSum.deviceList.Union(cosmosRow.deviceList).ToList();
  1749. cosmosAllSum.deviceCnt = cosmosAllSum.deviceList.Count;
  1750. cosmosAllSum.deviceAuthList = cosmosAllSum.deviceAuthList.Union(cosmosRow.deviceAuthList).ToList();
  1751. cosmosAllSum.deviceAuth = cosmosAllSum.deviceAuthList.Count;
  1752. cosmosAllSum.deviceNoAuthList = cosmosAllSum.deviceNoAuthList.Union(cosmosRow.deviceNoAuthList).ToList();
  1753. cosmosAllSum.deviceNoAuth = cosmosAllSum.deviceNoAuthList.Count;
  1754. cosmosAllSum.tmidList = cosmosAllSum.tmidList.Union(cosmosRow.tmidList).ToList();
  1755. cosmosAllSum.tmidCnt = cosmosAllSum.tmidList.Count;
  1756. cosmosAllSum.verList = cosmosAllSum.verList.Union(cosmosRow.verList).ToList();
  1757. decimal learnParticipationTmp = (cosmosAllSum.learnParticipationCnt > 0) ? (decimal)cosmosAllSum.learnParticipationT / (decimal)cosmosAllSum.learnParticipationCnt : 0;
  1758. cosmosAllSum.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1759. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSumDic[toolType] = cosmosAllSum;
  1760. else cosmosAllSumDic.Add(toolType, cosmosAllSum);
  1761. return cosmosAllSumDic;
  1762. }
  1763. catch (Exception ex)
  1764. {
  1765. return cosmosAllSumDic;
  1766. }
  1767. }
  1768. //累加各地理位置(geoId)每日/月總計資料,生成所有geoId每日/月總計資料
  1769. private static Dictionary<string, GeoAnalysisCosmos> GenAnalysisRowSumData(Dictionary<string, GeoAnalysisCosmos> cosmosAllSumDic, GeoAnalysisCosmos cosmosRow, List<string> calPropList, string dataType) //dataType:"day","month","year"
  1770. {
  1771. try
  1772. {
  1773. string toolType = cosmosRow.toolType;
  1774. string geoId = "allgeoid";
  1775. int year = 0;
  1776. int month = 1;
  1777. int day = 1;
  1778. switch (dataType)
  1779. {
  1780. case "day":
  1781. year = cosmosRow.year;
  1782. month = cosmosRow.month;
  1783. day = cosmosRow.day;
  1784. break;
  1785. case "month":
  1786. year = cosmosRow.year;
  1787. month = cosmosRow.month;
  1788. break;
  1789. case "year":
  1790. year = cosmosRow.year;
  1791. break;
  1792. }
  1793. GeoAnalysisCosmos cosmosAllSum = new GeoAnalysisCosmos();
  1794. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSum = cosmosAllSumDic[toolType];
  1795. foreach (PropertyInfo propertyInfo in cosmosAllSum.GetType().GetProperties()) //累加項目
  1796. {
  1797. if (calPropList.Contains(propertyInfo.Name))
  1798. {
  1799. var propType = propertyInfo.PropertyType;
  1800. var valNow = propertyInfo.GetValue(cosmosAllSum);
  1801. var valTodo = cosmosRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosRow);
  1802. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSum, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1803. else propertyInfo.SetValue(cosmosAllSum, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1804. }
  1805. }
  1806. cosmosAllSum.geoId = geoId;
  1807. cosmosAllSum.geo = null;
  1808. cosmosAllSum.toolType = cosmosRow.toolType;
  1809. cosmosAllSum.date = cosmosRow.date;
  1810. cosmosAllSum.id = $"{cosmosAllSum.toolType}-{cosmosAllSum.date}-{geoId}";
  1811. cosmosAllSum.dateUnit = dataType;
  1812. cosmosAllSum.year = year;
  1813. cosmosAllSum.month = month;
  1814. cosmosAllSum.day = day;
  1815. DateTimeOffset dateTime = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
  1816. cosmosAllSum.dateTime = dateTime.ToUnixTimeSeconds();
  1817. cosmosAllSum.deviceList = cosmosAllSum.deviceList.Union(cosmosRow.deviceList).ToList();
  1818. cosmosAllSum.deviceCnt = cosmosAllSum.deviceList.Count;
  1819. cosmosAllSum.deviceAuthList = cosmosAllSum.deviceAuthList.Union(cosmosRow.deviceAuthList).ToList();
  1820. cosmosAllSum.deviceAuth = cosmosAllSum.deviceAuthList.Count;
  1821. cosmosAllSum.deviceNoAuthList = cosmosAllSum.deviceNoAuthList.Union(cosmosRow.deviceNoAuthList).ToList();
  1822. cosmosAllSum.deviceNoAuth = cosmosAllSum.deviceNoAuthList.Count;
  1823. cosmosAllSum.tmidList = cosmosAllSum.tmidList.Union(cosmosRow.tmidList).ToList();
  1824. cosmosAllSum.tmidCnt = cosmosAllSum.tmidList.Count;
  1825. cosmosAllSum.verList = cosmosAllSum.verList.Union(cosmosRow.verList).ToList();
  1826. decimal learnParticipationTmp = (cosmosAllSum.learnParticipationCnt > 0) ? (decimal)cosmosAllSum.learnParticipationT / (decimal)cosmosAllSum.learnParticipationCnt : 0;
  1827. cosmosAllSum.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1828. if (cosmosAllSumDic.ContainsKey(toolType)) cosmosAllSumDic[toolType] = cosmosAllSum;
  1829. else cosmosAllSumDic.Add(toolType, cosmosAllSum);
  1830. return cosmosAllSumDic;
  1831. }
  1832. catch (Exception ex)
  1833. {
  1834. return cosmosAllSumDic;
  1835. }
  1836. }
  1837. /// <summary>
  1838. /// 取得Redis(8)符合搜尋模式的key
  1839. /// </summary>
  1840. /// <param name="pattern"></param>
  1841. /// <returns></returns>
  1842. public static List<string> ScanRedisKeysByPattern(AzureRedisFactory _azureRedis, string pattern)
  1843. {
  1844. var redisClinet8 = _azureRedis.GetRedisClient(8);
  1845. var keys = new HashSet<RedisKey>();
  1846. int nextCursor = 0;
  1847. do
  1848. {
  1849. RedisResult redisResult = redisClinet8.Execute("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000");
  1850. var innerResult = (RedisResult[])redisResult;
  1851. nextCursor = int.Parse((string)innerResult[0]);
  1852. List<RedisKey> resultLines = ((RedisKey[])innerResult[1]).ToList();
  1853. keys.UnionWith(resultLines);
  1854. }
  1855. while (nextCursor != 0);
  1856. List<string> result = new List<string>();
  1857. if (keys.Count > 0)
  1858. {
  1859. foreach (RedisKey key in keys)
  1860. {
  1861. result.Add(key.ToString());
  1862. }
  1863. }
  1864. return result;
  1865. }
  1866. //public static List<string> GetGeoFromIp(string ip)
  1867. //{
  1868. //}
  1869. }
  1870. }