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 }