comparison OrthancServer/Sources/ServerIndex.cpp @ 4554:efd90f778cd2 db-changes

simplification
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 03 Mar 2021 16:31:57 +0100
parents 350a22c094f2
children 456ed3fcff81
comparison
equal deleted inserted replaced
4552:beb8ba8a0b12 4554:efd90f778cd2
669 return 1; 669 return 1;
670 } 670 }
671 } 671 }
672 672
673 673
674 bool ServerIndex::IsUnstableResource(int64_t id)
675 {
676 return unstableResources_.Contains(id);
677 }
678
674 679
675 ServerIndex::ServerIndex(ServerContext& context, 680 ServerIndex::ServerIndex(ServerContext& context,
676 IDatabaseWrapper& db, 681 IDatabaseWrapper& db,
677 unsigned int threadSleep) : 682 unsigned int threadSleep) :
678 done_(false), 683 done_(false),
679 db_(db), 684 db_(db),
680 maximumStorageSize_(0), 685 maximumStorageSize_(0),
681 maximumPatients_(0), 686 maximumPatients_(0),
682 mainDicomTagsRegistry_(new MainDicomTagsRegistry), 687 mainDicomTagsRegistry_(new MainDicomTagsRegistry),
683 maxRetries_(0) 688 maxRetries_(10)
684 { 689 {
685 listener_.reset(new Listener(context)); 690 listener_.reset(new Listener(context));
686 db_.SetListener(*listener_); 691 db_.SetListener(*listener_);
687 692
688 // Initial recycling if the parameters have changed since the last 693 // Initial recycling if the parameters have changed since the last
1012 1017
1013 // Check whether the series of this new instance is now completed 1018 // Check whether the series of this new instance is now completed
1014 int64_t expectedNumberOfInstances; 1019 int64_t expectedNumberOfInstances;
1015 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary)) 1020 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary))
1016 { 1021 {
1017 SeriesStatus seriesStatus = GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); 1022 SeriesStatus seriesStatus = GetSeriesStatus(db_, status.seriesId_, expectedNumberOfInstances);
1018 if (seriesStatus == SeriesStatus_Complete) 1023 if (seriesStatus == SeriesStatus_Complete)
1019 { 1024 {
1020 LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries); 1025 LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries);
1021 } 1026 }
1022 } 1027 }
1055 countSeries = db_.GetResourceCount(ResourceType_Series); 1060 countSeries = db_.GetResourceCount(ResourceType_Series);
1056 countInstances = db_.GetResourceCount(ResourceType_Instance); 1061 countInstances = db_.GetResourceCount(ResourceType_Instance);
1057 } 1062 }
1058 1063
1059 1064
1060 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id, 1065 SeriesStatus ServerIndex::GetSeriesStatus(IDatabaseWrapper& db,
1066 int64_t id,
1061 int64_t expectedNumberOfInstances) 1067 int64_t expectedNumberOfInstances)
1062 { 1068 {
1063 std::list<std::string> values; 1069 std::list<std::string> values;
1064 db_.GetChildrenMetadata(values, id, MetadataType_Instance_IndexInSeries); 1070 db.GetChildrenMetadata(values, id, MetadataType_Instance_IndexInSeries);
1065 1071
1066 std::set<int64_t> instances; 1072 std::set<int64_t> instances;
1067 1073
1068 for (std::list<std::string>::const_iterator 1074 for (std::list<std::string>::const_iterator
1069 it = values.begin(); it != values.end(); ++it) 1075 it = values.begin(); it != values.end(); ++it)
1104 } 1110 }
1105 } 1111 }
1106 1112
1107 1113
1108 void ServerIndex::MainDicomTagsToJson(Json::Value& target, 1114 void ServerIndex::MainDicomTagsToJson(Json::Value& target,
1115 IDatabaseWrapper& db,
1109 int64_t resourceId, 1116 int64_t resourceId,
1110 ResourceType resourceType) 1117 ResourceType resourceType)
1111 { 1118 {
1112 DicomMap tags; 1119 DicomMap tags;
1113 db_.GetMainDicomTags(tags, resourceId); 1120 db.GetMainDicomTags(tags, resourceId);
1114 1121
1115 if (resourceType == ResourceType_Study) 1122 if (resourceType == ResourceType_Study)
1116 { 1123 {
1117 DicomMap t1, t2; 1124 DicomMap t1, t2;
1118 tags.ExtractStudyInformation(t1); 1125 tags.ExtractStudyInformation(t1);
1130 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags, true); 1137 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags, true);
1131 } 1138 }
1132 } 1139 }
1133 1140
1134 1141
1135 bool ServerIndex::LookupResource(Json::Value& result,
1136 const std::string& publicId,
1137 ResourceType expectedType)
1138 {
1139 result = Json::objectValue;
1140
1141 boost::mutex::scoped_lock lock(mutex_);
1142
1143 // Lookup for the requested resource
1144 int64_t id;
1145 ResourceType type;
1146 std::string parent;
1147 if (!db_.LookupResourceAndParent(id, type, parent, publicId) ||
1148 type != expectedType)
1149 {
1150 return false;
1151 }
1152
1153 // Set information about the parent resource (if it exists)
1154 if (type == ResourceType_Patient)
1155 {
1156 if (!parent.empty())
1157 {
1158 throw OrthancException(ErrorCode_DatabasePlugin);
1159 }
1160 }
1161 else
1162 {
1163 if (parent.empty())
1164 {
1165 throw OrthancException(ErrorCode_DatabasePlugin);
1166 }
1167
1168 switch (type)
1169 {
1170 case ResourceType_Study:
1171 result["ParentPatient"] = parent;
1172 break;
1173
1174 case ResourceType_Series:
1175 result["ParentStudy"] = parent;
1176 break;
1177
1178 case ResourceType_Instance:
1179 result["ParentSeries"] = parent;
1180 break;
1181
1182 default:
1183 throw OrthancException(ErrorCode_InternalError);
1184 }
1185 }
1186
1187 // List the children resources
1188 std::list<std::string> children;
1189 db_.GetChildrenPublicId(children, id);
1190
1191 if (type != ResourceType_Instance)
1192 {
1193 Json::Value c = Json::arrayValue;
1194
1195 for (std::list<std::string>::const_iterator
1196 it = children.begin(); it != children.end(); ++it)
1197 {
1198 c.append(*it);
1199 }
1200
1201 switch (type)
1202 {
1203 case ResourceType_Patient:
1204 result["Studies"] = c;
1205 break;
1206
1207 case ResourceType_Study:
1208 result["Series"] = c;
1209 break;
1210
1211 case ResourceType_Series:
1212 result["Instances"] = c;
1213 break;
1214
1215 default:
1216 throw OrthancException(ErrorCode_InternalError);
1217 }
1218 }
1219
1220 // Extract the metadata
1221 std::map<MetadataType, std::string> metadata;
1222 db_.GetAllMetadata(metadata, id);
1223
1224 // Set the resource type
1225 switch (type)
1226 {
1227 case ResourceType_Patient:
1228 result["Type"] = "Patient";
1229 break;
1230
1231 case ResourceType_Study:
1232 result["Type"] = "Study";
1233 break;
1234
1235 case ResourceType_Series:
1236 {
1237 result["Type"] = "Series";
1238
1239 int64_t i;
1240 if (LookupIntegerMetadata(i, metadata, MetadataType_Series_ExpectedNumberOfInstances))
1241 {
1242 result["ExpectedNumberOfInstances"] = static_cast<int>(i);
1243 result["Status"] = EnumerationToString(GetSeriesStatus(id, i));
1244 }
1245 else
1246 {
1247 result["ExpectedNumberOfInstances"] = Json::nullValue;
1248 result["Status"] = EnumerationToString(SeriesStatus_Unknown);
1249 }
1250
1251 break;
1252 }
1253
1254 case ResourceType_Instance:
1255 {
1256 result["Type"] = "Instance";
1257
1258 FileInfo attachment;
1259 if (!db_.LookupAttachment(attachment, id, FileContentType_Dicom))
1260 {
1261 throw OrthancException(ErrorCode_InternalError);
1262 }
1263
1264 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize());
1265 result["FileUuid"] = attachment.GetUuid();
1266
1267 int64_t i;
1268 if (LookupIntegerMetadata(i, metadata, MetadataType_Instance_IndexInSeries))
1269 {
1270 result["IndexInSeries"] = static_cast<int>(i);
1271 }
1272 else
1273 {
1274 result["IndexInSeries"] = Json::nullValue;
1275 }
1276
1277 break;
1278 }
1279
1280 default:
1281 throw OrthancException(ErrorCode_InternalError);
1282 }
1283
1284 // Record the remaining information
1285 result["ID"] = publicId;
1286 MainDicomTagsToJson(result, id, type);
1287
1288 std::string tmp;
1289
1290 if (LookupStringMetadata(tmp, metadata, MetadataType_AnonymizedFrom))
1291 {
1292 result["AnonymizedFrom"] = tmp;
1293 }
1294
1295 if (LookupStringMetadata(tmp, metadata, MetadataType_ModifiedFrom))
1296 {
1297 result["ModifiedFrom"] = tmp;
1298 }
1299
1300 if (type == ResourceType_Patient ||
1301 type == ResourceType_Study ||
1302 type == ResourceType_Series)
1303 {
1304 result["IsStable"] = !unstableResources_.Contains(id);
1305
1306 if (LookupStringMetadata(tmp, metadata, MetadataType_LastUpdate))
1307 {
1308 result["LastUpdate"] = tmp;
1309 }
1310 }
1311
1312 return true;
1313 }
1314
1315
1316 bool ServerIndex::LookupAttachment(FileInfo& attachment, 1142 bool ServerIndex::LookupAttachment(FileInfo& attachment,
1317 const std::string& instanceUuid, 1143 const std::string& instanceUuid,
1318 FileContentType contentType) 1144 FileContentType contentType)
1319 { 1145 {
1320 boost::mutex::scoped_lock lock(mutex_); 1146 boost::mutex::scoped_lock lock(mutex_);
1883 1709
1884 return db_.LookupMetadata(target, id, type); 1710 return db_.LookupMetadata(target, id, type);
1885 } 1711 }
1886 1712
1887 1713
1888 void ServerIndex::GetAllMetadata(std::map<MetadataType, std::string>& target,
1889 const std::string& publicId,
1890 ResourceType expectedType)
1891 {
1892 boost::mutex::scoped_lock lock(mutex_);
1893
1894 ResourceType type;
1895 int64_t id;
1896 if (!db_.LookupResource(id, type, publicId) ||
1897 expectedType != type)
1898 {
1899 throw OrthancException(ErrorCode_UnknownResource);
1900 }
1901
1902 return db_.GetAllMetadata(target, id);
1903 }
1904
1905
1906 void ServerIndex::ListAvailableAttachments(std::set<FileContentType>& target, 1714 void ServerIndex::ListAvailableAttachments(std::set<FileContentType>& target,
1907 const std::string& publicId, 1715 const std::string& publicId,
1908 ResourceType expectedType) 1716 ResourceType expectedType)
1909 { 1717 {
1910 boost::mutex::scoped_lock lock(mutex_); 1718 boost::mutex::scoped_lock lock(mutex_);
2630 2438
2631 /*** 2439 /***
2632 ** PROTOTYPING FOR DB REFACTORING BELOW 2440 ** PROTOTYPING FOR DB REFACTORING BELOW
2633 ***/ 2441 ***/
2634 2442
2635 ServerIndex::ExpandResourceOperation::ExpandResourceOperation(const std::string& resource,
2636 ResourceType level) :
2637 found_(false),
2638 resource_(resource),
2639 level_(level)
2640 {
2641 }
2642
2643
2644 void ServerIndex::ExpandResourceOperation::Apply(ServerIndex::ReadOnlyTransaction& transaction)
2645 {
2646 found_ = transaction.LookupResource(item_, resource_, level_);
2647 }
2648
2649
2650 const Json::Value& ServerIndex::ExpandResourceOperation::GetResource() const
2651 {
2652 if (found_)
2653 {
2654 return item_;
2655 }
2656 else
2657 {
2658 throw OrthancException(ErrorCode_BadSequenceOfCalls);
2659 }
2660 }
2661
2662
2663 class ServerIndex::ReadOnlyWrapper : public IReadOnlyOperations 2443 class ServerIndex::ReadOnlyWrapper : public IReadOnlyOperations
2664 { 2444 {
2665 private: 2445 private:
2666 ReadOnlyFunction func_; 2446 ReadOnlyFunction func_;
2667 2447
2711 2491
2712 for (;;) 2492 for (;;)
2713 { 2493 {
2714 try 2494 try
2715 { 2495 {
2496 boost::mutex::scoped_lock lock(mutex_); // TODO - REMOVE
2497
2716 if (readOperations != NULL) 2498 if (readOperations != NULL)
2717 { 2499 {
2718 ReadOnlyTransaction transaction(*this); 2500 ReadOnlyTransaction transaction(db_);
2719 readOperations->Apply(transaction); 2501 readOperations->Apply(transaction);
2720 } 2502 }
2721 else 2503 else
2722 { 2504 {
2723 assert(writeOperations != NULL); 2505 assert(writeOperations != NULL);
2724 ReadWriteTransaction transaction(*this); 2506 ReadWriteTransaction transaction(db_);
2725 writeOperations->Apply(transaction); 2507 writeOperations->Apply(transaction);
2726 } 2508 }
2727 2509
2728 return; // Success 2510 return; // Success
2729 } 2511 }
2780 void ServerIndex::Apply(ReadWriteFunction func) 2562 void ServerIndex::Apply(ReadWriteFunction func)
2781 { 2563 {
2782 ReadWriteWrapper wrapper(func); 2564 ReadWriteWrapper wrapper(func);
2783 Apply(wrapper); 2565 Apply(wrapper);
2784 } 2566 }
2567
2568
2569 bool ServerIndex::ExpandResource(Json::Value& target,
2570 const std::string& publicId,
2571 ResourceType level)
2572 {
2573 class Operations : public ServerIndex::IReadOnlyOperations
2574 {
2575 private:
2576 Json::Value& target_;
2577 bool found_;
2578 ServerIndex& index_;
2579 const std::string& publicId_;
2580 ResourceType level_;
2581
2582 public:
2583 Operations(Json::Value& target,
2584 ServerIndex& index,
2585 const std::string& publicId,
2586 ResourceType level) :
2587 target_(target),
2588 found_(false),
2589 index_(index),
2590 publicId_(publicId),
2591 level_(level)
2592 {
2593 }
2594
2595 virtual void Apply(ServerIndex::ReadOnlyTransaction& transaction) ORTHANC_OVERRIDE
2596 {
2597 // Lookup for the requested resource
2598 int64_t internalId; // unused
2599 ResourceType type;
2600 std::string parent;
2601 if (!transaction.LookupResourceAndParent(internalId, type, parent, publicId_) ||
2602 type != level_)
2603 {
2604 found_ = false;
2605 }
2606 else
2607 {
2608 target_ = Json::objectValue;
2609
2610 // Set information about the parent resource (if it exists)
2611 if (type == ResourceType_Patient)
2612 {
2613 if (!parent.empty())
2614 {
2615 throw OrthancException(ErrorCode_DatabasePlugin);
2616 }
2617 }
2618 else
2619 {
2620 if (parent.empty())
2621 {
2622 throw OrthancException(ErrorCode_DatabasePlugin);
2623 }
2624
2625 switch (type)
2626 {
2627 case ResourceType_Study:
2628 target_["ParentPatient"] = parent;
2629 break;
2630
2631 case ResourceType_Series:
2632 target_["ParentStudy"] = parent;
2633 break;
2634
2635 case ResourceType_Instance:
2636 target_["ParentSeries"] = parent;
2637 break;
2638
2639 default:
2640 throw OrthancException(ErrorCode_InternalError);
2641 }
2642 }
2643
2644 // List the children resources
2645 std::list<std::string> children;
2646 transaction.GetChildrenPublicId(children, internalId);
2647
2648 if (type != ResourceType_Instance)
2649 {
2650 Json::Value c = Json::arrayValue;
2651
2652 for (std::list<std::string>::const_iterator
2653 it = children.begin(); it != children.end(); ++it)
2654 {
2655 c.append(*it);
2656 }
2657
2658 switch (type)
2659 {
2660 case ResourceType_Patient:
2661 target_["Studies"] = c;
2662 break;
2663
2664 case ResourceType_Study:
2665 target_["Series"] = c;
2666 break;
2667
2668 case ResourceType_Series:
2669 target_["Instances"] = c;
2670 break;
2671
2672 default:
2673 throw OrthancException(ErrorCode_InternalError);
2674 }
2675 }
2676
2677 // Extract the metadata
2678 std::map<MetadataType, std::string> metadata;
2679 transaction.GetAllMetadata(metadata, internalId);
2680
2681 // Set the resource type
2682 switch (type)
2683 {
2684 case ResourceType_Patient:
2685 target_["Type"] = "Patient";
2686 break;
2687
2688 case ResourceType_Study:
2689 target_["Type"] = "Study";
2690 break;
2691
2692 case ResourceType_Series:
2693 {
2694 target_["Type"] = "Series";
2695
2696 int64_t i;
2697 if (LookupIntegerMetadata(i, metadata, MetadataType_Series_ExpectedNumberOfInstances))
2698 {
2699 target_["ExpectedNumberOfInstances"] = static_cast<int>(i);
2700 target_["Status"] = EnumerationToString(transaction.GetSeriesStatus(internalId, i));
2701 }
2702 else
2703 {
2704 target_["ExpectedNumberOfInstances"] = Json::nullValue;
2705 target_["Status"] = EnumerationToString(SeriesStatus_Unknown);
2706 }
2707
2708 break;
2709 }
2710
2711 case ResourceType_Instance:
2712 {
2713 target_["Type"] = "Instance";
2714
2715 FileInfo attachment;
2716 if (!transaction.LookupAttachment(attachment, internalId, FileContentType_Dicom))
2717 {
2718 throw OrthancException(ErrorCode_InternalError);
2719 }
2720
2721 target_["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize());
2722 target_["FileUuid"] = attachment.GetUuid();
2723
2724 int64_t i;
2725 if (LookupIntegerMetadata(i, metadata, MetadataType_Instance_IndexInSeries))
2726 {
2727 target_["IndexInSeries"] = static_cast<int>(i);
2728 }
2729 else
2730 {
2731 target_["IndexInSeries"] = Json::nullValue;
2732 }
2733
2734 break;
2735 }
2736
2737 default:
2738 throw OrthancException(ErrorCode_InternalError);
2739 }
2740
2741 // Record the remaining information
2742 target_["ID"] = publicId_;
2743 transaction.MainDicomTagsToJson(target_, internalId, type);
2744
2745 std::string tmp;
2746
2747 if (LookupStringMetadata(tmp, metadata, MetadataType_AnonymizedFrom))
2748 {
2749 target_["AnonymizedFrom"] = tmp;
2750 }
2751
2752 if (LookupStringMetadata(tmp, metadata, MetadataType_ModifiedFrom))
2753 {
2754 target_["ModifiedFrom"] = tmp;
2755 }
2756
2757 if (type == ResourceType_Patient ||
2758 type == ResourceType_Study ||
2759 type == ResourceType_Series)
2760 {
2761 target_["IsStable"] = !index_.IsUnstableResource(internalId);
2762
2763 if (LookupStringMetadata(tmp, metadata, MetadataType_LastUpdate))
2764 {
2765 target_["LastUpdate"] = tmp;
2766 }
2767 }
2768
2769 found_ = true;
2770 }
2771 }
2772
2773 bool HasFound() const
2774 {
2775 return found_;
2776 }
2777 };
2778
2779 Operations operations(target, *this, publicId, level);
2780 Apply(operations);
2781 return operations.HasFound();
2782 }
2783
2784
2785 void ServerIndex::GetAllMetadata(std::map<MetadataType, std::string>& target,
2786 const std::string& publicId,
2787 ResourceType level)
2788 {
2789 class Operations : public ServerIndex::IReadOnlyOperations
2790 {
2791 private:
2792 std::map<MetadataType, std::string>& metadata_;
2793 std::string publicId_;
2794 ResourceType level_;
2795
2796 public:
2797 Operations(std::map<MetadataType, std::string>& metadata,
2798 const std::string& publicId,
2799 ResourceType level) :
2800 metadata_(metadata),
2801 publicId_(publicId),
2802 level_(level)
2803 {
2804 }
2805
2806 virtual void Apply(ServerIndex::ReadOnlyTransaction& transaction) ORTHANC_OVERRIDE
2807 {
2808 ResourceType type;
2809 int64_t id;
2810 if (!transaction.LookupResource(id, type, publicId_) ||
2811 level_ != type)
2812 {
2813 throw OrthancException(ErrorCode_UnknownResource);
2814 }
2815 else
2816 {
2817 transaction.GetAllMetadata(metadata_, id);
2818 }
2819 }
2820 };
2821
2822 Operations operations(target, publicId, level);
2823 Apply(operations);
2824 }
2785 } 2825 }