changeset 2873:703d1e848907

Orthanc Explorer: Lookup and limit the results to 100 patients/studies
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 09 Oct 2018 16:27:49 +0200
parents 9d08edde614b
children 67ebfacf4467
files NEWS OrthancExplorer/explorer.html OrthancExplorer/explorer.js
diffstat 3 files changed, 268 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Oct 09 14:19:48 2018 +0200
+++ b/NEWS	Tue Oct 09 16:27:49 2018 +0200
@@ -7,6 +7,12 @@
 
 * Possibility to restrict the allowed DICOM commands for each modality
 
+Orthanc Explorer
+----------------
+
+* The first screen of Orthanc Explorer is now a form to do studies lookups
+* Support of large databases, by limiting the results to 100 patients or studies
+
 REST API
 --------
 
--- a/OrthancExplorer/explorer.html	Tue Oct 09 14:19:48 2018 +0200
+++ b/OrthancExplorer/explorer.html	Tue Oct 09 16:27:49 2018 +0200
@@ -34,11 +34,11 @@
     <script src="../plugins/explorer.js"></script>
   </head>
   <body>
-    <div data-role="page" id="find-patients" >
+    <div data-role="page" id="lookup" >
       <div data-role="header" >
-	<h1><span class="orthanc-name"></span>Find a patient</h1>
+	<h1><span class="orthanc-name"></span>Lookup studies</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
           <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
@@ -48,16 +48,67 @@
         </div>
       </div>
       <div data-role="content">
-        <ul id="all-patients" data-role="listview" data-inset="true" data-filter="true">
-        </ul>
+        <form data-ajax="false" id="lookup-form">
+          <div data-role="fieldcontain">
+	    <label for="lookup-patient-id">Patient ID:</label>
+	    <input type="text" name="lookup-patient-id" id="lookup-patient-id" value=""  />
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="lookup-patient-name">Patient Name:</label>
+	    <input type="text" name="lookup-patient-name" id="lookup-patient-name" value=""  />
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="lookup-accession-number">Accession Number:</label>
+	    <input type="text" name="lookup-accession-number" id="lookup-accession-number" value=""  />
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="lookup-study-description">Study Description:</label>
+	    <input type="text" name="lookup-study-description" id="lookup-study-description" value=""  />
+	  </div>
+
+          <div data-role="fieldcontain">
+	    <label for="lookup-study-date">Study Date:</label>
+            <select name="lookup-study-date" id="lookup-study-date">
+            </select>
+	  </div>
+
+          <fieldset class="ui-grid-b">
+	    <div class="ui-block-a">
+              <a href="#find-patients" data-role="button" data-theme="b" data-direction="reverse">All patients</a>
+            </div>
+	    <div class="ui-block-b">
+              <a href="#find-studies" data-role="button" data-theme="b" data-direction="reverse">All studies</a>
+            </div>
+	    <div class="ui-block-c">
+              <button id="lookup-submit" type="submit" data-theme="e">Do lookup</button>
+            </div>
+	  </fieldset>
+          <div>&nbsp;</div>
+        </form>
+        <div id="lookup-result">
+          <div id="lookup-alert">
+            <div class="ui-bar ui-bar-e">
+              <h3>Warning:</h3> Your lookup led to many results!
+              Showing only <span id="lookup-count">?</span> studies to
+              avoid performance issue. Please make your query more
+              specific, then relaunch the lookup.
+            </div>
+            <div>&nbsp;</div>
+          </div>
+          <ul data-role="listview" data-inset="true" data-filter="true">
+          </ul>
+        </div>
       </div>
     </div>
 
-    <div data-role="page" id="find-studies" >
+    <div data-role="page" id="find-patients" >
       <div data-role="header" >
-	<h1><span class="orthanc-name"></span>Find a study</h1>
+	<h1><span class="orthanc-name"></span>All patients</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
           <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
@@ -67,6 +118,43 @@
         </div>
       </div>
       <div data-role="content">
+        <div id="alert-patients">
+          <div class="ui-bar ui-bar-e">
+            <h3>Warning:</h3> This is a large Orthanc server. Showing
+            only <span id="count-patients">?</span> patients to avoid
+            performance issue. Make sure to use lookup if targeting
+            specific patients!
+          </div>
+          <div>&nbsp;</div>
+        </div>
+        <ul id="all-patients" data-role="listview" data-inset="true" data-filter="true">
+        </ul>
+      </div>
+    </div>
+
+    <div data-role="page" id="find-studies" >
+      <div data-role="header" >
+	<h1><span class="orthanc-name"></span>All studies</h1>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
+        </div>
+        <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
+          <a href="#upload" data-icon="gear" data-role="button">Upload</a>
+          <a href="#query-retrieve" data-icon="search" data-role="button">Query/Retrieve</a>
+          <a href="#jobs" data-icon="refresh" data-role="button" data-direction="reverse">Jobs</a>
+        </div>
+      </div>
+      <div data-role="content">
+        <div id="alert-studies">
+          <div class="ui-bar ui-bar-e">
+            <h3>Warning:</h3> This is a large Orthanc server. Showing
+            only <span id="count-studies">?</span> studies to avoid
+            performance issue. Make sure to use lookup if targeting
+            specific studies!
+          </div>
+          <div>&nbsp;</div>
+        </div>
         <ul id="all-studies" data-role="listview" data-inset="true" data-filter="true">
         </ul>
       </div>
@@ -76,8 +164,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Upload DICOM files</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
       </div>
       <div data-role="content">
@@ -105,8 +193,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Patient</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
           <a href="#upload" data-icon="gear" data-role="button">Upload</a>
@@ -166,8 +254,8 @@
           Study
         </h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
           <a href="#upload" data-icon="gear" data-role="button">Upload</a>
@@ -221,8 +309,8 @@
           Series
         </h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
           <a href="#upload" data-icon="gear" data-role="button">Upload</a>
@@ -278,8 +366,8 @@
           Instance
         </h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
           <a href="#upload" data-icon="gear" data-role="button">Upload</a>
@@ -334,8 +422,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Plugins</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
       </div>
       <div data-role="content">
@@ -348,8 +436,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (1/4)</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
       </div>
       <div data-role="content">
@@ -419,8 +507,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (2/4)</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <a href="#query-retrieve" data-icon="search" class="ui-btn-right" data-direction="reverse">Query/Retrieve</a>
       </div>
@@ -435,8 +523,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (3/4)</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <a href="#query-retrieve" data-icon="search" class="ui-btn-right" data-direction="reverse">Query/Retrieve</a>
       </div>
@@ -451,8 +539,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>DICOM Query/Retrieve (4/4)</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <a href="#query-retrieve" data-icon="search" class="ui-btn-right" data-direction="reverse">Query/Retrieve</a>
       </div>
@@ -480,8 +568,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Jobs</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
       </div>
       <div data-role="content">
@@ -494,8 +582,8 @@
       <div data-role="header" >
 	<h1><span class="orthanc-name"></span>Job</h1>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-left">
-          <a href="#find-patients" data-icon="home" data-role="button" data-direction="reverse">Patients</a>
-          <a href="#find-studies" data-icon="arrow-r" data-role="button" data-direction="reverse">Studies</a>
+          <a href="#lookup" data-icon="arrow-r" data-role="button" data-direction="reverse">Lookup</a>
+          <a href="#plugins" data-icon="grid" data-role="button" data-direction="reverse">Plugins</a>
         </div>
         <div data-type="horizontal" data-role="controlgroup" class="ui-btn-right"> 
           <a href="#jobs" data-icon="refresh" data-role="button" data-direction="reverse">Jobs</a>
--- a/OrthancExplorer/explorer.js	Tue Oct 09 14:19:48 2018 +0200
+++ b/OrthancExplorer/explorer.js	Tue Oct 09 16:27:49 2018 +0200
@@ -13,6 +13,8 @@
 //$.mobile.defaultPageTransition = 'slide';
 
 
+var LIMIT_RESOURCES = 100;
+
 var currentPage = '';
 var currentUuid = '';
 
@@ -360,57 +362,169 @@
 
 
 
+$('#lookup').live('pagebeforeshow', function() {
+  // NB: "GenerateDicomDate()" is defined in "query-retrieve.js"
+  var target = $('#lookup-study-date');
+  $('option', target).remove();
+  target.append($('<option>').attr('value', '*').text('Any date'));
+  target.append($('<option>').attr('value', GenerateDicomDate(0)).text('Today'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-1)).text('Yesterday'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-7) + '-').text('Last 7 days'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-31) + '-').text('Last 31 days'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-31 * 3) + '-').text('Last 3 months'));
+  target.append($('<option>').attr('value', GenerateDicomDate(-365) + '-').text('Last year'));
+  target.selectmenu('refresh');
+
+  $('#lookup-result').hide();
+});
+
+
+$('#lookup-submit').live('click', function() {
+  $('#lookup-result').hide();
+
+  var lookup = {
+    'Level' : 'Study',
+    'Expand' : true,
+    'Limit' : LIMIT_RESOURCES + 1,
+    'Query' : {
+      'StudyDate' : $('#lookup-study-date').val()
+    }
+  };
+
+  $('#lookup-form input').each(function(index, input) {
+    if (input.value.length != 0) {
+      if (input.id == 'lookup-patient-id') {
+        lookup['Query']['PatientID'] = input.value;
+      } 
+      else if (input.id == 'lookup-patient-name') {
+        lookup['Query']['PatientName'] = input.value;
+      } 
+      else if (input.id == 'lookup-accession-number') {
+        lookup['Query']['AccessionNumber'] = input.value;
+      } 
+      else if (input.id == 'lookup-study-description') {
+        lookup['Query']['StudyDescription'] = input.value;
+      }
+      else {
+        console.error('Unknown lookup field: ' + input.id);
+      }
+    } 
+  });
+
+  console.log(lookup);
+
+  $.ajax({
+    url: '../tools/find',
+    type: 'POST', 
+    data: JSON.stringify(lookup),
+    dataType: 'json',
+    async: false,
+    error: function() {
+      alert('Error during lookup');
+    },
+    success: function(studies) {
+      console.log(studies);
+      FormatListOfStudies('#lookup-result ul', '#lookup-alert', '#lookup-count', studies);
+      $('#lookup-result').show();
+    }
+  });
+
+  return false;
+});
+
+
 $('#find-patients').live('pagebeforeshow', function() {
-  GetResource('/patients?expand', function(patients) {
+  GetResource('/patients?expand&since=0&limit=' + (LIMIT_RESOURCES + 1), function(patients) {
     var target = $('#all-patients');
     $('li', target).remove();
     
     SortOnDicomTag(patients, 'PatientName', false, false);
 
-    for (var i = 0; i < patients.length; i++) {
+    var count, showAlert;
+    if (patients.length <= LIMIT_RESOURCES) {
+      count = patients.length;
+      showAlert = false;
+    }
+    else {
+      count = LIMIT_RESOURCES;
+      showAlert = true;
+    }
+
+    for (var i = 0; i < count; i++) {
       var p = FormatPatient(patients[i], '#patient?uuid=' + patients[i].ID);
       target.append(p);
     }
 
-    target.listview('refresh');
+    target.listview('refresh'); 
+
+    if (showAlert) {
+      $('#count-patients').text(LIMIT_RESOURCES);
+      $('#alert-patients').show();
+    } else {
+      $('#alert-patients').hide();
+    }
   });
 });
 
 
 
-$('#find-studies').live('pagebeforeshow', function() {
-  GetResource('/studies?expand', function(studies) {
-    var target = $('#all-studies');
-    $('li', target).remove();
+function FormatListOfStudies(targetId, alertId, countId, studies)
+{
+  var target = $(targetId);
+  $('li', target).remove();
+
+  for (var i = 0; i < studies.length; i++) {
+    var patient = studies[i].PatientMainDicomTags.PatientName;
+    var study = studies[i].MainDicomTags.StudyDescription;
 
-    for (var i = 0; i < studies.length; i++) {
-      var patient = studies[i].PatientMainDicomTags.PatientName;
-      var study = studies[i].MainDicomTags.StudyDescription;
+    var s;
+    if (typeof patient === 'string') {
+      s = patient;
+    }
 
-      var s;
-      if (typeof patient === 'string') {
-        s = patient;
+    if (typeof study === 'string') {
+      if (s.length > 0) {
+        s += ' - ';
       }
 
-      if (typeof study === 'string') {
-        if (s.length > 0) {
-          s += ' - ';
-        }
-
-        s += study;
-      }
-
-      studies[i]['Label'] = s;
+      s += study;
     }
 
-    Sort(studies, function(a) { return a.Label }, false, false);
+    studies[i]['Label'] = s;
+  }
+
+  Sort(studies, function(a) { return a.Label }, false, false);
+
+
+  var count, showAlert;
+  if (studies.length <= LIMIT_RESOURCES) {
+    count = studies.length;
+    showAlert = false;
+  }
+  else {
+    count = LIMIT_RESOURCES;
+    showAlert = true;
+  }
 
-    for (var i = 0; i < studies.length; i++) {
-      var p = FormatStudy(studies[i], '#study?uuid=' + studies[i].ID, false, true);
-      target.append(p);
-    }
+  for (var i = 0; i < count; i++) {
+    var p = FormatStudy(studies[i], '#study?uuid=' + studies[i].ID, false, true);
+    target.append(p);
+  }
+
+  target.listview('refresh');
 
-    target.listview('refresh');
+  if (showAlert) {
+    $(countId).text(LIMIT_RESOURCES);
+    $(alertId).show();
+  } else {
+    $(alertId).hide();
+  }
+}
+
+
+$('#find-studies').live('pagebeforeshow', function() {
+  GetResource('/studies?expand&since=0&limit=' + (LIMIT_RESOURCES + 1), function(studies) {
+    FormatListOfStudies('#all-studies', '#alert-studies', '#count-studies', studies);
   });
 });