Mercurial > hg > orthanc
comparison OrthancServer/Sources/ServerContext.cpp @ 4940:304514ce84ee more-tags
tools/find + C-Find + list-resources now all using the same code (ExpandResource) to build 'computed tags'
author | Alain Mazy <am@osimis.io> |
---|---|
date | Tue, 15 Mar 2022 15:57:21 +0100 |
parents | e8a2e145c80e |
children | 96a3e81eba90 |
comparison
equal
deleted
inserted
replaced
4939:e8a2e145c80e | 4940:304514ce84ee |
---|---|
2254 } | 2254 } |
2255 | 2255 |
2256 } | 2256 } |
2257 | 2257 |
2258 | 2258 |
2259 static void ComputeSeriesTags(ExpandedResource& resource, | |
2260 ServerContext& context, | |
2261 const std::string& seriesPublicId, | |
2262 const std::set<DicomTag>& requestedTags) | |
2263 { | |
2264 if (requestedTags.count(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES) > 0) | |
2265 { | |
2266 ServerIndex& index = context.GetIndex(); | |
2267 std::list<std::string> instances; | |
2268 | |
2269 index.GetChildren(instances, seriesPublicId); | |
2270 | |
2271 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, | |
2272 boost::lexical_cast<std::string>(instances.size()), false); | |
2273 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); | |
2274 } | |
2275 } | |
2276 | |
2277 static void ComputeStudyTags(ExpandedResource& resource, | |
2278 ServerContext& context, | |
2279 const std::string& studyPublicId, | |
2280 const std::set<DicomTag>& requestedTags) | |
2281 { | |
2282 ServerIndex& index = context.GetIndex(); | |
2283 std::list<std::string> series; | |
2284 std::list<std::string> instances; | |
2285 | |
2286 bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES) > 0; | |
2287 bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) > 0; | |
2288 bool hasModalitiesInStudy = requestedTags.count(DICOM_TAG_MODALITIES_IN_STUDY) > 0; | |
2289 bool hasSopClassesInStudy = requestedTags.count(DICOM_TAG_SOP_CLASSES_IN_STUDY) > 0; | |
2290 | |
2291 index.GetChildren(series, studyPublicId); | |
2292 | |
2293 if (hasModalitiesInStudy) | |
2294 { | |
2295 std::set<std::string> values; | |
2296 | |
2297 for (std::list<std::string>::const_iterator | |
2298 it = series.begin(); it != series.end(); ++it) | |
2299 { | |
2300 DicomMap tags; | |
2301 index.GetMainDicomTags(tags, *it, ResourceType_Series, ResourceType_Series); | |
2302 | |
2303 const DicomValue* value = tags.TestAndGetValue(DICOM_TAG_MODALITY); | |
2304 | |
2305 if (value != NULL && | |
2306 !value->IsNull() && | |
2307 !value->IsBinary()) | |
2308 { | |
2309 values.insert(value->GetContent()); | |
2310 } | |
2311 } | |
2312 | |
2313 std::string modalities; | |
2314 Toolbox::JoinStrings(modalities, values, "\\"); | |
2315 | |
2316 resource.tags_.SetValue(DICOM_TAG_MODALITIES_IN_STUDY, modalities, false); | |
2317 resource.missingRequestedTags_.erase(DICOM_TAG_MODALITIES_IN_STUDY); | |
2318 } | |
2319 | |
2320 if (hasNbRelatedSeries) | |
2321 { | |
2322 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, | |
2323 boost::lexical_cast<std::string>(series.size()), false); | |
2324 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES); | |
2325 } | |
2326 | |
2327 if (hasNbRelatedInstances || hasSopClassesInStudy) | |
2328 { | |
2329 for (std::list<std::string>::const_iterator | |
2330 it = series.begin(); it != series.end(); ++it) | |
2331 { | |
2332 std::list<std::string> seriesInstancesIds; | |
2333 index.GetChildren(seriesInstancesIds, *it); | |
2334 | |
2335 instances.splice(instances.end(), seriesInstancesIds); | |
2336 } | |
2337 | |
2338 if (hasNbRelatedInstances) | |
2339 { | |
2340 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, | |
2341 boost::lexical_cast<std::string>(instances.size()), false); | |
2342 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); | |
2343 } | |
2344 | |
2345 if (hasSopClassesInStudy) | |
2346 { | |
2347 std::set<std::string> values; | |
2348 | |
2349 for (std::list<std::string>::const_iterator | |
2350 it = instances.begin(); it != instances.end(); ++it) | |
2351 { | |
2352 std::string value; | |
2353 | |
2354 if (context.LookupOrReconstructMetadata(value, *it, ResourceType_Instance, MetadataType_Instance_SopClassUid)) | |
2355 { | |
2356 values.insert(value); | |
2357 } | |
2358 } | |
2359 | |
2360 if (values.size() > 0) | |
2361 { | |
2362 std::string sopClassUids; | |
2363 Toolbox::JoinStrings(sopClassUids, values, "\\"); | |
2364 resource.tags_.SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, sopClassUids, false); | |
2365 } | |
2366 | |
2367 resource.missingRequestedTags_.erase(DICOM_TAG_SOP_CLASSES_IN_STUDY); | |
2368 } | |
2369 } | |
2370 } | |
2371 | |
2372 static void ComputePatientTags(ExpandedResource& resource, | |
2373 ServerContext& context, | |
2374 const std::string& patientPublicId, | |
2375 const std::set<DicomTag>& requestedTags) | |
2376 { | |
2377 ServerIndex& index = context.GetIndex(); | |
2378 | |
2379 std::list<std::string> studies; | |
2380 std::list<std::string> series; | |
2381 std::list<std::string> instances; | |
2382 | |
2383 bool hasNbRelatedStudies = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES) > 0; | |
2384 bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) > 0; | |
2385 bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES) > 0; | |
2386 | |
2387 index.GetChildren(studies, patientPublicId); | |
2388 | |
2389 if (hasNbRelatedStudies) | |
2390 { | |
2391 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, | |
2392 boost::lexical_cast<std::string>(studies.size()), false); | |
2393 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); | |
2394 } | |
2395 | |
2396 if (hasNbRelatedSeries || hasNbRelatedInstances) | |
2397 { | |
2398 for (std::list<std::string>::const_iterator | |
2399 it = studies.begin(); it != studies.end(); ++it) | |
2400 { | |
2401 std::list<std::string> thisSeriesIds; | |
2402 index.GetChildren(thisSeriesIds, *it); | |
2403 series.splice(series.end(), thisSeriesIds); | |
2404 } | |
2405 | |
2406 if (hasNbRelatedSeries) | |
2407 { | |
2408 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, | |
2409 boost::lexical_cast<std::string>(series.size()), false); | |
2410 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES); | |
2411 } | |
2412 } | |
2413 | |
2414 if (hasNbRelatedInstances) | |
2415 { | |
2416 for (std::list<std::string>::const_iterator | |
2417 it = series.begin(); it != series.end(); ++it) | |
2418 { | |
2419 std::list<std::string> thisInstancesIds; | |
2420 index.GetChildren(thisInstancesIds, *it); | |
2421 instances.splice(instances.end(), thisInstancesIds); | |
2422 } | |
2423 | |
2424 resource.tags_.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, | |
2425 boost::lexical_cast<std::string>(instances.size()), false); | |
2426 resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); | |
2427 } | |
2428 } | |
2429 | |
2430 | |
2431 static void ComputeTags(ExpandedResource& resource, | |
2432 ServerContext& context, | |
2433 const std::string& resourceId, | |
2434 ResourceType level, | |
2435 const std::set<DicomTag>& requestedTags) | |
2436 { | |
2437 if (level == ResourceType_Patient | |
2438 && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Patient)) | |
2439 { | |
2440 ComputePatientTags(resource, context, resourceId, requestedTags); | |
2441 } | |
2442 | |
2443 if (level == ResourceType_Study | |
2444 && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Study)) | |
2445 { | |
2446 ComputeStudyTags(resource, context, resourceId, requestedTags); | |
2447 } | |
2448 | |
2449 if (level == ResourceType_Series | |
2450 && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Series)) | |
2451 { | |
2452 ComputeSeriesTags(resource, context, resourceId, requestedTags); | |
2453 } | |
2454 } | |
2455 | |
2259 bool ServerContext::ExpandResource(Json::Value& target, | 2456 bool ServerContext::ExpandResource(Json::Value& target, |
2260 const std::string& publicId, | 2457 const std::string& publicId, |
2261 ResourceType level, | 2458 ResourceType level, |
2262 DicomToJsonFormat format, | 2459 DicomToJsonFormat format, |
2263 const std::set<DicomTag>& requestedTags) | 2460 const std::set<DicomTag>& requestedTags) |
2264 { | 2461 { |
2265 std::string unusedInstanceId; | 2462 std::string unusedInstanceId; |
2266 Json::Value unusedValue; | 2463 Json::Value* unusedDicomAsJson = NULL; |
2267 | 2464 DicomMap unusedMainDicomTags; |
2268 return ExpandResource(target, publicId, unusedInstanceId, unusedValue, level, format, requestedTags); | 2465 |
2466 return ExpandResource(target, publicId, unusedMainDicomTags, unusedInstanceId, unusedDicomAsJson, level, format, requestedTags); | |
2269 } | 2467 } |
2270 | 2468 |
2271 bool ServerContext::ExpandResource(Json::Value& target, | 2469 bool ServerContext::ExpandResource(Json::Value& target, |
2272 const std::string& publicId, | 2470 const std::string& publicId, |
2471 const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) | |
2273 const std::string& instanceId, // optional: the id of an instance for the resource (if already available) | 2472 const std::string& instanceId, // optional: the id of an instance for the resource (if already available) |
2274 const Json::Value& dicomAsJson, // optional: the dicom-as-json for the resource (if already available) | 2473 const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) |
2275 ResourceType level, | 2474 ResourceType level, |
2276 DicomToJsonFormat format, | 2475 DicomToJsonFormat format, |
2277 const std::set<DicomTag>& requestedTags) | 2476 const std::set<DicomTag>& requestedTags) |
2278 { | 2477 { |
2279 ExpandedResource resource; | 2478 ExpandedResource resource; |
2280 | 2479 |
2281 if (ExpandResource(resource, publicId, instanceId, dicomAsJson, level, requestedTags)) | 2480 if (ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, level, requestedTags, ExpandResourceDbFlags_Default)) |
2282 { | 2481 { |
2283 SerializeExpandedResource(target, resource, format, requestedTags); | 2482 SerializeExpandedResource(target, resource, format, requestedTags); |
2284 return true; | 2483 return true; |
2285 } | 2484 } |
2286 | 2485 |
2287 return false; | 2486 return false; |
2288 } | 2487 } |
2289 | 2488 |
2290 bool ServerContext::ExpandResource(ExpandedResource& resource, | 2489 bool ServerContext::ExpandResource(ExpandedResource& resource, |
2291 const std::string& publicId, | 2490 const std::string& publicId, |
2491 const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) | |
2292 const std::string& instanceId, // optional: the id of an instance for the resource (if already available) | 2492 const std::string& instanceId, // optional: the id of an instance for the resource (if already available) |
2293 const Json::Value& dicomAsJson, // optional: the dicom-as-json for the resource (if already available) | 2493 const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) |
2294 ResourceType level, | 2494 ResourceType level, |
2295 const std::set<DicomTag>& requestedTags) | 2495 const std::set<DicomTag>& requestedTags, |
2296 { | 2496 ExpandResourceDbFlags expandFlags) |
2297 if (GetIndex().ExpandResource(resource, publicId, level, requestedTags)) | 2497 { |
2498 // first try to get the tags from what is already available | |
2499 | |
2500 if ((expandFlags & ExpandResourceDbFlags_IncludeMainDicomTags) | |
2501 && (mainDicomTags.GetSize() > 0) | |
2502 && (dicomAsJson != NULL)) | |
2503 { | |
2504 | |
2505 if (mainDicomTags.GetSize() > 0) | |
2506 { | |
2507 resource.tags_.Merge(mainDicomTags); | |
2508 } | |
2509 | |
2510 if (dicomAsJson != NULL && dicomAsJson->isObject()) | |
2511 { | |
2512 resource.tags_.FromDicomAsJson(*dicomAsJson); | |
2513 } | |
2514 | |
2515 std::set<DicomTag> retrievedTags; | |
2516 std::set<DicomTag> missingTags; | |
2517 resource.tags_.GetTags(retrievedTags); | |
2518 | |
2519 Toolbox::GetMissingsFromSet(missingTags, requestedTags, retrievedTags); | |
2520 | |
2521 // if all possible tags have been read, no need to get them from DB anymore | |
2522 if (missingTags.size() == 0 || DicomMap::HasOnlyComputedTags(missingTags)) | |
2523 { | |
2524 expandFlags = static_cast<ExpandResourceDbFlags>(expandFlags & ~ExpandResourceDbFlags_IncludeMainDicomTags); | |
2525 } | |
2526 | |
2527 if (missingTags.size() == 0 && expandFlags == ExpandResourceDbFlags_None) // we have already retrieved anything we need | |
2528 { | |
2529 return true; | |
2530 } | |
2531 } | |
2532 | |
2533 if (expandFlags != ExpandResourceDbFlags_None | |
2534 && GetIndex().ExpandResource(resource, publicId, level, requestedTags, expandFlags)) | |
2298 { | 2535 { |
2299 // check the main dicom tags list has not changed since the resource was stored | 2536 // check the main dicom tags list has not changed since the resource was stored |
2300 if (resource.mainDicomTagsSignature_ != DicomMap::GetMainDicomTagsSignature(resource.type_)) | 2537 if (resource.mainDicomTagsSignature_ != DicomMap::GetMainDicomTagsSignature(resource.type_)) |
2301 { | 2538 { |
2302 OrthancConfiguration::ReaderLock lock; | 2539 OrthancConfiguration::ReaderLock lock; |
2305 LOG(WARNING) << Orthanc::GetResourceTypeText(resource.type_, false , false) << " has been stored with another version of Main Dicom Tags list, you should POST to /" << Orthanc::GetResourceTypeText(resource.type_, true, false) << "/" << resource.id_ << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; | 2542 LOG(WARNING) << Orthanc::GetResourceTypeText(resource.type_, false , false) << " has been stored with another version of Main Dicom Tags list, you should POST to /" << Orthanc::GetResourceTypeText(resource.type_, true, false) << "/" << resource.id_ << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; |
2306 } | 2543 } |
2307 } | 2544 } |
2308 | 2545 |
2309 // possibly merge missing requested tags from dicom-as-json | 2546 // possibly merge missing requested tags from dicom-as-json |
2310 if (!resource.missingRequestedTags_.empty()) | 2547 if (!resource.missingRequestedTags_.empty() && !DicomMap::HasOnlyComputedTags(resource.missingRequestedTags_)) |
2311 { | 2548 { |
2312 std::string instanceId_ = instanceId; | 2549 std::string instanceId_ = instanceId; |
2313 Json::Value dicomAsJson_ = dicomAsJson; | 2550 DicomMap tagsFromJson; |
2314 if (dicomAsJson_.isNull()) | 2551 |
2552 if (dicomAsJson == NULL) | |
2315 { | 2553 { |
2316 if (instanceId_.empty()) | 2554 if (instanceId_.empty()) |
2317 { | 2555 { |
2318 if (level == ResourceType_Instance) | 2556 if (level == ResourceType_Instance) |
2319 { | 2557 { |
2329 } | 2567 } |
2330 instanceId_ = instancesIds.front(); | 2568 instanceId_ = instancesIds.front(); |
2331 } | 2569 } |
2332 } | 2570 } |
2333 | 2571 |
2334 // MORE_TAGS :TODO: log warning (add an option to disable them) | 2572 // MORE_TAGS :TODO: log "performance" warning (add an option to disable them) |
2335 ReadDicomAsJson(dicomAsJson_, instanceId_); | 2573 Json::Value tmpDicomAsJson; |
2336 } | 2574 ReadDicomAsJson(tmpDicomAsJson, instanceId_); |
2337 | 2575 tagsFromJson.FromDicomAsJson(tmpDicomAsJson); |
2338 DicomMap allTags; | 2576 } |
2339 allTags.FromDicomAsJson(dicomAsJson_); | 2577 else |
2340 | 2578 { |
2341 resource.tags_.Merge(allTags); | 2579 tagsFromJson.FromDicomAsJson(*dicomAsJson); |
2342 } | 2580 } |
2343 | 2581 |
2344 return true; | 2582 resource.tags_.Merge(tagsFromJson); |
2345 } | 2583 } |
2346 | 2584 |
2347 return false; | 2585 // compute the requested tags |
2586 ComputeTags(resource, *this, publicId, level, requestedTags); | |
2587 } | |
2588 else | |
2589 { | |
2590 return false; | |
2591 } | |
2592 | |
2593 return true; | |
2348 } | 2594 } |
2349 | 2595 |
2350 } | 2596 } |