2 Incheckningar 5763f09482 ... 9b9acb3699

Upphovsman SHA1 Meddelande Datum
  david 9b9acb3699 Various bug fix 3 år sedan
  david 6cb069302c Fixed DICOM FindSCU issues 3 år sedan

+ 2 - 0
api.ipsocloud.com/api/v1/IIMTAPI.class.php

@@ -688,6 +688,8 @@ class IIMTAPI extends API {
         }
       case 'POST':
         switch($verb) {
+          case 'files-existing':
+            return array_merge($resArray, $this->PatientInterface->patientFilesExistingPost($this->User, $this->request));
           case 'create':
             return array_merge($resArray, $this->PatientInterface->patientCreatePost($this->User, $this->request));
           case 'create-visit':

+ 25 - 3
api.ipsocloud.com/api/v1/Models/AdminInterface.class.php

@@ -157,6 +157,21 @@ namespace Models {
       return $this->exportByID($data['ID']);
     }
     public function exportByID($ID) {
+      // rm old files
+      $now   = time();
+      $files = glob('../../storage/tmp/*.zip');
+      foreach($files as $F) {
+        if ($now - filemtime($F) >= 60 * 60 * 24 * 1) { // 1 day
+          unlink($F);
+        }
+      }
+      $files = glob('../../storage/tmp/*.csv');
+      foreach($files as $F) {
+        if ($now - filemtime($F) >= 60 * 60 * 24 * 1) { // 1 day
+          unlink($F);
+        }
+      }
+
       $statement = $this->DataInterface->DatabaseConnection->prepare(
         "SELECT * FROM user WHERE ID = $ID"
       );
@@ -172,13 +187,13 @@ namespace Models {
       $data = [];
       // header
       $data[] = 
-        'Visit_PatientID,Visit_Date,Visit_Created,Visit_Area,'.
-        'Media_Location,Media_Incidence,Media_Filename,Media_Width,Media_Height,Media_PixelWidth,Media_PixelHeight,Media_FrameCount,Media_FramePerSecond,'.
+        'Visit_PatientID,Visit_PatientLastname,Visit_PatientFistname,Visit_PatientBirth,Visit_PatientSex,Visit_ID,Visit_Date,Visit_Created,Visit_Area,'.
+        'Media_Side,Media_Location,Media_Incidence,Media_Filename,Media_Width,Media_Height,Media_PixelWidth,Media_PixelHeight,Media_FrameCount,Media_FramePerSecond,'.
         'Measure_Created,Measure_Frame,Measure_Distance,Measure_ImtMean,Measure_ImtMax,Measure_ImtStddev,Measure_IntimaMean,Measure_MediaMean,Measure_NearWall,Measure_QualityIndex,Measure_NumberOfPoints';
 
       // visit
       $statement = $this->DataInterface->DatabaseConnection->prepare("
-        SELECT patient.patientID, visit.* 
+        SELECT patient.*, visit.* 
         FROM patient, visit 
         WHERE patient.ID = visit.fk_patient 
         AND visit.area = 'carotid'
@@ -224,10 +239,17 @@ namespace Models {
           
           foreach($measures as $measure) {
             $V['Visit_PatientID'] = $visit['patientID'];
+            $V['Visit_PatientLastname'] = $visit['lastname'];
+            $V['Visit_PatientFistname'] = $visit['firstname'];
+            $V['Visit_PatientBirth'] = $visit['birthDate'];
+            $V['Visit_PatientSex'] = $visit['gender'];
+
+            $V['Visit_ID'] = $visit['number'];
             $V['Visit_Date'] = $visit['visitDate'];
             $V['Visit_Created'] = $visit['created'];
             $V['Visit_Area'] = $visit['area'];
 
+            $V['Media_Side'] = $media['side'];
             $V['Media_Location'] = $media['location'];
             $V['Media_Incidence'] = $media['incidence'];
             $V['Media_Filename'] = $media['filename'];

+ 25 - 3
api.ipsocloud.com/api/v1/Models/CtAdminInterface.class.php

@@ -926,6 +926,21 @@ namespace Models {
     public function ctAdminAuditECRFPost($User, $data) {
       $userID = $User->ID;
 
+      // rm old files
+      $now   = time();
+      $files = glob('../../storage/tmp/*.zip');
+      foreach($files as $F) {
+        if ($now - filemtime($F) >= 60 * 60 * 24 * 1) { // 1 day
+          unlink($F);
+        }
+      }
+      $files = glob('../../storage/tmp/*.csv');
+      foreach($files as $F) {
+        if ($now - filemtime($F) >= 60 * 60 * 24 * 1) { // 1 day
+          unlink($F);
+        }
+      }
+            
       // CT general data
       $statement = $this->DataInterface->DatabaseConnection->prepare("
         SELECT * FROM clinical_trial
@@ -942,14 +957,14 @@ namespace Models {
       $data = [];
       // header
       $data[] = 
-        'Visit_PatientID,Visit_Date,Visit_Created,Visit_Completion,Visit_Area,'.
+        'Visit_PatientID,Visit_PatientLastname,Visit_PatientFistname,Visit_PatientBirth,Visit_PatientSex,Visit_ID,Visit_Date,Visit_Created,Visit_Completion,Visit_Area,'.
         'Visit_ReaderID,Visit_InvestigatorID,Visit_CenterName,'.
-        'Media_Location,Media_Incidence,Media_Filename,Media_Width,Media_Height,Media_PixelWidth,Media_PixelHeight,Media_FrameCount,Media_FramePerSecond,'.
+        'Media_Side,Media_Location,Media_Incidence,Media_Filename,Media_Width,Media_Height,Media_PixelWidth,Media_PixelHeight,Media_FrameCount,Media_FramePerSecond,'.
         'Measure_Created,Measure_Frame,Measure_Distance,Measure_ImtMean,Measure_ImtMax,Measure_ImtStddev,Measure_IntimaMean,Measure_MediaMean,Measure_NearWall,Measure_QualityIndex,Measure_NumberOfPoints';
 
       // visit
       $statement = $this->DataInterface->DatabaseConnection->prepare("
-        SELECT patient.ctPatientID, patient.fk_user AS fk_investigator, visit.* 
+        SELECT patient.*, patient.ctPatientID, patient.fk_user AS fk_investigator, visit.* 
         FROM patient, visit 
         WHERE patient.ID = visit.fk_patient 
         AND visit.completed IS NOT NULL
@@ -1010,6 +1025,12 @@ namespace Models {
           
           foreach($measures as $measure) {
             $V['Visit_PatientID'] = $visit['ctPatientID'];
+            $V['Visit_PatientLastname'] = $visit['lastname'];
+            $V['Visit_PatientFistname'] = $visit['firstname'];
+            $V['Visit_PatientBirth'] = $visit['birthDate'];
+            $V['Visit_PatientSex'] = $visit['gender'];
+
+            $V['Visit_ID'] = $visit['number'];
             $V['Visit_Date'] = $visit['visitDate'];
             $V['Visit_Created'] = $visit['created'];
             $V['Visit_Completion'] = $visit['completed'];
@@ -1019,6 +1040,7 @@ namespace Models {
             $V['Visit_InvestigatorID'] = $visit['fk_investigator'];
             $V['Visit_CenterName'] = $center['name'];
 
+            $V['Media_Side'] = $media['side'];
             $V['Media_Location'] = $media['location'];
             $V['Media_Incidence'] = $media['incidence'];
             $V['Media_Filename'] = $media['filename'];

+ 202 - 32
api.ipsocloud.com/api/v1/Models/PatientInterface.class.php

@@ -74,6 +74,66 @@ namespace Models {
       ];
     }
 
+    /**
+     * Get patient files existing data.
+     */
+    public function patientFilesExistingPost($User, $data) {
+      $ID_user = $User->ID;
+
+      $where = ' P.ID = V.fk_patient ';
+      if($User->type=='physician') {
+        $where .= ' AND P.fk_user = '.$User->ID;
+      }
+      else if($User->type=='reader') {
+        $where .= ' AND V.fk_reader = '.$User->ID.' AND V.completed IS NULL ';
+      }
+      else {
+        $where .= ' AND P.fk_user = '.$User->ID;
+      }
+
+      $filter = '(P.lastname LIKE \'%'.$data['filter'].'%\' OR P.firstname LIKE \'%'.$data['filter'].'%\')';
+
+      $statement = $this->DataInterface->DatabaseConnection->prepare(
+        "SELECT JSON_EXTRACT(data, '$.patientListFields') AS data FROM settings"
+      );
+      if(!$statement->execute()) {
+        return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
+      }
+      $settings = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
+      $patientListFields=[];
+      foreach($settings as $S) {
+        $patientListFields[str_replace(' ', '_', $S->name)] = $S->display;
+      }
+
+      $statement = $this->DataInterface->DatabaseConnection->prepare(
+        "SELECT 
+         P.*, 
+         (SELECT COUNT(visit.ID) FROM visit WHERE fk_patient = P.ID) as visitCount, 
+         (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.visitDate, ',', COALESCE(visit.area, '')) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as visits, 
+         (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.number) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as reader_visits, 
+         (SELECT COUNT(media.ID) FROM media, visit WHERE JSON_VALUE(media.metrics, '$.fps') IS NULL AND media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as imageCount, 
+         (SELECT COUNT(media.ID) FROM media, visit WHERE media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as mediaCount, 
+         V.visitDate AS lastVisit, 
+         V.number AS visitNumber, 
+         V.ID AS visitID 
+         FROM patient P, visit V
+         WHERE $where AND $filter 
+         GROUP BY P.ID 
+         ORDER BY V.visitDate DESC, V.ID DESC"
+      );
+      if(!$statement->execute()) {
+        return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
+      }
+      $visits = $statement->fetchAll(\PDO::FETCH_ASSOC);
+
+      return [
+        'result' => 'OK', 
+        'visits' => $visits,
+        'user' => $User,
+        'patientListFields' => $patientListFields
+      ];
+    }
+
     /**
      * Get patient files new data.
      */
@@ -487,9 +547,12 @@ namespace Models {
       }
       $conf = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
 
+      $cmdLines = [];
+
       $cmdLine = 'findscu '.$conf->serverAddress.' '.$conf->queryPort.' -aet '.$conf->callingAET.' -aec '.$conf->calledAET.
-        ' -P -k QueryRetrieveLevel="PATIENT" -k PatientID="*" -k PatientName="'.$data['patient'].'*" -k PatientBirthDate="*" -k PatientSex="*"'.
+        ' -P -k QueryRetrieveLevel="PATIENT" -k PatientID="*" -k PatientName="*'.$data['patient'].'*" -k PatientBirthDate="*" -k PatientSex="*"'.
         ' -v 2>&1';
+      $cmdLines[] = $cmdLine;
 
       $output=null;
       $retval=null;
@@ -543,7 +606,7 @@ namespace Models {
       $studies = [];
       foreach($patients as $P) {
         $cmdLine = 'findscu '.$conf->serverAddress.' '.$conf->queryPort.' -aet '.$conf->callingAET.' -aec '.$conf->calledAET.
-          ' -P -k QueryRetrieveLevel="STUDY" -k PatientID="'.$P['PatientID'].'" -k StudyInstanceUID="*" -k StudyID="*" -k StudyDate="*" -k StudyTime="*"'.
+          ' -P -k QueryRetrieveLevel="STUDY" -k PatientID="'.$P['PatientID'].'" -k StudyInstanceUID="" -k StudyID="" -k StudyDate="" -k StudyTime=""'.
           ' -v 2>&1';
 
         $cmdLines[] = $cmdLine;
@@ -609,7 +672,9 @@ namespace Models {
 
       return [
         'studies' => $studies,
-        'result' => 'OK'
+        'studies2' => $studies2, 
+        'result' => 'OK',
+        'cmdLines' => $cmdLines
       ];
     }
     
@@ -674,9 +739,9 @@ namespace Models {
       // create or select visit
       $visitID = 0;
       $statement = $this->DataInterface->DatabaseConnection->prepare(
-        "SELECT visit.* FROM visit, patient WHERE patient.ID = visit.fk_patient AND visit.number = :visitID"
+        "SELECT visit.* FROM visit, patient WHERE patient.ID = visit.fk_patient AND visit.studyInstanceUID = :studyInstanceUID"
       );
-      $statement->bindParam(':visitID', $data['all']['StudyID']);
+      $statement->bindParam(':studyInstanceUID', $data['all']['StudyInstanceUID']);
       if(!$statement->execute()) {
         return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
       }
@@ -684,12 +749,13 @@ namespace Models {
       // insert visit
       if(count($visits)==0) {
         $statement = $this->DataInterface->DatabaseConnection->prepare(
-          "INSERT INTO visit(ID, number, visitDate, fk_patient) VALUES(0, :number, :visitDate, :fk_patient)"
+          "INSERT INTO visit(ID, number, visitDate, fk_patient, studyInstanceUID) VALUES(0, :number, :visitDate, :fk_patient, :studyInstanceUID)"
         );
         $sdate = substr($data['all']['StudyDate'],0,4).'-'.substr($data['all']['StudyDate'],4,2).'-'.substr($data['all']['StudyDate'],6,2);
         $statement->bindParam(':number', $data['all']['StudyID']);
         $statement->bindParam(':visitDate', $sdate);
         $statement->bindParam(':fk_patient', $patientID);
+        $statement->bindParam(':studyInstanceUID', $data['all']['StudyInstanceUID']);
         if(!$statement->execute()) {
           return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
         }
@@ -754,36 +820,88 @@ namespace Models {
             $newFiles++;
             rename($F, "$F.dicom");
 
-            // convert JPEG
-            $cmdLine = "dcmj2pnm $F.dicom $F.jpeg +oj +Jq 100 +F 1 -v 2>&1";
+            // multiframe?
+            $cmdLine = 'dcmdump +P 0028,0008 "'.$F.'.dicom"';
             $output=null;
             $retval=null;
             exec($cmdLine, $output, $retval);
-            // error
-            if($retval !== 0 || count($output)<1) {
-              return [
-                'result' => 'ERROR',
-                'cmdLine' => $cmdLine,
-                'output' => $output, 
-                'retval' => $retval
-              ];
-            }
-  
-            // image metrics
-            $metrics = ['width' => 0, 'height' => 0, 'pxwidth' => 0, 'pxheight' => 0];
-            list($metrics['width'], $metrics['height'], $type, $attr) = getimagesize("$F.jpeg");
+            // OK
+            if($retval === 0 && count($output)==1) {
+              $tab = explode(' ',trim($output[0]));
+              $frameCount = intval(str_replace(['[', ']'], '', $tab[2]));
+              // error
+              if($frameCount===0) {
+                return [
+                  'result' => 'ERROR',
+                  'output' => $output, 
+                ];
+              }
 
-            // dicom metrics
-            $cmdLine = 'dcmdump -s +P 0018,602c +P 0018,602e +P 0028,0008 "'.$F.'.dicom"';
-            $output=null;
-            $retval=null;
-            exec($cmdLine, $output, $retval);
-            // error
-            if($retval !== 0) {
-              if(count($output)==0) {
-                // no such calibration, likely not ULTRASOUND!
+              // make JPEG
+              $frames = [];
+              for($i=1; $i<=$frameCount; $i++) {
+                $src = "$F.dicom";
+                $pad = str_pad($i, 3, "0", STR_PAD_LEFT);
+                $dst = str_replace('.dicom', '-'.$pad.'.jpeg', $src);
+                $cmdLine = 'dcmj2pnm "'.$src.'" "'.$dst.'" +oj +Jq 100 +F '.$i.' -v 2>&1';
+                
+                $output=null;
+                $retval=null;
+                exec($cmdLine, $output, $retval);
+                // error
+                if($retval !== 0 || count($output)<1) {
+                  return [
+                    'result' => 'ERROR',
+                    'cmdLine' => $cmdLine,
+                    'output' => $output, 
+                    'retval' => $retval
+                  ];
+                }
+
+                $frames[] = $dst;
               }
-              else {
+
+              // make MP4
+              $src = "$F.dicom";
+              $dst = str_replace('.dicom', '-%03d.jpeg', $src);
+              $mp4 = str_replace('.dicom', '.mp4', $src);
+              
+              $cmdLine = 'ffmpeg -f image2 -pattern_type sequence -r 11 -i '.$dst.' -y -c:v libx264 -pix_fmt yuv420p '.$mp4;
+              $output=null;
+              $retval=null;
+              exec($cmdLine, $output, $retval);
+              // error
+              if($retval !== 0) {
+                return [
+                  'result' => 'ERROR',
+                  'cmdLine' => $cmdLine,
+                  'output' => $output, 
+                  'retval' => $retval
+                ];
+              }
+              
+              // cleanup
+              for($i=0; $i<count($frames); $i++) {
+                unlink($frames[$i]);
+              }
+
+              // get metrics
+              $cmdLine = 'ffprobe -v error -show_entries stream=width,height,r_frame_rate -of csv=p=0 '.$mp4;
+              $output=null;
+              $retval=null;
+              exec($cmdLine, $output, $retval);
+              // error
+              if($retval !== 0 || count($output)!=1) {
+                return [
+                  'result' => 'ERROR',
+                  'cmdLine' => $cmdLine,
+                  'output' => $output, 
+                  'retval' => $retval
+                ];
+              }
+    
+              $tab = explode(',',trim($output[0]));
+              if(count($tab)!=3) {
                 return [
                   'result' => 'ERROR',
                   'cmdLine' => $cmdLine,
@@ -791,13 +909,65 @@ namespace Models {
                   'retval' => $retval
                 ];
               }
+    
+              $metrics['width'] = intval($tab[0]);
+              $metrics['height'] = intval($tab[1]);
+    
+              $fps = $tab[2];
+              $tab = explode('/', $fps);
+              if(count($tab) == 2) {
+                $fps = intval($tab[0]);
+              }
+              else {
+                $fps = intval($fps);
+              }
+              $metrics['fps'] = $fps;              
+            }
+            // singleframe
+            else {
+              $cmdLine = "dcmj2pnm $F.dicom $F.jpeg +oj +Jq 100 +F 1 -v 2>&1";
+              $output=null;
+              $retval=null;
+              exec($cmdLine, $output, $retval);
+              // error
+              if($retval !== 0 || count($output)<1) {
+                return [
+                  'result' => 'ERROR',
+                  'cmdLine' => $cmdLine,
+                  'output' => $output, 
+                  'retval' => $retval
+                ];
+              }              
+
+              // image metrics
+              $metrics = ['width' => 0, 'height' => 0, 'pxwidth' => 0, 'pxheight' => 0];
+              list($metrics['width'], $metrics['height'], $type, $attr) = getimagesize("$F.jpeg");
             }
-            if(count($output)) {
+
+            // dicom metrics
+            $cmdLine = 'dcmdump -s +P 0018,602c +P 0018,602e +P 0028,0008 "'.$F.'.dicom"';
+            $output=null;
+            $retval=null;
+            exec($cmdLine, $output, $retval);
+            // error
+            if($retval !== 0) {
+              return [
+                'result' => 'ERROR',
+                'cmdLine' => $cmdLine,
+                'output' => $output, 
+                'retval' => $retval
+              ];
+            }
+            if(count($output)>=2) {
               $tab = explode(' ',trim($output[0]));
               $metrics['pxwidth'] = floatval($tab[2])*10.0;
               $tab = explode(' ',trim($output[1]));
               $metrics['pxheight'] = floatval($tab[2])*10.0;
             }
+            if(count($output)==3) {
+              $tab = explode(' ',trim($output[2]));
+              $metrics['frameCount'] = intval(str_replace(['[', ']'], '', $tab[2]));
+            }
     
             // insert new media
             $statement = $this->DataInterface->DatabaseConnection->prepare(

+ 6 - 0
www.ipsocloud.com/TODO.txt

@@ -15,3 +15,9 @@ Restart service
 
 /etc/init.t/apache2 restart
 
+
+
+
+
+findscu www.dicomserver.co.uk 11112 -aet IIMT -aec IIMT -P -k QueryRetrieveLevel="STUDY" -k PatientName="*" -k PatientID="285285" -k StudyInstanceUID="" -k StudyDate="" -k StudyTime="" -k AccessionNumber="" -k StudyID="" -v 2>&1
+

+ 2 - 1
www.ipsocloud.com/webapp_webpack/src/app.js

@@ -365,7 +365,8 @@ global.app = new Framework7({
         visitID: null
       },
       settings: null,
-      user: {}
+      user: {},
+      patientFilter: ''
     };
   },
   // App root methods

+ 12 - 2
www.ipsocloud.com/webapp_webpack/src/pages/patient-files-existing.f7.html

@@ -7,13 +7,13 @@
         <div class="searchbar" style="display: flex; flex-shrink: 1; justify-content: left; align-items: center;">
           <div class="searchbar-inner" style="position: relative; width: auto;">
             <div class="searchbar-input-wrap iimt-searchbar">
-              <input type="search" placeholder="{{js "global.tr[global.lang].form.searchPatient"}}">
+              <input type="search" id="patient-filter" placeholder="{{js "global.tr[global.lang].form.searchPatient"}}" value="{{js "app.data.patientFilter"}}" @keyPress="checkSearch">
               <i class="searchbar-icon"></i>
               <span class="input-clear-button"></span>
             </div>
             <span class="searchbar-disable-button">{{js "global.tr[global.lang].topLevel.cancel"}}</span>
           </div>
-          <a href="#" class="button iimt-button" style="margin-left:16px;">{{js "global.tr[global.lang].form.search"}}</a>
+          <a href="#" class="button iimt-button" style="margin-left:16px;" @click="search">{{js "global.tr[global.lang].form.search"}}</a>
         </div>
 
         <table>
@@ -151,6 +151,16 @@
       }
     },
     methods: {
+      checkSearch: function(e) {
+        if(e && e.keyCode == 13) {
+          this.search();
+        }
+      },
+      search: function() {
+        let filter = $$('#patient-filter').val();
+        app.data.patientFilter = filter;
+        app.views.homePatientFilesExistingView.router.refreshPage();
+      },
       selectPatient: function(ID, visits, reader_visits) {
         $$('.patient-row').css({
           'background-color': 'unset'

+ 50 - 0
www.ipsocloud.com/webapp_webpack/src/routes.js

@@ -274,6 +274,41 @@ export default [
     path: '/patient-files-existing/',
     async: function (routeTo, routeFrom, resolve, reject) {
       var app = this.app;
+
+      var data = {};
+      data.apiKey = '';
+      data.filter = app.data.patientFilter;
+
+      app.preloader.show();
+      app.request.post(app.data.config.apiBaseURL + '/patient/files-existing/', data, function (data) {
+        // Refresh token if needed
+        app.methods.refreshToken(data);
+        // Process response
+        console.log("patient/files-existing", data);
+        app.preloader.hide();
+        if (data.result == 'ERROR') {
+          reject();
+          switch (data.reason) {
+            case 'denied':
+              app.methods.signout(global.tr[global.lang].topLevel.warning.disconnected);
+              break;
+            default:
+              app.dialog.alert(global.tr[global.lang].topLevel.error.internal_error);
+              break;
+          }
+        }
+        else {
+          resolve({
+            component: PatientFilesExistingPage,
+          }, {
+            context: data,
+          });
+        }
+      }, function (data) {
+        console.log('error', data);
+      }, 'json');      
+
+      /*
       app.preloader.show();
       app.request.promise.json(app.data.config.apiBaseURL + '/patient/files-existing/?apiKey=')
         .then(data => {
@@ -306,6 +341,7 @@ export default [
           app.preloader.hide();
           app.dialog.alert(global.tr[global.lang].topLevel.error.server_unavailable);
         });
+      */
     }
   },
   // Patient files new page
@@ -752,6 +788,20 @@ export default [
           // Process response
           console.log("measure", data);
           app.preloader.hide();
+          // no location?
+          for(let i=0; i<data.media.length;i++) {
+            if(data.media[i].location==null) {
+              app.notification.create({
+                icon: '<i class="icon material-icons">info</i>',
+                title: global.tr[global.lang].topLevel.notification.title,
+                text: global.tr[global.lang].topLevel.notification.acquire_no_location,
+                closeTimeout: 3000,
+                closeButton: true,
+              }).open();
+              reject();
+              return;
+            }
+          }
           if (data.result == 'ERROR') {
             reject();
             switch (data.reason) {

+ 1 - 0
www.ipsocloud.com/webapp_webpack/src/tr-en.js

@@ -35,6 +35,7 @@ export default {
     notification: {
       title: "Notification",
       acquire_media: "No media is currently associated with this visit.",
+      acquire_no_location: "At least one location is missing in this visit.",
       message_sent: "Message sent.",
       pacs_sent: "Report sent to PACS.",
       mail_sent: "Report sent by e-mail."

+ 1 - 0
www.ipsocloud.com/webapp_webpack/src/tr-fr.js

@@ -35,6 +35,7 @@ export default {
     notification: {
       title: "Notification",
       acquire_media: "Aucun media n'est actuellement associé à cette visite.",
+      acquire_no_location: "Au moins une localisation est manquante dans cette visite.",
       message_sent: "Message envoyé.",
       pacs_sent: "Rapport envoyé vers le PACS.",
       mail_sent: "Rapport envoyé par e-mail."