Changeset 1213

Show
Ignore:
Timestamp:
29-11-10 16:56:48 (3 years ago)
Author:
robert@…
Message:

Improved the study overview page such that the different tabs are loaded when needed. Also rewritten parts of the code. See ticket #155

Location:
trunk
Files:
8 added
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/studycapturing/StudyController.groovy

    r1203 r1213  
    124124        } 
    125125    } 
     126 
     127        /** 
     128     * Shows the subjects tab of one or more studies. Is called when opening the subjects-tab 
     129         * on the study overview screen. 
     130     */ 
     131    def show_subjects = { 
     132                def studyList = readStudies( params.id ); 
     133 
     134                if( !studyList ) 
     135                        return 
     136 
     137                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     138    } 
     139 
     140        /** 
     141     * Shows the events timeline tab of one or more studies. Is called when opening the events timeline-tab 
     142         * on the study overview screen. 
     143     */ 
     144    def show_events_timeline = { 
     145                def studyList = readStudies( params.id ); 
     146 
     147                if( !studyList ) 
     148                        return 
     149 
     150                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     151    } 
     152 
     153        /** 
     154     * Shows the events table tab of one or more studies. Is called when opening the events table-tab 
     155         * on the study overview screen. 
     156     */ 
     157    def show_events_table = { 
     158                def studyList = readStudies( params.id ); 
     159 
     160                if( !studyList ) 
     161                        return 
     162 
     163                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     164    } 
     165 
     166        /** 
     167     * Shows the assays tab of one or more studies. Is called when opening the assays tab 
     168         * on the study overview screen. 
     169     */ 
     170    def show_assays = { 
     171                def studyList = readStudies( params.id ); 
     172 
     173                if( !studyList ) 
     174                        return 
     175 
     176                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     177    } 
     178 
     179        /** 
     180     * Shows the samples tab of one or more studies. Is called when opening the samples-tab 
     181         * on the study overview screen. 
     182     */ 
     183    def show_samples = { 
     184                def studyList = readStudies( params.id ); 
     185 
     186                if( !studyList ) 
     187                        return 
     188 
     189                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     190    } 
     191 
     192        /** 
     193     * Shows the persons tab of one or more studies. Is called when opening the persons tab 
     194         * on the study overview screen. 
     195     */ 
     196    def show_persons = { 
     197                def studyList = readStudies( params.id ); 
     198 
     199                if( !studyList ) 
     200                        return 
     201 
     202                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     203    } 
     204 
     205        /** 
     206     * Shows the publications tab of one or more studies. Is called when opening the publications tab 
     207         * on the study overview screen. 
     208     */ 
     209    def show_publications = { 
     210                def studyList = readStudies( params.id ); 
     211 
     212                if( !studyList ) 
     213                        return 
     214 
     215                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ), loggedInUser: AuthenticationService.getLoggedInUser() ] 
     216    } 
     217 
     218        /** 
     219         * Creates the javascript for showing the timeline of one or more studies 
     220         */ 
     221        def createTimelineBandsJs = { 
     222                def studyList = readStudies( params.id ); 
     223 
     224                if( !studyList ) 
     225                        return 
     226 
     227                [studyList: studyList, studyInstanceTotal: Study.count(), multipleStudies: ( studyList.size() > 1 ) ] 
     228        } 
     229 
     230    /** 
     231         * Reads one or more studies from the database and checks whether the logged 
     232         * in user is allowed to access them. 
     233         *  
     234         * Is used by several show_-methods 
     235         * 
     236         * @return List with Study objects or false if an error occurred. 
     237         */ 
     238        private def readStudies( id ) { 
     239                // If nothing has been selected, redirect the user 
     240                if( !id || !( id instanceof String)) { 
     241            response.status = 500; 
     242            render 'No study selected'; 
     243            return false 
     244                } 
     245 
     246                // Check whether one id has been selected or multiple. 
     247                def ids = URLDecoder.decode( id ).split( "," ); 
     248 
     249                // Parse strings to a long 
     250                def long_ids = [] 
     251                ids.each { long_ids.add( Long.parseLong( it ) ) } 
     252 
     253                def c = Study.createCriteria() 
     254 
     255        def studyList = c { 
     256                        maxResults( Math.min(params.max ? params.int('max') : 10, 100) ) 
     257                        'in'( "id", long_ids ) 
     258                } 
     259 
     260                // Check whether the user may see these studies 
     261                def studiesAllowed = [] 
     262        def loggedInUser = AuthenticationService.getLoggedInUser() 
     263 
     264                studyList.each { studyInstance -> 
     265            if( studyInstance.canRead(loggedInUser) ) { 
     266                                studiesAllowed << studyInstance 
     267            } 
     268                } 
     269 
     270                // If the user is not allowed to see any of the studies, return 404 
     271                if( studiesAllowed.size() == 0 ) { 
     272            response.status = 404; 
     273            render 'Selected studies not found'; 
     274            return false 
     275                } 
     276                 
     277                return studyList 
     278        } 
    126279 
    127280    def showByToken = { 
  • trunk/grails-app/domain/dbnp/authentication/SecUser.groovy

    r1144 r1213  
    2929        } 
    3030 
    31         public boolean equals(Object y) 
    32         { 
    33             if( !( y instanceof SecUser ) ) { 
    34                 return false; 
    35             } 
     31        public boolean equals(Object y) 
     32        { 
     33                if( !( y instanceof SecUser ) ) { 
     34                        return false; 
     35                } 
    3636 
    37             if (y == null) return false; 
     37                if (y == null) return false; 
    3838 
    39             return this.id == y.id 
    40         } 
     39                return this.id == y.id 
     40        } 
     41 
     42        public boolean hasAdminRights() { 
     43                return getAuthorities().contains( SecRole.findByAuthority( 'ROLE_ADMIN' ) ); 
     44        } 
     45 
    4146} 
  • trunk/grails-app/domain/dbnp/studycapturing/Subject.groovy

    r1198 r1213  
    6868                return name 
    6969        } 
     70 
     71        /** 
     72         * Returns a human readable string of a list of subjects, with a maximum number 
     73         * of characters 
     74         * 
     75         * @param subjectList   List with Subject objects 
     76         * @param maxChars              maximum number of characters returned 
     77         * @return                              human readble string with at most maxChars characters, representing the subjects given. 
     78         */ 
     79        public static String trimSubjectNames( ArrayList subjectList, Integer maxChars ) { 
     80                def simpleSubjects = subjectList.name.join( ', ' ); 
     81                def showSubjects 
     82                 
     83                // If the subjects will fit, show them all 
     84                if( !maxChars || simpleSubjects.size() < maxChars ) { 
     85                  showSubjects = simpleSubjects; 
     86                } else { 
     87                  // Always add the first name 
     88                  def subjectNames = subjectList[0]?.name; 
     89 
     90                  // Continue adding names until the length is to long 
     91                  def id = 0; 
     92                  subjectList.each { subject -> 
     93                        if( id > 0 ) { 
     94                          if( subjectNames?.size() + subject.name?.size() < maxChars - 15 ) { 
     95                                subjectNames += ", " + subject.name; 
     96                          } else { 
     97                                return; 
     98                          } 
     99                        } 
     100                        id++; 
     101                  } 
     102 
     103                  // Add a postfix 
     104                  subjectNames += " and " + ( subjectList?.size() - id ) + " more"; 
     105 
     106                  showSubjects = subjectNames; 
     107                } 
     108 
     109                return showSubjects 
     110        } 
    70111} 
  • trunk/grails-app/domain/dbnp/studycapturing/TemplateField.groovy

    r1175 r1213  
    353353        } 
    354354 
     355        /** 
     356         * Checks whether this field is filled in any of the entities in the given list 
     357         * 
     358         * @param       List    List of TemplateEntities to search in 
     359         * @return      boolean True iff any of the given entities has this field as template field, and has a value for it. False otherwise 
     360         */ 
     361        def isFilledInList( entityList ) { 
     362                if( !entityList ) 
     363                        return false; 
     364                         
     365                return true in entityList.collect { it.fieldExists( this.name ) && it.getFieldValue( this.name ) != null }?.flatten() 
     366        } 
     367 
    355368} 
  • trunk/grails-app/views/study/show.gsp

    r1206 r1213  
    1  
    21<%@ page import="dbnp.studycapturing.Study" %> 
    32<%@ page import="dbnp.studycapturing.EventGroup" %> 
     
    109    <title><g:message code="default.show.label" args="[entityName]" /></title> 
    1110    <script type="text/javascript"> 
    12       // Flag whether the timelines have been loaded 
    13       var timelineloaded = false; 
    14        
    1511      // Number of timelines that should be loaded 
    1612      var numTimelines = ${studyList?.size()}; 
     
    1915      $(function() { 
    2016              $("#tabs").tabs({ 
    21                 show: function(event, ui) { 
     17                load: function(event, ui) { 
    2218                  // If the events tab is shown, the timeline should be redrawn 
    23                   if( ui.tab.hash == '#events-timeline' && !timelineloaded ) { 
     19                  if( ui.tab.hash == '#Events_timeline' ) { 
    2420                    loadTimeline( 'eventstimeline', 'eventtitles', 0 ); 
    25                     timelineloaded = true; 
    2621                  } 
    27                 } 
     22                }, 
     23                                ajaxOptions: { 
     24                                        error: function( xhr, status, index, anchor ) { 
     25                                                $( anchor.hash ).html( 
     26                                                        "Couldn't load this tab. We'll try to fix this as soon as possible." ); 
     27                                        } 
     28                                } 
    2829              }); 
    2930      }); 
     31 
     32      // Parameters for the SIMILE timeline 
     33          Timeline_ajax_url="${resource(dir: 'js', file: 'timeline-simile/timeline_ajax/simile-ajax-api.js')}"; 
     34      Timeline_urlPrefix='${resource(dir: 'js', file: 'timeline-simile/')}'; 
     35      Timeline_parameters='bundle=true'; 
     36 
    3037    </script> 
    3138    <link rel="stylesheet" type="text/css" href="${resource(dir: 'css', file: 'studies.css')}"/> 
    3239 
    3340    <!-- Include scripts for the SIMILE timeline. See http://simile-widgets.org/wiki/ --> 
    34     <script type="text/javascript"> 
    35       Timeline_ajax_url="${resource(dir: 'js', file: 'timeline-simile/timeline_ajax/simile-ajax-api.js')}"; 
    36       Timeline_urlPrefix='${resource(dir: 'js', file: 'timeline-simile/')}'; 
    37       Timeline_parameters='bundle=true'; 
    38     </script> 
    3941    <script src="${resource(dir: 'js', file: 'timeline-simile/timeline-api.js')}" type="text/javascript"></script> 
    4042    <script src="${resource(dir: 'js', file: 'timeline-simile/custom-timeline.js')}" type="text/javascript"></script> 
     
    4345 
    4446    <!-- Create the JSON objects for the timeline with events --> 
    45     <script type="text/javascript"> 
    46          /* 
    47           * Creates timeline bands for displaying different timelines 
    48           * 
    49           * @returns array with BandInfo objects, as described on http://simile-widgets.org/wiki/Timeline_GettingStarted 
    50           */ 
    51         function createTimelineBands( timelineNr ) { 
    52           var bandInfos = []; 
    53           var eventSources = []; 
    54           var overviewEventSource = new Timeline.DefaultEventSource(); 
    55  
    56           // The way the timeline should look. See http://www.linuxjournal.com/article/9301 
    57           var theme = Timeline.ClassicTheme.create(); 
    58           var emptyEtherPainter = new Timeline.EmptyEtherPainter( { theme: theme } ) 
    59  
    60           // Now create the bands for all studies, and add them to one timeline 
    61           // Multiple timeline on one page do not seem to work 
    62           <g:set var="bandNr" value="${0}" /> 
    63           <g:each in="${studyList}" var="study" status="timelineNr"> 
    64               // The date that the timeline should start on 
    65               var dateStr = "<g:formatDate format="yyyy/MM/dd HH:mm:ss" date="${study.startDate}"/>"; 
    66               firstDate = new Date ( dateStr ); 
    67  
    68               //------------- Eventgroup overview --------------- 
    69  
    70               <g:set var="datesBandNr" value="${bandNr}" /> 
    71               // Add an empty band to show the dates 
    72               bandInfos[${bandNr}] = 
    73                      Timeline.createBandInfo({ 
    74                         width:          40, 
    75                         intervalUnit:   Timeline.DateTime.DAY, 
    76                         intervalPixels: 40, 
    77                         showEventText:  false, 
    78                         date:           firstDate, 
    79                         timeZone:       +1, 
    80                         layout:         'original', 
    81                         theme:          theme 
    82                      }); 
    83  
    84                           // Make sure the date is printed using the relative time 
    85               bandInfos[${bandNr}].etherPainter = new Timeline.RelativeDateEtherPainter( { theme: theme, startDate: firstDate, unit: Timeline.DateTime.DAY } ); 
    86                           bandInfos[${bandNr}].labeller = new Timeline.RelativeDateLabeller( "en", 0, firstDate ); 
    87  
    88               bandTitleInfo[ timelineNr ][ ${bandNr} ] = { 
    89                 title: "${study.title}", 
    90                 subjects: "", 
    91                 className: "studytitle" 
    92               }; 
    93  
    94               <g:set var="bandNr" value="${bandNr+1}" /> 
    95               <%  
    96                 def sortedEventGroups = study.eventGroups.sort( { a, b -> 
    97                     return a.name <=> b.name; 
    98                 }  as Comparator ); 
    99  
    100                 def orphans = study.getOrphanEvents(); 
    101                 if( orphans?.size() > 0 ) { 
    102                   sortedEventGroups.add( new EventGroup( 
    103                     id: -1, 
    104                     name: 'No group', 
    105                     events: orphans, 
    106                     subjects: [] 
    107                   )); 
    108                 } 
    109  
    110               %> 
    111               <g:each in="${sortedEventGroups}" var="eventGroup" status="i"> 
    112  
    113                 //------------- Eventgroup ${bandNr} --------------- 
    114  
    115                 // Create an eventsource for all events 
    116                 eventSources[${bandNr}] = new Timeline.DefaultEventSource(); 
    117  
    118                 // Load events for this eventsource (using jquery) 
    119                 var event_url = '${createLink(controller:'study', action:'events', id:( eventGroup.id ? eventGroup.id : -1 ), params: [ startDate: study.startDate.getTime(), study: study.id ])}'; 
    120                 $.getJSON(event_url, $.callback( _loadJSONEvents, [0, ${bandNr}, eventSources[${bandNr}], overviewEventSource, event_url] ) ); 
    121  
    122                 // Create a new timeline band 
    123                 bandInfos[${bandNr}] = 
    124                        Timeline.createBandInfo({ 
    125                            eventSource:    eventSources[${bandNr}], 
    126                            width:          30, 
    127                            intervalUnit:   Timeline.DateTime.DAY, 
    128                            intervalPixels: 40, 
    129                            date:           firstDate, 
    130                            timeZone:       +1, 
    131                            syncWith:       1, 
    132                            layout:         'original', 
    133                            theme:          theme 
    134                        }); 
    135  
    136                 // Make sure the date isn't printed by using the empty ether painter 
    137                 bandInfos[${bandNr}].etherPainter = emptyEtherPainter; 
    138  
    139                 // Add a title to the bandinfo 
    140                 <%  
    141                   sortedGroupSubjects = eventGroup.subjects.sort( { a, b -> a.name <=> b.name } as Comparator ); 
    142                   def simpleSubjects = sortedGroupSubjects.name.join( ', ' ); 
    143  
    144                   // We can only show appr. 30 characters per line and as many lines as there are events 
    145                   def charsPerLine = 30; 
    146                   def numEvents = eventGroup.events?.size(); 
    147                   def maxChars = numEvents * charsPerLine; 
    148  
    149                   // If the subjects will fit, show them all 
    150                   if( simpleSubjects?.size() < maxChars ) { 
    151                     showSubjects = simpleSubjects; 
    152                   } else { 
    153                     // Always add the first name 
    154                     def subjectNames = sortedGroupSubjects[0]?.name; 
    155  
    156                     // Continue adding names until the length is to long 
    157                     id = 0; 
    158                     sortedGroupSubjects.each { subject -> 
    159                       if( id > 0 ) { 
    160                         println( "ID: " + id + " - " + subjectNames?.size() + " - " + subject.name?.size() + " - " + maxChars ); 
    161                         if( subjectNames?.size() + subject.name?.size() < maxChars - 15 ) { 
    162                           subjectNames += ", " + subject.name; 
    163                         } else { 
    164                           return; 
    165                         } 
    166                       } 
    167                       id++; 
    168                     } 
    169  
    170                     // Add a postfix 
    171                     subjectNames += " and " + ( sortedGroupSubjects?.size() - id ) + " more"; 
    172  
    173                     showSubjects = subjectNames; 
    174                   } 
    175  
    176  
    177                 %> 
    178                 bandTitleInfo[ timelineNr ][ ${bandNr} ] = { 
    179                   title: "${eventGroup.name}", 
    180                   className: "<g:if test="${ eventGroup.id == -1 || !eventGroup.id  }">no_group</g:if>", 
    181                   subjects: "${showSubjects}" 
    182                 }; 
    183  
    184                 <g:set var="bandNr" value="${bandNr+1}" /> 
    185               </g:each> 
    186  
    187               // Synchronize all bands 
    188               <g:each in="${sortedEventGroups}" var="eventGroup" status="i"> 
    189                 bandInfos[${i + datesBandNr +1}].syncWith = ${datesBandNr}; 
    190               </g:each> 
    191  
    192           </g:each> 
    193  
    194           return bandInfos; 
    195         } 
    196      </script> 
     47    <script type="text/javascript" src="<g:createLink action="createTimelineBandsJs" id="${studyList.id.join(',')}" />" type="text/javascript"></script> 
    19748</head> 
    19849<body> 
     
    20758        <ul> 
    20859          <li><a href="#study">Study Information</a></li> 
    209           <li><a href="#subjects">Subjects</a></li> 
    210           <li><a href="#events-timeline">Events timeline</a></li> 
    211           <li><a href="#events-table">Events table</a></li> 
    212           <li><a href="#assays">Assays</a></li> 
    213           <li><a href="#samples">Samples</a></li> 
    214           <li><a href="#persons">Persons</a></li> 
    215           <li><a href="#publications">Publications</a></li> 
     60          <li><a href="<g:createLink action="show_subjects" id="${studyList.id.join(',')}" />" title="Subjects"><span>Subjects</span></a></li> 
     61          <li><a href="<g:createLink action="show_events_timeline" id="${studyList.id.join(',')}" />" title="Events timeline"><span>Events timeline</span></a></li> 
     62          <li><a href="<g:createLink action="show_events_table" id="${studyList.id.join(',')}" />" title="Events table"><span>Events table</span></a></li> 
     63          <li><a href="<g:createLink action="show_assays" id="${studyList.id.join(',')}" />" title="Assays"><span>Assays</span></a></li> 
     64          <li><a href="<g:createLink action="show_samples" id="${studyList.id.join(',')}" />" title="Samples"><span>Samples</span></a></li> 
     65          <li><a href="<g:createLink action="show_persons" id="${studyList.id.join(',')}" />" title="Persons"><span>Persons</span></a></li> 
     66          <li><a href="<g:createLink action="show_publications" id="${studyList.id.join(',')}" />" title="Publications"><span>Publications</span></a></li> 
    21667        </ul> 
    21768 
     
    23788            <!-- Show all template and domain fields, if filled --> 
    23889            <g:each in="${studyFields}" var="field"> 
    239               <% 
    240                 // If a value is not set for any of the selected studies, the 
    241                 // field should not appear in the list 
    242                 showField = true in studyList.collect { it.fieldExists( field.name ) && it.getFieldValue( field.name ) != null }?.flatten() 
    243               %> 
    244               <g:if test="${showField}"> 
     90              <g:if test="${field.isFilledInList( studyList )}"> 
    24591                <tr> 
    24692                  <td>${field}</td> 
     
    331177        </div> 
    332178 
    333         <div id="subjects"> 
    334  
    335           <g:if test="${studyList*.subjects?.flatten()?.size()==0}"> 
    336             No subjects in the selected studies 
    337           </g:if> 
    338           <g:else> 
    339             <table> 
    340               <thead> 
    341                 <tr> 
    342                   <g:if test="${multipleStudies}"> 
    343                     <th></th> 
    344                   </g:if> 
    345                   <g:each in="${new dbnp.studycapturing.Subject().giveDomainFields()}" var="field"> 
    346                     <th>${field}</th> 
    347                   </g:each> 
    348  
    349                   <% 
    350                     // Determine a union of the fields for all different 
    351                     // subjects in all studies. In order to show a proper list.  
    352                     // We want every field to appear just once, 
    353                     // so the list is filtered for unique values 
    354                     subjectTemplates = studyList*.giveSubjectTemplates()?.flatten().unique() 
    355                     if( !subjectTemplates ) { 
    356                       subjectTemplates = []; 
    357                       subjectFields = []; 
    358                     } else { 
    359                       subjectFields = subjectTemplates*.fields?.flatten().unique() 
    360                       if( !subjectFields ) { 
    361                         subjectFields = []; 
    362                       } 
    363                     } 
    364  
    365                     /* 
    366                      * These lines are rewritten because 
    367                      * performance sucked 
    368                      * 
    369                      *   // These took about 9 seconds (for 31 subjects and 
    370                      *   allSubjects = studyList*.subjects?.flatten() 
    371                      * 
    372                      *   subjectFields = subjectFields.findAll { subjectField -> 
    373                      *     ( true in allSubjects.collect { subject -> subject.fieldExists( subjectField.name ) && subject.getFieldValue( subjectField.name ) != null }.flatten() ) 
    374                      *   } 
    375                      */ 
    376  
    377                     // Filter out all fields that are left blank for all subjects 
    378                     allSubjects = studyList*.subjects?.flatten() 
    379  
    380                     showSubjectFields = [] 
    381                     subjectFields.each { subjectField -> 
    382                       for( subject in allSubjects ) 
    383                       { 
    384                         // If the field is filled for this subject, we have to 
    385                         // show the field and should not check any other 
    386                         // subjects (hence the break) 
    387                         if( subject.fieldExists( subjectField.name ) && subject.getFieldValue( subjectField.name ) ) { 
    388                           showSubjectFields << subjectField; 
    389                           break; 
    390                         } 
    391                       } 
    392                     } 
    393                   %> 
    394  
    395                   <g:each in="${showSubjectFields}" var="field"> 
    396                     <th>${field}</th> 
    397                   </g:each> 
    398  
    399                 </tr> 
    400               </thead> 
    401  
    402               <g:set var="i" value="${1}" /> 
    403  
    404               <g:each in="${studyList}" var="studyInstance"> 
    405                 <g:each in="${studyInstance.subjects}" var="subject" status="j"> 
    406                   <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    407                     <g:if test="${multipleStudies && j==0}"> 
    408                       <td class="studytitle" rowspan="${sortedSubjects?.size()}"> 
    409                         ${studyInstance.title} 
    410                       </td> 
    411                     </g:if> 
    412                     <g:each in="${subject.giveDomainFields()}" var="field"> 
    413                       <td><wizard:showTemplateField field="${field}" entity="${subject}" /></td> 
    414                     </g:each> 
    415                    
    416                     <g:each in="${showSubjectFields}" var="field"> 
    417                       <td> 
    418                         <g:if test="${subject.fieldExists(field.name)}"> 
    419                                 <wizard:showTemplateField field="${field}" entity="${subject}" /> 
    420                         </g:if> 
    421                         <g:else> 
    422                           N/A 
    423                         </g:else> 
    424                       </td> 
    425                     </g:each> 
    426  
    427                   </tr> 
    428                   <g:set var="i" value="${i + 1}" /> 
    429                 </g:each> 
    430               </g:each> 
    431             </table> 
    432           </g:else> 
    433         </div> 
    434  
    435         <div id="events-timeline"> 
    436           <g:if test="${studyList*.events?.flatten()?.size()==0 && studyInstance*.samplingEvents?.flatten()?.size()==0 }"> 
    437             No events in these studies 
    438           </g:if> 
    439           <g:else> 
    440             <g:each in="${studyList}" var="study" status="i"> 
    441               <div style="margin: 10px; "> 
    442                 <div class="eventtitles" id="eventtitles-${i}"></div> 
    443                 <div class="eventstimeline" id="eventstimeline-${i}"></div> 
    444               </div> 
    445             </g:each> 
    446             <noscript> 
    447               <table> 
    448                 <thead> 
    449                   <tr> 
    450                     <g:if test="${multipleStudies}"> 
    451                       <th></th> 
    452                     </g:if> 
    453                     <th>Start time</th> 
    454                     <th>Duration</th> 
    455                     <th>Type</th> 
    456                     <th>Sampling event</th> 
    457                     <th>Parameters</th> 
    458                   </tr> 
    459                 </thead> 
    460  
    461                 <g:set var="i" value="${1}" /> 
    462  
    463                 <g:each in="${studyList}" var="studyInstance"> 
    464                   <% 
    465                     // Sort events by starttime and duration 
    466                     events = studyInstance.events + studyInstance.samplingEvents; 
    467                     sortedEvents = events.sort( { a, b -> 
    468                           //a.startTime == b.startTime ? 
    469                             //a.getDuration().getValue() <=> b.getDuration().getValue() : 
    470                           a.startTime <=> b.startTime 
    471                       } as Comparator ) 
    472                   %> 
    473  
    474                   <g:each in="${sortedEvents}" var="event" status="j"> 
    475                     <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    476                       <g:if test="${multipleStudies && j==0}"> 
    477                         <td class="studytitle" rowspan="${sortedEvents?.size()}"> 
    478                           ${studyInstance.title} 
    479                         </td> 
    480                       </g:if> 
    481                       <td>${event.getStartTimeString()}</td> 
    482                       <td>${((event.getClass() == 'Event') ? event.getDurationString() : '')}</td> 
    483                       <td>${event.template.name}</td> 
    484                       <td> 
    485                         <g:if test="${event instanceof dbnp.studycapturing.SamplingEvent}"> 
    486                           <g:checkBox name="samplingEvent" disabled="${true}" value="${true}"/> 
    487                         </g:if> 
    488                         <g:else> 
    489                           <g:checkBox name="event" disabled="${true}" value="${false}" /> 
    490                         </g:else> 
    491                       </td> 
    492                       <td> 
    493                         <g:set var="fieldCounter" value="${1}" /> 
    494                         <g:each in="${event.giveTemplateFields()}" var="field"> 
    495                           <g:if test="${event.getFieldValue(field.name)}"> 
    496                             <g:if test="${fieldCounter > 1}">, </g:if> 
    497                               ${field.name} = <wizard:showTemplateField field="${field}" entity="${event}" /> 
    498                             <g:set var="fieldCounter" value="${fieldCounter + 1}" /> 
    499                           </g:if> 
    500                         </g:each> 
    501                       </td> 
    502                     </tr> 
    503  
    504                     <g:set var="i" value="${i + 1}" /> 
    505                   </g:each> 
    506                 </g:each> 
    507               </table> 
    508  
    509             </noscript> 
    510  
    511           </g:else> 
    512         </div> 
    513  
    514         <div id="events-table"> 
    515           <g:if test="${studyList*.eventGroups?.flatten()?.size()==0}"> 
    516             No event groups in this study 
    517           </g:if> 
    518           <g:else> 
    519             <% 
    520               // Determine a union of the event templates for all different 
    521               // eventgroups in all studies, in order to show a proper list. 
    522               // We want every field to appear just once, 
    523               // so the list is filtered for unique values 
    524               def groupTemplates = studyList*.giveAllEventTemplates()?.flatten().unique() 
    525                           def showTemplates = groupTemplates; 
    526  
    527                           def showProperties = [:]; 
    528                           def allEvents = studyList*.events.flatten() + studyList*.samplingEvents.flatten(); 
    529                           def eventColumns = 0; 
    530  
    531                           showTemplates.each { template -> 
    532                                 // We want to show all properties only once. If the properties are never filled 
    533                                 // we shouldn't show them at all. 
    534                                 def showFields = [] 
    535                                 template.fields.each { field -> 
    536                                   for( def event: allEvents.findAll { it.template == template } ) { 
    537                                         if( event.getFieldValue( field.name ) ) { 
    538                                           showFields << field; 
    539                                           break; 
    540                                         } 
    541                                   } 
    542                                 } 
    543  
    544                                 showProperties[ template.name ] = showFields; 
    545  
    546                                 // Compute the total number of columns under 'Events' (the +1 is 
    547                                 // because of the 'start time' column) 
    548                                 eventColumns += [ 1, showFields.size() + 1 ].max(); 
    549                           } 
    550  
    551             %> 
    552             <table> 
    553               <thead> 
    554                 <tr> 
    555                   <g:if test="${multipleStudies}"> 
    556                     <th></th> 
    557                   </g:if> 
    558                   <th>Name</th> 
    559                   <th colspan="${eventColumns}">Events</th> 
    560                   <th>Subjects</th> 
    561                 </tr> 
    562                 <tr> 
    563                   <g:if test="${multipleStudies}"> 
    564                     <th></th> 
    565                   </g:if> 
    566                   <th></th> 
    567                   <g:each in="${showTemplates}" var="eventTemplate"> 
    568                     <th colspan="${[1, showProperties[ eventTemplate.name ].size() + 1 ].max()}">${eventTemplate.name}</th> 
    569                   </g:each> 
    570                   <th></th> 
    571                 </tr> 
    572                 <tr class="templateFields"> 
    573                   <g:if test="${multipleStudies}"> 
    574                     <th></th> 
    575                   </g:if> 
    576                   <th></th> 
    577                   <g:each in="${showTemplates}" var="eventTemplate"> 
    578                                         <th>start time</th> 
    579                                         <g:if test="${showProperties[ eventTemplate.name ].size() > 0}"> 
    580                                           <g:each in="${showProperties[ eventTemplate.name ]}" var="field"> 
    581                                                 <th>${field.name}</th> 
    582                                           </g:each> 
    583                                         </g:if> 
    584                   </g:each> 
    585                   <th></th> 
    586                 </tr> 
    587               </thead> 
    588  
    589               <g:set var="i" value="${1}" /> 
    590  
    591               <g:each in="${studyList}" var="studyInstance"> 
    592                 <% 
    593                   // Sort the groups by name 
    594                                   def sortedEventGroups = studyInstance.eventGroups.sort( { a, b -> 
    595                       return a.name <=> b.name; 
    596                   }  as Comparator ); 
    597  
    598                                   // Determine the number of rows per group (depending on the max 
    599                                   // number of events per template in a group) 
    600                                   def maxNumberEventsPerTemplate = [:]; 
    601                                   def rowsPerStudy = 0; 
    602                                   sortedEventGroups.each { group -> 
    603                                         def max = 1; 
    604                                         showTemplates.each { template -> 
    605                                           def num = ( group.events + group.samplingEvents ).findAll { it.template == template }.size(); 
    606                                           if( num > max ) 
    607                                                 max = num; 
    608                                         } 
    609                                         maxNumberEventsPerTemplate[group.name] = max; 
    610                                         rowsPerStudy += max; 
    611                                   } 
    612  
    613                   def orphans = studyInstance.getOrphanEvents(); 
    614                   if( orphans?.size() > 0 ) { 
    615                     sortedEventGroups.add( new EventGroup( 
    616                       id: -1, 
    617                       name: 'No group', 
    618                       events: orphans, 
    619                       subjects: [] 
    620                     )); 
    621                   } 
    622                 %> 
    623                 <g:each in="${sortedEventGroups}" var="eventGroup" status="j"> 
    624                                   <g:set var="n" value="${1}" /> 
    625                   <g:while test="${n <= maxNumberEventsPerTemplate[ eventGroup.name ]}"> 
    626  
    627                                         <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    628                                           <g:if test="${n == 1}"> 
    629                                                 <g:if test="${multipleStudies && j==0}"> 
    630                                                   <td class="studytitle" rowspan="${rowsPerStudy}"> 
    631                                                         ${studyInstance.title} 
    632                                                   </td> 
    633                                                 </g:if> 
    634                                                 <td rowspan="${maxNumberEventsPerTemplate[ eventGroup.name ]}">${eventGroup.name}</td> 
    635                                           </g:if> 
    636                                          
    637                                           <g:each in="${showTemplates}" var="currentEventTemplate"> 
    638                                                 <g:if test="${showProperties[ currentEventTemplate.name ].size() == 0}"> 
    639                                                   <td>&nbsp;</td> 
    640                                                 </g:if> 
    641                                                 <g:else> 
    642                                                   <% 
    643                                                         def templateEvents = (eventGroup.events + eventGroup.samplingEvents).findAll { it.template == currentEventTemplate }.sort { a, b -> a.startTime <=> b.startTime }.asType(List) 
    644                                                         def event = templateEvents.size() >= n ? templateEvents[ n - 1 ] : null; 
    645                                                   %> 
    646                                                   <td class="templateFieldValue"><g:if test="${event}">${new RelTime( event.startTime ).toString()}</g:if></td> 
    647                                                   <g:each in="${showProperties[ currentEventTemplate.name ]}" var="field"> 
    648                                                         <td class="templateFieldValue"><wizard:showTemplateField field="${field}" entity="${event}" /></td> 
    649                                                   </g:each> 
    650                                                 </g:else> 
    651                                           </g:each> 
    652                                          
    653                                           <g:if test="${n == 1}"> 
    654                                                 <% sortedGroupSubjects = eventGroup.subjects.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
    655  
    656                                                 <td rowspan="${maxNumberEventsPerTemplate[ eventGroup.name ]}" title="${sortedGroupSubjects.name.join( ', ' )}"> 
    657                                                         <g:if test="${eventGroup.subjects.size()==0}"> 
    658                                                                 - 
    659                                                         </g:if> 
    660                                                         <g:else> 
    661                                                                 <g:each in="${eventGroup.subjects.species.unique()}" var="currentSpecies" status="k"> 
    662                                                                         <g:if test="${k > 0}">,</g:if> 
    663                                                                         <%=eventGroup.subjects.findAll { return it.species == currentSpecies; }.size() %> 
    664                                                                         ${currentSpecies} 
    665                                                                 </g:each> 
    666                                                         </g:else> 
    667                                                 </td> 
    668                                           </g:if> 
    669                                         </tr> 
    670  
    671  
    672                                         <g:set var="n" value="${n+1}" /> 
    673                                   </g:while> 
    674  
    675                   <g:set var="i" value="${i + 1}" /> 
    676                 </g:each> 
    677  
    678               </g:each> 
    679  
    680             </table> 
    681           </g:else> 
    682         </div> 
    683  
    684         <div id="assays"> 
    685           <g:if test="${studyList*.assays?.flatten()?.size()==0}"> 
    686             No assays in these studies 
    687           </g:if> 
    688           <g:else> 
    689             <table> 
    690               <thead> 
    691                 <tr> 
    692                   <g:if test="${multipleStudies}"> 
    693                     <th></th> 
    694                   </g:if> 
    695                         <th width="100">Assay Code</th> 
    696                   <th width="100">Assay Name</th> 
    697                   <th width="100">Module</th> 
    698                   <th width="150">Platform</th> 
    699                   <th>Link</th> 
    700                   <th>Samples</th> 
    701                 </tr> 
    702               </thead> 
    703               <g:set var="i" value="${1}" /> 
    704  
    705               <g:each in="${studyList}" var="studyInstance"> 
    706                 <g:each in="${studyInstance.assays}" var="assay" status="j"> 
    707                   <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    708                     <g:if test="${multipleStudies && j==0}"> 
    709                       <td class="studytitle" rowspan="${studyInstance.assays?.size()}"> 
    710                         ${studyInstance.title} 
    711                       </td> 
    712                     </g:if> 
    713                           <td>${assay.token}</td> 
    714                     <td>${assay.name}</td> 
    715                     <td>${assay.module.name}</td> 
    716                     <td>${assay.module.platform}</td> 
    717                     %{--<td><a href="${assay.module.url}/assay/${assay.externalAssayID}">view</a></td>--}% 
    718                     <td><jumpbar:link 
    719                       linkDest="${createLink(action:'show', id:studyInstance.id)}" 
    720                       linkText='Go back to GSCF' 
    721                       frameSource="${assay.module.url}/assay/showByToken/${assay.externalAssayID}" 
    722                       pageTitle="Assay View in Module"> 
    723                       view 
    724                     </jumpbar:link></td> 
    725                     <td> 
    726                       <% sortedAssaySamples = assay.samples.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
    727                       ${sortedAssaySamples.name.join( ', ' )} 
    728                     </td> 
    729                   </tr> 
    730                   <g:set var="i" value="${i + 1}" /> 
    731  
    732                 </g:each> 
    733               </g:each> 
    734             </table> 
    735           </g:else> 
    736         </div> 
    737  
    738         <div id="samples"> 
    739  
    740           <g:if test="${studyList*.samples.flatten()?.size()==0}"> 
    741             No samples in the selected studies 
    742           </g:if> 
    743           <g:else> 
    744             <table> 
    745               <thead> 
    746                 <tr> 
    747                   <g:if test="${multipleStudies}"> 
    748                     <th></th> 
    749                   </g:if> 
    750  
    751                         <th>Parent Subject</th> 
    752                         <th>Parent Sampling Event</th> 
    753  
    754  
    755                   <g:each in="${new dbnp.studycapturing.Sample().giveDomainFields()}" var="field"> 
    756                     <th>${field}</th> 
    757                   </g:each> 
    758  
    759                   <% 
    760                     // Determine a union of the fields for all different 
    761                     // samples in all studies. In order to show a proper list. 
    762                     // We want every field to appear just once, 
    763                     // so the list is filtered for unique values 
    764                     sampleTemplates = studyList*.giveSampleTemplates().flatten().unique() 
    765  
    766                     if( !sampleTemplates ) { 
    767                       sampleTemplates = []; 
    768                       sampleFields = []; 
    769                       showSampleFields = []; 
    770                     } else { 
    771                       sampleFields = sampleTemplates*.fields.flatten().unique() 
    772                       if( !sampleFields ) { 
    773                         sampleFields = []; 
    774                         showSampleFields = []; 
    775                       } else { 
    776                         // Filter out all fields that are left blank for all samples 
    777                         allSamples = studyList*.samples.flatten() 
    778  
    779                         showSampleFields = []; 
    780                         sampleFields.each { sampleField -> 
    781                           for( sample in allSamples ) 
    782                           { 
    783                             // If the field is filled for this subject, we have to 
    784                             // show the field and should not check any other 
    785                             // samples (hence the break) 
    786                             if( sample.fieldExists( sampleField.name ) && sample.getFieldValue( sampleField.name ) ) { 
    787                               showSampleFields << sampleField; 
    788                               break; 
    789                             } 
    790                           } 
    791                         } 
    792                       } 
    793                     } 
    794                   %> 
    795  
    796                   <g:each in="${showSampleFields}" var="field"> 
    797                     <th>${field}</th> 
    798                   </g:each> 
    799  
    800                 </tr> 
    801               </thead> 
    802  
    803               <g:set var="i" value="${1}" /> 
    804  
    805               <g:each in="${studyList}" var="studyInstance"> 
    806                 <% 
    807                   // Sort samples by name 
    808                   samples = studyInstance.samples; 
    809                   sortedSamples = samples.sort( { a, b -> a.name <=> b.name } as Comparator ) 
    810                 %> 
    811  
    812                 <g:each in="${sortedSamples}" var="sample" status="j"> 
    813                   <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    814                     <g:if test="${multipleStudies && j==0}"> 
    815                       <td class="studytitle" rowspan="${sortedSamples?.size()}"> 
    816                         ${studyInstance.title} 
    817                       </td> 
    818                     </g:if> 
    819                           <td>${sample.parentSubject?.name}</td> 
    820                           <td>${sample.parentEvent?.template?.name} at ${sample.parentEvent?.getStartTimeString()}</td> 
    821                     <g:each in="${sample.giveDomainFields()}" var="field"> 
    822                       <td><wizard:showTemplateField field="${field}" entity="${sample}" /></td> 
    823                     </g:each> 
    824  
    825                     <g:each in="${showSampleFields}" var="field"> 
    826                       <td> 
    827                         <g:if test="${sample.fieldExists(field.name)}"> 
    828                           <wizard:showTemplateField field="${field}" entity="${sample}" /> 
    829                         </g:if> 
    830                         <g:else> 
    831                           N/A 
    832                         </g:else> 
    833                       </td> 
    834                     </g:each> 
    835  
    836                   </tr> 
    837                   <g:set var="i" value="${i + 1}" /> 
    838                 </g:each> 
    839               </g:each> 
    840  
    841             </table> 
    842           </g:else> 
    843         </div> 
    844  
    845         <div id="persons"> 
    846           <% 
    847             // Determine a list of all persons 
    848             allPersons = studyList*.persons*.person.flatten().unique() 
    849           %> 
    850           <g:if test="${allPersons?.size()==0}"> 
    851             No persons involved in these studies 
    852           </g:if> 
    853           <g:else> 
    854             <table> 
    855               <tr> 
    856                 <thead> 
    857                   <th>Name</th> 
    858                   <th>Affiliations</th> 
    859                   <th>Phone</th> 
    860                   <th>Email</th> 
    861                   <g:if test="${multipleStudies}"> 
    862                     <g:each in="${studyList}" var="studyInstance"> 
    863                       <th>${studyInstance.title}</th> 
    864                     </g:each> 
    865                   </g:if> 
    866                   <g:else> 
    867                     <th>Role</th> 
    868                   </g:else> 
    869                 </thead> 
    870               </tr> 
    871               <g:each in="${allPersons}" var="person" status="i"> 
    872                 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    873                   <td>${person.firstName} ${person.prefix} ${person.lastName}</td> 
    874                   <td> 
    875                     ${person.affiliations.join(', ')} 
    876                   </td> 
    877                   <td>${person.phone}</td> 
    878                   <td>${person.email}</td> 
    879                   <g:each in="${studyList}" var="studyInstance"> 
    880                     <% 
    881                       studyperson = studyInstance.persons.find { it.person == person } 
    882                     %> 
    883                     <td> 
    884                       <g:if test="${studyperson}"> 
    885                         ${studyperson.role.name} 
    886                       </g:if> 
    887                      </td> 
    888                   </g:each> 
    889  
    890                 </tr> 
    891               </g:each> 
    892             </table> 
    893           </g:else> 
    894         </div> 
    895  
    896         <div id="publications"> 
    897           <% 
    898             // Determine a list of all persons 
    899             allPublications = studyList*.publications.flatten().unique() 
    900           %> 
    901           <g:if test="${allPublications?.size()==0}"> 
    902             No publications attached to these studies 
    903           </g:if> 
    904           <g:else> 
    905             <table> 
    906               <tr> 
    907                 <thead> 
    908                   <th>Title</th> 
    909                   <th>Authors</th> 
    910                   <th>Comments</th> 
    911  
    912                   <g:if test="${multipleStudies}"> 
    913                     <g:each in="${studyList}" var="studyInstance"> 
    914                       <th>${studyInstance.title}</th> 
    915                     </g:each> 
    916                   </g:if> 
    917                 </thead> 
    918               </tr> 
    919               <g:each in="${allPublications}" var="publication" status="i"> 
    920                 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    921                   <td>${publication.title}</td> 
    922                   <td> 
    923                     ${publication.authorsList} 
    924                   </td> 
    925                   <td>${publication.comments}</td> 
    926                   <g:if test="${multipleStudies}"> 
    927                     <g:each in="${studyList}" var="studyInstance"> 
    928                       <td> 
    929                         <g:if test="${publication in studyInstance.publications}"> 
    930                           x 
    931                         </g:if> 
    932                       </td> 
    933                     </g:each> 
    934                   </g:if> 
    935                 </tr> 
    936               </g:each> 
    937             </table> 
    938           </g:else> 
    939         </div> 
     179                <% /* 
     180                  All other tabs are moved to separate views and are loaded using 
     181                  ajax calls when a tab is opened. See http://jqueryui.com/demos/tabs/#ajax 
     182                */ %> 
    940183 
    941184      </div> 
  • trunk/web-app/js/timeline-simile/relative-time.js

    r1203 r1213  
    112112    this._backgroundLayer = band.createLayerDiv(0); 
    113113    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging 
    114     this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()]; 
     114    //this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()]; 
    115115 
    116116    this._markerLayer = null;