Changeset 518


Ignore:
Timestamp:
Jun 3, 2010, 12:27:08 PM (9 years ago)
Author:
roberth
Message:

Publications can be added to a study in the create study wizard. Publications are searched for on Pubmed, and when needed, added to the system.

Location:
trunk
Files:
2 added
11 edited

Legend:

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

    r496 r518  
    144144        }
    145145    }
     146
     147    /**
     148     * Searches for an ID in the current database, based on the pubMedID
     149     * If the publication is not found in the database, it is added
     150     */
     151    def getID = {
     152        // Find the ID
     153        def pubMedID = params.get( 'publication-pubMedID' );
     154        if( pubMedID ) {
     155            def publication = Publication.findByPubMedID( pubMedID );
     156            if( !publication ) {
     157                publication = new Publication(
     158                    title: params.get( 'publication-title' ),
     159                    authorsList: params.get( 'publication-authorsList' ),
     160                    pubMedID: params.get( 'publication-pubMedID' ),
     161                    DOI: params.get( 'publication-doi' )
     162                ).save(flush:true);
     163            }
     164
     165            // Return the ID
     166            render publication.id;
     167        } else {
     168            response.status = 500;
     169            render "No pubMedID found in request";
     170        }
     171    }
    146172}
  • trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy

    r512 r518  
    133133                                success()
    134134                        }.to "study"
    135                         on("switchTemplate") {
     135                        on("switchTemplate") {
    136136                                flash.values = params
    137137
     
    604604                }
    605605
     606                // handle Publications
     607                handlePublications(flow, flash, params)
     608
    606609                // validate study
    607610                if (flow.study.validate()) {
     
    614617                }
    615618        }
     619
     620        /**
     621         * re-usable code for handling publications form data in a web flow
     622         * @param Map LocalAttributeMap (the flow scope)
     623         * @param Map localAttributeMap (the flash scope)
     624         * @param Map GrailsParameterMap (the flow parameters = form data)
     625         * @returns boolean
     626         */
     627        def handlePublications(flow, flash, params) {
     628                // create study instance if we have none
     629                if (!flow.study) flow.study = new Study();
     630                if (!flow.study.publications ) flow.study.publications = [];
     631
     632                // Check the ids of the pubblications that should be attached
     633                // to this study. If they are already attached, keep 'm. If
     634                // studies are attached that are not in the selected (i.e. the
     635                // user deleted them), remove them
     636                def publicationIDs = params.get( 'publication_ids' );
     637                if( publicationIDs ) {
     638                    // Find the individual IDs and make integers
     639                    publicationIDs = publicationIDs.split(',').collect { Integer.parseInt( it, 10 ) };
     640
     641                    // First remove the publication that are not present in the array
     642                    flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
     643
     644                    // Add those publications not yet present in the database
     645                    publicationIDs.each { id ->
     646                        if( !flow.study.publications.find { publication -> id == publication.id } ) {
     647                            def publication = Publication.get( id );
     648                            if( publication ) {
     649                                flow.study.addToPublications( publication );
     650                            } else {
     651                                println( 'Publication with ID ' + id + ' not found in database.' );
     652                            }
     653                        }
     654                    }
     655
     656                } else {
     657                    println( 'No publications selected.')
     658                    flow.study.publications.clear();
     659                }
     660
     661        }
     662
    616663
    617664        /**
  • trunk/grails-app/domain/dbnp/studycapturing/Publication.groovy

    r496 r518  
    4646 */
    4747
    48 class Publication {
     48class Publication implements Serializable {
    4949
    5050    String title
  • trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy

    r507 r518  
    983983                }
    984984        }
     985
     986        def PublicationSelectElement = { attrs, body ->
     987
     988            attrs.description = 'Publications';
     989            // render list with publications currently available
     990            baseElement.call(
     991                    '_publicationList',
     992                    attrs,
     993                    body
     994            )
     995
     996            attrs.description = '';
     997
     998            // render 'Add publication button'
     999            baseElement.call(
     1000                    '_publicationAddButton',
     1001                    attrs,
     1002                    body
     1003            )
     1004        }
     1005
     1006        /**
     1007         * Renders a input box for publications
     1008         */
     1009        def publicationSelect = { attrs, body ->
     1010                if( attrs.get( 'value' ) == null ) {
     1011                    attrs.value = [];
     1012                }
     1013                if( attrs.get( 'description' ) == null ) {
     1014                    attrs.description = '';
     1015                }
     1016                out << '<form id="' + attrs.name + '_form">';
     1017                out << textField(
     1018                            name: attrs.get( "name" ),
     1019                            value: '',
     1020                            rel: 'publication-pubmed',
     1021                            style: 'width: 400px;'
     1022                );
     1023                out << '</form>';
     1024                out << '<script type="text/javascript">';
     1025                out << '  iField = $( "#' + attrs.get( 'name' ) + '" );';
     1026                out << '  new PublicationChooser().initAutocomplete( iField );';
     1027                out << '</script>';
     1028        }
     1029
     1030        def _publicationList = { attrs, body ->
     1031           def display_none = 'none';
     1032           if( !attrs.get( 'value' ) || attrs.get( 'value' ).size() == 0 ) {
     1033                display_none =  'inline';
     1034           }
     1035           
     1036            // Add a unordered list
     1037            out << '<ul class="publication_list" id="' + attrs.name + '_list">';
     1038
     1039            out << '<li>';
     1040            out << '<span class="publication_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
     1041            out << 'No publications selected';
     1042            out << '</span>';
     1043            out << '</li>';
     1044
     1045            out << '</ul>';
     1046
     1047           // Add the publications using javascript
     1048           out << '<script type="text/javascript">'
     1049           if( attrs.get( 'value' ) && attrs.get( 'value' ).size() > 0 ) {
     1050               def i = 0;
     1051               attrs.get( 'value' ).each {
     1052                    out << 'showPublication( ';
     1053                    out << '  "' + attrs.name + '",';
     1054                    out << '  ' + it.id + ',';
     1055                    out << '  "' + it.title + '",';
     1056                    out << '  "' + it.authorsList + '",';
     1057                    out << '  ' + i++;
     1058                    out << ');';
     1059                }
     1060            }
     1061            out << '</script>';
     1062
     1063            def ids;
     1064            if( attrs.get( 'value' ) && attrs.get( 'value' ).size() > 0 ) {
     1065                ids = attrs.get( 'value' ).id.join( ',' )
     1066            } else {
     1067                ids = '';
     1068            }
     1069            out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids" value="' + ids + '">';
     1070        }
     1071
     1072        def _publicationAddButton = { attrs, body ->
     1073
     1074            // Output the dialog for the publications
     1075            out << '<div id="' + attrs.name + '_dialog">';
     1076            out << publicationSelect( attrs, body );
     1077            out << '</div>';
     1078            out << '<script type="text/javascript">';
     1079            out << '     $("#' + attrs.name + '_dialog").dialog({';
     1080            out << '         title   : "Add publication",';
     1081            out << '         autoOpen: false,';
     1082            out << '         width   : 800,';
     1083            out << '         height  : 400,';
     1084            out << '         modal   : true,';
     1085            out << '         position: "center",';
     1086            out << '         buttons : {';
     1087            out << '            Add  : function() { addPublication( "' + attrs.name + '" ); $(this).dialog("close"); },';
     1088            out << '            Close  : function() { $(this).dialog("close"); }';
     1089            out << '         },';
     1090            out << '         close   : function() {';
     1091            out << '             /* closeFunc(this); */';
     1092            out << '         }';
     1093            out << '     }).width(790).height(400);';
     1094            out << '</script>';
     1095
     1096            out << '<input type="button" onClick="var field = $( \'#' + attrs.name + '\' ); field.autocomplete( \'close\' ); field.val( \'\' );$( \'#' + attrs.name + '_dialog\' ).dialog( \'open\' ); field.focus();" value="Add Publication">';
     1097        }
     1098
    9851099}
  • trunk/grails-app/views/publication/add.gsp

    r496 r518  
    2222  <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.pubmed.js')}"></script>
    2323  <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.programminglanguages.js')}"></script>
    24 
     24  <script type="text/javascript">
     25    PublicationChooser.prototype.availableDBs[ "pubmed" ].select = selectPubMedAdd;
     26    PublicationChooser.prototype.availableDBs[ "pubmed" ].close  = closePubMedAdd;
     27</script>
    2528 </head>
    2629 <body>
    27 
    28  <g:form action="createFromPubmed">
    29   <g:textField name="publication" rel="publication-pubmed" style="width:300px;"/>
    30   <g:submitButton name="add" value="Add publication" />
    31  </g:form>
    32 
    33 
     30   <p>
     31     Search for a publication on pubmed. You can search on a part of the title or authors.
     32   </p>
     33   <g:form action="createFromPubmed">
     34    <g:textField name="publication" rel="publication-pubmed" style="width:500px;"/>
     35    <!-- <g:submitButton name="add" value="Add publication" /> -->
     36   </g:form>
    3437
    3538 ${errors}
  • trunk/grails-app/views/wizard/index.gsp

    r507 r518  
    2323        <script type="text/javascript" src="${resource(dir: 'js', file: 'table-editor.min.js')}"></script>
    2424        <script type="text/javascript" src="${resource(dir: 'js', file: 'ontology-chooser.min.js')}"></script>
     25        <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.js')}"></script>
     26        <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.pubmed.js')}"></script>
    2527        <script type="text/javascript" src="${resource(dir: 'js', file: 'SelectAddMore.min.js')}"></script>
    2628        <script type="text/javascript" src="${resource(dir: 'js', file: 'timepicker-0.2.1.min.js')}"></script>
     
    3335        <script type="text/javascript" src="${resource(dir: 'js', file: 'table-editor.js')}"></script>
    3436        <script type="text/javascript" src="${resource(dir: 'js', file: 'ontology-chooser.js')}"></script>
     37        <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.js')}"></script>
     38        <script type="text/javascript" src="${resource(dir: 'js', file: 'publication-chooser.pubmed.js')}"></script>
    3539        <script type="text/javascript" src="${resource(dir: 'js', file: 'SelectAddMore.js')}"></script>
    3640        <script type="text/javascript" src="${resource(dir: 'js', file: 'timepicker-0.2.1.js')}"></script>
  • trunk/grails-app/views/wizard/pages/_study.gsp

    r455 r518  
    3030        </wizard:templateElement>
    3131        <g:if test="${study}"><wizard:templateElements entity="${study}" /></g:if>
     32
     33        <wizard:publicationSelectElement name="publication" value="${study?.publications}" />
     34
    3235</wizard:pageContent>
  • trunk/web-app/css/wizard.css

    r507 r518  
    348348    color: red;
    349349}
    350 }
     350
     351.wizard ul.publication_list {
     352    list-style-type: none;
     353    margin: -10px 0 0 255px;
     354    padding: 0;
     355}
     356
     357.wizard ul.publication_list li {
     358    margin-left: 0px;
     359    padding: 4px 6px;
     360}
     361
     362.wizard ul.publication_list li.even {
     363    background-color: #F0F0F0;
     364}
     365.wizard ul.publication_list li.odd {
     366    background-color: #F8F8F8;
     367}
     368
     369.wizard ul.publication_list li .delete_button { float: right; margin-left: 10px; }
     370.wizard ul.publication_list li .authors { font-size: 10px; margin-top: 3px; color: #333; }
  • trunk/web-app/js/publication-chooser.js

    r496 r518  
    149149 * initialize object
    150150 */
    151 PublicationChooser.init = function() {
     151PublicationChooser.init = function( events ) {
    152152    // find all ontology elements
    153153    $("input[rel*='publication']").each(function() {
    154         new PublicationChooser().initAutocomplete(this);
     154        new PublicationChooser().initAutocomplete( this, events );
    155155    });
    156156};
     
    159159    minLength       : 3,        // minimum input length before launching Ajax request
    160160    cache           : [],       // ontology cache
    161     maxResults      : 10,       // Max number of results retrieved
     161    maxResults      : 8,        // Max number of results retrieved
    162162    database        : '',       // Default database to be used. As for now, also the only possible database
    163163    availableDBs    : {},       // Available databases, filled by extensions. Key is databasename and value is reference to method to be called
     164    events          : {},       // Stores the events to fire
    164165   
    165166    /**
     
    167168     * @param element
    168169     */
    169     initAutocomplete: function(element) {
     170    initAutocomplete: function( element, customEvents ) {
    170171        var that = this
    171172        var inputElement = $(element);
    172173        var selected = false;
    173174
     175        // Add spinner element
     176        if( !baseUrl ) {
     177            var baseUrl = '..';
     178        }
     179        var imgEl = document.createElement( 'img' );
     180        imgEl.setAttribute( 'id', inputElement.attr( 'id' ) + '_spinner' );
     181        imgEl.setAttribute( 'src', baseUrl + '/images/spinner.gif' );
     182        imgEl.setAttribute( 'style', 'margin-left: 5px;');
     183
     184        // Add the element next to the input box
     185        inputElement.after( imgEl );
     186        $( imgEl ).hide();
     187
    174188        // determine what database to use
    175189        var values = inputElement.attr('rel').split("-");
     190
     191        // Check which database the user wants
    176192        if( values.length > 1 ) {
    177193            // check for supported databases
    178194            if( this.availableDBs[ values[ 1 ] ] ) {
    179195                this.database = values[1];
     196                this.events = this.availableDBs[ this.database ];
    180197            } else {
    181                 alert( 'Database ' + values[1] + ' not supported. Using default: ' + this.database );
     198                this.database = values[1] + " (custom)";
     199                this.events = {};
    182200            }
     201        } else {
     202            this.database = "(custom)";
     203            this.events = {};
     204        }
     205
     206        // Add custom events to this object
     207        if( customEvents ) {
     208            $.each( customEvents, function( id, func ) {
     209                that.events[ id ] = func;
     210            })
     211        }
     212       
     213        // If no 'source' function is defined, nothing can be searched for
     214        if( !this.events[ 'source' ]) {
     215            alert( 'Database ' + this.database + ' not supported. Using none.' );
    183216        }
    184217
     
    189222        inputElement.autocomplete({
    190223            minLength: that.minLength,
    191             delay: 400,
     224            delay: 300,
    192225
    193226            source: function(request, response) {
     
    199232                    response(that.cache[ that.database ][ q ]);
    200233                } else {
    201                     if( that.database != "" && that.availableDBs[ that.database ] && that.availableDBs[ that.database ][ 'source' ] ) {
    202                         that.availableDBs[ that.database ][ 'source' ]( that, q, response );
     234                    if( that.database != "" && that.events[ 'source' ] ) {
     235                       that.events[ 'source' ]( that, q, response );
    203236                    }
    204237                }
     
    206239            search: function(event, ui ) {
    207240                that.selected = false;
     241                $( '#' + inputElement.attr( 'id' ) + '_spinner' ).show();
     242            },
     243            open: function(event, ui ) {
     244                $( '#' + inputElement.attr( 'id' ) + '_spinner' ).hide();
    208245            },
    209246            select: function(event, ui) {
     
    211248                that.selected = true;
    212249
    213                 if( that.database != "" && that.availableDBs[ that.database ] && that.availableDBs[ that.database ][ 'select' ] ) {
    214                     that.availableDBs[ that.database ][ 'select' ]( that, inputElement, event, ui );
     250                if( that.database != "" && that.events[ 'select' ] ) {
     251                    that.events[ 'select' ]( that, inputElement, event, ui );
    215252                }
    216253            },
    217254            close: function(event, ui) {
    218255                if( !that.selected ) {
    219                     if( that.database != "" && that.availableDBs[ that.database ] && that.availableDBs[ that.database ][ 'close' ] ) {
    220                         that.availableDBs[ that.database ][ 'close' ]( that, inputElement, event, ui );
     256                    if( that.database != "" && that.events[ 'close' ] ) {
     257                        that.events[ 'close' ]( that, inputElement, event, ui );
     258                    }
     259
     260                    if( inputElement.closePublication ) {
     261                        inputElement.closePublication( that, event, ui );
    221262                    }
    222263                }
  • trunk/web-app/js/publication-chooser.pubmed.js

    r496 r518  
    44    var terms = searchterm.split( " " );
    55    for( var i = 0; i < terms.length; i++ ) {
    6         searchFor[ searchFor.length ] = "(" + terms[i].trim() + "[title]" + " OR " + terms[i].trim() + "[author]" + ")";
     6        searchFor[ searchFor.length ] = "(" + $.trim( terms[i] ) + "[title]" + " OR " + $.trim( terms[i] ) + "[author]" + ")";
    77    }
    88
     
    3838};
    3939
    40 selectPubMed = function( chooserObject, inputElement, event, ui ) {
     40// Handler that handles the select of a publication
     41selectPubMedAdd = function( chooserObject, inputElement, event, ui ) {
    4142
    4243    // option selected, set hidden fields
     
    5253    element.removeClass('error');
    5354};
    54 closePubMed  = function( chooserObject, inputElement, event, ui ) {
     55
     56// Handler that handles the closing of the autocomplete
     57closePubMedAdd  = function( chooserObject, inputElement, event, ui ) {
    5558    // no he didn't, clear the field(s)
    5659    var element = inputElement;
     
    116119}
    117120
     121// Only the source method should be used for all pubmed autocompletes
     122// The select and close handlers that are defined here, should only be used
     123// on the add publication page. They can be overridden in the initialization
     124// of the publication chooser class
    118125PublicationChooser.prototype.availableDBs[ "pubmed" ] = {
    119126    'source': sourcePubMed,
    120     'select': selectPubMed,
    121     'close':  closePubMed
     127    'select': selectPubMedAdd,
     128    'close':  closePubMedAdd
    122129};
     130
  • trunk/web-app/js/wizard.js

    r507 r518  
    351351    return '<a target="_blank" href="' + baseUrl + '/file/get/' + filename + '">' + filename + '</a>';
    352352}
     353
     354function addPublication( element_id ) {
     355  /* Find publication ID and add to form */
     356  jQuery.ajax({
     357    type:"GET",
     358    url: baseUrl + "/publication/getID?" +  $("#" + element_id + "_form").serialize(),
     359    success: function(data,textStatus){
     360        var id = parseInt( data );
     361
     362        // Put the ID in the array, but only if it does not yet exist
     363        var ids = getPublicationIds( element_id );
     364
     365        if( $.inArray(id, ids ) == -1 ) {
     366            ids[ ids.length ] = id;
     367            $( '#' + element_id + '_ids' ).val( ids.join( ',' ) );
     368
     369            // Show the title and a remove button
     370            showPublication( element_id, id, $("#" + element_id + "_form").find( '[name=publication-title]' ).val(), $("#" + element_id + "_form").find( '[name=publication-authorsList]' ).val(), ids.length - 1 );
     371
     372            // Hide the 'none box'
     373            $( '#' + element_id + '_none' ).css( 'display', 'none' );
     374        }
     375    },
     376    error:function(XMLHttpRequest,textStatus,errorThrown){ alert( "Publication could not be added." ) }
     377  }); return false;
     378}
     379
     380function removePublication( element_id, id ) {
     381    var ids = getPublicationIds( element_id );
     382    if( $.inArray(id, ids ) != -1 ) {
     383        // Remove the ID
     384        ids.splice($.inArray(id, ids ), 1);
     385        $( '#' + element_id + '_ids' ).val( ids.join( ',' ) );
     386
     387        // Remove the title from the list
     388        var li = $( "#" + element_id + '_item_' + id );
     389        if( li ) {
     390            li.remove();
     391        }
     392
     393        // Show the 'none box' if needed
     394        if( ids.length == 0 ) {
     395            $( '#' + element_id + '_none' ).css( 'display', 'inline' );
     396        }
     397
     398    }
     399}
     400
     401function getPublicationIds( element_id ) {
     402    var ids = $( '#' + element_id + '_ids' ).val();
     403    if( ids == "" ) {
     404        return new Array();
     405    } else {
     406        ids_array = ids.split( ',' );
     407        for( var i = 0; i < ids_array.length; i++ ) {
     408            ids_array[ i ] = parseInt( ids_array[ i ] );
     409        }
     410
     411        return ids_array;
     412    }
     413}
     414
     415function showPublication( element_id, id, title, authors, nr ) {
     416    var deletebutton = document.createElement( 'img' );
     417    deletebutton.setAttribute( 'class', 'famfamfam delete_button' );
     418    deletebutton.setAttribute( 'alt', 'remove this publication' );
     419    deletebutton.setAttribute( 'src', baseUrl + '/images/icons/famfamfam/delete.png' );
     420    deletebutton.setAttribute( 'onClick', 'removePublication( "' + element_id + '", ' + id + ' ); return false;' );
     421    deletebutton.setAttribute( 'style', 'cursor: pointer;' );
     422
     423    var titleDiv = document.createElement( 'div' );
     424    titleDiv.setAttribute( 'class', 'title' );
     425    titleDiv.appendChild( document.createTextNode( title ) );
     426
     427    var authorsDiv = document.createElement( 'div' );
     428    authorsDiv.setAttribute( 'class', 'authors' );
     429    authorsDiv.appendChild( document.createTextNode( authors ) );
     430
     431    var li = document.createElement( 'li' );
     432    li.setAttribute( 'id', element_id + '_item_' + id );
     433    li.setAttribute( 'class', nr % 2 == 0 ? 'even' : 'odd' );
     434    li.appendChild( deletebutton );
     435    li.appendChild( titleDiv );
     436    li.appendChild( authorsDiv );
     437
     438    $( '#' + element_id + '_list' ).append( li );
     439}
Note: See TracChangeset for help on using the changeset viewer.