changeset 3195:5099415c8a08

Task #15167: Add full_sync for initial sync, refactor functions
author Chris Zimmerman <chris@teffalump.com>
date Sun, 03 Feb 2019 00:05:54 -0800
parents d3d1c96d090f
children 40b3470c3b75
files tryton/health_orthanc/health_orthanc.py tryton/health_orthanc/view/config_form.xml
diffstat 2 files changed, 105 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/tryton/health_orthanc/health_orthanc.py
+++ b/tryton/health_orthanc/health_orthanc.py
@@ -57,6 +57,7 @@
                ]
        cls._buttons.update({
               'do_sync': {},
+              'do_full_sync': {},
               })
 
     @classmethod
@@ -65,7 +66,14 @@
         cls.sync(servers)
 
     @classmethod
+    @ModelView.button
+    def do_full_sync(cls, servers):
+        cls.full_sync(servers)
+
+    @classmethod
     def sync(cls, servers=None):
+        """Sync from changes endpoint"""
+
         pool = Pool()
         patient = pool.get('gnuhealth.orthanc.patient')
         study = pool.get('gnuhealth.orthanc.study')
@@ -73,21 +81,26 @@
         if not servers:
             servers = cls.search(['domain', '!=', None])
 
+        logger.info('Starting sync')
         for server in servers:
+            logger.info('Getting new changes for {}'.format(server.label))
             orthanc = RestClient(server.domain, auth=auth(server.user, server.password))
             curr = server.last
             new_studies = []
+            update_studies = []
             new_patients = []
-            logger.info('Getting new changes')
+            update_patients = []
             while True:
                 changes = orthanc.get_changes(since=curr)
                 for change in changes['Changes']:
                     if change['ChangeType'] == 'NewStudy':
                         new_studies.append(orthanc.get_study(change['ID']))
-                        logger.info('New Study {}'.format(change['ID']))
+                    elif change['ChangeType'] == 'StableStudy':
+                        update_studies.append(orthanc.get_study(change['ID']))
                     elif change['ChangeType'] == 'NewPatient':
                         new_patients.append(orthanc.get_patient(change['ID']))
-                        logger.info('New Patient {}'.format(change['ID']))
+                    elif change['ChangeType'] == 'StablePatient':
+                        update_patients.append(orthanc.get_patient(change['ID']))
                     else:
                         pass
                 curr = changes['Last']
@@ -98,8 +111,34 @@
             study.create_studies(new_studies, server)
             server.last = curr
             server.sync_time = datetime.now()
+            logger.info('{} sync complete: {} new patients, {} new studies'.format(server.label, len(new_patients), len(new_studies)))
         cls.save(servers)
-        logger.info('Sync complete: {} new patients, {} new studies'.format(len(new_patients), len(new_studies)))
+
+    @classmethod
+    def full_sync(cls, servers=None):
+        """Import and create all current patients and studies on remote DICOM server
+
+                Useful for first sync on adding new server
+        """
+
+        pool = Pool()
+        Patient = pool.get('gnuhealth.orthanc.patient')
+        Study = pool.get('gnuhealth.orthanc.study')
+
+        if not servers:
+            servers = cls.search(['domain', '!=', None])
+
+        for server in servers:
+            logger.info('Full sync for {}'.format(server.label))
+            orthanc = RestClient(server.domain, auth=auth(server.user, server.password))
+            patients = [p for p in orthanc.get_patients(params={'expand': ''})]
+            studies = [s for s in orthanc.get_studies(params={'expand': ''})]
+            Patient.create_patients(patients, server)
+            Study.create_studies(studies, server)
+            server.last = orthanc.get_changes(last=True).get('Last')
+            server.sync_time = datetime.now()
+            logger.info('{} sync complete: {} new patients, {} new studies'.format(server.label, len(patients), len(studies)))
+        cls.save(servers)
 
     def get_since_sync(self, name):
         return datetime.now() - self.sync_time
@@ -124,32 +163,45 @@
     studies = fields.One2Many('gnuhealth.orthanc.study', 'patient', 'Studies', readonly=True)
     server = fields.Many2One('gnuhealth.orthanc.config', 'Server', readonly=True)
 
-    @classmethod
-    def create_patients(cls, patients, server=None):
-        '''Add patients'''
-        pool = Pool()
-        Patient = pool.get('gnuhealth.patient')
+    @staticmethod
+    def get_info_from_dicom(patients):
+        """Extract information for writing to database"""
 
-        create = []
+        data = []
         for patient in patients:
             try:
                 bd = datetime.strptime(patient['MainDicomTags']['PatientBirthDate'], '%Y%m%d').date()
             except:
                 bd = None
-            try:
-                g_patient = Patient.search([('puid', '=', patient['MainDicomTags']['PatientID'])], limit=1)[0]
-                logger.info('Matching PUID found for {}'.format(patient['ID']))
-            except:
-                g_patient = None
-            create.append({
+            data.append({
                     'name': patient.get('MainDicomTags').get('PatientName'),
                     'bd': bd,
                     'ident': patient.get('MainDicomTags').get('PatientID'),
                     'uuid': patient.get('ID'),
-                    'patient': g_patient,
-                    'server': server,
                     })
-        cls.create(create)
+        return data
+
+    @classmethod
+    def update_patients(cls, patients, server):
+        pass
+
+    @classmethod
+    def create_patients(cls, patients, server):
+        """Create patients"""
+
+        pool = Pool()
+        Patient = pool.get('gnuhealth.patient')
+
+        entries = cls.get_info_from_dicom(patients)
+        for entry in entries:
+            try:
+                g_patient = Patient.search([('puid', '=', entry['ident'])], limit=1)[0]
+                logger.info('Matching PUID found for {}'.format(entry['uuid']))
+            except:
+                g_patient = None
+            entry['server'] = server
+            entry['patient'] = g_patient
+        cls.create(entries)
 
 class OrthancStudy(ModelSQL, ModelView):
     '''Orthanc study'''
@@ -169,20 +221,14 @@
     def get_rec_name(self, name):
         return ': '.join((self.ident or self.uuid, self.description or ''))
 
-    @classmethod
-    def create_studies(cls, studies, server=None):
-        '''Add studies'''
-        create = []
-        pool = Pool()
-        Patient = pool.get('gnuhealth.orthanc.patient')
+    @staticmethod
+    def get_info_from_dicom(studies):
+        """Extract information for writing to database"""
+
+        data = []
 
         for study in studies:
             try:
-                patient = Patient.search([('uuid', '=', study['ParentPatient'])], limit=1)[0]
-            except:
-                patient = None
-                logger.warning('No parent patient found for study {}'.format(study['ID']))
-            try:
                 date = datetime.strptime(study['MainDicomTags']['StudyDate'], '%Y%m%d').date()
             except:
                 date = None
@@ -190,8 +236,8 @@
                 description = study['MainDicomTags']['RequestedProcedureDescription']
             except:
                 description = None
-            create.append({
-                'patient': patient,
+            data.append({
+                'parent_patient': study['ParentPatient'],
                 'uuid': study['ID'],
                 'description': description,
                 'date': date,
@@ -199,9 +245,32 @@
                 'institution': study.get('MainDicomTags').get('InstitutionName'),
                 'ref_phys': study.get('MainDicomTags').get('ReferringPhysicianName'),
                 'req_phys': study.get('MainDicomTags').get('RequestingPhysician'),
-                'server': server,
                 })
-        cls.create(create)
+        return data
+
+    @classmethod
+    def update_studies(cls, studies, server):
+        pass
+
+    @classmethod
+    def create_studies(cls, studies, server):
+        """Create studies"""
+
+        pool = Pool()
+        Patient = pool.get('gnuhealth.orthanc.patient')
+
+        entries = cls.get_info_from_dicom(studies)
+        for entry in entries:
+            try:
+                patient = Patient.search([('uuid', '=', entry['parent_patient']),
+                                            ('server', '=', server)], limit=1)[0]
+            except:
+                patient = None
+                logger.warning('No parent patient found for study {}'.format(entry['ID']))
+            entry.pop('parent_patient')
+            entry['server'] = server
+            entry['patient'] = patient
+        cls.create(entries)
 
 class Orthanc(ModelSQL, ModelView):
     '''Add Orthanc patient(s) to the main patient data'''
--- a/tryton/health_orthanc/view/config_form.xml
+++ b/tryton/health_orthanc/view/config_form.xml
@@ -22,4 +22,6 @@
     </group>
     <newline/>
     <button name="do_sync" help="Synchronize from Orthanc server" string="Sync" icon="gnuhealth-execute" confirm="Synchronize?"/>
+    <newline/>
+    <button name="do_full_sync" help="Fully synchronize from Orthanc server" string="Full sync" icon="gnuhealth-execute" confirm="Synchronize?"/>
 </form>