Mercurial > hg > orthanc
comparison OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp @ 5301:f26ed26a7793 am-experimental
merge
author | Alain Mazy <am@osimis.io> |
---|---|
date | Wed, 24 May 2023 08:56:41 +0200 |
parents | c04230962098 c78138dc3889 |
children |
comparison
equal
deleted
inserted
replaced
5300:7d913ee2f665 | 5301:f26ed26a7793 |
---|---|
2035 { | 2035 { |
2036 remainingAncestor_["RemainingAncestor"] = Json::Value(Json::objectValue); | 2036 remainingAncestor_["RemainingAncestor"] = Json::Value(Json::objectValue); |
2037 remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); | 2037 remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); |
2038 remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); | 2038 remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); |
2039 remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId; | 2039 remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId; |
2040 | |
2041 { // update the LastUpdate metadata of all parents | |
2042 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | |
2043 ResourcesContent content(true); | |
2044 | |
2045 int64_t parentId = 0; | |
2046 if (transaction.LookupResource(parentId, remainingLevel, remainingPublicId)) | |
2047 { | |
2048 | |
2049 do | |
2050 { | |
2051 content.AddMetadata(parentId, MetadataType_LastUpdate, now); | |
2052 } | |
2053 while (transaction.LookupParent(parentId, parentId)); | |
2054 | |
2055 transaction.SetResourcesContent(content); | |
2056 } | |
2057 } | |
2040 } | 2058 } |
2041 else | 2059 else |
2042 { | 2060 { |
2043 remainingAncestor_["RemainingAncestor"] = Json::nullValue; | 2061 remainingAncestor_["RemainingAncestor"] = Json::nullValue; |
2044 } | 2062 } |
3097 return storeStatus_; | 3115 return storeStatus_; |
3098 } | 3116 } |
3099 | 3117 |
3100 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE | 3118 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE |
3101 { | 3119 { |
3102 try | 3120 IDatabaseWrapper::CreateInstanceResult status; |
3103 { | 3121 int64_t instanceId; |
3104 IDatabaseWrapper::CreateInstanceResult status; | 3122 |
3105 int64_t instanceId; | 3123 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, |
3106 | 3124 hashStudy_, hashSeries_, hashInstance_); |
3107 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, | 3125 |
3108 hashStudy_, hashSeries_, hashInstance_); | 3126 if (isReconstruct_ && isNewInstance) |
3109 | 3127 { |
3110 if (isReconstruct_ && isNewInstance) | 3128 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex |
3111 { | 3129 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance |
3112 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex | 3130 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); |
3113 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance | 3131 } |
3114 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); | 3132 |
3115 } | 3133 // Check whether this instance is already stored |
3116 | 3134 if (!isNewInstance && !isReconstruct_) |
3117 // Check whether this instance is already stored | 3135 { |
3118 if (!isNewInstance && !isReconstruct_) | 3136 // The instance already exists |
3119 { | 3137 if (overwrite_) |
3120 // The instance already exists | 3138 { |
3121 if (overwrite_) | 3139 // Overwrite the old instance |
3140 LOG(INFO) << "Overwriting instance: " << hashInstance_; | |
3141 transaction.DeleteResource(instanceId); | |
3142 | |
3143 // Re-create the instance, now that the old one is removed | |
3144 if (!transaction.CreateInstance(status, instanceId, hashPatient_, | |
3145 hashStudy_, hashSeries_, hashInstance_)) | |
3122 { | 3146 { |
3123 // Overwrite the old instance | 3147 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); |
3124 LOG(INFO) << "Overwriting instance: " << hashInstance_; | |
3125 transaction.DeleteResource(instanceId); | |
3126 | |
3127 // Re-create the instance, now that the old one is removed | |
3128 if (!transaction.CreateInstance(status, instanceId, hashPatient_, | |
3129 hashStudy_, hashSeries_, hashInstance_)) | |
3130 { | |
3131 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen."); | |
3132 } | |
3133 } | 3148 } |
3134 else | 3149 } |
3150 else | |
3151 { | |
3152 // Do nothing if the instance already exists and overwriting is disabled | |
3153 transaction.GetAllMetadata(instanceMetadata_, instanceId); | |
3154 storeStatus_ = StoreStatus_AlreadyStored; | |
3155 return; | |
3156 } | |
3157 } | |
3158 | |
3159 | |
3160 if (!isReconstruct_) // don't signal new resources if this is a reconstruction | |
3161 { | |
3162 // Warn about the creation of new resources. The order must be | |
3163 // from instance to patient. | |
3164 | |
3165 // NB: In theory, could be sped up by grouping the underlying | |
3166 // calls to "transaction.LogChange()". However, this would only have an | |
3167 // impact when new patient/study/series get created, which | |
3168 // occurs far less often that creating new instances. The | |
3169 // positive impact looks marginal in practice. | |
3170 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); | |
3171 | |
3172 if (status.isNewSeries_) | |
3173 { | |
3174 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); | |
3175 } | |
3176 | |
3177 if (status.isNewStudy_) | |
3178 { | |
3179 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); | |
3180 } | |
3181 | |
3182 if (status.isNewPatient_) | |
3183 { | |
3184 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); | |
3185 } | |
3186 } | |
3187 | |
3188 // Ensure there is enough room in the storage for the new instance | |
3189 uint64_t instanceSize = 0; | |
3190 for (Attachments::const_iterator it = attachments_.begin(); | |
3191 it != attachments_.end(); ++it) | |
3192 { | |
3193 instanceSize += it->GetCompressedSize(); | |
3194 } | |
3195 | |
3196 if (!isReconstruct_) // reconstruction should not affect recycling | |
3197 { | |
3198 if (maximumStorageMode_ == MaxStorageMode_Reject) | |
3199 { | |
3200 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) | |
3135 { | 3201 { |
3136 // Do nothing if the instance already exists and overwriting is disabled | 3202 storeStatus_ = StoreStatus_StorageFull; |
3137 transaction.GetAllMetadata(instanceMetadata_, instanceId); | 3203 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction |
3138 storeStatus_ = StoreStatus_AlreadyStored; | |
3139 return; | |
3140 } | 3204 } |
3141 } | 3205 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) |
3142 | 3206 { |
3143 | 3207 storeStatus_ = StoreStatus_StorageFull; |
3144 if (!isReconstruct_) // don't signal new resources if this is a reconstruction | 3208 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction |
3145 { | 3209 } |
3146 // Warn about the creation of new resources. The order must be | 3210 } |
3147 // from instance to patient. | 3211 else |
3148 | 3212 { |
3149 // NB: In theory, could be sped up by grouping the underlying | 3213 transaction.Recycle(maximumStorageSize_, maximumPatientCount_, |
3150 // calls to "transaction.LogChange()". However, this would only have an | 3214 instanceSize, hashPatient_ /* don't consider the current patient for recycling */); |
3151 // impact when new patient/study/series get created, which | 3215 } |
3152 // occurs far less often that creating new instances. The | 3216 } |
3153 // positive impact looks marginal in practice. | 3217 |
3154 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); | 3218 // Attach the files to the newly created instance |
3219 for (Attachments::const_iterator it = attachments_.begin(); | |
3220 it != attachments_.end(); ++it) | |
3221 { | |
3222 if (isReconstruct_) | |
3223 { | |
3224 // we are replacing attachments during a reconstruction | |
3225 transaction.DeleteAttachment(instanceId, it->GetContentType()); | |
3226 } | |
3227 | |
3228 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); | |
3229 } | |
3230 | |
3231 if (!isReconstruct_) | |
3232 { | |
3233 ResourcesContent content(true /* new resource, metadata can be set */); | |
3234 | |
3235 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) | |
3236 for (MetadataMap::const_iterator | |
3237 it = metadata_.begin(); it != metadata_.end(); ++it) | |
3238 { | |
3239 switch (it->first.first) | |
3240 { | |
3241 case ResourceType_Patient: | |
3242 content.AddMetadata(status.patientId_, it->first.second, it->second); | |
3243 break; | |
3244 | |
3245 case ResourceType_Study: | |
3246 content.AddMetadata(status.studyId_, it->first.second, it->second); | |
3247 break; | |
3248 | |
3249 case ResourceType_Series: | |
3250 content.AddMetadata(status.seriesId_, it->first.second, it->second); | |
3251 break; | |
3252 | |
3253 case ResourceType_Instance: | |
3254 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3255 it->first.second, it->second); | |
3256 break; | |
3257 | |
3258 default: | |
3259 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
3260 } | |
3261 } | |
3262 | |
3263 if (!isReconstruct_) | |
3264 { | |
3265 // Populate the tags of the newly-created resources | |
3266 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); | |
3267 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 | |
3268 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 | |
3155 | 3269 |
3156 if (status.isNewSeries_) | 3270 if (status.isNewSeries_) |
3157 { | 3271 { |
3158 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); | 3272 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); |
3273 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 | |
3274 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 | |
3159 } | 3275 } |
3160 | 3276 |
3161 if (status.isNewStudy_) | 3277 if (status.isNewStudy_) |
3162 { | 3278 { |
3163 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); | 3279 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); |
3280 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 | |
3281 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 | |
3164 } | 3282 } |
3165 | 3283 |
3166 if (status.isNewPatient_) | 3284 if (status.isNewPatient_) |
3167 { | 3285 { |
3168 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); | 3286 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); |
3287 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 | |
3288 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 | |
3169 } | 3289 } |
3170 } | 3290 |
3291 // Attach the auto-computed metadata for the patient/study/series levels | |
3292 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | |
3293 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); | |
3294 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); | |
3295 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); | |
3296 | |
3297 if (status.isNewSeries_) | |
3298 { | |
3299 if (hasExpectedInstances_) | |
3300 { | |
3301 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, | |
3302 boost::lexical_cast<std::string>(expectedInstances_)); | |
3303 } | |
3304 | |
3305 // New in Orthanc 1.9.0 | |
3306 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, | |
3307 origin_.GetRemoteAetC()); | |
3308 } | |
3309 // Attach the auto-computed metadata for the instance level, | |
3310 // reflecting these additions into the input metadata map | |
3311 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3312 MetadataType_Instance_ReceptionDate, now); | |
3313 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, | |
3314 origin_.GetRemoteAetC()); | |
3315 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, | |
3316 EnumerationToString(origin_.GetRequestOrigin())); | |
3317 | |
3318 std::string s; | |
3319 | |
3320 if (origin_.LookupRemoteIp(s)) | |
3321 { | |
3322 // New in Orthanc 1.4.0 | |
3323 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3324 MetadataType_Instance_RemoteIp, s); | |
3325 } | |
3326 | |
3327 if (origin_.LookupCalledAet(s)) | |
3328 { | |
3329 // New in Orthanc 1.4.0 | |
3330 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3331 MetadataType_Instance_CalledAet, s); | |
3332 } | |
3333 | |
3334 if (origin_.LookupHttpUsername(s)) | |
3335 { | |
3336 // New in Orthanc 1.4.0 | |
3337 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3338 MetadataType_Instance_HttpUsername, s); | |
3339 } | |
3340 } | |
3341 | |
3342 // Following metadatas are also updated if reconstructing the instance. | |
3343 // They might be missing since they have been introduced along Orthanc versions. | |
3344 | |
3345 if (hasTransferSyntax_) | |
3346 { | |
3347 // New in Orthanc 1.2.0 | |
3348 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3349 MetadataType_Instance_TransferSyntax, | |
3350 GetTransferSyntaxUid(transferSyntax_)); | |
3351 } | |
3352 | |
3353 if (hasPixelDataOffset_) | |
3354 { | |
3355 // New in Orthanc 1.9.1 | |
3356 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3357 MetadataType_Instance_PixelDataOffset, | |
3358 boost::lexical_cast<std::string>(pixelDataOffset_)); | |
3359 } | |
3171 | 3360 |
3172 // Ensure there is enough room in the storage for the new instance | 3361 const DicomValue* value; |
3173 uint64_t instanceSize = 0; | 3362 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && |
3174 for (Attachments::const_iterator it = attachments_.begin(); | 3363 !value->IsNull() && |
3175 it != attachments_.end(); ++it) | 3364 !value->IsBinary()) |
3176 { | 3365 { |
3177 instanceSize += it->GetCompressedSize(); | 3366 SetInstanceMetadata(content, instanceMetadata_, instanceId, |
3178 } | 3367 MetadataType_Instance_SopClassUid, value->GetContent()); |
3179 | 3368 } |
3180 if (!isReconstruct_) // reconstruction should not affect recycling | 3369 |
3181 { | 3370 |
3182 if (maximumStorageMode_ == MaxStorageMode_Reject) | 3371 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || |
3183 { | 3372 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) |
3184 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) | 3373 { |
3185 { | 3374 if (!value->IsNull() && |
3186 storeStatus_ = StoreStatus_StorageFull; | |
3187 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction | |
3188 } | |
3189 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) | |
3190 { | |
3191 storeStatus_ = StoreStatus_StorageFull; | |
3192 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction | |
3193 } | |
3194 } | |
3195 else | |
3196 { | |
3197 transaction.Recycle(maximumStorageSize_, maximumPatientCount_, | |
3198 instanceSize, hashPatient_ /* don't consider the current patient for recycling */); | |
3199 } | |
3200 } | |
3201 | |
3202 // Attach the files to the newly created instance | |
3203 for (Attachments::const_iterator it = attachments_.begin(); | |
3204 it != attachments_.end(); ++it) | |
3205 { | |
3206 if (isReconstruct_) | |
3207 { | |
3208 // we are replacing attachments during a reconstruction | |
3209 transaction.DeleteAttachment(instanceId, it->GetContentType()); | |
3210 } | |
3211 | |
3212 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */); | |
3213 } | |
3214 | |
3215 if (!isReconstruct_) | |
3216 { | |
3217 ResourcesContent content(true /* new resource, metadata can be set */); | |
3218 | |
3219 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep) | |
3220 for (MetadataMap::const_iterator | |
3221 it = metadata_.begin(); it != metadata_.end(); ++it) | |
3222 { | |
3223 switch (it->first.first) | |
3224 { | |
3225 case ResourceType_Patient: | |
3226 content.AddMetadata(status.patientId_, it->first.second, it->second); | |
3227 break; | |
3228 | |
3229 case ResourceType_Study: | |
3230 content.AddMetadata(status.studyId_, it->first.second, it->second); | |
3231 break; | |
3232 | |
3233 case ResourceType_Series: | |
3234 content.AddMetadata(status.seriesId_, it->first.second, it->second); | |
3235 break; | |
3236 | |
3237 case ResourceType_Instance: | |
3238 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3239 it->first.second, it->second); | |
3240 break; | |
3241 | |
3242 default: | |
3243 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
3244 } | |
3245 } | |
3246 | |
3247 if (!isReconstruct_) | |
3248 { | |
3249 // Populate the tags of the newly-created resources | |
3250 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_); | |
3251 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0 | |
3252 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1 | |
3253 | |
3254 if (status.isNewSeries_) | |
3255 { | |
3256 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_); | |
3257 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0 | |
3258 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1 | |
3259 } | |
3260 | |
3261 if (status.isNewStudy_) | |
3262 { | |
3263 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_); | |
3264 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0 | |
3265 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1 | |
3266 } | |
3267 | |
3268 if (status.isNewPatient_) | |
3269 { | |
3270 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_); | |
3271 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0 | |
3272 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1 | |
3273 } | |
3274 | |
3275 // Attach the auto-computed metadata for the patient/study/series levels | |
3276 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | |
3277 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); | |
3278 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); | |
3279 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); | |
3280 | |
3281 if (status.isNewSeries_) | |
3282 { | |
3283 if (hasExpectedInstances_) | |
3284 { | |
3285 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, | |
3286 boost::lexical_cast<std::string>(expectedInstances_)); | |
3287 } | |
3288 | |
3289 // New in Orthanc 1.9.0 | |
3290 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet, | |
3291 origin_.GetRemoteAetC()); | |
3292 } | |
3293 // Attach the auto-computed metadata for the instance level, | |
3294 // reflecting these additions into the input metadata map | |
3295 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3296 MetadataType_Instance_ReceptionDate, now); | |
3297 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet, | |
3298 origin_.GetRemoteAetC()); | |
3299 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin, | |
3300 EnumerationToString(origin_.GetRequestOrigin())); | |
3301 | |
3302 std::string s; | |
3303 | |
3304 if (origin_.LookupRemoteIp(s)) | |
3305 { | |
3306 // New in Orthanc 1.4.0 | |
3307 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3308 MetadataType_Instance_RemoteIp, s); | |
3309 } | |
3310 | |
3311 if (origin_.LookupCalledAet(s)) | |
3312 { | |
3313 // New in Orthanc 1.4.0 | |
3314 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3315 MetadataType_Instance_CalledAet, s); | |
3316 } | |
3317 | |
3318 if (origin_.LookupHttpUsername(s)) | |
3319 { | |
3320 // New in Orthanc 1.4.0 | |
3321 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3322 MetadataType_Instance_HttpUsername, s); | |
3323 } | |
3324 } | |
3325 | |
3326 // Following metadatas are also updated if reconstructing the instance. | |
3327 // They might be missing since they have been introduced along Orthanc versions. | |
3328 | |
3329 if (hasTransferSyntax_) | |
3330 { | |
3331 // New in Orthanc 1.2.0 | |
3332 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3333 MetadataType_Instance_TransferSyntax, | |
3334 GetTransferSyntaxUid(transferSyntax_)); | |
3335 } | |
3336 | |
3337 if (hasPixelDataOffset_) | |
3338 { | |
3339 // New in Orthanc 1.9.1 | |
3340 SetInstanceMetadata(content, instanceMetadata_, instanceId, | |
3341 MetadataType_Instance_PixelDataOffset, | |
3342 boost::lexical_cast<std::string>(pixelDataOffset_)); | |
3343 } | |
3344 | |
3345 const DicomValue* value; | |
3346 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL && | |
3347 !value->IsNull() && | |
3348 !value->IsBinary()) | 3375 !value->IsBinary()) |
3349 { | 3376 { |
3350 SetInstanceMetadata(content, instanceMetadata_, instanceId, | 3377 SetInstanceMetadata(content, instanceMetadata_, instanceId, |
3351 MetadataType_Instance_SopClassUid, value->GetContent()); | 3378 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); |
3352 } | 3379 } |
3353 | 3380 } |
3354 | 3381 |
3355 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || | 3382 |
3356 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) | 3383 transaction.SetResourcesContent(content); |
3357 { | 3384 } |
3358 if (!value->IsNull() && | 3385 |
3359 !value->IsBinary()) | 3386 |
3360 { | 3387 // Check whether the series of this new instance is now completed |
3361 SetInstanceMetadata(content, instanceMetadata_, instanceId, | 3388 int64_t expectedNumberOfInstances; |
3362 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); | 3389 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) |
3363 } | 3390 { |
3364 } | 3391 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); |
3365 | 3392 if (seriesStatus == SeriesStatus_Complete) |
3393 { | |
3394 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); | |
3395 } | |
3396 } | |
3366 | 3397 |
3367 transaction.SetResourcesContent(content); | 3398 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); |
3368 } | 3399 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); |
3369 | 3400 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); |
3370 | 3401 |
3371 // Check whether the series of this new instance is now completed | 3402 // Mark the parent resources of this instance as unstable |
3372 int64_t expectedNumberOfInstances; | 3403 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); |
3373 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) | 3404 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); |
3374 { | 3405 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); |
3375 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); | 3406 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); |
3376 if (seriesStatus == SeriesStatus_Complete) | 3407 |
3377 { | 3408 storeStatus_ = StoreStatus_Success; |
3378 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_); | |
3379 } | |
3380 } | |
3381 | |
3382 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_); | |
3383 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_); | |
3384 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_); | |
3385 | |
3386 // Mark the parent resources of this instance as unstable | |
3387 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_); | |
3388 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_); | |
3389 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_); | |
3390 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize); | |
3391 | |
3392 storeStatus_ = StoreStatus_Success; | |
3393 } | |
3394 catch (OrthancException& e) | |
3395 { | |
3396 if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize) | |
3397 { | |
3398 throw; // the transaction has failed -> do not commit the current transaction (and retry) | |
3399 } | |
3400 else | |
3401 { | |
3402 LOG(ERROR) << "EXCEPTION [" << e.What() << " - " << e.GetDetails() << "]"; | |
3403 | |
3404 if (e.GetErrorCode() == ErrorCode_FullStorage) | |
3405 { | |
3406 throw; // do not commit the current transaction | |
3407 } | |
3408 | |
3409 // this is an expected failure, exit normaly and commit the current transaction | |
3410 storeStatus_ = StoreStatus_Failure; | |
3411 } | |
3412 } | |
3413 } | 3409 } |
3414 }; | 3410 }; |
3415 | 3411 |
3416 | 3412 |
3417 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, | 3413 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, |
3418 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, | 3414 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, |
3419 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); | 3415 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); |
3420 Apply(operations); | 3416 |
3421 return operations.GetStoreStatus(); | 3417 try |
3418 { | |
3419 Apply(operations); | |
3420 return operations.GetStoreStatus(); | |
3421 } | |
3422 catch (OrthancException& e) | |
3423 { | |
3424 if (e.GetErrorCode() == ErrorCode_FullStorage) | |
3425 { | |
3426 return StoreStatus_StorageFull; | |
3427 } | |
3428 else | |
3429 { | |
3430 // the transaction has failed -> do not commit the current transaction (and retry) | |
3431 throw; | |
3432 } | |
3433 } | |
3422 } | 3434 } |
3423 | 3435 |
3424 | 3436 |
3425 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision, | 3437 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision, |
3426 const FileInfo& attachment, | 3438 const FileInfo& attachment, |
3474 { | 3486 { |
3475 ResourceType resourceType; | 3487 ResourceType resourceType; |
3476 int64_t resourceId; | 3488 int64_t resourceId; |
3477 if (!transaction.LookupResource(resourceId, resourceType, publicId_)) | 3489 if (!transaction.LookupResource(resourceId, resourceType, publicId_)) |
3478 { | 3490 { |
3479 status_ = StoreStatus_Failure; // Inexistent resource | 3491 throw OrthancException(ErrorCode_InexistentItem, HttpStatus_404_NotFound); |
3480 } | 3492 } |
3481 else | 3493 else |
3482 { | 3494 { |
3483 // Possibly remove previous attachment | 3495 // Possibly remove previous attachment |
3484 { | 3496 { |