123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- using Microsoft.AspNetCore.Mvc;
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading.Tasks;
- using TEAMModelOS.Models;
- using TEAMModelOS.SDK;
- using TEAMModelOS.SDK.DI;
- using System.Text.Json;
- using TEAMModelOS.SDK.Models;
- using TEAMModelOS.SDK.Extension;
- using Azure.Cosmos;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.Options;
- using System.IO;
- using System.Dynamic;
- using System.Net.Http;
- using System.Net;
- using Newtonsoft.Json;
- using System.Linq;
- using StackExchange.Redis;
- using static TEAMModelOS.SDK.Models.Teacher;
- using Microsoft.Extensions.Configuration;
- using TEAMModelOS.Filter;
- using Microsoft.AspNetCore.Authorization;
- using HTEXLib.COMM.Helpers;
- using HTEXLib.Translator;
- using TEAMModelOS.Models.Dto;
- namespace TEAMModelAPI.Controllers
- {
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ApiController]
- [Route("{scope}")]
- public class ExamController : ControllerBase
- {
- public AzureCosmosFactory _azureCosmos;
- private readonly AzureStorageFactory _azureStorage;
- private readonly AzureRedisFactory _azureRedis;
- private readonly DingDing _dingDing;
- private readonly Option _option;
- private readonly IConfiguration _configuration;
- public DOXC2HTMLTranslator _DOXC2HTMLTranslator { get; set; }
- //public PPTX2HTEXTranslator _PPTX2HTEXTranslator { get; set; }
- public HTML2ITEMV3Translator _HTML2ITEMV3Translator { get; set; }
- public ExamController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration)
- {
- _azureCosmos = azureCosmos;
- _azureStorage = azureStorage;
- _azureRedis = azureRedis;
- _dingDing = dingDing;
- _option = option?.Value;
- _configuration = configuration;
- }
- /// <summary>
- /// 获取试卷和评测的条件信息
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("get-paper-exam-condition")]
- [ApiToken(Auth = "1101", Name = "试卷和评测的条件信息", RW = "R", Limit = false)]
- public async Task<IActionResult> GetPaperExamCondition(JsonElement json)
- {
- json.TryGetProperty("periodId", out JsonElement _periodId);
- var (id, school) = HttpContext.GetApiTokenInfo();
- School data = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>(school, new PartitionKey("Base"));
- var exmaType = new { type = new List<string> { "regular", "simulation", "normal" } };//regula ,正规考, simulation 模拟靠考,normal 普通考
- var exmaMode = new { type = new List<string> { "0", "1", "2" } };//0 线上评测, 1 课中评测 ,2 阅卷评测
- var period = data.period.Find(x => x.id.Equals($"{_periodId}"));
- if (period != null)
- {
- return Ok(new { period.subjects, period.analysis, period.grades, exmaType, exmaMode });
- }
- else
- {
- return Ok(new { error = 1, msg = "学段不存在!" });
- }
- }
- [ProducesDefaultResponseType]
- [HttpPost("import-exam")]
- [ApiToken(Auth = "1102", Name = "汇入评测基础数据", Limit = false)]
- public async Task<IActionResult> importExam(JsonElement request)
- {
- //获取评测的ID
- if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
- if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
- try
- {
- var client = _azureCosmos.GetCosmosClient();
- ExamInfo info = exam.ToObject<ExamInfo>();
- info.progress = "going";
- await client.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(info, new PartitionKey($"Exam-{code}"));
- return Ok(new { info });
- }
- catch (Exception e)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/import-exam()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
- }
- [ProducesDefaultResponseType]
- [HttpPost("upsert-record")]
- [ApiToken(Auth = "1103", Name = "批量汇入作答数据", Limit = false)]
- public async Task<IActionResult> upsertRecord(JsonElement request)
- {
- if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
- if (!request.TryGetProperty("students", out JsonElement students)) return BadRequest();
- if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
- //根据不同评测的类型返回对应的编码
- if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
- try
- {
- var client = _azureCosmos.GetCosmosClient();
- //List<string> ids = students.ToObject<List<string>>();
- List<students> stus = students.ToObject<List<students>>();
- ExamInfo info = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(id.GetString(), new PartitionKey($"Exam-{code}"));
- string classCode = info.scope.Equals("school") ? info.school : info.creatorId;
- List<ExamClassResult> examClassResults = new();
- await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamClassResult>(
- queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}'",
- requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{classCode}") }))
- {
- examClassResults.Add(item);
- }
- int n = 0;
- foreach (ExamSubject subject in info.subjects)
- {
- if (!subject.id.Equals(subjectId.GetString()))
- {
- n++;
- }
- else
- {
- break;
- }
- }
- //获取试卷信息
- PaperSimple standerAnswers = new PaperSimple();
- List<List<string>> standard = new List<List<string>>();
- List<double> points = new List<double>();
- standerAnswers = info.papers[n];
- standard = standerAnswers.answers;
- points = standerAnswers.point;
- int rule = standerAnswers.multipleRule;
- List<string> value = new List<string>();
- await foreach (var s in stuTask(stus, examClassResults, standard, points, rule, info, subjectId.GetString(), client))
- {
- if (s.code == 1)
- {
- value.Add(s.value);
- }
- }
- if (value.Count > 0)
- {
- return Ok(new { code = 1, msg = "学生ID异常", value = value });
- }
- else
- {
- return Ok(new { code = 0 });
- }
- }
- catch (Exception e)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},activity/upsert-record()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
- }
- private async IAsyncEnumerable<(int code, string value)> stuTask(List<students> stus, List<ExamClassResult> examClassResults, List<List<string>> standard,
- List<double> points, int rule, ExamInfo info, string subjectId, CosmosClient client)
- {
- foreach (var s in stus)
- {
- string value = "";
- int code = 0;
- List<List<string>> ans = s.answer;
- bool isExist = examClassResults.Exists(e => e.studentIds.Contains(s.id));
- if (!isExist)
- {
- value = s.id;
- code = 1;
- }
- else
- {
- foreach (ExamClassResult result in examClassResults)
- {
- if (!result.studentIds.Contains(s.id))
- {
- continue;
- }
- else
- {
- int newIndex = result.studentIds.IndexOf(s.id);
- StringBuilder builder = new StringBuilder();
- builder.Append(result.examId).Append('/');
- builder.Append(result.subjectId).Append('/');
- builder.Append(s.id).Append('/');
- builder.Append("ans.json");
- result.studentAnswers[newIndex].Clear();
- result.studentAnswers[newIndex].Add(builder.ToString());
- result.status[newIndex] = 0;
- for (int i = 0; i < ans.Count; i++)
- {
- if (ans[i] == null)
- {
- continue;
- //ans[i] = new List<string>();
- }
- var ac = ans[i].Count;
- var sc = standard[i].Count;
- //记录次数
- int n = 0;
- //算分处理
- if (sc > 0)
- {
- result.ans[newIndex][i] = ans[i];
- if (ac == sc && sc == 1)
- {
- foreach (string right in ans[i])
- {
- if (standard[i].Contains(right))
- {
- result.studentScores[newIndex][i] = points[i];
- }
- else
- {
- result.studentScores[newIndex][i] = 0;
- }
- }
- }
- else
- {
- if (rule > 0)
- {
- int falseCount = 0;
- if (ac > 0)
- {
- foreach (string obj in ans[i])
- {
- if (!standard[i].Contains(obj))
- {
- falseCount++;
- }
- }
- switch (rule)
- {
- case 1:
- if (ac == sc)
- {
- if (falseCount == 0)
- {
- result.studentScores[newIndex][i] = points[i];
- }
- else
- {
- result.studentScores[newIndex][i] = 0;
- }
- }
- else
- {
- result.studentScores[newIndex][i] = 0;
- }
- break;
- case 2:
- if (falseCount > 0)
- {
- result.studentScores[newIndex][i] = 0;
- }
- else
- {
- if (ac == sc)
- {
- result.studentScores[newIndex][i] = points[i];
- }
- else
- {
- result.studentScores[newIndex][i] = points[i] / 2;
- }
- }
- break;
- case 3:
- if (falseCount > 0)
- {
- result.studentScores[newIndex][i] = 0;
- }
- else
- {
- if (ac == sc)
- {
- result.studentScores[newIndex][i] = points[i];
- }
- else
- {
- result.studentScores[newIndex][i] = System.Math.Round((double)ac / sc * points[i], 1);
- }
- }
- break;
- case 4:
- if (ac == sc)
- {
- result.studentScores[newIndex][i] = points[i];
- }
- else
- {
- double persent = (double)(sc - 2 * falseCount) / sc;
- if (persent <= 0)
- {
- result.studentScores[newIndex][i] = 0;
- }
- else
- {
- result.studentScores[newIndex][i] = System.Math.Round(persent * points[i], 1);
- }
- }
- break;
- }
- }
- else
- {
- result.studentScores[newIndex][i] = 0;
- }
- }
- }
- }
- }
- bool flag = true;
- foreach (List<double> scores in result.studentScores)
- {
- foreach (double score in scores)
- {
- if (score == -1)
- {
- flag = false;
- break;
- }
- }
- }
- if (flag)
- {
- result.progress = true;
- info.subjects.ForEach(s =>
- {
- if (s.id.Equals(subjectId.ToString()))
- {
- s.classCount += 1;
- }
- });
- await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(info, info.id.ToString(), new PartitionKey($"{info.code}"));
- }
- result.sum[newIndex] = result.studentScores[newIndex].Sum();
- await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(result, result.id, new PartitionKey($"{result.code}"));
- break;
- }
- }
- }
- yield return (code, value);
- }
- }
- [ProducesDefaultResponseType]
- [HttpPost("parse-word")]
- [ApiToken(Auth = "1104", Name = "录入试卷数据", Limit = false)]
- public async Task<IActionResult> ParseWord([FromForm] FileDto fileDto)
- {
- if (!FileType.GetExtention(fileDto.file.FileName).ToLower().Equals("docx"))
- {
- return BadRequest(new Dictionary<string, object> { { "msg", "type is not docx!" }, { "code", 404 } });
- }
- try
- {
-
- var client = _azureCosmos.GetCosmosClient();
- var response = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemStreamAsync(fileDto.examId, new PartitionKey($"Exam-{fileDto.code}"));
- ExamInfo examInfo;
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- examInfo = json.ToObject<ExamInfo>();
- }
- else
- {
- return Ok(new { error = 404, msg = "请先导入评测信息" });
- }
- foreach (PaperSimple paper in examInfo.papers)
- {
- if (paper.id.Contains(fileDto.subjectId))
- {
- return Ok(new { error = 500, msg = "该试卷信息已存在" });
- }
- }
- PaperSimple simple = new();
- var doc = _DOXC2HTMLTranslator.Translate(fileDto.file.OpenReadStream());
- (List<HTEXLib.DOCX.Models.ItemInfo> tests, List<string> error) = _HTML2ITEMV3Translator.Translate(doc);
- List<Task<string>> tasks = new List<Task<string>>();
- PaperDto paperDto = new()
- {
- id = Guid.NewGuid().ToString(),
- name = fileDto.name,
- code = fileDto.code,
- scope = "school",
- multipleRule = fileDto.multipleRule,
- gradeIds = fileDto.gradeIds,
- subjectId = fileDto.subjectId,
- periodId = fileDto.periodId
- };
- foreach (HTEXLib.DOCX.Models.ItemInfo item in tests)
- {
- Slides slides = new();
- ItemDto dto = new();
- Scoring scoring = new();
- dto.id = Guid.NewGuid().ToString();
- dto.exercise.answer = item.answer;
- dto.exercise.explain = item.explain;
- dto.exercise.type = item.type;
- dto.exercise.opts = item.option.Count;
- dto.exercise.knowledge = item.knowledge;
- dto.exercise.field = item.field;
- dto.exercise.level = item.level;
- dto.exercise.subjectId = fileDto.subjectId;
- dto.exercise.periodId = fileDto.periodId;
- dto.exercise.gradeIds = fileDto.gradeIds;
- slides.url = dto.id + ".json";
- slides.type = dto.exercise.type;
- scoring.ans = dto.exercise.answer;
- scoring.score = dto.exercise.score;
- scoring.knowledge = dto.exercise.knowledge;
- scoring.field = dto.exercise.field;
- slides.scoring = scoring;
- //添加试卷信息
- paperDto.slides.Add(slides);
- if (!slides.type.Equals("compose"))
- {
- simple.point.Add(dto.exercise.score);
- simple.answers.Add(dto.exercise.answer);
- simple.knowledge.Add(dto.exercise.knowledge);
- simple.type.Add(dto.exercise.type);
- simple.field.Add((int)dto.exercise.field);
- }
- if (item.children.Count > 0)
- {
- foreach (HTEXLib.DOCX.Models.ItemInfo its in item.children)
- {
- Slides cslides = new();
- Scoring cscoring = new();
- ItemDto dtoChildren = new ItemDto();
- dtoChildren.id = Guid.NewGuid().ToString();
- dtoChildren.pid = dto.id;
- dtoChildren.exercise.answer = its.answer;
- dtoChildren.exercise.explain = its.explain;
- dtoChildren.exercise.type = its.type;
- dtoChildren.exercise.opts = its.option.Count;
- dtoChildren.exercise.knowledge = its.knowledge;
- dtoChildren.exercise.field = its.field;
- dtoChildren.exercise.level = its.level;
- dtoChildren.exercise.scope = "school";
- dtoChildren.exercise.score = its.score;
- dtoChildren.exercise.subjectId = fileDto.subjectId;
- dtoChildren.exercise.periodId = fileDto.periodId;
- dtoChildren.exercise.gradeIds = fileDto.gradeIds;
- dtoChildren.exercise.children.Add(dtoChildren.id);
- info info1 = new();
- info1.uid = dtoChildren.id;
- info1.question = its.question;
- info1.option = its.option;
- dtoChildren.item.Add(info1);
- dto.exercise.children.Add(dtoChildren.id);
- //处理子题的slides
- cslides.url = dtoChildren.id + ".json";
- cslides.type = dtoChildren.exercise.type;
- cscoring.ans = dtoChildren.exercise.answer;
- cscoring.score = dtoChildren.exercise.score;
- cscoring.knowledge = dtoChildren.exercise.knowledge;
- cscoring.field = dtoChildren.exercise.field;
- cslides.scoring = scoring;
- paperDto.slides.Add(cslides);
- //添加试卷信息
- simple.point.Add(dtoChildren.exercise.score);
- simple.answers.Add(dto.exercise.answer);
- simple.knowledge.Add(dto.exercise.knowledge);
- simple.type.Add(dto.exercise.type);
- simple.field.Add((int)dto.exercise.field);
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.Append(fileDto.examId).Append("/");
- stringBuilder.Append("paper").Append("/");
- stringBuilder.Append(fileDto.subjectId).Append("/");
- stringBuilder.Append(dtoChildren.id + ".json");
- tasks.Add(_azureStorage.GetBlobContainerClient(fileDto.code).UploadFileByContainer( dtoChildren.ToJsonString(), "exam", stringBuilder.ToString(), false));
- }
- }
- info @info = new();
- @info.uid = dto.id;
- @info.question = item.question;
- @info.option = item.option;
- dto.item.Add(@info);
- dto.exercise.scope = "school";
- dto.exercise.score = item.score;
- StringBuilder builder = new StringBuilder();
- builder.Append(fileDto.examId).Append('/');
- builder.Append("paper").Append('/');
- builder.Append(fileDto.subjectId).Append('/');
- builder.Append(dto.id + ".json");
- tasks.Add(_azureStorage.GetBlobContainerClient(fileDto.code).UploadFileByContainer(dto.ToJsonString(), "exam", builder.ToString(), false));
- }
- StringBuilder paperBuilder = new StringBuilder();
- paperBuilder.Append(fileDto.examId).Append('/');
- paperBuilder.Append("paper").Append('/');
- paperBuilder.Append(fileDto.subjectId).Append('/');
- paperBuilder.Append("index.json");
- tasks.Add(_azureStorage.GetBlobContainerClient(fileDto.code).UploadFileByContainer(paperDto.ToJsonString(), "exam", paperBuilder.ToString(), false));
- //开始给ExamInfo paper赋值
- simple.id = fileDto.subjectId;
- simple.code = "Paper-" + fileDto.code;
- simple.name = fileDto.name;
- simple.blob = paperBuilder.ToString().Replace("index.json", "");
- simple.scope = "school";
- simple.multipleRule = fileDto.multipleRule;
- examInfo.papers.Add(simple);
- await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(examInfo, examInfo.id, new PartitionKey($"{examInfo.code}"));
- await Task.WhenAll(tasks);
- return Ok(new { code = 200 });
- }
- catch (Exception e)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/word()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
- }
- private class students
- {
- public string id { get; set; }
- public List<List<string>> answer { get; set; }
- }
- public class FileDto
- {
- public string periodId { get; set; }
- public string code { get; set; }
- public string name { get; set; }
- public int multipleRule { get; set; }
- public string examId { get; set; }
- public string subjectId { get; set; }
- public List<string> gradeIds { get; set; }
- public IFormFile file { get; set; }
- }
- }
- }
|