Changeset 1213
- Timestamp:
- Nov 29, 2010, 4:56:48 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/dbnp/studycapturing/StudyController.groovy
r1203 r1213 124 124 } 125 125 } 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 } 126 279 127 280 def showByToken = { -
trunk/grails-app/domain/dbnp/authentication/SecUser.groovy
r1144 r1213 29 29 } 30 30 31 32 33 34 35 31 public boolean equals(Object y) 32 { 33 if( !( y instanceof SecUser ) ) { 34 return false; 35 } 36 36 37 37 if (y == null) return false; 38 38 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 41 46 } -
trunk/grails-app/domain/dbnp/studycapturing/Subject.groovy
r1198 r1213 68 68 return name 69 69 } 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 } 70 111 } -
trunk/grails-app/domain/dbnp/studycapturing/TemplateField.groovy
r1175 r1213 353 353 } 354 354 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 355 368 } -
trunk/grails-app/views/study/show.gsp
r1206 r1213 1 2 1 <%@ page import="dbnp.studycapturing.Study" %> 3 2 <%@ page import="dbnp.studycapturing.EventGroup" %> … … 10 9 <title><g:message code="default.show.label" args="[entityName]" /></title> 11 10 <script type="text/javascript"> 12 // Flag whether the timelines have been loaded13 var timelineloaded = false;14 15 11 // Number of timelines that should be loaded 16 12 var numTimelines = ${studyList?.size()}; … … 19 15 $(function() { 20 16 $("#tabs").tabs({ 21 show: function(event, ui) {17 load: function(event, ui) { 22 18 // 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' ) { 24 20 loadTimeline( 'eventstimeline', 'eventtitles', 0 ); 25 timelineloaded = true;26 21 } 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 } 28 29 }); 29 30 }); 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 30 37 </script> 31 38 <link rel="stylesheet" type="text/css" href="${resource(dir: 'css', file: 'studies.css')}"/> 32 39 33 40 <!-- 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>39 41 <script src="${resource(dir: 'js', file: 'timeline-simile/timeline-api.js')}" type="text/javascript"></script> 40 42 <script src="${resource(dir: 'js', file: 'timeline-simile/custom-timeline.js')}" type="text/javascript"></script> … … 43 45 44 46 <!-- 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> 197 48 </head> 198 49 <body> … … 207 58 <ul> 208 59 <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> 216 67 </ul> 217 68 … … 237 88 <!-- Show all template and domain fields, if filled --> 238 89 <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 )}"> 245 91 <tr> 246 92 <td>${field}</td> … … 331 177 </div> 332 178 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> </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 */ %> 940 183 941 184 </div> -
trunk/web-app/js/timeline-simile/relative-time.js
r1203 r1213 112 112 this._backgroundLayer = band.createLayerDiv(0); 113 113 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()]; 115 115 116 116 this._markerLayer = null;
Note: See TracChangeset
for help on using the changeset viewer.