comparison OrthancServer/Sources/main.cpp @ 4235:b3ec19f369d1

working on sorting by dates
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 08 Oct 2020 18:13:01 +0200
parents a38376b80cd1
children 5639ffda467b
comparison
equal deleted inserted replaced
4234:a38376b80cd1 4235:b3ec19f369d1
777 777
778 778
779 779
780 static const char* const BY_PATIENTS = "by-patients"; 780 static const char* const BY_PATIENTS = "by-patients";
781 static const char* const BY_STUDIES = "by-studies"; 781 static const char* const BY_STUDIES = "by-studies";
782 static const char* const BY_DATE = "by-date";
782 static const char* const BY_UIDS = "by-uids"; 783 static const char* const BY_UIDS = "by-uids";
783 static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; 784 static const char* const MAIN_DICOM_TAGS = "MainDicomTags";
784 785
785 class DummyBucket2 : public IWebDavBucket // TODO 786 class DummyBucket2 : public IWebDavBucket // TODO
786 { 787 {
787 private: 788 private:
788 ServerContext& context_; 789 typedef std::map<ResourceType, std::string> Templates;
789 790
790 791
791 static void LookupTime(boost::posix_time::ptime& target, 792 static void LookupTime(boost::posix_time::ptime& target,
792 ServerContext& context, 793 ServerContext& context,
793 const std::string& publicId, 794 const std::string& publicId,
818 Collection& target_; 819 Collection& target_;
819 ResourceType level_; 820 ResourceType level_;
820 821
821 public: 822 public:
822 DicomIdentifiersVisitor(ServerContext& context, 823 DicomIdentifiersVisitor(ServerContext& context,
823 Collection& target, 824 Collection& target,
824 ResourceType level) : 825 ResourceType level) :
825 context_(context), 826 context_(context),
826 isComplete_(false), 827 isComplete_(false),
827 target_(target), 828 target_(target),
828 level_(level) 829 level_(level)
829 { 830 {
1309 class ResourcesNode : public INode 1310 class ResourcesNode : public INode
1310 { 1311 {
1311 private: 1312 private:
1312 typedef std::map<std::string, INode*> Children; 1313 typedef std::map<std::string, INode*> Children;
1313 1314
1314 ServerContext& context_; 1315 ServerContext& context_;
1315 ResourcesIndex index_; 1316 const Templates& templates_;
1316 MetadataType timeMetadata_; 1317 std::unique_ptr<ResourcesIndex> index_;
1317 Children children_; // Maps Orthanc resource IDs to subnodes 1318 MetadataType timeMetadata_;
1319 Children children_; // Maps Orthanc resource IDs to subnodes
1318 1320
1319 void Refresh() 1321 void Refresh()
1320 { 1322 {
1321 std::list<std::string> resources; 1323 std::list<std::string> resources;
1322 GetCurrentResources(resources); 1324 GetCurrentResources(resources);
1323 1325
1324 std::set<std::string> removedPaths; 1326 std::set<std::string> removedPaths;
1325 index_.Refresh(removedPaths, std::set<std::string>(resources.begin(), resources.end())); 1327 index_->Refresh(removedPaths, std::set<std::string>(resources.begin(), resources.end()));
1326 1328
1327 // Remove the children that have been removed 1329 // Remove the children that have been removed
1328 for (std::set<std::string>::const_iterator 1330 for (std::set<std::string>::const_iterator
1329 it = removedPaths.begin(); it != removedPaths.end(); ++it) 1331 it = removedPaths.begin(); it != removedPaths.end(); ++it)
1330 { 1332 {
1338 } 1340 }
1339 } 1341 }
1340 1342
1341 INode* GetChild(const std::string& path) // Don't free the resulting pointer! 1343 INode* GetChild(const std::string& path) // Don't free the resulting pointer!
1342 { 1344 {
1343 ResourcesIndex::Map::const_iterator resource = index_.GetPathToResource().find(path); 1345 ResourcesIndex::Map::const_iterator resource = index_->GetPathToResource().find(path);
1344 if (resource == index_.GetPathToResource().end()) 1346 if (resource == index_->GetPathToResource().end())
1345 { 1347 {
1346 return NULL; 1348 return NULL;
1347 } 1349 }
1348 else 1350 else
1349 { 1351 {
1380 virtual INode* CreateChild(const std::string& resource) = 0; 1382 virtual INode* CreateChild(const std::string& resource) = 0;
1381 1383
1382 public: 1384 public:
1383 ResourcesNode(ServerContext& context, 1385 ResourcesNode(ServerContext& context,
1384 ResourceType level, 1386 ResourceType level,
1385 const std::string& templateString) : 1387 const Templates& templates) :
1386 context_(context), 1388 context_(context),
1387 index_(context, level, templateString) 1389 templates_(templates)
1388 { 1390 {
1391 Templates::const_iterator t = templates.find(level);
1392 if (t == templates.end())
1393 {
1394 throw OrthancException(ErrorCode_ParameterOutOfRange);
1395 }
1396
1397 index_.reset(new ResourcesIndex(context, level, t->second));
1398
1389 if (level == ResourceType_Instance) 1399 if (level == ResourceType_Instance)
1390 { 1400 {
1391 timeMetadata_ = MetadataType_Instance_ReceptionDate; 1401 timeMetadata_ = MetadataType_Instance_ReceptionDate;
1392 } 1402 }
1393 else 1403 else
1405 } 1415 }
1406 } 1416 }
1407 1417
1408 ResourceType GetLevel() const 1418 ResourceType GetLevel() const
1409 { 1419 {
1410 return index_.GetLevel(); 1420 return index_->GetLevel();
1421 }
1422
1423 const Templates& GetTemplates() const
1424 {
1425 return templates_;
1411 } 1426 }
1412 1427
1413 virtual bool ListCollection(IWebDavBucket::Collection& target, 1428 virtual bool ListCollection(IWebDavBucket::Collection& target,
1414 const UriComponents& path) ORTHANC_OVERRIDE 1429 const UriComponents& path) ORTHANC_OVERRIDE
1415 { 1430 {
1416 Refresh(); 1431 Refresh();
1417 1432
1418 if (index_.GetLevel() == ResourceType_Instance) 1433 if (index_->GetLevel() == ResourceType_Instance)
1419 { 1434 {
1420 // Not a collection, no subfolders 1435 // Not a collection, no subfolders
1421 return false; 1436 return false;
1422 } 1437 }
1423 else if (path.empty()) 1438 else if (path.empty())
1424 { 1439 {
1425 const ResourcesIndex::Map& paths = index_.GetPathToResource(); 1440 const ResourcesIndex::Map& paths = index_->GetPathToResource();
1426 1441
1427 for (ResourcesIndex::Map::const_iterator it = paths.begin(); it != paths.end(); ++it) 1442 for (ResourcesIndex::Map::const_iterator it = paths.begin(); it != paths.end(); ++it)
1428 { 1443 {
1429 boost::posix_time::ptime time; 1444 boost::posix_time::ptime time;
1430 LookupTime(time, context_, it->second, timeMetadata_); 1445 LookupTime(time, context_, it->second, timeMetadata_);
1480 } 1495 }
1481 }; 1496 };
1482 1497
1483 1498
1484 1499
1485 class ParentNode : public ResourcesNode 1500 class ResourceChildrenNode : public ResourcesNode
1486 { 1501 {
1487 private: 1502 private:
1488 std::string parentId_; 1503 std::string parentId_;
1489 1504
1490 protected: 1505 protected:
1511 { 1526 {
1512 return new InstancesNode(GetContext(), resource); 1527 return new InstancesNode(GetContext(), resource);
1513 } 1528 }
1514 else 1529 else
1515 { 1530 {
1516 std::string t;
1517
1518 ResourceType l = GetChildResourceType(GetLevel()); 1531 ResourceType l = GetChildResourceType(GetLevel());
1519 switch (l) 1532 return new ResourceChildrenNode(GetContext(), l, resource, GetTemplates());
1520 {
1521 case ResourceType_Study:
1522 t = "{{StudyDate}} - {{StudyDescription}}";
1523 break;
1524
1525 case ResourceType_Series:
1526 t = "{{Modality}} - {{SeriesDescription}}";
1527 break;
1528
1529 default:
1530 throw OrthancException(ErrorCode_InternalError);
1531 }
1532
1533 return new ParentNode(GetContext(), l, resource, t);
1534 } 1533 }
1535 } 1534 }
1536 1535
1537 public: 1536 public:
1538 ParentNode(ServerContext& context, 1537 ResourceChildrenNode(ServerContext& context,
1539 ResourceType level, 1538 ResourceType level,
1540 const std::string& parentId, 1539 const std::string& parentId,
1541 const std::string& templateString) : 1540 const Templates& templates) :
1542 ResourcesNode(context, level, templateString), 1541 ResourcesNode(context, level, templates),
1543 parentId_(parentId) 1542 parentId_(parentId)
1544 { 1543 {
1545 } 1544 }
1546 }; 1545 };
1547 1546
1560 { 1559 {
1561 return new InstancesNode(GetContext(), resource); 1560 return new InstancesNode(GetContext(), resource);
1562 } 1561 }
1563 else 1562 else
1564 { 1563 {
1565 std::string t;
1566
1567 ResourceType l = GetChildResourceType(GetLevel()); 1564 ResourceType l = GetChildResourceType(GetLevel());
1568 switch (l) 1565 return new ResourceChildrenNode(GetContext(), l, resource, GetTemplates());
1569 {
1570 case ResourceType_Study:
1571 t = "{{StudyDate}} - {{StudyDescription}}";
1572 break;
1573
1574 case ResourceType_Series:
1575 t = "{{Modality}} - {{SeriesDescription}}";
1576 break;
1577
1578 default:
1579 throw OrthancException(ErrorCode_InternalError);
1580 }
1581
1582 printf("OPENING CHILDREN of %s %s\n", EnumerationToString(GetLevel()), resource.c_str());
1583
1584 return new ParentNode(GetContext(), l, resource, t);
1585 } 1566 }
1586 } 1567 }
1587 1568
1588 public: 1569 public:
1589 RootNode(ServerContext& context, 1570 RootNode(ServerContext& context,
1590 ResourceType level, 1571 ResourceType level,
1591 const std::string& templateString) : 1572 const Templates& templates) :
1592 ResourcesNode(context, level, templateString) 1573 ResourcesNode(context, level, templates)
1593 { 1574 {
1575 }
1576 };
1577
1578
1579
1580 class NodeWithChildren : public INode
1581 {
1582 private:
1583 typedef std::map<std::string, INode*> Children;
1584
1585 Children children_;
1586
1587 INode* GetChild(const std::string& path) // Don't delete the result pointer!
1588 {
1589 Children::const_iterator found = children_.find(path);
1590 if (found == children_.end())
1591 {
1592 INode* child = CreateChild(path);
1593
1594 if (child == NULL)
1595 {
1596 return NULL;
1597 }
1598 else
1599 {
1600 children_[path] = child;
1601 return child;
1602 }
1603 }
1604 else
1605 {
1606 assert(found->second != NULL);
1607 return found->second;
1608 }
1609 }
1610
1611 protected:
1612 virtual void ListContent(IWebDavBucket::Collection& target) = 0;
1613
1614 virtual INode* CreateChild(const std::string& path) = 0;
1615
1616 public:
1617 virtual ~NodeWithChildren()
1618 {
1619 for (Children::iterator it = children_.begin(); it != children_.end(); ++it)
1620 {
1621 assert(it->second != NULL);
1622 delete it->second;
1623 }
1624 }
1625
1626 virtual bool ListCollection(IWebDavBucket::Collection& target,
1627 const UriComponents& path) ORTHANC_OVERRIDE
1628 {
1629 if (path.empty())
1630 {
1631 ListContent(target);
1632 return true;
1633 }
1634 else
1635 {
1636 INode* child = GetChild(path[0]);
1637 if (child == NULL)
1638 {
1639 return false;
1640 }
1641 else
1642 {
1643 UriComponents subpath(path.begin() + 1, path.end());
1644 return child->ListCollection(target, subpath);
1645 }
1646 }
1647 }
1648
1649 virtual bool GetFileContent(MimeType& mime,
1650 std::string& content,
1651 boost::posix_time::ptime& time,
1652 const UriComponents& path) ORTHANC_OVERRIDE
1653 {
1654 if (path.empty())
1655 {
1656 return false;
1657 }
1658 else
1659 {
1660 INode* child = GetChild(path[0]);
1661 if (child == NULL)
1662 {
1663 return false;
1664 }
1665 else
1666 {
1667 UriComponents subpath(path.begin() + 1, path.end());
1668 return child->GetFileContent(mime, content, time, subpath);
1669 }
1670 }
1594 } 1671 }
1595 }; 1672 };
1596 1673
1674
1675 class StudyMonthsNode : public NodeWithChildren
1676 {
1677 private:
1678 ServerContext& context_;
1679 std::string year_;
1680 const Templates& templates_;
1681
1682 class Visitor : public ServerContext::ILookupVisitor
1683 {
1684 private:
1685 std::set<std::string> months_;
1686
1687 public:
1688 Visitor()
1689 {
1690 }
1691
1692 const std::set<std::string>& GetMonths() const
1693 {
1694 return months_;
1695 }
1696
1697 virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE
1698 {
1699 return false; // (*)
1700 }
1701
1702 virtual void MarkAsComplete() ORTHANC_OVERRIDE
1703 {
1704 }
1705
1706 virtual void Visit(const std::string& publicId,
1707 const std::string& instanceId /* unused */,
1708 const DicomMap& mainDicomTags,
1709 const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE
1710 {
1711 std::string s;
1712 if (mainDicomTags.LookupStringValue(s, DICOM_TAG_STUDY_DATE, false) &&
1713 s.size() == 8)
1714 {
1715 months_.insert(s.substr(4, 2)); // Get the month from "YYYYMMDD"
1716 }
1717 }
1718 };
1719
1720 protected:
1721 virtual void ListContent(IWebDavBucket::Collection& target)
1722 {
1723 DatabaseLookup query;
1724 query.AddRestConstraint(DICOM_TAG_STUDY_DATE, year_ + "0101-" + year_ + "1231",
1725 true /* case sensitive */, true /* mandatory tag */);
1726
1727 Visitor visitor;
1728 context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */);
1729
1730 for (std::set<std::string>::const_iterator it = visitor.GetMonths().begin();
1731 it != visitor.GetMonths().end(); ++it)
1732 {
1733 target.AddResource(new IWebDavBucket::Folder(year_ + "-" + *it));
1734 }
1735 }
1736
1737 virtual INode* CreateChild(const std::string& path) ORTHANC_OVERRIDE
1738 {
1739 return NULL;
1740 }
1741
1742 public:
1743 StudyMonthsNode(ServerContext& context,
1744 const std::string& year,
1745 const Templates& templates) :
1746 context_(context),
1747 year_(year),
1748 templates_(templates)
1749 {
1750 }
1751 };
1752
1597 1753
1598 1754 class StudyYearsNode : public NodeWithChildren
1599 RootNode patients_; 1755 {
1600 RootNode studies_; 1756 private:
1757 ServerContext& context_;
1758 const Templates& templates_;
1759
1760 protected:
1761 virtual void ListContent(IWebDavBucket::Collection& target)
1762 {
1763 std::list<std::string> resources;
1764 context_.GetIndex().GetAllUuids(resources, ResourceType_Study);
1765
1766 std::set<std::string> years;
1767
1768 for (std::list<std::string>::const_iterator it = resources.begin(); it != resources.end(); ++it)
1769 {
1770 DicomMap tags;
1771 std::string studyDate;
1772 if (context_.GetIndex().GetMainDicomTags(tags, *it, ResourceType_Study, ResourceType_Study) &&
1773 tags.LookupStringValue(studyDate, DICOM_TAG_STUDY_DATE, false) &&
1774 studyDate.size() == 8)
1775 {
1776 years.insert(studyDate.substr(0, 4)); // Get the year from "YYYYMMDD"
1777 }
1778 }
1779
1780 for (std::set<std::string>::const_iterator it = years.begin(); it != years.end(); ++it)
1781 {
1782 target.AddResource(new IWebDavBucket::Folder(*it));
1783 }
1784 }
1785
1786 virtual INode* CreateChild(const std::string& path) ORTHANC_OVERRIDE
1787 {
1788 return new StudyMonthsNode(context_, path, templates_);
1789 }
1790
1791 public:
1792 StudyYearsNode(ServerContext& context,
1793 const Templates& templates) :
1794 context_(context),
1795 templates_(templates)
1796 {
1797 }
1798 };
1799
1800
1801 ServerContext& context_;
1802 std::unique_ptr<INode> patients_;
1803 std::unique_ptr<INode> studies_;
1804 std::unique_ptr<INode> dates_;
1805 Templates patientsTemplates_;
1806 Templates studiesTemplates_;
1601 1807
1602 1808
1603 public: 1809 public:
1604 DummyBucket2(ServerContext& context) : 1810 DummyBucket2(ServerContext& context) :
1605 context_(context), 1811 context_(context)
1606 patients_(context, ResourceType_Patient, "{{PatientID}} - {{PatientName}}"), 1812 {
1607 studies_(context, ResourceType_Study, "{{PatientID}} - {{PatientName}} - {{StudyDescription}}") 1813 patientsTemplates_[ResourceType_Patient] = "{{PatientID}} - {{PatientName}}";
1608 { 1814 patientsTemplates_[ResourceType_Study] = "{{StudyDate}} - {{StudyDescription}}";
1815 patientsTemplates_[ResourceType_Series] = "{{Modality}} - {{SeriesDescription}}";
1816
1817 studiesTemplates_[ResourceType_Study] = "{{PatientID}} - {{PatientName}} - {{StudyDescription}}";
1818 studiesTemplates_[ResourceType_Series] = patientsTemplates_[ResourceType_Series];
1819
1820 patients_.reset(new RootNode(context, ResourceType_Patient, patientsTemplates_));
1821 studies_.reset(new RootNode(context, ResourceType_Study, studiesTemplates_));
1822 dates_.reset(new StudyYearsNode(context, studiesTemplates_));
1609 } 1823 }
1610 1824
1611 virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE 1825 virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE
1612 { 1826 {
1613 if (path.empty()) 1827 if (path.empty())
1620 (path.size() != 3 || path[2] != "study.json")); 1834 (path.size() != 3 || path[2] != "study.json"));
1621 } 1835 }
1622 else if (path[0] == BY_PATIENTS) 1836 else if (path[0] == BY_PATIENTS)
1623 { 1837 {
1624 IWebDavBucket::Collection tmp; 1838 IWebDavBucket::Collection tmp;
1625 return patients_.ListCollection(tmp, UriComponents(path.begin() + 1, path.end())); 1839 return patients_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1626 } 1840 }
1627 else if (path[0] == BY_STUDIES) 1841 else if (path[0] == BY_STUDIES)
1628 { 1842 {
1629 IWebDavBucket::Collection tmp; 1843 IWebDavBucket::Collection tmp;
1630 return studies_.ListCollection(tmp, UriComponents(path.begin() + 1, path.end())); 1844 return studies_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1845 }
1846 else if (path[0] == BY_DATE)
1847 {
1848 IWebDavBucket::Collection tmp;
1849 return dates_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end()));
1631 } 1850 }
1632 else 1851 else
1633 { 1852 {
1634 return false; 1853 return false;
1635 } 1854 }
1638 virtual bool ListCollection(Collection& collection, 1857 virtual bool ListCollection(Collection& collection,
1639 const UriComponents& path) ORTHANC_OVERRIDE 1858 const UriComponents& path) ORTHANC_OVERRIDE
1640 { 1859 {
1641 if (path.empty()) 1860 if (path.empty())
1642 { 1861 {
1643 collection.AddResource(new Folder(BY_UIDS)); 1862 collection.AddResource(new Folder(BY_DATE));
1644 collection.AddResource(new Folder(BY_PATIENTS)); 1863 collection.AddResource(new Folder(BY_PATIENTS));
1645 collection.AddResource(new Folder(BY_STUDIES)); 1864 collection.AddResource(new Folder(BY_STUDIES));
1865 collection.AddResource(new Folder(BY_UIDS));
1646 return true; 1866 return true;
1647 } 1867 }
1648 else if (path[0] == BY_UIDS) 1868 else if (path[0] == BY_UIDS)
1649 { 1869 {
1650 DatabaseLookup query; 1870 DatabaseLookup query;
1684 1904
1685 return true; 1905 return true;
1686 } 1906 }
1687 else if (path[0] == BY_PATIENTS) 1907 else if (path[0] == BY_PATIENTS)
1688 { 1908 {
1689 return patients_.ListCollection(collection, UriComponents(path.begin() + 1, path.end())); 1909 return patients_->ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1690 } 1910 }
1691 else if (path[0] == BY_STUDIES) 1911 else if (path[0] == BY_STUDIES)
1692 { 1912 {
1693 return studies_.ListCollection(collection, UriComponents(path.begin() + 1, path.end())); 1913 return studies_->ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1914 }
1915 else if (path[0] == BY_DATE)
1916 {
1917 return dates_->ListCollection(collection, UriComponents(path.begin() + 1, path.end()));
1694 } 1918 }
1695 else 1919 else
1696 { 1920 {
1697 return false; 1921 return false;
1698 } 1922 }
1758 return visitor.IsSuccess(); 1982 return visitor.IsSuccess();
1759 } 1983 }
1760 } 1984 }
1761 else if (path[0] == BY_PATIENTS) 1985 else if (path[0] == BY_PATIENTS)
1762 { 1986 {
1763 return patients_.GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); 1987 return patients_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1764 } 1988 }
1765 else if (path[0] == BY_STUDIES) 1989 else if (path[0] == BY_STUDIES)
1766 { 1990 {
1767 return studies_.GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); 1991 return studies_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1992 }
1993 else if (path[0] == BY_DATE)
1994 {
1995 return dates_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end()));
1768 } 1996 }
1769 1997
1770 return false; 1998 return false;
1771 } 1999 }
1772 2000