质控下载dir 文件处理
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
695de70894
commit
d10152b720
|
@ -0,0 +1,134 @@
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Media;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper
|
||||||
|
{
|
||||||
|
|
||||||
|
public class StudyDIRInfo
|
||||||
|
{
|
||||||
|
// Study
|
||||||
|
public Guid DicomStudyId { get; set; }
|
||||||
|
|
||||||
|
public string PatientId { get; set; }
|
||||||
|
public string PatientName { get; set; }
|
||||||
|
public string PatientBirthDate { get; set; }
|
||||||
|
public string PatientSex { get; set; }
|
||||||
|
|
||||||
|
public string StudyInstanceUid { get; set; }
|
||||||
|
public string StudyId { get; set; }
|
||||||
|
public string DicomStudyDate { get; set; }
|
||||||
|
public string DicomStudyTime { get; set; }
|
||||||
|
public string AccessionNumber { get; set; }
|
||||||
|
public string StudyDescription { get; set; }
|
||||||
|
|
||||||
|
// Series
|
||||||
|
public string SeriesInstanceUid { get; set; }
|
||||||
|
public string Modality { get; set; }
|
||||||
|
public string DicomSeriesDate { get; set; }
|
||||||
|
public string DicomSeriesTime { get; set; }
|
||||||
|
public int SeriesNumber { get; set; }
|
||||||
|
public string SeriesDescription { get; set; }
|
||||||
|
|
||||||
|
// Instance
|
||||||
|
public Guid InstanceId { get; set; }
|
||||||
|
public string SopInstanceUid { get; set; }
|
||||||
|
public string SOPClassUID { get; set; }
|
||||||
|
public int InstanceNumber { get; set; }
|
||||||
|
public string MediaStorageSOPClassUID { get; set; }
|
||||||
|
public string MediaStorageSOPInstanceUID { get; set; }
|
||||||
|
public string TransferSytaxUID { get; set; }
|
||||||
|
}
|
||||||
|
public static class DicomDIRHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static async Task GenerateStudyDIRAndUploadAsync(List<StudyDIRInfo> list, string ossFolder, IOSSService _oSSService)
|
||||||
|
{
|
||||||
|
var mappings = new List<string>();
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
|
||||||
|
var dicomDir = new DicomDirectory();
|
||||||
|
|
||||||
|
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
|
||||||
|
{
|
||||||
|
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID);
|
||||||
|
var ts = DicomTransferSyntax.Query(dicomUid);
|
||||||
|
|
||||||
|
var dataset = new DicomDataset(ts)
|
||||||
|
{
|
||||||
|
{ DicomTag.PatientID, item.PatientId ?? string.Empty },
|
||||||
|
{ DicomTag.PatientName, item.PatientName ?? string.Empty },
|
||||||
|
{ DicomTag.PatientBirthDate, item.PatientBirthDate ?? string.Empty },
|
||||||
|
{ DicomTag.PatientSex, item.PatientSex ?? string.Empty },
|
||||||
|
|
||||||
|
{ DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty },
|
||||||
|
{ DicomTag.StudyID, item.StudyId ?? string.Empty },
|
||||||
|
{ DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty },
|
||||||
|
{ DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty },
|
||||||
|
{ DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty },
|
||||||
|
{ DicomTag.StudyDescription, item.StudyDescription ?? string.Empty },
|
||||||
|
|
||||||
|
{ DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty },
|
||||||
|
{ DicomTag.Modality, item.Modality ?? string.Empty },
|
||||||
|
{ DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty },
|
||||||
|
{ DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty },
|
||||||
|
{ DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty },
|
||||||
|
{ DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty },
|
||||||
|
|
||||||
|
{ DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty },
|
||||||
|
{ DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty },
|
||||||
|
{ DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty },
|
||||||
|
{ DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty },
|
||||||
|
{ DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty },
|
||||||
|
{ DicomTag.TransferSyntaxUID, item.TransferSytaxUID ?? string.Empty },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dicomFile = new DicomFile(dataset);
|
||||||
|
|
||||||
|
// 文件名递增格式:IM_00001, IM_00002, ...
|
||||||
|
string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
|
||||||
|
|
||||||
|
mappings.Add($"{filename} => {item.InstanceId}");
|
||||||
|
|
||||||
|
dicomDir.AddFile(dicomFile, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 写入临时路径
|
||||||
|
var tempFilePath = Path.GetTempFileName();
|
||||||
|
|
||||||
|
// 保存 DICOMDIR 到临时文件 不能直接写入到流种
|
||||||
|
await dicomDir.SaveAsync(tempFilePath);
|
||||||
|
|
||||||
|
using (var memoryStream = new MemoryStream(File.ReadAllBytes(tempFilePath)))
|
||||||
|
{
|
||||||
|
// 重置流位置
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//清理临时文件
|
||||||
|
File.Delete(tempFilePath);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 映射上传
|
||||||
|
|
||||||
|
// 将映射写入内存流
|
||||||
|
var mappingText = string.Join(Environment.NewLine, mappings);
|
||||||
|
await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText));
|
||||||
|
|
||||||
|
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using DocumentFormat.OpenXml.EMMA;
|
using DocumentFormat.OpenXml.EMMA;
|
||||||
using FellowOakDicom;
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Media;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
|
@ -14,9 +15,11 @@ using MassTransit;
|
||||||
using MassTransit.Initializers;
|
using MassTransit.Initializers;
|
||||||
using Medallion.Threading;
|
using Medallion.Threading;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NetTopologySuite.Mathematics;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NPOI.SS.Formula.Functions;
|
using NPOI.SS.Formula.Functions;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
@ -32,7 +35,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
}
|
}
|
||||||
|
|
||||||
[ApiExplorerSettings(GroupName = "Trial")]
|
[ApiExplorerSettings(GroupName = "Trial")]
|
||||||
public class DownloadAndUploadService(IRepository<SystemAnonymization> _systemAnonymizationRepository,
|
public class DownloadAndUploadService(IRepository<SystemAnonymization> _systemAnonymizationRepository, IRepository<DicomStudy> _dicomStudyRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
IOSSService _oSSService,
|
IOSSService _oSSService,
|
||||||
|
@ -779,6 +782,62 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
var imageType = (isQueryDicom && isQueryNoneDicom) ? ImageType.DicomAndNoneDicom : (isQueryDicom ? ImageType.Dicom : ImageType.NoneDicom);
|
var imageType = (isQueryDicom && isQueryNoneDicom) ? ImageType.DicomAndNoneDicom : (isQueryDicom ? ImageType.Dicom : ImageType.NoneDicom);
|
||||||
|
|
||||||
|
#region 处理导出文件名,并将对应关系上传到OSS里面存储
|
||||||
|
|
||||||
|
|
||||||
|
var list = _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).SelectMany(t => t.StudyList)
|
||||||
|
.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||||
|
.SelectMany(t => t.InstanceList)
|
||||||
|
.Select(t => new StudyDIRInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
DicomStudyId = t.DicomStudy.Id,
|
||||||
|
|
||||||
|
PatientId = t.DicomStudy.PatientId,
|
||||||
|
PatientName = t.DicomStudy.PatientName,
|
||||||
|
PatientBirthDate = t.DicomStudy.PatientBirthDate,
|
||||||
|
PatientSex = t.DicomStudy.PatientSex,
|
||||||
|
|
||||||
|
StudyInstanceUid = t.StudyInstanceUid,
|
||||||
|
StudyId = t.DicomStudy.StudyId,
|
||||||
|
DicomStudyDate = t.DicomStudy.DicomStudyDate,
|
||||||
|
DicomStudyTime = t.DicomStudy.DicomStudyTime,
|
||||||
|
AccessionNumber = t.DicomStudy.AccessionNumber,
|
||||||
|
|
||||||
|
StudyDescription = t.DicomStudy.Description,
|
||||||
|
|
||||||
|
SeriesInstanceUid = t.DicomSerie.SeriesInstanceUid,
|
||||||
|
Modality = t.DicomSerie.Modality,
|
||||||
|
DicomSeriesDate = t.DicomSerie.DicomSeriesDate,
|
||||||
|
DicomSeriesTime = t.DicomSerie.DicomSeriesTime,
|
||||||
|
SeriesNumber = t.DicomSerie.SeriesNumber,
|
||||||
|
SeriesDescription = t.DicomSerie.Description,
|
||||||
|
|
||||||
|
InstanceId = t.Id,
|
||||||
|
SopInstanceUid = t.SopInstanceUid,
|
||||||
|
SOPClassUID = t.SOPClassUID,
|
||||||
|
InstanceNumber = t.InstanceNumber,
|
||||||
|
MediaStorageSOPClassUID = t.MediaStorageSOPClassUID,
|
||||||
|
MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID,
|
||||||
|
TransferSytaxUID = t.TransferSytaxUID,
|
||||||
|
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var pathInfo = await _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId).Select(t => new { t.TrialId, t.SubjectId, VisitId = t.Id }).FirstNotNullAsync();
|
||||||
|
|
||||||
|
foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
|
||||||
|
{
|
||||||
|
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{pathInfo.VisitId}/{item.Key.StudyInstanceUid}";
|
||||||
|
|
||||||
|
await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), ossFolder, _oSSService);
|
||||||
|
|
||||||
|
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
var query = from sv in _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId)
|
var query = from sv in _subjectVisitRepository.Where(t => t.Id == inQuery.SubjectVisitId)
|
||||||
|
|
||||||
|
|
||||||
|
@ -789,6 +848,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
SubjectCode = sv.Subject.Code,
|
SubjectCode = sv.Subject.Code,
|
||||||
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
||||||
VisitName = sv.VisitName,
|
VisitName = sv.VisitName,
|
||||||
|
VisitId = sv.Id,
|
||||||
|
|
||||||
StudyList = sv.StudyList.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
StudyList = sv.StudyList.Where(t => isQueryDicom ? inQuery.DicomStudyIdList.Contains(t.Id) : false)
|
||||||
|
|
||||||
|
@ -797,6 +857,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
u.PatientId,
|
u.PatientId,
|
||||||
u.StudyTime,
|
u.StudyTime,
|
||||||
u.StudyCode,
|
u.StudyCode,
|
||||||
|
u.StudyInstanceUid,
|
||||||
|
u.StudyDIRPath,
|
||||||
|
|
||||||
SeriesList = u.SeriesList.Select(z => new
|
SeriesList = u.SeriesList.Select(z => new
|
||||||
{
|
{
|
||||||
|
@ -831,6 +893,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
var result = query.FirstOrDefault();
|
var result = query.FirstOrDefault();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var preDownloadInfo = new TrialImageDownload()
|
var preDownloadInfo = new TrialImageDownload()
|
||||||
{
|
{
|
||||||
Id = NewId.NextSequentialGuid(),
|
Id = NewId.NextSequentialGuid(),
|
||||||
|
@ -1268,9 +1332,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
|
|
||||||
var totalImageSize = subjectImageList.Sum(t => t.ImageSize);
|
var totalImageSize = subjectImageList.Sum(t => t.ImageSize);
|
||||||
|
|
||||||
var totalReadingImageSize= subjectImageList.Sum(t => t.ReadingImageSize);
|
var totalReadingImageSize = subjectImageList.Sum(t => t.ReadingImageSize);
|
||||||
|
|
||||||
return ResponseOutput.Ok(new TrialImageStatInfo { SubjectCount = subjectCount, SubjectVisitCount = subjectVisitCount, TotalImageSize = totalImageSize, TotalReadingImageSize= totalReadingImageSize });
|
return ResponseOutput.Ok(new TrialImageStatInfo { SubjectCount = subjectCount, SubjectVisitCount = subjectVisitCount, TotalImageSize = totalImageSize, TotalReadingImageSize = totalReadingImageSize });
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,5 +108,8 @@ public class DicomStudy : BaseFullDeleteAuditEntity, IEntitySeqId
|
||||||
public string DicomStudyDate { get; set; }
|
public string DicomStudyDate { get; set; }
|
||||||
|
|
||||||
public string DicomStudyTime { get; set; }
|
public string DicomStudyTime { get; set; }
|
||||||
|
|
||||||
|
public string StudyDIRPath { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class dir2 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StudyDIRPath",
|
||||||
|
table: "DicomStudy",
|
||||||
|
type: "nvarchar(400)",
|
||||||
|
maxLength: 400,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StudyDIRPath",
|
||||||
|
table: "DicomStudy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1325,6 +1325,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
.HasMaxLength(400)
|
.HasMaxLength(400)
|
||||||
.HasColumnType("nvarchar(400)");
|
.HasColumnType("nvarchar(400)");
|
||||||
|
|
||||||
|
b.Property<string>("StudyDIRPath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(400)
|
||||||
|
.HasColumnType("nvarchar(400)");
|
||||||
|
|
||||||
b.Property<string>("StudyId")
|
b.Property<string>("StudyId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(400)
|
.HasMaxLength(400)
|
||||||
|
|
Loading…
Reference in New Issue