Changeset 453

Show
Ignore:
Timestamp:
20-05-10 17:02:22 (4 years ago)
Author:
roberth
Message:

Improved study list, study detail view and study comparison. Also improved performance issues

Location:
trunk
Files:
1 added
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/conf/BootStrap.groovy

    r442 r453  
    104104                        ).save(); 
    105105 
    106                         // Create 30 roles to test pagination 
    107                         def roleCounter = 1; 
    108                         30.times { new PersonRole( name: "Rol #${roleCounter++}" ).save() } 
    109  
    110106                        // Create persons 
    111107                        def person1 = new Person( 
     
    138134                        30.times { new Person( firstName: "Person #${personCounter}", lastName: "Testperson", email: "email${personCounter++}@testdomain.com" ).save() } 
    139135 
    140                         // Create templates 
     136                        // Create a few publications 
     137                        println ".adding publications" 
     138                        def publication1 = new Publication( 
     139                            title: "Postnatal development of hypothalamic leptin receptors", 
     140                            authorsList: "Cottrell EC, Mercer JG, Ozanne SE.", 
     141                            pubMedID: "20472140", 
     142                            comments: "Not published yet", 
     143                            DOI: "unknown" 
     144                        ) 
     145                        .save(); 
     146 
     147                        def publication2 = new Publication( 
     148                            title: "Induction of regulatory T cells decreases adipose inflammation and alleviates insulin resistance in ob/ob mice", 
     149                            authorsList: "Ilan Y, Maron R, Tukpah AM, Maioli TU, Murugaiyan G, Yang K, Wu HY, Weiner HL.", 
     150                            pubMedID: "20445103", 
     151                            comments: "", 
     152                            DOI: "" 
     153                        ) 
     154                        .save(); 
     155 
     156            // Create templates 
    141157 
    142158                        def genderField = new TemplateField( 
     
    554570                                .addToEventGroups(HFBL4) 
    555571 
    556                                 // Add persons to study 
     572                                // Add persons and publications to study 
    557573                                def studyperson1 = new StudyPerson( person: person1, role: role1 ).save(); 
    558574                                def studyperson2 = new StudyPerson( person: person2, role: role2 ).save(); 
     
    561577                                .addToPersons( studyperson1 ) 
    562578                                .addToPersons( studyperson2 ) 
     579                                .addToPublications( publication1 ) 
     580                                .addToPublications( publication2 ) 
    563581                                .save() 
    564582 
     
    624642                                humanStudy.addToSamplingEvents(bloodSamplingEvent) 
    625643                                humanStudy.addToEventGroups rootGroup 
    626                                 humanStudy.save() 
     644 
     645 
     646                                // Add persons to study 
     647                                def studyperson3 = new StudyPerson( person: person1, role: role2 ).save(); 
     648 
     649                                humanStudy 
     650                                .addToPersons( studyperson3 ) 
     651                                .addToPublications( publication2 ) 
     652                                .save() 
    627653 
    628654                                /* 
     
    697723                                humanStudy.addToAssays(lipidAssayRef); 
    698724                                humanStudy.save() 
    699                                  
     725 
     726                                mouseStudy.addToAssays(lipidAssayRef); 
     727                                mouseStudy.save() 
     728 
    700729                        } 
    701730                } 
  • trunk/grails-app/controllers/dbnp/studycapturing/StudyController.groovy

    r397 r453  
    5757    } 
    5858 
     59    /** 
     60     * Shows a comparison of multiple studies using the show view 
     61     * 
     62     *  
     63     */ 
    5964    def list_extended = { 
     65         
    6066        params.max = Math.min(params.max ? params.int('max') : 10, 100) 
    61         [studyList: Study.list(params), studyInstanceTotal: Study.count()] 
     67         
     68        render(view:'show',model:[studyList: Study.list(params), studyInstanceTotal: Study.count() ] ) 
    6269    } 
    6370 
     
    8087 
    8188    def show = { 
     89 
    8290        def studyInstance = Study.get(params.id) 
    8391        if (!studyInstance) { 
     
    8694        } 
    8795        else { 
    88             [studyInstance: studyInstance] 
     96            // The study instance is packed into an array, to be able to 
     97            // use the same view for showing the study and comparing multiple 
     98            // studies 
     99            [studyList: [ studyInstance ] ] 
    89100        } 
    90101    } 
  • trunk/grails-app/domain/dbnp/studycapturing/Event.groovy

    r416 r453  
    4343         * @return List 
    4444         */ 
    45         List<TemplateField> giveDomainFields() { 
     45        List<TemplateField> giveDomainFields() { return Event.domainFields } 
     46        static final List<TemplateField> domainFields =  
    4647                [ 
    4748                        new TemplateField( 
     
    5253                                type: TemplateFieldType.DATE) 
    5354                ] 
    54         } 
    5555 
    5656        /** 
  • trunk/grails-app/domain/dbnp/studycapturing/Study.groovy

    r421 r453  
    2121         * @return List 
    2222         */ 
    23         List<TemplateField> giveDomainFields() { 
     23        List<TemplateField> giveDomainFields() { return Study.domainFields } 
     24 
     25        static final List<TemplateField> domainFields = 
    2426                [ 
    2527                        new TemplateField( 
     
    3032                                type: TemplateFieldType.DATE) 
    3133                ] 
    32         } 
    3334 
    3435        static hasMany = [ 
  • trunk/grails-app/domain/dbnp/studycapturing/TemplateEntity.groovy

    r435 r453  
    282282         */ 
    283283        def getFieldValue(String fieldName) { 
    284                 TemplateField field = getField(this.giveFields(),fieldName) 
    285                 if (isDomainField(field)) { 
    286                         return this[field.name] 
     284 
     285                if (isDomainField(fieldName)) { 
     286                    return this[fieldName] 
    287287                } 
    288288                else { 
    289                         return getStore(field.type)[fieldName] 
    290                 } 
     289                    TemplateField field = getField(this.giveTemplateFields(),fieldName) 
     290                    return getStore(field.type)[fieldName] 
     291                } 
     292 
    291293        } 
    292294 
     
    404406         */ 
    405407        boolean isDomainField(TemplateField field) { 
    406                 return this.giveDomainFields()*.name.contains(field.name) 
     408            return isDomainField( field.name ) 
    407409        } 
    408410 
     
    411413         * @param String        field name 
    412414         * @return boolean 
    413          */     boolean isDomainField(String fieldName) { 
    414                 return this.giveDomainFields()*.name.contains(fieldName) 
    415         } 
    416  
     415         */ 
     416        boolean isDomainField(String fieldName) { 
     417            return this.giveDomainFields()*.name.contains(fieldName) 
     418        } 
     419         
    417420        /** 
    418421         * Return all fields defined in the underlying template and the built-in 
  • trunk/grails-app/views/study/show.gsp

    r442 r453  
    1212    }); 
    1313  </script> 
     14  <link rel="stylesheet" href="${resource(dir: 'css', file: 'studies.css')}"/> 
    1415 
    1516</head> 
    1617<body> 
    1718 
    18   <div class="body"> 
     19  <div class="body" id="studies"> 
    1920    <h1><g:message code="default.show.label" args="[entityName]" /></h1> 
    2021    <g:if test="${flash.message}"> 
     
    3738 
    3839          <table> 
     40            <!-- only show the head section if there are multiple studies --> 
     41            <g:if test="${studyList.size()>1}"> 
     42              <thead> 
     43                <tr> 
     44                  <th></th> 
     45                  <g:each in="${studyList}" var="studyInstance"> 
     46                    <th>${studyInstance.title}</th> 
     47                  </g:each> 
     48                </tr> 
     49              </thead> 
     50            </g:if> 
     51            <%  
     52              // Determine a union of the fields from all studies, in order 
     53              // to show a proper list. We want every field to appear just once, 
     54              // so the list is filtered for unique values 
     55              studyFields = studyList[0].giveDomainFields() + studyList*.giveTemplateFields().flatten().unique() 
     56            %> 
    3957            <!-- Show all template and domain fields, if filled --> 
    40             <g:each in="${studyInstance.giveFields()}" var="field"> 
    41               <g:if test="${studyInstance.getFieldValue(field.name)}"> 
     58            <g:each in="${studyFields}" var="field"> 
     59              <% 
     60                // If a value is not set for any of the selected studies, the 
     61                // field should not appear in the list 
     62                showField = true in studyList.collect { it.fieldExists( field.name ) && it.getFieldValue( field.name ) != null }.flatten() 
     63              %> 
     64              <g:if test="${showField}"> 
    4265                <tr> 
    4366                  <td>${field}</td> 
    44                   <td>${studyInstance.getFieldValue(field.name)}</td> 
     67                  <g:each in="${studyList}" var="studyInstance"> 
     68                    <td>${studyInstance.getFieldValue(field.name)}</td> 
     69                  </g:each> 
    4570                </tr> 
    4671              </g:if> 
     
    5075            <tr> 
    5176              <td>Events</td> 
    52               <td> 
    53                 <g:if test="${studyInstance.giveEventTemplates().size()==0}"> 
    54                   - 
    55                 </g:if> 
    56                 <g:else> 
    57                  ${studyInstance.giveEventTemplates().name.join(", ")} 
    58                 </g:else> 
    59               </td> 
     77              <g:each in="${studyList}" var="studyInstance"> 
     78                <td> 
     79                  <g:if test="${studyInstance.giveEventTemplates().size()==0}"> 
     80                    - 
     81                  </g:if> 
     82                  <g:else> 
     83                   ${studyInstance.giveEventTemplates().name.join(", ")} 
     84                  </g:else> 
     85                </td> 
     86              </g:each> 
    6087            </tr> 
    6188            <tr> 
    6289              <td>Sampling events</td> 
    63               <td> 
    64                 <g:if test="${studyInstance.giveSamplingEventTemplates().size()==0}"> 
    65                   - 
    66                 </g:if> 
    67                 <g:else> 
    68                  ${studyInstance.giveSamplingEventTemplates().name.join(", ")} 
    69                 </g:else> 
    70               </td> 
     90              <g:each in="${studyList}" var="studyInstance"> 
     91                <td> 
     92                  <g:if test="${studyInstance.giveSamplingEventTemplates().size()==0}"> 
     93                    - 
     94                  </g:if> 
     95                  <g:else> 
     96                   ${studyInstance.giveSamplingEventTemplates().name.join(", ")} 
     97                  </g:else> 
     98                </td> 
     99              </g:each> 
    71100            </tr> 
    72101            <tr> 
    73102              <td>Readers</td> 
    74               <td> 
    75                 <g:if test="${studyInstance.readers.size()==0}"> 
    76                   - 
    77                 </g:if> 
    78                 <g:else> 
    79                   <g:each in="${studyInstance.readers}" var="r" status="i"> 
    80                     <g:if test="${i > 0}">, </g:if> 
    81                     <g:link controller="user" action="show" id="${r.id}">${r?.encodeAsHTML()}</g:link> 
    82                   </g:each> 
    83                 </g:else> 
    84               </td> 
     103              <g:each in="${studyList}" var="studyInstance"> 
     104                <td> 
     105                  <g:if test="${studyInstance.readers.size()==0}"> 
     106                    - 
     107                  </g:if> 
     108                  <g:else> 
     109                    <g:each in="${studyInstance.readers}" var="r" status="i"> 
     110                      <g:if test="${i > 0}">, </g:if> 
     111                      <g:link controller="user" action="show" id="${r.id}">${r?.encodeAsHTML()}</g:link> 
     112                    </g:each> 
     113                  </g:else> 
     114                </td> 
     115              </g:each> 
    85116            </tr> 
    86117            <tr> 
    87118              <td>Editors</td> 
    88               <td> 
    89                 <g:if test="${studyInstance.editors.size()==0}"> 
    90                   - 
    91                 </g:if> 
    92                 <g:else> 
    93                   <g:each in="${studyInstance.editors}" var="r" status="i"> 
    94                     <g:if test="${i > 0}">, </g:if> 
    95                     <g:link controller="user" action="show" id="${r.id}">${r?.encodeAsHTML()}</g:link> 
    96                   </g:each> 
    97                 </g:else> 
    98               </td> 
     119              <g:each in="${studyList}" var="studyInstance"> 
     120                <td> 
     121                  <g:if test="${studyInstance.editors.size()==0}"> 
     122                    - 
     123                  </g:if> 
     124                  <g:else> 
     125                    <g:each in="${studyInstance.editors}" var="r" status="i"> 
     126                      <g:if test="${i > 0}">, </g:if> 
     127                      <g:link controller="user" action="show" id="${r.id}">${r?.encodeAsHTML()}</g:link> 
     128                    </g:each> 
     129                  </g:else> 
     130                </td> 
     131              </g:each> 
    99132            </tr> 
    100133 
     
    103136 
    104137        <div id="subjects"> 
    105           <g:each in="${studyInstance.giveSubjectTemplates()}" var="template"> 
     138 
     139          <g:if test="${studyList*.subjects.flatten().size()==0}"> 
     140            No subjects in the selected studies 
     141          </g:if> 
     142          <g:else> 
    106143            <table> 
    107144              <thead> 
    108145                <tr> 
     146                  <th></th> 
    109147                  <g:each in="${new dbnp.studycapturing.Subject().giveDomainFields()}" var="field"> 
    110148                    <th>${field}</th> 
    111149                  </g:each> 
    112                   <g:each in="${template.fields}" var="field"> 
     150 
     151                  <% 
     152                    // Determine a union of the fields for all different 
     153                    // subjects in all studies. In order to show a proper list.  
     154                    // We want every field to appear just once, 
     155                    // so the list is filtered for unique values 
     156                    subjectTemplates = studyList*.giveSubjectTemplates().flatten().unique() 
     157                    subjectFields = subjectTemplates*.fields.flatten().unique() 
     158 
     159                    showSubjectFields = subjectFields 
     160 
     161                    /*  
     162                     * These lines are rewritten because 
     163                     * performance sucked 
     164                     * 
     165                     *   // These took about 9 seconds (for 31 subjects and 
     166                     *   allSubjects = studyList*.subjects.flatten() 
     167                     * 
     168                     *   subjectFields = subjectFields.findAll { subjectField -> 
     169                     *     ( true in allSubjects.collect { subject -> subject.fieldExists( subjectField.name ) && subject.getFieldValue( subjectField.name ) != null }.flatten() ) 
     170                     *   } 
     171                     */ 
     172 
     173                    // Filter out all fields that are left blank for all subjects 
     174                    allSubjects = studyList*.subjects.flatten() 
     175 
     176                    showSubjectFields = [] 
     177                    subjectFields.each { subjectField -> 
     178                      for( subject in allSubjects ) 
     179                      { 
     180                        // If the field is filled for this subject, we have to 
     181                        // show the field and should not check any other 
     182                        // subjects (hence the break) 
     183                        if( subject.fieldExists( subjectField.name ) && subject.getFieldValue( subjectField.name ) ) { 
     184                          showSubjectFields << subjectField; 
     185                          break; 
     186                        } 
     187                      } 
     188                    } 
     189                      
     190                  %> 
     191 
     192                  <g:each in="${showSubjectFields}" var="field"> 
    113193                    <th>${field}</th> 
    114194                  </g:each> 
     195 
    115196                </tr> 
    116197              </thead> 
    117                
    118               <% 
    119                 subjects = studyInstance.subjects.findAll {it.template == template}; 
    120                 sortedSubjects = subjects.sort( { a, b -> a.name <=> b.name } as Comparator ) 
    121               %> 
    122               <g:each in="${sortedSubjects}" var="s" status="i"> 
    123                 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    124                   <g:each in="${s.giveDomainFields()}" var="field"> 
    125                     <td>${s.getFieldValue(field.name)}</td> 
    126                   </g:each> 
    127                   <g:each in="${template.fields}" var="field"> 
    128                     <td> 
    129                       ${s.getFieldValue(field.name)} 
    130                     </td> 
     198 
     199              <g:set var="i" value="${1}" /> 
     200 
     201              <g:each in="${studyList}" var="studyInstance"> 
     202                <% 
     203                  // Sort subjects by name 
     204                  subjects = studyInstance.subjects; 
     205                  sortedSubjects = subjects.sort( { a, b -> a.name <=> b.name } as Comparator ) 
     206                %> 
     207 
     208                <g:each in="${sortedSubjects}" var="subject" status="j"> 
     209                  <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
     210                    <g:if test="${j==0}"> 
     211                      <td class="studytitle" rowspan="${sortedSubjects.size()}"> 
     212                        ${studyInstance.title} 
     213                      </td> 
     214                    </g:if> 
     215                    <g:each in="${subject.giveDomainFields()}" var="field"> 
     216                      <td>${subject.getFieldValue(field.name)}</td> 
     217                    </g:each> 
     218                   
     219                    <g:each in="${showSubjectFields}" var="field"> 
     220                      <td> 
     221                        <g:if test="${subject.fieldExists(field.name)}"> 
     222                          ${subject.getFieldValue(field.name)} 
     223                        </g:if> 
     224                        <g:else> 
     225                          N/A 
     226                        </g:else> 
     227                      </td> 
     228                    </g:each> 
     229 
     230                  </tr> 
     231                  <g:set var="i" value="${i + 1}" /> 
    131232                </g:each> 
    132                 </tr> 
    133               </g:each> 
    134  
     233              </g:each> 
    135234            </table> 
    136  
    137           </g:each> 
     235          </g:else> 
    138236        </div> 
    139237 
    140238        <div id="events"> 
    141           <g:if test="${studyInstance.events.size()==0}"> 
    142             No events in this study 
     239          <g:if test="${studyList*.events.flatten().size()==0 && studyInstance*.samplingEvents.flatten().size()==0 }"> 
     240            No events in these studies 
    143241          </g:if> 
    144242          <g:else> 
     
    147245                <thead> 
    148246                  <tr> 
     247                    <th></th> 
    149248                    <th>Start time</th> 
    150249                    <th>Duration</th> 
     
    155254                </thead> 
    156255 
    157               <% 
    158                 // Sort events by starttime and duration 
    159                 events = studyInstance.events + studyInstance.samplingEvents; 
    160                 sortedEvents = events.sort( { a, b -> 
    161                       a.startTime == b.startTime ? 
    162                         a.getDuration().toMilliseconds() <=> b.getDuration().toMilliseconds() : 
    163                         a.startTime <=> b.startTime 
    164                   } as Comparator ) 
    165               %> 
    166  
    167                 <g:each in="${sortedEvents}" var="event" status="i"> 
    168                   <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    169                     <td>${event.getPrettyDuration(studyInstance.startDate,event.startTime)}</td> 
    170                     <td>${event.getPrettyDuration()}</td> 
    171                     <td>${event.template.name}</td> 
    172                     <td> 
    173                       <g:if test="${event instanceof dbnp.studycapturing.SamplingEvent}"> 
    174                         <g:checkBox name="samplingEvent" disabled="${true}" value="${true}"/> 
     256                <g:set var="i" value="${1}" /> 
     257 
     258                <g:each in="${studyList}" var="studyInstance"> 
     259                  <% 
     260                    // Sort events by starttime and duration 
     261                    events = studyInstance.events + studyInstance.samplingEvents; 
     262                    sortedEvents = events.sort( { a, b -> 
     263                          a.startTime == b.startTime ? 
     264                            a.getDuration().toMilliseconds() <=> b.getDuration().toMilliseconds() : 
     265                            a.startTime <=> b.startTime 
     266                      } as Comparator ) 
     267                  %> 
     268 
     269                  <g:each in="${sortedEvents}" var="event" status="j"> 
     270                    <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
     271                      <g:if test="${j==0}"> 
     272                        <td class="studytitle" rowspan="${sortedEvents.size()}"> 
     273                          ${studyInstance.title} 
     274                        </td> 
    175275                      </g:if> 
    176                       <g:else> 
    177                         <g:checkBox name="event" disabled="${true}" value="${false}" /> 
    178                       </g:else> 
    179                     </td> 
    180                     <td> 
    181                       <g:set var="fieldCounter" value="${1}" /> 
    182                       <g:each in="${event.giveTemplateFields()}" var="field"> 
    183                         <g:if test="${event.getFieldValue(field.name)}"> 
    184                           <g:if test="${fieldCounter > 1}">, </g:if> 
    185                             ${field.name} = ${event.getFieldValue( field.name )} 
    186                           <g:set var="fieldCounter" value="${fieldCounter + 1}" /> 
     276                      <td>${event.getPrettyDuration(studyInstance.startDate,event.startTime)}</td> 
     277                      <td>${event.getPrettyDuration()}</td> 
     278                      <td>${event.template.name}</td> 
     279                      <td> 
     280                        <g:if test="${event instanceof dbnp.studycapturing.SamplingEvent}"> 
     281                          <g:checkBox name="samplingEvent" disabled="${true}" value="${true}"/> 
    187282                        </g:if> 
    188                       </g:each> 
    189                     </td> 
    190                   </tr> 
     283                        <g:else> 
     284                          <g:checkBox name="event" disabled="${true}" value="${false}" /> 
     285                        </g:else> 
     286                      </td> 
     287                      <td> 
     288                        <g:set var="fieldCounter" value="${1}" /> 
     289                        <g:each in="${event.giveTemplateFields()}" var="field"> 
     290                          <g:if test="${event.getFieldValue(field.name)}"> 
     291                            <g:if test="${fieldCounter > 1}">, </g:if> 
     292                              ${field.name} = ${event.getFieldValue( field.name )} 
     293                            <g:set var="fieldCounter" value="${fieldCounter + 1}" /> 
     294                          </g:if> 
     295                        </g:each> 
     296                      </td> 
     297                    </tr> 
     298 
     299                    <g:set var="i" value="${i + 1}" /> 
     300                  </g:each> 
    191301                </g:each> 
    192  
    193302              </table> 
    194303 
     
    197306 
    198307        <div id="event-group"> 
    199           <g:if test="${studyInstance.eventGroups.size()==0}"> 
     308          <g:if test="${studyList*.eventGroups.flatten().size()==0}"> 
    200309            No event groups in this study 
    201310          </g:if> 
    202311          <g:else> 
     312            <% 
     313              // Determine a union of the event templates for all different 
     314              // eventgroups in all studies, in order to show a proper list. 
     315              // We want every field to appear just once, 
     316              // so the list is filtered for unique values 
     317              groupTemplates = studyList*.giveAllEventTemplates().flatten().unique() 
     318              subjectFields = subjectTemplates*.fields.flatten().unique() 
     319            %> 
    203320            <table> 
    204               <tr> 
    205                 <td><b>Name</b></td> 
    206                 <td colspan="${studyInstance.giveAllEventTemplates().size()}"><b>Events</b></td> 
    207                 <td><b>Subjects</b></td> 
    208               </tr> 
    209               <tr> 
    210                 <td></td> 
    211                 <g:each in="${studyInstance.giveAllEventTemplates()}" var="eventTemplate"> 
    212                   <td><b>${eventTemplate.name}</b></td> 
     321              <thead> 
     322                <tr> 
     323                  <th></th> 
     324                  <th>Name</th> 
     325                  <th colspan="${groupTemplates.size()}">Events</th> 
     326                  <th>Subjects</th> 
     327                </tr> 
     328                <tr> 
     329                  <th></th> 
     330                  <th></th> 
     331                  <g:each in="${groupTemplates}" var="eventTemplate"> 
     332                    <th>${eventTemplate.name}</th> 
     333                  </g:each> 
     334                  <th></th> 
     335                </tr> 
     336              </thead> 
     337 
     338              <g:set var="i" value="${1}" /> 
     339 
     340              <g:each in="${studyList}" var="studyInstance"> 
     341 
     342                <g:each in="${studyInstance.eventGroups}" var="eventGroup" status="j"> 
     343                  <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
     344                    <g:if test="${j==0}"> 
     345                      <td class="studytitle" rowspan="${studyInstance.eventGroups.size()}"> 
     346                        ${studyInstance.title} 
     347                      </td> 
     348                    </g:if> 
     349                    <td>${eventGroup.name}</td> 
     350 
     351                    <g:each in="${groupTemplates}" var="currentEventTemplate"> 
     352                      <td> 
     353                        <g:each in="${eventGroup.events}" var="event"> 
     354                          <g:if test="${event.template.name==currentEventTemplate.name}"> 
     355 
     356                            <g:set var="fieldCounter" value="${1}" /> 
     357                            <g:each in="${event.giveTemplateFields()}" var="field"> 
     358                              <g:if test="${event.getFieldValue(field.name)}"> 
     359                                <g:if test="${fieldCounter > 1}">, </g:if> 
     360                                  ${field.name} = ${event.getFieldValue( field.name )} 
     361                                <g:set var="fieldCounter" value="${fieldCounter + 1}" /> 
     362                              </g:if> 
     363                            </g:each> 
     364                          </g:if> 
     365                        </g:each> 
     366                      </td> 
     367                    </g:each> 
     368                    <td> 
     369                      <% sortedGroupSubjects = eventGroup.subjects.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
     370                      ${sortedGroupSubjects.name.join( ', ' )} 
     371                    </td> 
     372                  </tr> 
     373 
     374                  <g:set var="i" value="${i + 1}" /> 
    213375                </g:each> 
    214                 <td></td> 
    215               </tr> 
    216               <g:each in="${studyInstance.eventGroups}" var="eventGroup"> 
    217                 <tr> 
    218                   <td>${eventGroup.name}</td> 
    219  
    220                   <g:each in="${studyInstance.giveAllEventTemplates()}" var="currentEventTemplate"> 
    221                     <td> 
    222                       <g:each in="${eventGroup.events}" var="event"> 
    223                         <g:if test="${event.template.name==currentEventTemplate.name}"> 
    224  
    225                           <g:set var="fieldCounter" value="${1}" /> 
    226                           <g:each in="${event.giveTemplateFields()}" var="field"> 
    227                             <g:if test="${event.getFieldValue(field.name)}"> 
    228                               <g:if test="${fieldCounter > 1}">, </g:if> 
    229                                 ${field.name} = ${event.getFieldValue( field.name )} 
    230                               <g:set var="fieldCounter" value="${fieldCounter + 1}" /> 
    231                             </g:if> 
    232                           </g:each> 
    233                         </g:if> 
    234                       </g:each> 
    235                      </td> 
    236                   </g:each> 
    237                   <td> 
    238                     <% sortedGroupSubjects = eventGroup.subjects.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
    239                     ${sortedGroupSubjects.name.join( ', ' )} 
    240                   </td> 
    241                 </tr> 
    242               </g:each> 
     376 
     377              </g:each> 
     378 
    243379            </table> 
    244380          </g:else> 
     
    246382 
    247383        <div id="assays"> 
    248           <g:if test="${studyInstance.assays.size()==0}"> 
    249             No assays in this study 
     384          <g:if test="${studyList*.assays.flatten().size()==0}"> 
     385            No assays in these studies 
    250386          </g:if> 
    251387          <g:else> 
     
    253389              <thead> 
    254390                <tr> 
     391                  <th></th> 
    255392                  <th width="100">Assay Name</th> 
    256393                  <th width="100">Module</th> 
     
    261398                </tr> 
    262399              </thead> 
    263               <g:each in="${studyInstance.assays}" var="assay" status="i"> 
    264                 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    265                   <td>${assay.name}</td> 
    266                   <td>${assay.module.name}</td> 
    267                   <td>${assay.module.type}</td> 
    268                   <td>${assay.module.platform}</td> 
    269                   <td>${assay.module.url}</td> 
    270                   <td> 
    271                     <% sortedAssaySamples = assay.samples.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
    272                     ${sortedAssaySamples.name.join( ', ' )} 
    273                   </td> 
    274                 </tr> 
    275               </g:each> 
    276  
     400              <g:set var="i" value="${1}" /> 
     401 
     402              <g:each in="${studyList}" var="studyInstance"> 
     403                <g:each in="${studyInstance.assays}" var="assay" status="j"> 
     404                  <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
     405                    <g:if test="${j==0}"> 
     406                      <td class="studytitle" rowspan="${studyInstance.assays.size()}"> 
     407                        ${studyInstance.title} 
     408                      </td> 
     409                    </g:if> 
     410                    <td>${assay.name}</td> 
     411                    <td>${assay.module.name}</td> 
     412                    <td>${assay.module.type}</td> 
     413                    <td>${assay.module.platform}</td> 
     414                    <td>${assay.module.url}</td> 
     415                    <td> 
     416                      <% sortedAssaySamples = assay.samples.sort( { a, b -> a.name <=> b.name } as Comparator )  %> 
     417                      ${sortedAssaySamples.name.join( ', ' )} 
     418                    </td> 
     419                  </tr> 
     420                  <g:set var="i" value="${i + 1}" /> 
     421 
     422                </g:each> 
     423              </g:each> 
    277424            </table> 
    278425          </g:else> 
     
    280427 
    281428        <div id="persons"> 
    282           <g:if test="${studyInstance.persons.size()==0}"> 
    283             No persons involved in this study 
     429          <% 
     430            // Determine a list of all persons 
     431            allPersons = studyList*.persons*.person.flatten().unique() 
     432          %> 
     433          <g:if test="${allPersons.size()==0}"> 
     434            No persons involved in these studies 
    284435          </g:if> 
    285436          <g:else> 
     
    289440                  <th>Name</th> 
    290441                  <th>Affiliations</th> 
    291                   <th>Role</th> 
    292442                  <th>Phone</th> 
    293443                  <th>Email</th> 
     444                  <g:each in="${studyList}" var="studyInstance"> 
     445                    <th>${studyInstance.title}</th> 
     446                  </g:each> 
    294447                </thead> 
    295448              </tr> 
    296               <g:each in="${studyInstance.persons}" var="studyperson" status="i"> 
     449              <g:each in="${allPersons}" var="person" status="i"> 
    297450                <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    298                   <td>${studyperson.person.firstName} ${studyperson.person.prefix} ${studyperson.person.lastName}</td> 
     451                  <td>${person.firstName} ${person.prefix} ${person.lastName}</td> 
    299452                  <td> 
    300                     ${studyperson.person.affiliations.join(', ')} 
     453                    ${person.affiliations.join(', ')} 
    301454                  </td> 
    302                   <td>${studyperson.role.name}</td> 
    303                   <td>${studyperson.person.phone}</td> 
    304                   <td>${studyperson.person.email}</td> 
     455                  <td>${person.phone}</td> 
     456                  <td>${person.email}</td> 
     457                  <g:each in="${studyList}" var="studyInstance"> 
     458                    <% 
     459                      studyperson = studyInstance.persons.find { it.person == person } 
     460                    %> 
     461                    <td> 
     462                      <g:if test="${studyperson}"> 
     463                        ${studyperson.role.name} 
     464                      </g:if> 
     465                     </td> 
     466                  </g:each> 
     467 
    305468                </tr> 
    306469              </g:each> 
     
    310473 
    311474        <div id="publications"> 
    312           <g:if test="${studyInstance.publications.size()==0}"> 
    313             No publications attached to this study 
     475          <% 
     476            // Determine a list of all persons 
     477            allPublications = studyList*.publications.flatten().unique() 
     478          %> 
     479          <g:if test="${allPublications.size()==0}"> 
     480            No publications attached to these studies 
    314481          </g:if> 
    315482          <g:else> 
     
    320487                  <th>Authors</th> 
    321488                  <th>Comments</th> 
     489                  <g:each in="${studyList}" var="studyInstance"> 
     490                    <th>${studyInstance.title}</th> 
     491                  </g:each> 
    322492                </thead> 
    323493              </tr> 
    324               <g:each in="${studyInstance.publications}" var="publication" status="i"> 
     494              <g:each in="${allPublications}" var="publication" status="i"> 
    325495                <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 
    326496                  <td>${publication.title}</td> 
    327497                  <td> 
    328                     ${publication.authorlist} 
     498                    ${publication.authorsList} 
    329499                  </td> 
    330                   <td>${publication.comment}</td> 
     500                  <td>${publication.comments}</td> 
     501                  <g:each in="${studyList}" var="studyInstance"> 
     502                    <td> 
     503                      <g:if test="${publication in studyInstance.publications}"> 
     504                        x 
     505                      </g:if> 
     506                    </td> 
     507                  </g:each> 
    331508                </tr> 
    332509              </g:each> 
     
    340517    <div class="buttons"> 
    341518      <g:form> 
    342         <g:hiddenField name="id" value="${studyInstance?.id}" /> 
    343         <span class="button"><g:actionSubmit class="edit" action="edit" value="${message(code: 'default.button.edit.label', default: 'Edit')}" /></span> 
    344         <span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></span> 
     519        <g:if test="${studyList.size() == 1}"> 
     520          <g:set var="studyInstance" value="${studyList[0]}" /> 
     521          <g:hiddenField name="id" value="${studyInstance?.id}" /> 
     522          <span class="button"><g:actionSubmit class="edit" action="edit" value="${message(code: 'default.button.edit.label', default: 'Edit')}" /></span> 
     523          <span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></span> 
     524        </g:if> 
    345525        <span class="button"><g:link class="backToList" action="list">Back to list</g:link></span> 
    346526      </g:form> 
    347527    </div> 
     528 
    348529  </div> 
    349530</body>