irc-netcore-api/IRaCIS.Core.Application/Service/ReadingCalculate/General/ReadingCalculateService.cs

771 lines
30 KiB
C#

using DocumentFormat.OpenXml.Office.SpreadSheetML.Y2023.MsForms;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using Org.BouncyCastle.Asn1.X509;
using System.Data;
using System.IO;
namespace IRaCIS.Core.Application.Service.ReadingCalculate
{
[ApiExplorerSettings(GroupName = "Image")]
public class ReadingCalculateService(IEnumerable<ICriterionCalculateService> _criterionServices,
IRepository<VisitTask> _visitTaskRepository,
IRepository<ReadingImportFile> _readingImportFileRepository,
IHttpContextAccessor httpContext,
IOSSService oSSService,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IStringLocalizer _localizer, IUserInfo _userInfo
) : BaseService, IReadingCalculateService
{
private ICriterionCalculateService _useCriterion;
/// <summary>
/// 标准和服务对应
/// </summary>
Dictionary<CriterionType, Type> CalculateServiceDic = new Dictionary<CriterionType, Type>()
{
{CriterionType.RECIST1Point1,typeof(RECIST1Point1CalculateService) }, //RECIST1.1
{CriterionType.PCWG3,typeof(PCWG3CalculateService) },
{CriterionType.SelfDefine,typeof(SelfDefineCalculateService) },
{CriterionType.RECIST1Pointt1_MB,typeof(RECIST1Point1_BMCalculateService) },
{CriterionType.IRECIST1Point1,typeof(IRECIST1Point1CalculateService) },
{CriterionType.Lugano2014,typeof(LuganoCalculateService) },
{CriterionType.Lugano2014WithoutPET,typeof(LuganoWithoutPETCalculateService) },
{CriterionType.IVUS,typeof(IVUSCalculateService) },
{CriterionType.OCT,typeof(OCTCalculateService) },
{CriterionType.MRIPDFF,typeof(MRIPDFFCalculateService) },
{CriterionType.mRECISTHCC,typeof(MRECISTHCCCalculateService) },
};
/// <summary>
/// 阅片导入
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task ReadingImport()
{
var request = httpContext.HttpContext!.Request;
var file = request.Form.Files[0];
Guid visitTaskId = Guid.Parse(request.Form["VisitTaskId"]);
string tableName = request.Form["TableName"].ToString();
var service = await this.GetService(visitTaskId);
if (service != null)
{
var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync();
var streamCopy = new MemoryStream();
// 将上传的文件内容复制到 MemoryStream
await file.CopyToAsync(streamCopy);
// 重置流的位置,以便后续读取
streamCopy.Position = 0;
var ossRelativePath = await oSSService.UploadToOSSAsync(streamCopy, $"{visitTaskInfo.TrialId.ToString()}/InspectionUpload/ReadingImport", file.FileName);
await _readingImportFileRepository.AddAsync(new ReadingImportFile()
{
FilePath = ossRelativePath,
VisitTaskId = visitTaskId,
TrialId = visitTaskInfo.TrialId,
SubjectId = visitTaskInfo.SubjectId,
SubjectVisitId = visitTaskInfo.SourceSubjectVisitId,
TrialReadingCriterionId = visitTaskInfo.TrialReadingCriterionId,
TableName = tableName,
});
await service.ReadingImport();
}
}
/// <summary>
/// 获取Service
/// </summary>
/// <param name="visitTaskId"></param>
/// <returns></returns>
public async Task<ICriterionCalculateService> GetService(Guid visitTaskId)
{
if (_useCriterion == null)
{
var criterionId = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Select(x => x.TrialReadingCriterionId).FirstNotNullAsync();
var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == criterionId).Select(x => x.CriterionType).FirstOrDefaultAsync();
if (criterionType == null)
{
throw new BusinessValidationFailedException(_localizer["ReadingCalculate_NoDeveloped"]);
}
try
{
CriterionType thisCriterionType = criterionType;
Type thisServiceType = CalculateServiceDic[thisCriterionType];
_useCriterion = _criterionServices.FirstOrDefault(x => x.GetType().Name == thisServiceType.Name);
}
catch (Exception)
{
_useCriterion = null;
}
return _useCriterion;
}
else
{
return _useCriterion;
}
}
/// <summary>
/// 自动计算 并修改值
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
public async Task CalculateTask(CalculateTaskInDto inDto)
{
_userInfo.IsNotNeedInspection = true;
var service = await this.GetService(inDto.VisitTaskId);
if (service != null)
{
await service.CalculateTask(inDto);
}
}
/// <summary>
/// 验证访视提交
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
if (service != null)
{
await service.VerifyVisitTaskQuestions(inDto);
}
}
/// <summary>
/// 获取阅片的计算数据
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<object> GetReadingCalculationData(GetReadingCalculationDataInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
return await service.GetReadingCalculationData(inDto);
}
/// <summary>
/// 将上一次的访视病灶添加到这一次
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
public async Task<AddTaskLesionAnswerFromLastTaskOutDto> AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync();
var result = new AddTaskLesionAnswerFromLastTaskOutDto();
var readingTaskState = visitTaskInfo.ReadingTaskState;
if (service != null && visitTaskInfo.SourceSubjectVisitId != null)
{
if (readingTaskState == ReadingTaskState.WaitReading)
{
if (visitTaskInfo.ReadingCategory == ReadingCategory.Visit)
{
result = await service.AddTaskLesionAnswerFromLastTask(inDto);
await service.CalculateTask(new CalculateTaskInDto()
{
IsChangeOtherTask = false,
VisitTaskId = inDto.VisitTaskId,
ComputationTrigger = ComputationTrigger.InitialCalculation,
});
}
}
}
if (readingTaskState == ReadingTaskState.WaitReading)
{
await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask()
{
ReadingTaskState = ReadingTaskState.Reading,
}, true);
}
return result;
}
/// <summary>
/// 获取报告验证的信息(这里每个标准可能不一样 返回用object)
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
public async Task<GetReportVerifyOutDto> GetReportVerify(GetReportVerifyInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
if (service != null)
{
return await service.GetReportVerify(inDto);
}
else
{
return new GetReportVerifyOutDto() { };
}
}
/// <summary>
/// 获取报告图表数据
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<GetReportsChartDataOutDto> GetReportsChartData(GetReportsChartDataInDto inDto)
{
var criterionId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.TrialReadingCriterionId).FirstNotNullAsync();
var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == criterionId).Select(x => x.CriterionType).FirstOrDefaultAsync();
var data = await GetReadingReportEvaluation(new GetReadingReportEvaluationInDto()
{
TrialId = inDto.TrialId,
VisitTaskId = inDto.VisitTaskId,
});
GetReportsChartDataOutDto result = new GetReportsChartDataOutDto()
{
VisitTaskNameList = data.VisitTaskList.Select(x => x.BlindName).ToList(),
LatestScanDateList = data.VisitTaskList.Select(x => x.LatestScanDate).ToList(),
ChartDataList = new List<ReportChartData>() { },
};
result.VisitTaskNameList = data.VisitTaskList.Select(x => x.BlindName).ToList();
if (inDto.ReportChartTypeEnum != null)
{
var reportData = await GetReportsChartTypeData(new GetReportsChartTypeDataInDto()
{
CriterionType = criterionType,
Data = data,
ReportChartTypeEnum = inDto.ReportChartTypeEnum.Value,
});
result.ChartDataList = reportData.ChartDataList;
result.Unit = reportData.Unit;
}
else if (inDto.QuestionId != null)
{
var question = data.TaskQuestions.SelectMany(x => x.Childrens)
.Where(x => x.QuestionId == inDto.QuestionId.Value).FirstOrDefault();
if (question != null)
{
result.Unit = question.Unit;
ReportChartData chartData = new ReportChartData()
{
Name = question.QuestionName,
Value = new List<string>(),
};
foreach (var answer in question.Answer)
{
chartData.Value.Add(answer.Answer);
}
result.ChartDataList.Add(chartData);
}
}
else
{
var lesion = data.TaskQuestions
// 问题 靶病灶
.SelectMany(x => x.Childrens)
// 病灶
.SelectMany(x => x.Childrens).Where(x => x.RowIndex == inDto.RowIndex)
// 表格问题
.SelectMany(x => x.Childrens).Where(x => x.TableQuestionId == inDto.TableQuestionId)
.FirstOrDefault();
if (lesion != null)
{
result.Unit = lesion.Unit;
ReportChartData chartData = new ReportChartData()
{
Name = lesion.QuestionName,
Value = new List<string>(),
};
foreach (var answer in lesion.Answer)
{
chartData.Value.Add(answer.Answer);
}
result.ChartDataList.Add(chartData);
}
}
return result;
}
private async Task<GetReportsChartDataOutDto> GetReportsChartTypeData(GetReportsChartTypeDataInDto inDto)
{
var visitTaskNameList = inDto.Data.VisitTaskList.Select(x => x.BlindName).ToList();
GetReportsChartDataOutDto result = new GetReportsChartDataOutDto() {
ChartDataList=new List<ReportChartData>() { },
};
switch (inDto.ReportChartTypeEnum)
{
case ReportChartType.Target:
{
// 这是病灶
var target = inDto.Data.TaskQuestions.SelectMany(x => x.Childrens)
.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.Childrens)
.ToList();
foreach (var item in target)
{
ReportChartData chartData = new ReportChartData()
{
Name = item.QuestionName,
Value = new List<string>(),
};
for (var i = 0; i < visitTaskNameList.Count; i++)
{
switch (inDto.CriterionType)
{
case CriterionType.RECIST1Point1:
case CriterionType.RECIST1Pointt1_MB:
case CriterionType.mRECISTHCC:
case CriterionType.IRECIST1Point1:
// 淋巴结的短径
if (item.Childrens.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer[i].Answer.EqEnum(ReadingYesOrNo.Yes)))
{
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Unit).FirstOrDefault();
}
else
{
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer[i].Answer).FirstOrDefault());
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Unit).FirstOrDefault();
}
break;
case CriterionType.Lugano2014:
case CriterionType.Lugano2014WithoutPET:
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Answer[i].Answer).FirstOrDefault());
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Unit).FirstOrDefault();
break;
}
}
result.ChartDataList.Add(chartData);
}
}
break;
case ReportChartType.BaseLineTarget:
// 这是病灶
var baseTarget = inDto.Data.TaskQuestions.SelectMany(x => x.Childrens)
.Where(x => x.LesionType == LesionType.BaselineLesions).SelectMany(x => x.Childrens)
.ToList();
foreach (var item in baseTarget)
{
ReportChartData chartData = new ReportChartData()
{
Name = item.QuestionName,
Value = new List<string>(),
};
for (var i = 0; i < result.VisitTaskNameList.Count; i++)
{
chartData.Value.Add(item.Childrens.Where(x => x.QuestionMark == QuestionMark.LesionNumber).Select(x => x.Answer[i].Answer).FirstOrDefault());
result.Unit = item.Childrens.Where(x => x.QuestionMark == QuestionMark.LesionNumber).Select(x => x.Unit).FirstOrDefault();
}
result.ChartDataList.Add(chartData);
}
break;
default:
break;
}
return result;
}
/// <summary>
/// 获取报告图表汇总
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<GetReportsChartSummaryOutDto> GetReportsChartSummary(GetReportsChartSummaryInDto inDto)
{
var criterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialCriterionId).FirstNotNullAsync();
var result= new GetReportsChartSummaryOutDto();
var r1Data = await GetData(new List<Arm>() { Arm.SingleReadingArm, Arm.DoubleReadingArm1 });
var r2Data = await GetData(new List<Arm>() { Arm.DoubleReadingArm2 });
var alldata = r1Data.VisitTaskList.Count() > r2Data.VisitTaskList.Count() ? r1Data : r2Data;
var visitTaskName = alldata.VisitTaskList.Select(x => x.BlindName).ToList();
var length = alldata.VisitTaskList.Count();
// -1转为空
List<QuestionType?> negativeToString = new List<QuestionType?>()
{
QuestionType.DaysBetween,
};
async Task<GetReadingReportEvaluationOutDto> GetData(List<Arm> arms)
{
var data = new GetReadingReportEvaluationOutDto() { };
var task = await _visitTaskRepository.Where(x =>
x.SubjectId == inDto.SubjectId
&& arms.Contains(x.ArmEnum)
&& x.ReadingCategory== ReadingCategory.Visit
&& x.ReadingTaskState == ReadingTaskState.HaveSigned
&& x.TaskState == TaskState.Effect
&& x.TrialReadingCriterionId==inDto.TrialCriterionId
).OrderByDescending(x => x.VisitTaskNum).FirstOrDefaultAsync();
if (task != null)
{
data = await GetReadingReportEvaluation(new GetReadingReportEvaluationInDto()
{
TrialId = task.TrialId,
VisitTaskId = task.Id,
});
}
return data;
}
EvaluationColumn BuildEvaluationTable(GetReadingReportEvaluationOutDto r1, GetReadingReportEvaluationOutDto r2)
{
EvaluationColumn result = new EvaluationColumn()
{
Evaluation = new List<List<EvaluationValue>>() { }
};
var visitAnswerType = QuestionType.Tumor;
if (criterion.CriterionType == CriterionType.PCWG3)
{
visitAnswerType = QuestionType.SiteVisitForTumorEvaluation;
}
result.Evaluation.Add(visitTaskName.Select(x=> new EvaluationValue() {
Value=x
}).ToList());
var r1baseLine= r1.TaskQuestions
.SelectMany(x => x.Childrens)
.Where(x => x.QuestionType == QuestionType.ExistDisease)
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
{
DictionaryCode = x.DictionaryCode,
Value = a.Answer
}))
.ToList();
var r1data = r1.TaskQuestions
.SelectMany(x => x.Childrens)
.Where(x => x.QuestionType == visitAnswerType)
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
{
DictionaryCode = x.DictionaryCode,
Value = a.Answer
}))
.ToList();
r1data = r1baseLine.Take(1).Concat(r1data.Skip(1)).ToList();
r1data= r1data.Concat(Enumerable.Repeat(new EvaluationValue() { Value = "" }, length))
.Take(length)
.ToList();
result.Evaluation.Add(r1data);
if (criterion.ReadingType == ReadingMethod.Double)
{
var r2baseLine = r2.TaskQuestions
.SelectMany(x => x.Childrens)
.Where(x => x.QuestionType == QuestionType.ExistDisease)
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
{
DictionaryCode = x.DictionaryCode,
Value = a.Answer
}))
.ToList();
var r2data = r2.TaskQuestions
.SelectMany(x => x.Childrens)
.Where(x => x.QuestionType == visitAnswerType)
.SelectMany(x => x.Answer.Select(a => new EvaluationValue
{
DictionaryCode = x.DictionaryCode,
Value = a.Answer
}))
.ToList();
r2data = r2baseLine.Take(1).Concat(r2data.Skip(1)).ToList();
r2data = r2data.Concat(Enumerable.Repeat(new EvaluationValue() { Value = "" }, length))
.Take(length)
.ToList();
result.Evaluation.Add(r2data);
}
return result;
}
List<ChartItem> BuildShowChartGroups(GetReadingReportEvaluationOutDto data, Arm arm)
{
var groups = new List<ChartItem>();
if (alldata.TaskQuestions == null || alldata.VisitTaskList == null) return groups;
foreach (var g in alldata.TaskQuestions)
{
var items = g.Childrens?.Where(c => c.ShowChartTypeEnum != ShowChartType.NotShow).ToList() ?? new List<ReadingReportDto>();
if (items.Count == 0) continue;
var chartList = new List<ReportChartData>();
foreach (var item in items)
{
var itemdata= data.TaskQuestions.SelectMany(x => x.Childrens)
.Where(x => x.QuestionId == item.QuestionId)
.FirstOrDefault();
var cd = new ReportChartData
{
Name = item?.QuestionName??string.Empty,
Value = itemdata?.Answer?.Select(a => a.Answer).ToList() ?? new List<string>()
};
if (negativeToString.Contains(item.QuestionType))
{
cd.Value.ForEach(item1 =>
{
item1 = item1 == "-1" ? string.Empty : item1;
});
}
chartList.Add(cd);
}
groups.Add(new ChartItem
{
Arm = arm,
ChartType = null,
GroupName = g.GroupName,
Unit = items.FirstOrDefault()?.Unit,
VisitTaskNameList = alldata.VisitTaskList.Select(x => x.BlindName).ToList(),
LatestScanDateList = alldata.VisitTaskList.Select(x => x.LatestScanDate).ToList(),
ChartDataList = chartList
});
}
return groups;
}
async Task<ChartItem> BuildTargetChart(GetReadingReportEvaluationOutDto data, Arm arm)
{
var item = new ChartItem
{
Arm = arm,
ChartType = ReportChartType.Target,
GroupName = _localizer["Tumor_TargetLesions"],
VisitTaskNameList = alldata.VisitTaskList?.Select(x => x.BlindName).ToList() ?? new List<string>(),
LatestScanDateList = alldata.VisitTaskList?.Select(x => x.LatestScanDate).ToList() ?? new List<DateTime?>(),
ChartDataList = new List<ReportChartData>(),
};
if (data.VisitTaskList != null && data.TaskQuestions != null)
{
var chart = await GetReportsChartTypeData(new GetReportsChartTypeDataInDto
{
CriterionType = criterion.CriterionType,
Data = data,
ReportChartTypeEnum = ReportChartType.Target
});
item.ChartDataList = chart.ChartDataList;
item.Unit = chart.Unit;
}
return item;
}
result.Evaluation = BuildEvaluationTable(r1Data, r2Data);
if (r1Data != null)
{
var r1Target = await BuildTargetChart(r1Data, Arm.DoubleReadingArm1);
result.TargetCharts.Add(r1Target);
var r1Groups = BuildShowChartGroups(r1Data, Arm.DoubleReadingArm1);
result.QuestionCharts.AddRange(r1Groups);
}
if (r2Data != null)
{
var r2Target = await BuildTargetChart(r2Data, Arm.DoubleReadingArm2);
result.TargetCharts.Add(r2Target);
var r2Groups = BuildShowChartGroups(r2Data, Arm.DoubleReadingArm2);
result.QuestionCharts.AddRange(r2Groups);
}
if (criterion.CriterionType == CriterionType.PCWG3)
{
result.TargetCharts = new List<ChartItem>() { };
}
return result;
}
/// <summary>
/// 获取阅片报告
/// </summary>
/// <returns></returns>
public async Task<GetReadingReportEvaluationOutDto> GetReadingReportEvaluation(GetReadingReportEvaluationInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
if (service != null)
{
var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync();
var result = await service.GetReadingReportEvaluation(inDto);
// 这里统一处理字典
Dictionary<Guid, List<CrterionDictionaryGroup>> dictionaryGroup = result.VisitTaskList.ToDictionary(x => x.VisitTaskId, x => x.CrterionDictionaryGroup);
result.TaskQuestions.ForEach(y =>
{
SetCrterionDictionaryGroup(y, dictionaryGroup);
});
// 处理标红 高亮
#region 当第N个 存在被评估为NE的单个靶病灶 评估为 为是 之后 N+1 之后的最低点访视 都标红
var index = result.TaskQuestions.SelectMany(x => x.Childrens).Where(x => x.QuestionType == QuestionType.NETarget).SelectMany(x => x.Answer).ToList().FindIndex(x => x.Answer == ExistOrNA.Exist.GetEnumInt());
if (index >= 0)
{
result.TaskQuestions.ForEach(x =>
{
x.Childrens.ForEach(y =>
{
if (y.QuestionType == QuestionType.LowVisit)
{
int idx = 0;
y.Answer.ForEach(z =>
{
if (idx > index)
{
z.IsHighlight = true;
}
idx++;
});
}
});
});
}
#endregion
return result;
}
else
{
return new GetReadingReportEvaluationOutDto();
}
}
public void SetCrterionDictionaryGroup(ReadingReportDto item, Dictionary<Guid, List<CrterionDictionaryGroup>> dictionaryGroup)
{
item.Answer.ForEach(z =>
{
try
{
z.CrterionDictionaryGroup = dictionaryGroup[z.VisitTaskId];
}
catch (Exception)
{
}
});
item.Childrens.ForEach(x =>
{
SetCrterionDictionaryGroup(x, dictionaryGroup);
});
}
/// <summary>
/// 删除病灶获取起始病灶序号
/// </summary>
/// <returns></returns>
public async Task<int> GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto)
{
var service = await this.GetService(inDto.VisitTaskId);
if (service != null)
{
return await service.GetDeleteLesionStatrIndex(inDto);
}
else
{
return 1;
}
}
}
}