Uat_Study
he 2022-06-10 10:00:15 +08:00
parent f63bfe2947
commit db1c6bbe72
12 changed files with 160 additions and 97 deletions

View File

@ -399,20 +399,20 @@ namespace IRaCIS.Core.API.Controllers
/// <summary> /// <summary>
/// 上传阅片临床数据 /// 上传阅片临床数据
/// </summary> /// </summary>
/// <param name="readingClinicalDataId"></param> /// <param name="readingId"></param>
/// <returns></returns> /// <returns></returns>
[HttpPost("ClinicalData/UploadClinicalData/{trialId:guid}/{readingClinicalDataId:guid}")] [HttpPost("ClinicalData/UploadClinicalData/{trialId:guid}/{subjectId:guid}/{readingId:guid}")]
[DisableRequestSizeLimit] [DisableRequestSizeLimit]
public async Task<IResponseOutput> UploadReadClinicalData(Guid readingClinicalDataId) public async Task<IResponseOutput> UploadReadClinicalData(Guid trialId, Guid subjectId, Guid readingId)
{ {
var filerelativePath = string.Empty; var filerelativePath = string.Empty;
// 写法一致 使用 _repository // 写法一致 使用 _repository
var clinicalData = (await _repository.Where<ReadingClinicalData>(x => x.Id == readingClinicalDataId).FirstOrDefaultAsync()).IfNullThrowException();
var siteid =await _repository.Where<Subject>(x => x.Id == clinicalData.SubjectId).Select(x => x.SiteId).FirstOrDefaultAsync(); var siteid =await _repository.Where<Subject>(x => x.Id == subjectId).Select(x => x.SiteId).FirstOrDefaultAsync();
await FileUploadAsync(async (fileName) => await FileUploadAsync(async (fileName) =>
{ {
var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetReadClinicalDataPath(_hostEnvironment, fileName, clinicalData.TrialId, siteid, clinicalData.SubjectId, clinicalData.ObjectId, clinicalData.Id); var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetReadClinicalDataPath(_hostEnvironment, fileName, trialId, siteid, subjectId, readingId);
//插入临床pdf 路径 //插入临床pdf 路径
filerelativePath = relativePath; filerelativePath = relativePath;
return serverFilePath; return serverFilePath;

View File

@ -171,11 +171,11 @@
<param name="subjectVisitId"></param> <param name="subjectVisitId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadReadClinicalData(System.Guid)"> <member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadReadClinicalData(System.Guid,System.Guid,System.Guid)">
<summary> <summary>
上传阅片临床数据 上传阅片临床数据
</summary> </summary>
<param name="readingClinicalDataId"></param> <param name="readingId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(Microsoft.AspNetCore.Http.IFormCollection,System.Guid,System.Guid)"> <member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(Microsoft.AspNetCore.Http.IFormCollection,System.Guid,System.Guid)">

View File

@ -211,18 +211,18 @@ public static class FileStoreHelper
} }
public static (string PhysicalPath, string RelativePath, string FileRealName) GetReadClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid objectId,Guid readingClinicalDataId) public static (string PhysicalPath, string RelativePath, string FileRealName) GetReadClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid readingId)
{ {
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), "Reading", objectId.ToString(), readingClinicalDataId.ToString(), StaticData.Folder.TreatmenthistoryFolder); string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), "Reading", readingId.ToString(), StaticData.Folder.TreatmenthistoryFolder);
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/Reading/{objectId}/{readingClinicalDataId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/Reading/{readingId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}";
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);

View File

@ -955,9 +955,9 @@
受试者ID 受试者ID
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataDto.objectId"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataDto.ReadingId">
<summary> <summary>
对象ID 阅片ID
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataDto.ClinicalDataTrialSetId"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataDto.ClinicalDataTrialSetId">
@ -970,6 +970,21 @@
临床数据类型Id 临床数据类型Id
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataDto.FileList">
<summary>
文件列表
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.FileDto.FileName">
<summary>
文件名称
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.FileDto.Path">
<summary>
路径
</summary>
</member>
<member name="T:IRaCIS.Core.Application.Service.Reading.Dto.GetReadingClinicalDataListIndto"> <member name="T:IRaCIS.Core.Application.Service.Reading.Dto.GetReadingClinicalDataListIndto">
<summary> <summary>
获取访视列表 获取访视列表
@ -980,7 +995,7 @@
项目ID 项目ID
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetReadingClinicalDataPDFListOutDto.ObjectId"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetReadingClinicalDataPDFListOutDto.ReadingId">
<summary> <summary>
对象ID 对象ID
</summary> </summary>
@ -1015,7 +1030,7 @@
项目ID 项目ID
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataPDFDto.ObjectId"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataPDFDto.ReadingId">
<summary> <summary>
对象ID 对象ID
</summary> </summary>
@ -1080,6 +1095,26 @@
文件数量 文件数量
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectIndto.TrialId">
<summary>
项目ID
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectIndto.ReadingClinicalDataId">
<summary>
阅片期临床数据ID
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectIndto.ReadingId">
<summary>
对象ID
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectIndto.IsVisit">
<summary>
是否是访视
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectOutDto.ClinicalDataSetName"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetTrialClinicalDataSelectOutDto.ClinicalDataSetName">
<summary> <summary>
名称 名称
@ -3516,13 +3551,6 @@
<param name="inDto"></param> <param name="inDto"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Application.Services.ClinicalDataSetService.SetClinicalDataCheckd(IRaCIS.Core.Application.Service.Reading.Dto.SetClinicalDataCheckdIndto)">
<summary>
设置勾选
</summary>
<param name="setClinicalData"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.ClinicalDataSetService.AddTrialClinicalDataTrialSet(System.Guid)"> <member name="M:IRaCIS.Application.Services.ClinicalDataSetService.AddTrialClinicalDataTrialSet(System.Guid)">
<summary> <summary>
将系统配置添加到项目配置 将系统配置添加到项目配置
@ -3610,16 +3638,9 @@
<param name="inDto"></param> <param name="inDto"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Application.Services.ReadingClinicalDataService.AddOrUpdateReadingClinicalDataPDF(IRaCIS.Core.Application.Service.Reading.Dto.AddOrUpdateReadingClinicalDataPDFDto)">
<summary>
新增或者修改PDF
</summary>
<param name="indto"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.ReadingClinicalDataService.DeleteReadingClinicalDataPDF(System.Guid)"> <member name="M:IRaCIS.Application.Services.ReadingClinicalDataService.DeleteReadingClinicalDataPDF(System.Guid)">
<summary> <summary>
删除PDF 删除PDF单个文件
</summary> </summary>
<param name="id"></param> <param name="id"></param>
<returns></returns> <returns></returns>

View File

@ -100,35 +100,6 @@ namespace IRaCIS.Application.Services
} }
/// <summary>
/// 设置勾选
/// </summary>
/// <param name="setClinicalData"></param>
/// <returns></returns>
public async Task<IResponseOutput> SetClinicalDataCheckd(SetClinicalDataCheckdIndto setClinicalData)
{
// 为了后面稽查这样写
var nocheckids =await _clinicalDataTrialSetRepository.Where(x => x.TrialId == setClinicalData.TrialId && !setClinicalData.ClinicalDataTrialIds.Contains(x.Id)).Select(x => x.Id).ToListAsync();
foreach (var item in setClinicalData.ClinicalDataTrialIds)
{
await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet()
{
IsCheck = true
});
}
foreach (var item in nocheckids)
{
await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet()
{
IsCheck = false
});
}
await _clinicalDataTrialSetRepository.SaveChangesAsync();
return ResponseOutput.Ok(true);
}
/// <summary> /// <summary>
/// 将系统配置添加到项目配置 /// 将系统配置添加到项目配置

View File

@ -24,9 +24,9 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public Guid SubjectId { get; set; } public Guid SubjectId { get; set; }
/// <summary> /// <summary>
/// 对象ID /// 阅片ID
/// </summary> /// </summary>
public Guid objectId { get; set; } public Guid ReadingId { get; set; }
/// <summary> /// <summary>
/// 临床数据类型Id /// 临床数据类型Id
@ -37,15 +37,32 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
/// 临床数据类型Id /// 临床数据类型Id
/// </summary> /// </summary>
public bool IsVisit { get; set; } public bool IsVisit { get; set; }
/// <summary>
/// 文件列表
/// </summary>
public List<FileDto> FileList { get; set; }
} }
public class FileDto
{
/// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 路径
/// </summary>
public string Path { get; set; }
}
/// <summary> /// <summary>
/// 获取访视列表 /// 获取访视列表
/// </summary> /// </summary>
public class GetReadingClinicalDataListIndto :PageInput public class GetReadingClinicalDataListIndto :PageInput
{ {
public Guid ObjectId { get; set; } public Guid ReadingId { get; set; }
} }
public class GetReadingClinicalDataPDFListIndto:PageInput public class GetReadingClinicalDataPDFListIndto:PageInput
@ -65,7 +82,7 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
/// <summary> /// <summary>
/// 对象ID /// 对象ID
/// </summary> /// </summary>
public Guid ObjectId { get; set; } public Guid ReadingId { get; set; }
/// <summary> /// <summary>
/// 受试者ID /// 受试者ID
@ -108,7 +125,7 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
/// <summary> /// <summary>
/// 对象ID /// 对象ID
/// </summary> /// </summary>
public Guid ObjectId { get; set; } public Guid ReadingId { get; set; }
/// <summary> /// <summary>
/// 受试者ID /// 受试者ID
@ -185,7 +202,26 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public class GetTrialClinicalDataSelectIndto public class GetTrialClinicalDataSelectIndto
{ {
/// <summary>
/// 项目ID
/// </summary>
public Guid TrialId { get; set; } public Guid TrialId { get; set; }
/// <summary>
/// 阅片期临床数据ID
/// </summary>
public Guid? ReadingClinicalDataId { get; set; }
/// <summary>
/// 对象ID
/// </summary>
public Guid ReadingId { get; set; }
/// <summary>
/// 是否是访视
/// </summary>
public bool IsVisit { get; set; }
} }
public class GetTrialClinicalDataSelectOutDto public class GetTrialClinicalDataSelectOutDto

View File

@ -44,12 +44,49 @@ namespace IRaCIS.Application.Services
[HttpPost] [HttpPost]
public async Task<IResponseOutput> AddOrUpdateReadingClinicalData(AddOrUpdateReadingClinicalDataDto indto) public async Task<IResponseOutput> AddOrUpdateReadingClinicalData(AddOrUpdateReadingClinicalDataDto indto)
{ {
var entity = await _readingClinicalDataRepository.InsertOrUpdateAsync(indto,true);
return ResponseOutput.Ok(entity.Id.ToString());
if (indto.Id == null)
{
var entity = _mapper.Map<ReadingClinicalData>(indto);
entity.ReadingClinicalDataPDFList = indto.FileList.Select(x => new ReadingClinicalDataPDF()
{
TrialId = entity.TrialId,
SubjectId= indto.SubjectId,
FileName=x.FileName,
Path=x.Path,
IsVisit= indto.IsVisit,
ReadingId=indto.ReadingId
}).ToList();
await _readingClinicalDataRepository.AddAsync(entity, true);
return ResponseOutput.Ok(entity.Id);
}
else
{
var entity = (await _readingClinicalDataRepository.Where(t => t.Id == indto.Id, true).Include(t => t.ReadingClinicalDataPDFList).FirstOrDefaultAsync()).IfNullThrowException();
_mapper.Map(indto, entity);
entity.ReadingClinicalDataPDFList = indto.FileList.Select(x => new ReadingClinicalDataPDF()
{
TrialId = entity.TrialId,
SubjectId = indto.SubjectId,
FileName = x.FileName,
Path = x.Path,
IsVisit = indto.IsVisit,
ReadingId = indto.ReadingId
}).ToList();
var success = await _readingClinicalDataRepository.SaveChangesAsync();
return ResponseOutput.Ok(entity.Id);
} }
}
/// <summary> /// <summary>
/// 删除 /// 删除
@ -64,7 +101,6 @@ namespace IRaCIS.Application.Services
} }
/// <summary> /// <summary>
/// 获取下拉菜单 /// 获取下拉菜单
/// </summary> /// </summary>
@ -73,7 +109,12 @@ namespace IRaCIS.Application.Services
[HttpPost] [HttpPost]
public async Task<List<GetTrialClinicalDataSelectOutDto>> GetTrialClinicalDataSelect(GetTrialClinicalDataSelectIndto inDto) public async Task<List<GetTrialClinicalDataSelectOutDto>> GetTrialClinicalDataSelect(GetTrialClinicalDataSelectIndto inDto)
{ {
List<GetTrialClinicalDataSelectOutDto> clinicalList = await _clinicalDataTrialSetRepository.Select(x => new GetTrialClinicalDataSelectOutDto()
List<GetTrialClinicalDataSelectOutDto> clinicalList = await _clinicalDataTrialSetRepository
//.WhereIf(inDto.ReadingClinicalDataId==null, x=> _readingClinicalDataPDFRepository.Where(y=>y.ObjectId==inDto.ObjectId&&))
.Select(x => new GetTrialClinicalDataSelectOutDto()
{ {
ClinicalDataLevel = x.ClinicalDataLevel, ClinicalDataLevel = x.ClinicalDataLevel,
ClinicalDataSetName = x.ClinicalDataSetName, ClinicalDataSetName = x.ClinicalDataSetName,
@ -96,7 +137,7 @@ namespace IRaCIS.Application.Services
[HttpPost] [HttpPost]
public async Task<PageOutput<GetReadingClinicalDataListOutDto>> GetReadingClinicalDataList(GetReadingClinicalDataListIndto inDto) public async Task<PageOutput<GetReadingClinicalDataListOutDto>> GetReadingClinicalDataList(GetReadingClinicalDataListIndto inDto)
{ {
var result = await _readingClinicalDataRepository.Where(x => x.ObjectId == inDto.ObjectId).ProjectTo<GetReadingClinicalDataListOutDto>(_mapper.ConfigurationProvider).ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField == null ? nameof(GetReadingClinicalDataListOutDto.ClinicalDataSetName) : inDto.SortField, var result = await _readingClinicalDataRepository.ProjectTo<GetReadingClinicalDataListOutDto>(_mapper.ConfigurationProvider).ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField == null ? nameof(GetReadingClinicalDataListOutDto.ClinicalDataSetName) : inDto.SortField,
inDto.Asc); inDto.Asc);
result.CurrentPageData.ForEach(x => { result.CurrentPageData.ForEach(x => {
@ -123,21 +164,21 @@ namespace IRaCIS.Application.Services
return result; return result;
} }
/// <summary> ///// <summary>
/// 新增或者修改PDF ///// 新增或者修改PDF
/// </summary> ///// </summary>
/// <param name="indto"></param> ///// <param name="indto"></param>
/// <returns></returns> ///// <returns></returns>
[HttpPost] //[HttpPost]
public async Task<IResponseOutput> AddOrUpdateReadingClinicalDataPDF(AddOrUpdateReadingClinicalDataPDFDto indto) //public async Task<IResponseOutput> AddOrUpdateReadingClinicalDataPDF(AddOrUpdateReadingClinicalDataPDFDto indto)
{ //{
var entity = await _readingClinicalDataPDFRepository.InsertOrUpdateAsync(indto, true); // var entity = await _readingClinicalDataPDFRepository.InsertOrUpdateAsync(indto, true);
return ResponseOutput.Ok(entity.Id.ToString()); // return ResponseOutput.Ok(entity.Id.ToString());
} //}
/// <summary> /// <summary>
/// 删除PDF /// 删除PDF单个文件
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <returns></returns> /// <returns></returns>

View File

@ -167,7 +167,7 @@ namespace IRaCIS.Core.Application
{ {
await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet() await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet()
{ {
IsCheck= true,
IsConfirm = true IsConfirm = true
}); });
} }
@ -176,7 +176,7 @@ namespace IRaCIS.Core.Application
{ {
await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet() await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet()
{ {
IsCheck = false,
IsConfirm = false IsConfirm = false
}); });
} }

View File

@ -143,7 +143,7 @@ namespace IRaCIS.Core.Application.Service
CreateMap<AssginSiteCRCCommand, TrialSiteUser>(); CreateMap<AssginSiteCRCCommand, TrialSiteUser>();
CreateMap<Domain.Models.Trial, TrialConfigDTO>().ForMember(t => t.TrialId, u => u.MapFrom(c => c.Id)) CreateMap<Domain.Models.Trial, TrialConfigDTO>().ForMember(t => t.TrialId, u => u.MapFrom(c => c.Id))
.ForMember(t => t.ClinicalDataSetNames, u => u.MapFrom(c => c.clinicalDataTrialSets.Where(v => v.IsCheck&&v.IsConfirm).Select(r => r.ClinicalDataSetName))) .ForMember(t => t.ClinicalDataSetNames, u => u.MapFrom(c => c.clinicalDataTrialSets.Where(v => v.IsConfirm).Select(r => r.ClinicalDataSetName)))
.ForMember(t => t.CriterionIds, u => u.MapFrom(c => c.TrialDicList.Where(v => v.KeyName == StaticData.Criterion).Select(r => r.DictionaryId))); .ForMember(t => t.CriterionIds, u => u.MapFrom(c => c.TrialDicList.Where(v => v.KeyName == StaticData.Criterion).Select(r => r.DictionaryId)));
CreateMap<Domain.Models.Trial, TrialSubjectConfig>(); CreateMap<Domain.Models.Trial, TrialSubjectConfig>();
CreateMap<Domain.Models.Trial, TrialSubjectAndSVConfig>(); CreateMap<Domain.Models.Trial, TrialSubjectAndSVConfig>();

View File

@ -53,12 +53,6 @@ namespace IRaCIS.Core.Domain.Models
/// </summary> /// </summary>
public Guid CreateUserId { get; set; } public Guid CreateUserId { get; set; }
/// <summary>
/// 是否勾选
/// </summary>
public bool IsCheck { get; set; }
/// <summary> /// <summary>
/// 是否确认 /// 是否确认
/// </summary> /// </summary>

View File

@ -21,9 +21,9 @@ namespace IRaCIS.Core.Domain.Models
public Guid TrialId { get; set; } public Guid TrialId { get; set; }
/// <summary> /// <summary>
/// 对象ID /// 阅片ID
/// </summary> /// </summary>
public Guid ObjectId { get; set; } public Guid ReadingId { get; set; }
/// <summary> /// <summary>
/// 受试者ID /// 受试者ID

View File

@ -22,7 +22,7 @@ namespace IRaCIS.Core.Domain.Models
/// <summary> /// <summary>
/// 对象ID /// 对象ID
/// </summary> /// </summary>
public Guid ObjectId { get; set; } public Guid ReadingId { get; set; }
/// <summary> /// <summary>
/// 受试者ID /// 受试者ID