using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore.Common; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using static IRaCIS.Core.Application.Service.ExcelExportHelper; namespace IRaCIS.Core.Application; [ApiExplorerSettings(GroupName = "Trial")] public class TrialStatService( IRepository _trialRepository, IRepository _readingQuestionTrialRepository, IRepository _subjectVisitRepository, IRepository _trialDocumentRepository, IRepository _systemDocumentRepository, IRepository _systemNoticeRepository, IRepository _visitTaskRepository, IRepository _dictionaryRepository, IRepository _readingQuestionCriterionTrialRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService { /// /// 访视完成度 /// /// [HttpPost] public async Task GetTrialVisitFinishedStatList(VisitFinishedStatQuery inQuery) { var result = await _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new VisitFinishedStatViewModel() { UploadedCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.SubmitState == SubmitStateEnum.Submitted).Count(), QCFinishedCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.AuditState == AuditStateEnum.QCPassed || t.AuditState == AuditStateEnum.QCFailed).Count(), CheckFinishedCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.CheckState == CheckStateEnum.CVPassed).Count(), CriterionList = t.TrialReadingCriterionList.Where(t => inQuery.TrialReadingCriterionId != null ? t.Id == inQuery.TrialReadingCriterionId : true) .Select(t => new VisitReadingCriterionInfo() { TrialReadingCriterionId = t.Id, TrialReadingCriterionName = t.CriterionName, ReadingFinishedCount = t.VisitTaskList .Where(t => inQuery.TrialSiteId != null ? t.Subject.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.ReadingCategory == ReadingCategory.Visit) .GroupBy(t => t.SourceSubjectVisitId) .Where(g => t.ReadingType == ReadingMethod.Double ? g.Count() == 2 : true) .Count() }).ToList() }).FirstOrDefaultAsync(); return result; } /// /// 质疑统计列表 --医学审核不区分标准 /// /// [HttpPost] public async Task GetTrialQuestionStatList(VisitQuestionStatQuery inQuery) { var result = await _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new VisitQuestionViewModel() { QCQuestion_ClosedCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .SelectMany(t => t.QCChallengeList).Where(c => c.IsClosed).Count(), QCQuestion_IngCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .SelectMany(t => t.QCChallengeList).Where(c => c.IsClosed == false).Count(), CheckQuestion_ClosedCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.CheckChallengeState == CheckChanllengeTypeEnum.Closed).Count(), CheckQuestion_IngCount = t.SubjectVisitList.Where(t => inQuery.TrialSiteId != null ? t.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.CheckChallengeState != CheckChanllengeTypeEnum.Closed).Count(), MedicalReviewQuestion_ClosedCount = t.TaskMedicalReviewList.Where(t => inQuery.TrialSiteId != null ? t.VisitTask.Subject.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.VisitTask.IsAnalysisCreate == false && t.IsClosedDialog).Count(), MedicalReviewQuestion_IngCount = t.TaskMedicalReviewList.Where(t => inQuery.TrialSiteId != null ? t.VisitTask.Subject.TrialSiteId == inQuery.TrialSiteId : true) .Where(t => t.VisitTask.IsAnalysisCreate == false && t.IsClosedDialog == false).Count(), }).FirstOrDefaultAsync(); return result; } /// /// 疗效统计表,只针对肿瘤标准 排除基线 /// /// /// [HttpPost] public async Task>> GetTrialEfficacyEvaluationStatList(EfficacyEvaluationQuery inQuery) { var trialReadingCriterionId = inQuery.TrialReadingCriterionId; //每次查询必须是单标准的 var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == trialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionGroup, t.IsGlobalReading, t.IsArbitrationReading, t.IsOncologyReading, t.CriterionName, t.ArbitrationRule, t.TrialId }).FirstNotNullAsync(); if (criterion.CriterionGroup == CriterionGroup.Nontumorous) { return ResponseOutput.Ok(new List()); } var questionType = QuestionType.Tumor; if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1 || criterion.CriterionType == CriterionType.mRECISTHCC) { questionType = QuestionType.Tumor; //iresist pfs 是icpd } else if (criterion.CriterionType == CriterionType.Lugano2014) { questionType = QuestionType.ImgOncology; } else if (criterion.CriterionType == CriterionType.Lugano2014WithoutPET) { questionType = QuestionType.CTandMRI; } else if (criterion.CriterionType == CriterionType.PCWG3) { } var query = _visitTaskRepository.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId) .Where(t => t.TrialId == inQuery.TrialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == false && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze)) //访视和全局查询已签名完成的,裁判可以是未签名,未完成的 .Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge) .Select(t => new EfficacyEvaluationExport() { Id = t.Id, SubjectId = t.SubjectId, SubjectCode = t.Subject.Code, VisitTaskNum = t.VisitTaskNum, TaskName = t.TaskName, TaskBlindName = t.TaskBlindName, ReadingTaskState = t.ReadingTaskState, ReadingCategory = t.ReadingCategory, ArmEnum = t.ArmEnum, JudgeArmEnum = t.JudgeResultTask.ArmEnum, LatestScanDate = t.SourceSubjectVisit.LatestScanDate, EarliestScanDate = t.SourceSubjectVisit.EarliestScanDate, IsTrigerJudge = criterion.ArbitrationRule == ArbitrationRule.Visit ? t.JudgeVisitTaskId != null : (criterion.ArbitrationRule == ArbitrationRule.Reading ? t.Subject.SubjectVisitTaskList.Any(c => c.TaskState == TaskState.Effect && c.IsAnalysisCreate == false && c.ReadingCategory == ReadingCategory.Judge && c.TrialReadingCriterionId == trialReadingCriterionId && t.VisitTaskNum < c.VisitTaskNum) : false), JudgeNote = t.ReadingCategory == ReadingCategory.Judge ? t.JudgeResultRemark : "", SubjectCriterionReadingPeriodVisitNumList = t.Subject.ReadModuleList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.ReadingSetType == ReadingSetType.ImageReading).Select(c => c.SubjectVisit.VisitNum).ToList(), DictionaryCode = t.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == questionType).Select(t => t.ReadingQuestionTrial.DictionaryCode).FirstOrDefault(), //整体肿瘤评估答案 OverallTumorEvaluation = t.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == questionType).Select(t => t.IsGlobalChange ? t.GlobalChangeAnswer : t.Answer).FirstOrDefault() }); var list = await query.ToListAsync(); var dicName = await _readingQuestionTrialRepository.Where(t => t.TrialId == criterion.TrialId) .Where(t => t.QuestionType == questionType).Select(t => t.DictionaryCode).FirstOrDefaultAsync(); if (criterion.IsArbitrationReading) { //找到只有一个人阅片的受试者 和访视 var exceptVisit = list.Where(t => t.ReadingCategory == ReadingCategory.Visit) .GroupBy(t => new { t.SubjectCode, t.TaskName }).Where(g => g.Count() == 1).Select(g => new { g.Key.SubjectCode, g.Key.TaskName }).ToList(); list = list.Where(t => !exceptVisit.Any(ev => ev.SubjectCode == t.SubjectCode && ev.TaskName == t.TaskName)).ToList(); } list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList(); //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, criterion.IsGlobalReading, list); //基线(最晚拍片日期)-首次PD(所有触发PD的病灶的检查的最早拍片日期)的中位数,单位是天 // 构建字典:SubjectCode -> LatestScanDate var baseLineDict = list.Where(t => t.VisitTaskNum == 0).GroupBy(t => t.SubjectCode) .ToDictionary( g => g.Key, g => g.First().LatestScanDate // 如果同一个 SubjectCode 有多条记录,只取第一条 ); //一定要两个人做完了,产生了裁判并且有结果的,否则就不算,并且排除基线 list = list.Where(t => t.IsJudgeSelect == true && t.VisitTaskNum != 0 && t.ReadingCategory==ReadingCategory.Visit)//全局的答案已经放在对应访视上了 .GroupBy(t => t.SubjectCode).Select(g => g.OrderByDescending(t => t.VisitTaskNum).First()).ToList(); //总subject 数量 var totalSubjectCount = list.Select(t => t.SubjectId).Distinct().Count(); //判断subject的逻辑需要确认,这么多次访视任务,有的符合,有的不符合,准则是什么? var resultList = list.GroupBy(t => t.OverallTumorEvaluation).Select(g => new EfficacyEvaluationStatViewModel() { DictionaryCode = g.FirstOrDefault()?.DictionaryCode, OverallTumorEvaluation = g.Key, Code = g.Key, SubjectCodeList = g.Select(t => t.SubjectCode).Distinct().ToList() }).ToList(); // 先把 resultList 转成内存字典 var resultDict = resultList.ToDictionary(r => r.OverallTumorEvaluation, r => r.SubjectCodeList); // 查询字典表,先把数据拉出来到内存,再合并 var translateList = _dictionaryRepository .Where(t => t.Parent.Code == dicName) .Select(t => new { t.Code, t.Value, }) .ToList() .Select(t => new EfficacyEvaluationStatViewModel { DictionaryCode = dicName, Code = t.Code, //方便找到首次PD的日期 OverallTumorEvaluation = t.Value,//翻译后的值 SubjectCodeList = resultDict.ContainsKey(t.Code) ? resultDict[t.Code] : new List() }) .ToList(); // (cr +pr) /总人数 var crAddPr = translateList.Where(t => t.OverallTumorEvaluation == "PR" || t.OverallTumorEvaluation == "CR").Sum(t => t.SubjectCount); var orrPercent = totalSubjectCount > 0 ? ((decimal)crAddPr / totalSubjectCount * 100).ToString("0.00") + "%" : "0.00%"; var pdInfo = translateList .WhereIf(criterion.CriterionType == CriterionType.IRECIST1Point1, t => t.OverallTumorEvaluation == "ICPD") .WhereIf(criterion.CriterionType == CriterionType.IRECIST1Point1, t => t.OverallTumorEvaluation == "PD") .FirstOrDefault(); var firstPdList = new List(); if (pdInfo != null) { firstPdList = list.Where(t => t.OverallTumorEvaluation == pdInfo.Code).GroupBy(t => t.SubjectCode) .Select(g => new FirstPdInfo { SubjectCode = g.Key, EarliestScanDate = g.Where(t => t.OverallTumorEvaluation == pdInfo.Code).OrderBy(t => t.VisitTaskNum).First().EarliestScanDate }) .ToList(); foreach (var item in firstPdList) { if (baseLineDict.TryGetValue(item.SubjectCode, out var latestScanDate)) { item.BaseLineLatestScanDate = latestScanDate; } } } return ResponseOutput.Ok(translateList, new { PDList = firstPdList, ORR = orrPercent }); } public List DealJudgeMark(ArbitrationRule arbitrationRule, bool isGlobalReading, IEnumerable list) where T : EfficacyEvaluationExport { //处理访视任务的裁判标记 var resultExceptJudgeList = list.Where(t => t.ReadingCategory != ReadingCategory.Judge).ToList(); var judegeList = list.Where(t => t.ReadingCategory == ReadingCategory.Judge).ToList(); if (arbitrationRule == ArbitrationRule.Visit) { foreach (var item in resultExceptJudgeList) { var findJudge = judegeList.FirstOrDefault(t => t.SubjectCode == item.SubjectCode && (t.VisitTaskNum - ReadingCommon.TaskNumDic[ReadingCategory.Judge]) == item.VisitTaskNum); if (findJudge != null) { if (findJudge.ReadingTaskState == ReadingTaskState.HaveSigned) { item.IsJudgeSelect = findJudge.JudgeArmEnum == item.ArmEnum ? true : false; item.JudgeNote = findJudge.JudgeArmEnum == item.ArmEnum ? findJudge.JudgeNote : string.Empty; } else { //默认也是null 其实不用赋值 item.IsJudgeSelect = null; } } else { //两个人都做了 if (resultExceptJudgeList.Where(t => t.VisitTaskNum == item.VisitTaskNum && t.SubjectCode == item.SubjectCode).Select(t => t.ArmEnum).Distinct().Count() == 2) { //如果没有产生裁判,默认选择R1 if (item.ArmEnum == Arm.DoubleReadingArm1) { item.IsJudgeSelect = true; } else { item.IsJudgeSelect = false; } } else { item.IsJudgeSelect = null; item.IsTrigerJudge = null; } } } } else if (arbitrationRule == ArbitrationRule.Reading) { //处理访视裁判标记 foreach (var visitItem in resultExceptJudgeList.Where(t => t.ReadingCategory == ReadingCategory.Visit)) { ////默认设置为false 只处理为true 和 空的情况 //visitItem.IsJudgeSelect = false; var subjectJudgeList = judegeList.Where(t => t.SubjectCode == visitItem.SubjectCode).ToList(); //阅片期访视号 var subjectReadingPeriondVisitNumList = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode).FirstOrDefault()?.SubjectCriterionReadingPeriodVisitNumList; //两个人完成最大得任务号(访视+全局) var subjectMaxFinishedTaskNum = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode) .GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max(); var addReadingPeriodNum = isGlobalReading ? ReadingCommon.TaskNumDic[ReadingCategory.Global] : 0; var finishedGlobalCount = 0; //没有配置阅片期 if (subjectReadingPeriondVisitNumList == null) { finishedGlobalCount = 0; } else { //已完成的全局数量 finishedGlobalCount = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode && subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum) /*&& t.ReadingCategory == ReadingCategory.Global*/) .Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct() .GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).Count(); } visitItem.IsJudgeSelect = null; visitItem.IsTrigerJudge = null; if (finishedGlobalCount != 0) { //最大的全局是否产生裁判 var subjectMaxFinishedGlobalTaskNum = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode && subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum) /*&& t.ReadingCategory == ReadingCategory.Global*/) .Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct() .GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max(); //最大的完成的全局是否产生裁判 if (subjectJudgeList.Any(t => t.VisitTaskNum == (subjectMaxFinishedGlobalTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge]))) { var maxJudge = subjectJudgeList.FirstOrDefault(t => t.VisitTaskNum == (subjectMaxFinishedGlobalTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge])); //最大裁判完成了 if (maxJudge.ReadingTaskState == ReadingTaskState.HaveSigned) { if (visitItem.VisitTaskNum < maxJudge.VisitTaskNum) { //触发裁判 visitItem.IsTrigerJudge = true; if (visitItem.ArmEnum == maxJudge.JudgeArmEnum) { visitItem.IsJudgeSelect = true; visitItem.JudgeNote = maxJudge.JudgeNote; } //裁判没选择的人设置为false else { visitItem.IsJudgeSelect = false; } } else { //后续访视 未知 默认都是null } } else { //找到当前未阅最大裁判之前的已完成的最大裁判任务 var maxFinishedJudge = subjectJudgeList.Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault(); if (maxFinishedJudge == null) { // 为空 裁判选择标记默认就是null 不用处理 if (visitItem.VisitTaskNum < maxJudge.VisitTaskNum) { visitItem.IsTrigerJudge = true; } } else { if (visitItem.VisitTaskNum < maxFinishedJudge.VisitTaskNum) { visitItem.IsTrigerJudge = true; if (visitItem.ArmEnum == maxFinishedJudge.JudgeArmEnum) { visitItem.IsJudgeSelect = true; visitItem.JudgeNote = maxFinishedJudge.JudgeNote; } //裁判没选择的人设置为false else { visitItem.IsJudgeSelect = false; } } else if (visitItem.VisitTaskNum > maxFinishedJudge.VisitTaskNum && visitItem.VisitTaskNum < maxJudge.VisitTaskNum) { //完成裁判 和未完成裁判之间的 裁判选择标记默认是null visitItem.IsTrigerJudge = true; } else { //在未完成全局裁判之后的访视 未知 默认都是null } } } } else { //最大的完成的全局未产生裁判 if (visitItem.VisitTaskNum <= subjectMaxFinishedGlobalTaskNum) { visitItem.IsTrigerJudge = false; if (visitItem.ArmEnum == Arm.DoubleReadingArm1) { visitItem.IsJudgeSelect = true; } else { visitItem.IsJudgeSelect = false; } } else { //未产生裁判的全局之后的访视 两个标记都是null (后续可能还有全局,但是全局两个人没做完) } } } } } else { foreach (var visitItem in resultExceptJudgeList.Where(t => t.ReadingCategory == ReadingCategory.Visit)) { visitItem.IsJudgeSelect = null; visitItem.IsTrigerJudge = null; } } return resultExceptJudgeList; } }