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

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