Skip to content
This repository has been archived by the owner on Jan 21, 2021. It is now read-only.

Commit

Permalink
Improved subject selection for subject groups
Browse files Browse the repository at this point in the history
Subject selection can now be done using a datatable. This enables
the user to use the study design also for studies with large amounts
of subjects.
  • Loading branch information
roberthorlings committed Jul 24, 2015
1 parent 112bdf0 commit ce29680
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import dbnp.authentication.SecUser
class StudyEditDesignController {
def authenticationService
def studyEditService
def datatablesService

def index() {
def study = getStudyFromRequest( params )
Expand Down Expand Up @@ -679,7 +680,7 @@ class StudyEditDesignController {
*/
def subjectGroupUpdate() {
def subjectGroup = SubjectGroup.get( params.long( "id" ) );

if( !subjectGroup ) {
response.status = 404
render "Not found"
Expand All @@ -688,20 +689,20 @@ class StudyEditDesignController {

def name = params.get( "name" )
def result = [ "OK" ]

def subjectIds = params.subjects?.split(",")
if( name && name != subjectGroup.name ) {
subjectGroup.name = name

if( subjectGroup.save() ) {
handleSubjectsInSubjectGroup( params.list( "subjects[]" ), subjectGroup )
handleSubjectsInSubjectGroup( subjectIds, subjectGroup )
studyEditService.generateSamples( subjectGroup )
} else {
response.status = 500
result = [ status: "Error" ]
}

} else {
handleSubjectsInSubjectGroup( params.list( "subjects[]" ), subjectGroup )
handleSubjectsInSubjectGroup( subjectIds, subjectGroup )
studyEditService.generateSamples( subjectGroup )
}

Expand Down Expand Up @@ -781,6 +782,48 @@ class StudyEditDesignController {
}

}

/**
* Returns data for the datatable to select subjects in a subjectgroup
* @return
*/
def dataTableSubjectSelection() {
def study = Study.read( params.long( "id" ) )

if( !study ) {
render dataTableError( "Invalid study given: " + study ) as JSON
return
}

def searchParams = datatablesService.parseParams( params )
def data = studyEditService.getSubjectsForSubjectSelection( searchParams, study )

render datatablesService.createDatatablesOutput( data, params, { entry ->
def output = entry as List
def subject = entry[ 0 ]

// Convert columns
output[ 0 ] = subject.id

// Generate output (the checkbox columns should be first
output
}) as JSON
}

/**
* Returns an error response for the datatable
* @param error
* @return
*/
protected def dataTableError( error ) {
return [
sEcho: params.sEcho,
iTotalRecords: 0,
iTotalDisplayRecords: 0,
aaData: [],
errorMessage: error
]
}


/**
Expand Down
103 changes: 101 additions & 2 deletions grails-app/services/dbnp/studycapturing/StudyEditService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,35 @@ class StudyEditService {

output
}

/**
* Returns a proper list of subjects samples to generate a datatable for subject selection in a group
* @param params Parameters to search
int offset Display start point in the current data set.
int max Number of records that the table can display in the current draw. It is expected that the number of records returned will be equal to this number, unless the server has fewer records to return.
string search Global search field
int sortColumn Column being sorted on (you will need to decode this number for your database)
string sortDirection Direction to be sorted - "desc" or "asc".
Template template Template for the entities to read
* @return A map with all data. For example:
List entities List with all entities
int total Total number of records in the whole dataset (without taking search, offset and max into account)
int totalFiltered Total number of records in the search (without taking offset and max into account)
int ids Total list of filtered ids
*/
def getSubjectsForSubjectSelection( searchParams, study ) {
def query = generateHQLForSubjectSelection( searchParams, study )

// Also count the total number of results in the dataset
def output = generateOutput( query, searchParams, Subject )
output.total = Subject.countByParent( study )

output
}


/**
* Returns a proper list of samples to generate a datatable with templated entities.
Expand Down Expand Up @@ -90,7 +119,6 @@ class StudyEditService {

}


/**
* Generates the HQL to search
* @return Map
Expand Down Expand Up @@ -308,7 +336,78 @@ class StudyEditService {
params: hqlParams
]
}


/**
* Generates the HQL to search assay samples
* @return Map
* select
* from
* where
* order
* params
*/
def generateHQLForSubjectSelection( searchParams, study ) {
def entity = Subject

// Search in
// subject name
// subject template name
// subject speciies

// Create an HQL query as it gives us the most flexibility in searching and ordering
def from = " Subject s "
def joins = []
def whereClause = []
def hqlParams = [ study: study ]
def orderBy = ""

// Add joins for related information
joins << "s.template as template"

// First add searching
if( searchParams.search ) {
// With searching, retrieving the data requires joining all text and term fields
def searchTerm = searchParams.search.toLowerCase()
hqlParams[ "search" ] = "%" + searchTerm + "%"

whereClause << "lower(s.name) LIKE :search"
whereClause << "lower(template.name) LIKE :search"
whereClause << "lower(s.species.name) LIKE :search"
}

// Add ordering; to determine the column to sort on
def sortColumnIndex = searchParams.sortColumn ?: 0
def sortOrder = searchParams.sortDirection ?: "ASC"

def fields = [
"s.name",
"template.name",
"s.species.name",
]

if( sortColumnIndex != null || sortColumnIndex < fields.size() ) {
orderBy = fields[ sortColumnIndex ] + " " + sortOrder
}

// Now build up the query, except for the SELECT part.
if( joins )
from += " LEFT JOIN " + joins.join( " LEFT JOIN " )

def where = "s.parent = :study"

if( whereClause )
where += " AND (" + whereClause.join( " OR " ) + ") "

[
select: "s, " + fields.join( ", " ),
from: from,
where: where,
order: orderBy,
params: hqlParams
]
}


def putParentIntoEntity( entity, params ) {
// Was a parentID given
if( params.parentId ) {
Expand Down
35 changes: 20 additions & 15 deletions grails-app/views/studyEditDesign/index.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta name="layout" content="main" />
<title>Study edit wizard</title>

<r:require modules="studyEdit" />
<r:require modules="studyEdit,gscf-datatables" />
</head>
<body>
<div class="basicTabLayout studyEdit studyProperties">
Expand Down Expand Up @@ -98,20 +98,25 @@
<label>Name: </label><input type="text" name="subjectgroup-name" id="subjectgroup-name" /><br />

<div id="design-subjects">
<span class="subjects">
<g:set var="subjectCount" value="${study.subjects?.size()}" />
<g:set var="numRows" value="${Math.max( (int)subjectCount / 3, 1 )}" />
<g:each in="${study.subjects}" var="subject" status="i">
<span class="subject">
<input type="checkbox" name="subjectgroup_subjects" id="subjectgroup_subjects_${subject.id}" value="${subject.id}" /> ${subject.name}
</span>

<g:if test="${( i + 1 ) % numRows == 0}">
</span>
<span class="subjects">
</g:if>
</g:each>
</span>
<table id="selectSubjectsTable" data-fieldPrefix="subject" class="subjectsTable selectMulti" rel="${g.createLink(action:"dataTableSubjectSelection", id: study.id)}">
<thead>
<tr>
<th>Name</th>
<th>Template</th>
<th>Species</th>
</tr>
</thead>
<tfoot>
<tr><td>
<div class="messagebar loadingSelection">
Loading selection of subjects. Please be patient.
</div>
<div class="messagebar selectAll">
You selected all items on this page. Would you <a href="#">select all items on other pages</a> as well?
</div>
</td></tr>
</tfoot>
</table>
</div>
</div>

Expand Down
23 changes: 23 additions & 0 deletions web-app/css/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -1504,3 +1504,26 @@ img.icon {
/** END :: Message Boxes **/

div.usermanagement { font-size: 0.8em; }

.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000;
background-color: white;
opacity: 0.8;
}

.overlay .message {
display: block;
position: fixed;

top: 50%;
width: 100%;
margin-top: -0.5em;

text-align: center;
font-weight: bold;
}
1 change: 1 addition & 0 deletions web-app/css/studyEdit.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ div .contacts_dialog {

/** Study design styles **/

#subjectGroupDialog table { width: 100%; }
.subjectsTable th { white-space: nowrap;}
table.dataTable td.dataTables_empty { text-align: left; }

Expand Down
Loading

0 comments on commit ce29680

Please sign in to comment.