123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- using Azure.Cosmos;
- using ClouDASLibx;
- using DocumentFormat.OpenXml.Drawing.Charts;
- using DocumentFormat.OpenXml.Office2010.Excel;
- using DocumentFormat.OpenXml.Spreadsheet;
- using DocumentFormat.OpenXml.VariantTypes;
- using Google.Protobuf.WellKnownTypes;
- using Microsoft.AspNetCore.Authorization;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Options;
- using OpenXmlPowerTools;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Text.Json;
- using System.Threading.Tasks;
- using TEAMModelOS.Filter;
- using TEAMModelOS.SDK;
- using TEAMModelOS.SDK.DI;
- using TEAMModelOS.SDK.Extension;
- using TEAMModelOS.SDK.Helper.Common.StringHelper;
- using TEAMModelOS.SDK.Models;
- using TEAMModelOS.SDK.Models.Cosmos.Common;
- using TEAMModelOS.SDK.Models.Service;
- using Top.Api;
- using static Microsoft.Azure.Amqp.Serialization.SerializableType;
- using WebSocketSharp.Frame;
- using static OpenXmlPowerTools.RevisionProcessor;
- using Period = TEAMModelOS.SDK.Models.Period;
- namespace TEAMModelOS.Controllers.Analysis
- {
- [Route("analysis/art")]
- [ApiController]
- public class ArtAnalysisController : ControllerBase
- {
- private readonly AzureCosmosFactory _azureCosmos;
- private readonly DingDing _dingDing;
- private readonly Option _option;
- private readonly CoreAPIHttpService _coreAPIHttpService;
- public ArtAnalysisController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, CoreAPIHttpService coreAPIHttpService)
- {
- _azureCosmos = azureCosmos;
- _dingDing = dingDing;
- _option = option?.Value;
- _coreAPIHttpService = coreAPIHttpService;
- }
- /* [ProducesDefaultResponseType]
- [HttpPost("find")]
- //[Authorize(Roles = "IES")]
- //[AuthToken(Roles = "teacher,admin")]
- public async Task<IActionResult> find(JsonElement request)
- {
- if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
- var client = _azureCosmos.GetCosmosClient();
- List<ArtEvaluation> arts = new();
- try {
- await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT top 1 * FROM c where c.period.id ='{period}' order by c.createTime ",
- requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Art-{code}") }))
- {
- using var json = await JsonDocument.ParseAsync(item.ContentStream);
- foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
- {
- arts.Add(obj.ToObject<ArtEvaluation>());
- }
- }
- } catch (Exception ex) {
- await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-analysis/art,find()\n{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
- }
- }*/
- [ProducesDefaultResponseType]
- [HttpPost("statistics")]
- [Authorize(Roles = "IES")]
- [AuthToken(Roles = "teacher,admin")]
- public async Task<IActionResult> getAnalysis(JsonElement request)
- {
- var client = _azureCosmos.GetCosmosClient();
- if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
- if (!request.TryGetProperty("periodId", out JsonElement periodId)) return BadRequest();
- if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
- if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
- if (!request.TryGetProperty("classIds", out JsonElement cIds)) return BadRequest();
- if (!request.TryGetProperty("examId", out JsonElement examId)) return BadRequest();
- try
- {
- //List<double> scores = new();
- List<ArtSubjectScore> As = new();
- List<(List<ArtSubjectScore> scs, string sIds, string cd)> stus = new();
- //List<ArtEvaluation> arts = new();
- string artId = id.GetString();
- List<string> classIds = cIds.ToObject<List<string>>();
- if (!request.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
- ArtSetting setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<ArtSetting>($"{_areaId}", partitionKey: new PartitionKey("ArtSetting"));
- (List<RMember> tchList, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classIds, code.GetString(), null);
- //学校基本信息
- School scInfo = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{code}", partitionKey: new PartitionKey("Base"));
- var perMore = scInfo.period.Where(c => c.id.Equals(periodId.GetString())).FirstOrDefault()?.grades;
- string queryScore = $" select c.studentId,c.classIds,c.totalScore,c.subjectScores,c.results from c ";
- //List<(string stu,List<string> cId)> clads = new();
- List<List<ArtQuotaResult>> results = new();
- await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetItemQueryStreamIterator
- (queryText: queryScore, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"ArtResult-{artId}") }))
- {
- using var json = await JsonDocument.ParseAsync(item.ContentStream);
- if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
- {
- foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
- {
- if (obj.TryGetProperty("subjectScores", out JsonElement subScore))
- {
- string sId = obj.GetProperty("studentId").GetString();
- List<ArtSubjectScore> sc = subScore.ToObject<List<ArtSubjectScore>>();
- string cd = obj.GetProperty("classIds").ToObject<List<string>>()[0];
- //clads.Add((sId,obj.GetProperty("classIds").ToObject<List<string>>()));
- As.AddRange(sc);
- stus.Add((sc, sId, cd));
- }
- else
- {
- As.AddRange(new List<ArtSubjectScore>());
- }
- results.Add(obj.GetProperty("results").ToObject<List<ArtQuotaResult>>());
- }
- }
- }
- var subjectScore = new
- {
- name = As.Where(a => a.subjectId.Equals(subjectId.GetString())).Select(x => x.score).Where(c => c > 0)
- };
- double max = subjectScore.name.Max(s => Math.Abs(s));
- double min = subjectScore.name.Min(s => Math.Abs(s));
- double total = subjectScore.name.Sum();
- double average = Math.Round(total / stus.Count, 2);
- double excellent = Math.Round(subjectScore.name.Where(s => s >= 80).Count() * 1.0 / stus.Count, 2);
- double pass = Math.Round(subjectScore.name.Where(s => s >= 60).Count() * 1.0 / stus.Count, 2);
- double powSum = 0;
- foreach (var sc in subjectScore.name)
- {
- powSum += Math.Pow(sc - average, 2);
- }
- var pow = Math.Round(stus.Count > 0 ? Math.Pow(powSum / stus.Count, 0.5) : 0, 2);
-
- //获取本次评测所有科目结算结果
- List<ExamResult> examResults = new();
- ExamInfo info = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(examId.ToString(), new PartitionKey($"Exam-{code}"));
- //获取评测ID
- //var examId = arts[0].settings.SelectMany(s => s.task).Where(a => a.type == 1 && a.subject.Equals(subjectId.GetString())).FirstOrDefault().acId;
- //根据科目标识获取科目ID以及知识块和知识点关系TODO 引用不同试卷时 获取知识点得差异
- int index = 0;
- foreach (var pr in info.subjects)
- {
- if (pr.id.Equals(subjectId.GetString()))
- {
- break;
- }
- index++;
- }
- List<(string name, List<string> kno)> knos = new();
- if (string.IsNullOrEmpty(info.papers[index].periodId))
- {
- (string subId, List<(string name, List<string> kno)> values) = await getKnowledge("hbcn", client, subjectId.GetString(), "be32942d-97a9-52ba-45d6-2e5b722583f5");
- knos = values;
- }
- else {
- (string subId, List<(string name, List<string> kno)> values) = await getKnowledge(info.papers[index].code, client, subjectId.GetString(), info.papers[index].periodId);
- knos = values;
- }
- var query = $"select c.id,c.name,c.subjectId,c.studentScores,c.studentIds,c.paper,c.classes,c.sRate,c.average,c.standard,c.lostStus,c.record,c.phc,c.plc from c where c.examId = '{examId}' and c.subjectId = '{subjectId}' ";
- await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamResult>(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamResult-{examId}") }))
- {
- examResults.Add(item);
- }
- List<KeyValuePair<string, List<(string name, double score)>>> pointPersent = new();
- (KeyValuePair<string, List<string>> key1, KeyValuePair<string, List<string>> key2, KeyValuePair<string, List<(string name, double score)>> key3, KeyValuePair<string, List<(string name, double score)>> key4) = DoKnowledgePoint(examResults[0], info);
- KeyValuePair<string, List<(string id, double sta, double pass, string stu)>> key = DoSubjectScatter(examResults[0]);
- pointPersent.Add(key3);
- List<(string name, double score)> blockScore = new();
- foreach (var block in knos)
- {
- double sc = 0;
- foreach (var no in pointPersent)
- {
- foreach (var (name, score) in no.Value)
- {
- if (null != block.kno && block.kno.Contains(name))
- {
- sc += score;
- }
- }
- }
- blockScore.Add((block.name, sc));
- }
- var blk = blockScore.Select(x => new
- {
- x.name,
- x.score,
- dimension = setting.dimensions.Where(s => s.blocks.Contains(x.name)).Select(x => x.dimension)
- });
- var kno = key4.Value.Select(x => new
- {
- x.name,
- x.score,
- block = knos.Where(v => null != v.kno && v.kno.Contains(x.name)).Select(x => x.name)
- });
- //学生信息
- var students = stus.Select(s => new
- {
- id = s.sIds,
- s.scs.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.subjectId) && x.subjectId.Equals(subjectId.GetString()))?.score,
- tchList.Where(t => t.id.Equals(s.sIds)).FirstOrDefault()?.name,
- classId = s.cd,
- className = examResults[0].classes.Where(c => c.id.Equals(s.cd)).FirstOrDefault()?.name,
- examResults[0].classes.Where(c => c.id.Equals(s.cd)).FirstOrDefault()?.gradeId,
- key.Value.Where(c => c.id.Equals(s.sIds)).FirstOrDefault().sta,
- key.Value.Where(c => c.id.Equals(s.sIds)).FirstOrDefault().pass,
- key.Value.Where(c => c.id.Equals(s.sIds)).FirstOrDefault().stu,
- });
- List<(string cId, double sc, double max, double min, double excellent, double pass)> clsInfo = new();
- foreach (var cls in classLists)
- {
- double classTotal = 0;
- List<double> scores = new();
- foreach (var member in cls.members)
- {
- double sc = (double)students.FirstOrDefault(s => s.id.Equals(member.id)).score;
- scores.Add(sc);
- classTotal += sc;
- }
- scores = scores.Where(x => x > 0).ToList();
- double maxc = scores.Max(s => Math.Abs(s));
- double minc = scores.Min(s => Math.Abs(s));
- double excellentc = scores.Where(s => s >= 80).Count();
- double ex = Math.Round(excellentc / scores.Count,2);
- double passc = scores.Where(s => s >= 60).Count();
- double pa = Math.Round(passc / scores.Count, 2);
- clsInfo.Add((cls.id, classTotal / scores.Count, maxc, minc, ex, pa));
- }
- //班级信息
- var cInfo = clsInfo.Select(x => new
- {
- id = x.cId,
- classLists.Where(c => c.id.Equals(x.cId)).FirstOrDefault().name,
- score = Math.Round(x.sc, 2),
- x.max,
- x.min,
- x.excellent,
- x.pass,
- examResults[0].classes.Where(c => c.id.Equals(x.cId)).FirstOrDefault().gradeId
- });
- //年级信息
- var grades = students.GroupBy(c => c.gradeId).Select(x => new { gradeId = x.Key, list = x.ToList().Select(v => v.score).Where(c => c > 0) });
- var gscore = grades.Select(x => new
- {
- id = x.gradeId,
- name = perMore[int.Parse(x.gradeId)],
- score = Math.Round((double)(x.list.Sum() / x.list.Count()), 2),
- max = x.list.Max(s => Math.Abs((double)s)),
- min = x.list.Min(s => Math.Abs((double)s)),
- excellent = Math.Round( x.list.Where(s => s >= 80).Count() * 1.0 / x.list.Count(),2),
- pass = Math.Round(x.list.Where(s => s >= 60).Count() * 1.0 / x.list.Count(), 2)
- });
- //获奖次数
- List<List<string>> tag = new();
- results.ForEach(r =>
- {
- tag.AddRange(r.Where(x => null != x.files).SelectMany(f => f.files).Select(c => c.tag).ToList());
- });
- List<string> newTag = new();
- tag.ForEach(t =>
- {
- if (t.Count > 0)
- {
- newTag.AddRange(t);
- }
- });
- //List<Dictionary<string, int>> recorde = new List<Dictionary<string, int>>();
- Dictionary<string, int> optCount = new Dictionary<string, int>();
- foreach (var s in newTag)
- {
- if (optCount.ContainsKey(s))
- {
- optCount[s] = optCount[s] + 1;
- }
- else
- {
- optCount[s] = 1;
- }
- }
- /*var prizeCount = optCount.Select(x => new {
- x.Key,
- x.Value
- });*/
- return Ok(new { count = tchList.Count, scount = stus.Count - info.lostStu.Count, max, min, average, excellent, pass, pow, students, cInfo, blk, kno, optCount, gscore });
- }
- catch (Exception e)
- {
- return Ok( new { code = 500});
- }
- }
- private static async Task<(string subId, List<(string name, List<string> kno)>)> getKnowledge(string school, CosmosClient client, string subjectBid, string pId)
- {
- try
- {
- var response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school, new PartitionKey($"Base"));
- string subjectId = string.Empty;
- List<Knowledge> knowledges = new();
- List<(string name, List<string> kno)> blocks = new();
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- School sc = json.ToObject<School>();
- var subjects = sc.period.Where(p => p.id.Equals(pId)).Select(x => x.subjects);
- foreach (var sj in subjects)
- {
- foreach (var s in sj)
- {
- if (!string.IsNullOrWhiteSpace(s.bindId) && s.bindId.Equals(subjectBid))
- {
- subjectId = s.id;
- }
- }
- }
- string code = $"Knowledge-{school}-{subjectId}";
- StringBuilder sql = new StringBuilder($"select value(c) from c");
- if (string.IsNullOrWhiteSpace(pId))
- {
- sql.Append($" where c.periodId = '{pId}'");
- }
- await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Knowledge>(queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
- {
- knowledges.Add(item);
- }
- }
- foreach (var know in knowledges)
- {
- foreach (var block in know.blocks)
- {
- blocks.Add((block.name, block.points));
- }
- }
- return (subjectId, blocks);
- }
- catch (Exception e)
- {
- return (null, null);
- }
- }
- //获取知识点得分率
- private static (KeyValuePair<string, List<string>>, KeyValuePair<string, List<string>>, KeyValuePair<string, List<(string name, double score)>>, KeyValuePair<string, List<(string name, double score)>>) DoKnowledgePoint(ExamResult exam, ExamInfo info)
- {
- HashSet<string> knowledge = new HashSet<string>();
- List<double> point = new List<double>();
- List<List<double>> result = new List<List<double>>();
- List<ClassRange> classes = new List<ClassRange>();
- //求单个知识点所占分数
- List<string> per = new List<string>();
- //定位试卷信息
- int index = 0;
- foreach (ExamSubject subject in info.subjects)
- {
- if (subject.id.Equals(exam.subjectId))
- {
- break;
- }
- else
- {
- index++;
- }
- }
- if (info.papers[index].knowledge != null && info.papers[index].knowledge.Count > 0)
- {
- info.papers[index].knowledge.ForEach(k =>
- {
- k.ForEach(e =>
- {
- knowledge.Add(e);
- });
- });
- }
- else
- {
- return (default, default, default, default);
- }
- point = info.papers[index].point;
- result = exam.studentScores;
- classes = exam.classes;
- List<string> knowledgeName = new List<string>();
- foreach (string cla in knowledge)
- {
- knowledgeName.Add(cla);
- }
- for (int k = 0; k < knowledgeName.Count; k++)
- {
- if (null == knowledgeName[k])
- {
- knowledgeName.Remove(knowledgeName[k]);
- }
- }
- //初始化年级总分
- double total = 0;
- //处理年级单个知识点得分率
- foreach (List<double> grade in result)
- {
- total += grade.Sum();
- }
- //试卷总分
- double TotalPoint = point.Sum();
- List<double> knowScore = new List<double>();
- //学生得分情况
- List<double> Score = new List<double>();
- List<(string name, double score)> pointScore = new();
- List<(string name, double score)> pointTScore = new();
- for (int k = 0; k < knowledgeName.Count; k++)
- {
- double OnePoint = 0;
- List<string> itemNo = new List<string>();
- int n = 0;
- double scores = 0;
- info.papers[index].knowledge.ForEach(kno =>
- {
- if (kno.Contains(knowledgeName[k]))
- {
- var itemPersent = kno.Count > 0 ? 1 / Convert.ToDouble(kno.Count) : 0;
- OnePoint += point[n] * itemPersent;
- foreach (string id in exam.studentIds)
- {
- int index = exam.studentIds.IndexOf(id);
- if (exam.studentScores[index][n] > 0)
- {
- scores += exam.studentScores[index][n] * itemPersent;
- }
- }
- }
- n++;
- });
- Score.Add(scores);
- //单个知识点的配分
- pointScore.Add((knowledgeName[k], OnePoint));
- //该知识点平均得分
- double sc = exam.studentIds.Count > 0 ? Math.Round(scores * 1.0 / exam.studentIds.Count, 2) : 0;
- //知识点占比
- double average = sc * 1.5;
- if (average > OnePoint)
- {
- average = sc;
- }
- double persent = Math.Round(OnePoint > 0 ? average / OnePoint : 0, 2);
- per.Add(persent.ToString("0.00"));
- //单个知识点所有学生得分率
- pointTScore.Add((knowledgeName[k], persent));
- }
- KeyValuePair<string, List<string>> key1 = new(exam.subjectId, knowledgeName);
- KeyValuePair<string, List<string>> key2 = new(exam.subjectId, per);
- KeyValuePair<string, List<(string name, double score)>> key3 = new(exam.subjectId, pointScore);
- KeyValuePair<string, List<(string name, double score)>> key4 = new(exam.subjectId, pointTScore);
- //KeyValuePair<string, List<double>> key3 = new KeyValuePair<string, List<double>>(exam.subjectId, allPer);
- return (key1, key2, key3, key4);
- }
- private KeyValuePair<string, List<(string id, double sta, double pass, string stu)>> DoSubjectScatter(ExamResult e)
- {
- double[] point = StringHelper.ListTodouble(e.paper.point);
- double[,] result = StringHelper.ListToDouble(e.studentScores);
- try
- {
- var cdm = new ClouDASMatrix(result, point);
- //学生通过率
- List<double> pass = cdm.ScoringRate;
- //学生稳定度
- List<double> sta = cdm.StabilityRate;
- //落点区域
- List<string> stu = cdm.StuFallArea;
- int i = 0;
- List<(string id, double sta, double pass, string stu)> stus = new();
- e.studentIds.ForEach(s =>
- {
- var stuSta = sta[i] > 1 ? 1 : sta[i];
- stus.Add((s, stuSta, pass[i] * 0.01, stu[i]));
- i++;
- });
- return new KeyValuePair<string, List<(string id, double sta, double pass, string stu)>>(e.subjectId, stus);
- }
- catch (Exception ex)
- {
- BadRequest(ex.Message + ex.StackTrace);
- }
- return default;
- }
- }
- }
|