source: trunk/grails-app/services/nl/tno/massSequencing/integration/SynchronizationService.groovy @ 50

Last change on this file since 50 was 50, checked in by robert@…, 9 years ago
  • Added links to remove samples from a run
  • Added synchronization of authorization for all studies, if a user logs in
  • Improved excel import to handle Error-values in a cell
File size: 24.3 KB
Line 
1package nl.tno.massSequencing.integration
2
3import nl.tno.massSequencing.*
4import nl.tno.massSequencing.auth.*
5import org.codehaus.groovy.grails.commons.ConfigurationHolder
6
7class SynchronizationService {
8        def gscfService
9        def trashService
10
11        String sessionToken = ""        // Session token to use for communication
12        User user = null                        // Currently logged in user. Must be set when synchronizing authorization
13        boolean eager = false           // When set to true, this method fetches data about all studies from GSCF. Otherwise, it will only look at the
14        // studies marked as dirty in the database. Defaults to false.
15
16        // Keeps track of the last time this module performed a full synchronization.
17        static Date lastFullSynchronization = null;
18
19        static transactional = true
20
21        /**
22         * Determines whether the synchronization should be performed or not. This can be entered
23         * in configuration, to avoid synchronization when developing.
24         * @return
25         */
26        protected performSynchronization() {
27                def conf = ConfigurationHolder.config.massSequencing.synchronization;
28
29                // If nothing is entered in configuration, return true (default value)
30                if( conf == null )
31                        return true
32
33                return conf
34        }
35       
36        /**
37         * Returns true iff a full synchronization should be performed
38         * @return
39         */
40        public boolean timeForFullSynchronization() {
41                if( SynchronizationService.lastFullSynchronization == null )
42                        return true
43                       
44                // Compute the time since the last full synchronization in  milliseconds
45                Date today = new Date();
46                long difference = SynchronizationService.lastFullSynchronization.getTime() - today.getTime()
47               
48                if( difference / 1000 > ConfigurationHolder.config.massSequencing.fullSynchronization )
49                        return true
50                else
51                        return false
52        }
53
54        /**
55         * Redirects to a temporary page to give the user a 'waiting' page while synchronizing
56         * @return
57         */
58        public String urlForFullSynchronization( def params ) {
59                def returnUrl = ConfigurationHolder.config.grails.serverURL
60                if (params.controller != null){
61                        returnUrl += "/${params.controller}"
62                        if (params.action != null){
63                                returnUrl += "/${params.action}"
64                                if (params.id != null){
65                                        returnUrl += "/${params.id}"
66                                }
67                        }
68                }
69               
70                // Append other parameters
71                returnUrl += "?" + params.collect {
72                        if( it.key != "controller" && it.key != "action" && it.key != "id" )
73                                return it.key.toString().encodeAsURL() + "=" + it.value.toString().encodeAsURL();
74                        else
75                                return ""
76                }.findAll { it }.join( "&" );
77               
78                if( timeForFullSynchronization() ) {
79                        return ConfigurationHolder.config.grails.serverURL + "/synchronize/full?redirect=" + returnUrl.encodeAsURL()
80                } else {
81                        return returnUrl
82                }
83        }
84
85        /**
86         * Performs a full synchronization in order to retrieve all studies
87         * @return
88         */
89        public void fullSynchronization() {
90                def previousEager = this.eager
91                this.eager = true
92                this.synchronizeStudies();
93                this.eager = previousEager
94               
95                SynchronizationService.lastFullSynchronization = new Date();
96        }
97
98        /**
99         * Synchronizes all studies with the data from GSCF.
100         * @return      ArrayList       List of studies or null if the synchronization has failed
101         */
102        public ArrayList<Study> synchronizeStudies() throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception {
103                if( !performSynchronization() )
104                        return Study.findAllWhereTrashcan(false)
105
106                // When eager fetching is enabled, ask for all studies, otherwise only ask for studies marked dirty
107                // Synchronization is performed on all studies, not only the studies the user has access to. Otherwise
108                // we would never notice that a user was given read-access to a study.
109                def studies
110                if( eager ) {
111                        studies = []
112                        log.trace "Eager synchronization";
113                } else {
114                        studies = Study.findAllWhere( [trashcan: false, isDirty: true] );
115                        log.trace "Default synchronization: " + studies.size()
116
117                        // Perform no synchronization if no studies have to be synchronized
118                        if( studies.size() == 0 )
119                                return []
120                }
121               
122                // Perform synchronization on only one study directly, because otherwise
123                // the getStudies method could throw a ResourceNotFoundException or NotAuthorizedException
124                // that can better be handled by synchronizeStudy
125                if( studies.size() == 1 ) {
126                        def newStudy = synchronizeStudy( studies[0] );
127                        if( newStudy )
128                                return [ newStudy ];
129                        else
130                                return []
131                }
132
133                // Fetch all studies from GSCF
134                def newStudies
135                try {
136                        if( !eager ) {
137                                def studyTokens = studies.studyToken;
138
139                                if( studyTokens instanceof String ) {
140                                        studyTokens = [studyTokens];
141                                }
142                                println "Only updating studies with tokens " + studyTokens.join( ',' );
143                                newStudies = gscfService.getStudies(sessionToken, studyTokens)
144                        } else {
145                                newStudies = gscfService.getStudies(sessionToken)
146                        }
147                } catch( Exception e ) { // All exceptions are thrown.
148                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
149                        // synchronizing and return null
150                        log.error( "Exception occurred when fetching studies: " + e.getMessage() )
151                        throw e
152                }
153
154                synchronizeStudies( newStudies );
155                studies = handleDeletedStudies( studies, newStudies );
156
157                log.trace( "Returning " + studies.size() + " studies after synchronization" )
158
159                return studies
160        }
161
162        /**
163         * Synchronizes all studies given by 'newStudies' with existing studies in the database, and adds them
164         * if they don't exist
165         *
166         * @param newStudies            JSON object with studies as returned by GSCF
167         * @return
168         */
169        protected synchronizeStudies( def newStudies ) {
170                // Synchronize all studies that are returned. Studies that are not returned by GSCF might be removed
171                // but could also be invisible for the current user.
172                newStudies.each { gscfStudy ->
173                        if( gscfStudy.studyToken ) {
174                                log.trace( "Processing GSCF study " + gscfStudy.studyToken + ": " + gscfStudy )
175
176                                Study studyFound = Study.findByStudyToken( gscfStudy.studyToken as String )
177
178                                if(studyFound) {
179                                        log.trace( "Study found with name " + studyFound.name )
180
181                                        // Synchronize the study itself with the data retrieved
182                                        synchronizeStudy( studyFound, gscfStudy );
183                                } else {
184                                        log.trace( "Study not found. Creating a new one" )
185
186                                        // If it doesn't exist, create a new object
187                                        studyFound = new Study( studyToken: gscfStudy.studyToken, name: gscfStudy.title, isDirty: true );
188                                        studyFound.save();
189
190                                        // Synchronize authorization and study assays (since the study itself is already synchronized)
191                                        synchronizeAuthorization(studyFound);
192                                        if( studyFound.canRead( user ) )
193                                                synchronizeStudyAssays(studyFound);
194
195                                        // Mark the study as clean
196                                        studyFound.isDirty = false
197                                        studyFound.save();
198                                }
199                        }
200                }
201        }
202
203        /**
204         * Removes studies from the database that are expected but not found in the list from GSCF
205         * @param studies               List with existing studies in the database that were expected in the output of GSCF
206         * @param newStudies    JSON object with studies as returned by GSCF
207         * @return                              List of remaining studies
208         */
209        protected ArrayList<Study> handleDeletedStudies( def studies, def newStudies ) {
210                // If might also be that studies have been removed from the system. In that case, the studies
211                // should be deleted from this module as well. Looping backwards in order to avoid conflicts
212                // when removing elements from the list
213               
214                def numStudies = studies.size();
215                for( int i = numStudies - 1; i >= 0; i-- ) {
216                        def existingStudy = studies[i];
217
218                        def studyFound = newStudies.find { it.studyToken == existingStudy.studyToken }
219
220                        if( !studyFound ) {
221                                log.trace( "Study " + existingStudy.studyToken + " not found. Check whether it is removed or the user just can't see it." )
222
223                                // Study was not given to us by GSCF. This might be because the study is removed, or because the study is not visible (anymore)
224                                // to the current user.
225                                // Synchronize authorization and see what is the case (it returns null if the study has been deleted)
226                                if( synchronizeAuthorization( existingStudy ) == null ) {
227                                        // Update studies variable to keep track of all existing studies
228                                        studies.remove( existingStudy )
229                                }
230                        }
231                }
232
233                return studies
234        }
235
236        /**
237         * Synchronizes the given study with the data from GSCF
238         * @param       study   Study to synchronize
239         * @return      Study   Synchronized study or null if the synchronization has failed
240         */
241        public Study synchronizeStudy(Study study ) {
242                if( !performSynchronization() )
243                        return study
244
245                if( study == null )
246                        return null
247
248                // Trashcan should never be synchronized
249                if( study.trashcan )
250                        return study
251                       
252                // If the study hasn't changed, don't update anything
253                if( !eager && !study.isDirty )
254                        return study;
255
256                // Retrieve the study from GSCF
257                def newStudy
258                try {
259                        newStudy = gscfService.getStudy(sessionToken, study.studyToken)
260                } catch( NotAuthorizedException e ) {
261                        // User is not authorized to access this study. Update the authorization within the module and return
262                        synchronizeAuthorization( study );
263                        return null
264                } catch( ResourceNotFoundException e ) {
265                        // Study can't be found within GSCF.
266                        trashService.moveToTrash( study );
267                        return null
268                } catch( Exception e ) { // All other exceptions
269                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
270                        // synchronizing and return null
271                        e.printStackTrace()
272                        log.error( "Exception occurred when fetching study " + study.studyToken + ": " + e.getMessage() )
273                        throw new Exception( "Error while fetching study " + study.studyToken, e)
274                }
275
276                // If no study is returned, something went wrong.
277                if( newStudy.size() == 0 ) {
278                        throw new Exception( "No data returned for study " + study.studyToken + " but no error has occurred either. Please contact your system administrator" );
279                        return null;
280                }
281
282                return synchronizeStudy(study, newStudy);
283        }
284
285        /**
286         * Synchronizes the given study with the data from GSCF
287         * @param       study           Study to synchronize
288         * @param       newStudy        Data to synchronize the study with
289         * @return      Study           Synchronized study or null if the synchronization has failed
290         */
291        protected Study synchronizeStudy(Study study, def newStudy) {
292                if( !performSynchronization() )
293                        return study
294
295                if( study == null || newStudy == null)
296                        return null
297
298                // If the study hasn't changed, don't update anything
299                if( !eager && !study.isDirty ) {
300                        return study;
301                }
302
303                // If no study is returned, something went wrong.
304                if( newStudy.size() == 0 ) {
305                        return null;
306                }
307
308                // Mark study dirty to enable synchronization
309                study.isDirty = true;
310                synchronizeAuthorization( study );
311                if( study.canRead( user ) )
312                        synchronizeStudyAssays( study );
313
314                // Update properties and mark as clean
315                study.name = newStudy.title
316                study.isDirty = false;
317                study.save(flush:true)
318
319                return study
320        }
321
322        /**
323         * Synchronizes the assays of the given study with the data from GSCF
324         * @param study         Study of which the assays should be synchronized
325         * @return      ArrayList       List of assays or null if the synchronization has failed
326         */
327        protected ArrayList<Assay> synchronizeStudyAssays( Study study ) {
328                if( !performSynchronization() )
329                        return study.assays.toList()
330
331                if( !eager && !study.isDirty )
332                        return study.assays as List
333
334                // Also update all assays, belonging to this study
335                // Retrieve the assays from GSCF
336                def newAssays
337                try {
338                        newAssays = gscfService.getAssays(sessionToken, study.studyToken)
339                } catch( Exception e ) { // All exceptions are thrown. If we get a NotAuthorized or NotFound Exception, something has changed in between the two requests. This will result in an error
340                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
341                        // synchronizing and return null
342                        log.error( "Exception occurred when fetching assays for study " + study.studyToken + ": " + e.getMessage() )
343                        throw new Exception( "Error while fetching samples for assay " + study.studyToken, e)
344                }
345
346                // If no assay is returned, we remove all assays
347                // from this study and return an empty list
348                if( newAssays.size() == 0 && study.assays != null ) {
349                        def studyAssays = study.assays.toArray();
350                        def numStudyAssays = study.assays.size();
351                        for( int i = numStudyAssays - 1; i >= 0; i-- ) {
352                                def existingAssay = studyAssays[i];
353                               
354                                // Move data to trash
355                                trashService.moveToTrash( existingAssay );
356                        }
357
358                        return []
359                }
360
361                synchronizeStudyAssays( study, newAssays );
362                return handleDeletedAssays( study, newAssays );
363        }
364
365        /**
366         * Synchronizes the assays of a study with the given data from GSCF
367         * @param study         Study to synchronize
368         * @param newAssays     JSON object given by GSCF to synchronize the assays with
369         */
370        protected void synchronizeStudyAssays( Study study, def newAssays ) {
371                // Otherwise, we search for all assays in the new list, if they
372                // already exist in the list of assays
373                newAssays.each { gscfAssay ->
374                        if( gscfAssay.assayToken ) {
375                                log.trace( "Processing GSCF assay " + gscfAssay.assayToken + ": " + gscfAssay )
376
377                                Assay assayFound = study.assays.find { it.assayToken == gscfAssay.assayToken }
378
379                                if(assayFound) {
380                                        log.trace( "Assay found with name " + assayFound.name )
381
382                                        // Synchronize the assay itself with the data retrieved
383                                        synchronizeAssay( assayFound, gscfAssay );
384                                } else {
385                                        log.trace( "Assay not found in study. Creating a new one" )
386
387                                        // If it doesn't exist, create a new object
388                                        assayFound = new Assay( assayToken: gscfAssay.assayToken, name: gscfAssay.name, study: study );
389
390                                        log.trace( "Connecting assay to study" )
391                                        study.addToAssays( assayFound );
392                                        assayFound.save()
393
394                                        // Synchronize assay samples (since the assay itself is already synchronized)
395                                        synchronizeAssaySamples(assayFound)
396                                }
397                        }
398                }
399        }
400
401        /**
402         * Removes assays from the system that have been deleted from GSCF
403         * @param study         Study to synchronize
404         * @param newAssays     JSON object given by GSCF to synchronize the assays with
405         * @return      List with all assays from the study
406         */
407        protected ArrayList<Assay> handleDeletedAssays( Study study, def newAssays ) {
408                if( study.assays == null ) {
409                        return []
410                }
411
412                // If might also be that assays have been removed from this study. In that case, the removed assays
413                // should be deleted from this study in the module as well. Looping backwards in order to avoid conflicts
414                // when removing elements from the list
415                def assays = study.assays.toArray();
416                def numAssays = assays.size();
417                for( int i = numAssays - 1; i >= 0; i-- ) {
418                        def existingAssay = assays[i];
419
420                        Assay assayFound = newAssays.find { it.assayToken == existingAssay.assayToken }
421
422                        if( !assayFound ) {
423                                log.trace( "Assay " + existingAssay.assayToken + " not found. Removing it." )
424
425                                // The assay has been removed
426                                trashService.moveToTrash( existingAssay );
427                        }
428                }
429
430                return study.assays.toList()
431        }
432
433        /**
434         * Retrieves the authorization for the currently logged in user
435         * Since GSCF only provides authorization information about the currently
436         * logged in user, we can not guarantee that the authorization information
437         * is synchronized for all users.
438         *
439         * Make sure synchronizationService.user is set beforehand
440         * 
441         * @param study Study to synchronize authorization for
442         * @return      Auth object for the given study and user
443         */
444        public Auth synchronizeAuthorization(Study study) {
445                // If the user is not set, we can't save anything to the database.
446                if( user == null ) {
447                        throw new Exception( "Property user of SynchronizationService must be set to the currently logged in user" );
448                }
449
450                // Only perform synchronization if needed
451                if( !eager && !study.isDirty )
452                        return Auth.findByUserAndStudy( user, study )
453
454                if( !performSynchronization() )
455                        return Auth.findByUserAndStudy( user, study )
456
457                def gscfAuthorization
458                try {
459                        gscfAuthorization = gscfService.getAuthorizationLevel( sessionToken, study.studyToken )
460                } catch( ResourceNotFoundException e ) {
461                        // Study has been deleted, remove all authorization on that study
462                        log.trace( "Study " + study.studyToken + " has been deleted. Remove all authorization on that study")
463                        trashService.moveToTrash( study );
464
465                        return null
466                }
467
468                // Update the authorization object, or create a new one
469                Auth a = Auth.authorization( study, user )
470
471                if( !a ) {
472                        log.trace( "Authorization not found for " + study.studyToken + " and " + user.username + ". Creating a new object" );
473
474                        a = Auth.createAuth( study, user );
475                }
476
477                // Copy properties from gscf object
478                if( gscfAuthorization.canRead instanceof Boolean  )
479                        a.canRead = gscfAuthorization.canRead.booleanValue()
480               
481                if( gscfAuthorization.canWrite instanceof Boolean )
482                        a.canWrite = gscfAuthorization.canWrite.booleanValue()
483               
484                if( gscfAuthorization.isOwner instanceof Boolean )
485                        a.isOwner = gscfAuthorization.isOwner.booleanValue()
486                       
487                a.save()
488
489                return a
490        }
491
492        /**
493         * Synchronizes the given assay with the data from GSCF
494         * @param assay         Assay to synchronize
495         * @return      Assay   Synchronized assay or null if the synchronization has failed
496         */
497        public Assay synchronizeAssay(Assay assay) {
498                if( !performSynchronization() )
499                        return assay
500
501                if( assay == null )
502                        return null
503
504                // Only perform synchronization if needed
505                if( !eager && !assay.study.isDirty )
506                        return assay
507
508                // Retrieve the assay from GSCF
509                def newAssay
510                try {
511                        newAssay = gscfService.getAssay(sessionToken, assay.study.studyToken, assay.assayToken)
512                } catch( NotAuthorizedException e ) {
513                        // User is not authorized to access this study. Update the authorization within the module and return
514                        synchronizeAuthorization( assay.study );
515                        return null
516                } catch( ResourceNotFoundException e ) {
517                        // Assay can't be found within GSCF.
518                        trashService.moveToTrash( assay );
519                        return null
520                } catch( Exception e ) { // All other exceptions are thrown
521                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
522                        // synchronizing and return null
523                        log.error( "Exception occurred when fetching assay " + assay.assayToken + ": " + e.getMessage() )
524                        throw new Exception( "Error while fetching assay " + assay.assayToken, e)
525                }
526
527                // If new assay is empty, this means that the assay does exist, but now belongs to another module. Remove it from our system
528                if( newAssay.size() == 0 ) {
529                        log.info( "No data is returned by GSCF for assay  " + assay.assayToken + "; probably the assay is connected to another module." )
530                        trashService.moveToTrash( assay );
531                        return null;
532                }
533
534                return synchronizeAssay( assay, newAssay );
535        }
536
537        /**
538         * Synchronizes the given assay with the data given
539         * @param assay         Assay to synchronize
540         * @param newAssay      New data for the assay, retrieved from GSCF
541         * @return      Assay   Synchronized assay or null if the synchronization has failed
542         */
543        protected Assay synchronizeAssay(Assay assay, def newAssay) {
544                if( !performSynchronization() )
545                        return assay
546
547                if( assay == null || newAssay == null )
548                        return null
549
550                // Only perform synchronization if needed
551                if( !eager && !assay.study.isDirty )
552                        return assay
553
554                // If new assay is empty, something went wrong
555                if( newAssay.size() == 0 ) {
556                        return null;
557                }
558
559                log.trace( "Assay is found in GSCF: " + assay.name + " / " + newAssay )
560                if( newAssay?.name ) {
561                        assay.name = newAssay.name
562                        assay.save()
563                }
564
565                // Synchronize samples
566                synchronizeAssaySamples(assay);
567
568                return assay
569        }
570
571        /**
572         * Synchronizes the samples of a given assay with the data from GSCF
573         * @param       assay   Assay to synchronize
574         * @return      Sample  List of samples or null if the synchronization failed
575         */
576        protected ArrayList<AssaySample> synchronizeAssaySamples(Assay assay) {
577                if( !performSynchronization() )
578                        return assay.assaySamples.toList()
579
580                // If no assay is given, return null
581                if( assay == null )
582                        return null
583
584                // Retrieve the assay from GSCF
585                def newSamples
586                try {
587                        newSamples = gscfService.getSamples(sessionToken, assay.assayToken)
588                } catch( NotAuthorizedException e ) {
589                        // User is not authorized to access this study. Update the authorization within the module and return
590                        synchronizeAuthorization( assay.study );
591                        return null
592                } catch( ResourceNotFoundException e ) {
593                        // Assay can't be found within GSCF. Samples will be removed
594                        trashService.moveToTrash( assay );
595
596                        return null
597                } catch( Exception e ) {
598                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
599                        // synchronizing and return null
600                        log.error( "Exception occurred when fetching samples for assay " + assay.assayToken + ": " + e.getMessage() )
601                        throw new Exception( "Error while fetching samples for assay " + assay.assayToken, e)
602                }
603
604                // If no sample is returned, we remove all samples from the list
605                if( newSamples.size() == 0 ) {
606                        assay.removeAssaySamples();
607                        return []
608                }
609
610                synchronizeAssaySamples( assay, newSamples );
611                return handleDeletedSamples( assay, newSamples );
612        }
613
614        /**
615         * Synchronize all samples for a given assay with the data from GSCF
616         * @param assay                 Assay to synchronize samples for
617         * @param newSamples    New samples in JSON object, as given by GSCF
618         */
619        protected void synchronizeAssaySamples( Assay assay, def newSamples ) {
620                // Otherwise, we search for all samples in the new list, if they
621                // already exist in the list of samples
622                newSamples.each { gscfSample ->
623                        log.trace( "Processing GSCF sample " + gscfSample.sampleToken + ": " + gscfSample )
624                        if( gscfSample.name ) {
625
626                                AssaySample assaySampleFound = assay.assaySamples.find { it.sample.sampleToken == gscfSample.sampleToken }
627                                Sample sampleFound
628
629                                if(assaySampleFound) {
630                                        sampleFound = assaySampleFound.sample
631                                        log.trace( "AssaySample found with sample name " + sampleFound.name )
632
633                                        // Update the sample object if necessary
634                                        sampleFound.name = gscfSample.name
635                                        setSubjectAndEventFromGSCF( sampleFound, gscfSample );
636                                        sampleFound.save();
637                                } else {
638                                        log.trace( "AssaySample not found in assay" )
639
640                                        // Check if the sample already exists in the database.
641                                        sampleFound = Sample.findBySampleTokenAndStudy( gscfSample.sampleToken as String, assay.study )
642
643                                        if( sampleFound ){
644                                                log.trace( "Sample " + gscfSample.sampleToken + " is found in database. Updating if necessary" )
645
646                                                // Update the sample object if necessary
647                                                sampleFound.name = gscfSample.name
648                                                setSubjectAndEventFromGSCF( sampleFound, gscfSample );
649                                                sampleFound.save();
650                                        } else {
651                                                log.trace( "Sample " + gscfSample.sampleToken + " not found in database. Creating a new object." )
652
653                                                // If it doesn't exist, create a new object
654                                                sampleFound = new Sample( sampleToken: gscfSample.sampleToken, name: gscfSample.name, study: assay.study );
655                                                setSubjectAndEventFromGSCF( sampleFound, gscfSample );
656                                                assay.study.addToSamples( sampleFound );
657                                                sampleFound.save();
658                                        }
659
660                                        // Create a new assay-sample combination
661                                        log.trace( "Connecting sample to assay" )
662                                        assaySampleFound = new AssaySample();
663
664                                        assay.addToAssaySamples( assaySampleFound );
665                                        sampleFound.addToAssaySamples( assaySampleFound );
666
667                                        assaySampleFound.save()
668                                }
669                        }
670                }
671        }
672       
673        /**
674         * Copies the subject and event properties from the gscf sample to the local sample
675         * @param sample                Sample object to update
676         * @param gscfSample    Map with properties about the gscf sample ('subject', 'event' and 'startTime' are used)
677         */
678        private void setSubjectAndEventFromGSCF( sample, gscfSample ) {
679                sample.subject = gscfSample.subject && gscfSample.subject != "null" ? gscfSample.subject.toString() : ""
680
681                sample.event = gscfSample.event && gscfSample.event != "null" ? gscfSample.event.toString() : ""
682               
683                if( gscfSample.startTime && gscfSample.startTime != "null" ) 
684                        sample.event += " (" + gscfSample.startTime + ")";
685        }
686
687        /**
688         * Removes samples from the system that have been removed from an assay in GSCF
689         * @param assay                 Assay to remove samples for
690         * @param newSamples    JSON object with all samples for this assay as given by GSCF
691         */
692        protected ArrayList<AssaySample> handleDeletedSamples( Assay assay, def newSamples ) {
693                // If might also be that samples have been removed from this assay. In that case, the removed samples
694                // should be deleted from this assay. Looping backwards in order to avoid conflicts when removing elements
695                // from the list
696                if( assay.assaySamples != null ) {
697                        def assaySamples = assay.assaySamples.toArray();
698                        def numAssaySamples = assay.assaySamples.size();
699                        for( int i = numAssaySamples - 1; i >= 0; i-- ) {
700                                def existingSample = assaySamples[i];
701
702                                AssaySample sampleFound = newSamples.find { it.sampleToken == existingSample.sample.sampleToken }
703
704                                if( !sampleFound ) {
705                                        log.trace( "Sample " + existingSample.sample.sampleToken + " not found. Removing it." )
706
707                                        // The sample has been removed
708                                        trashService.moveToTrash( existingSample );
709                                }
710                        }
711                }
712
713                // Create a list of samples to return
714                if( assay.assaySamples )
715                        return assay.assaySamples.toList()
716                else
717                        return []
718        }
719}
Note: See TracBrowser for help on using the repository browser.