Changeset 487


Ignore:
Timestamp:
May 27, 2010, 4:03:31 PM (7 years ago)
Author:
roberth
Message:

Added net RELTIME template field type to contain relative times (e.g. 1d 3h). The type has been added as template type, but will probably not be rendered correctly yet.

Location:
trunk
Files:
1 added
6 deleted
4 edited
5 moved

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/domain/dbnp/studycapturing/TemplateEntity.groovy

    r453 r487  
    2121        Map templateDoubleFields = [:]
    2222        Map templateDateFields = [:]
     23        Map templateRelTimeFields = [:] // Contains relative times in seconds
    2324        Map templateTermFields = [:]
    2425
     
    3233                templateDateFields: Date,
    3334                templateTermFields: Term,
     35                templateRelTimeFields: long,
    3436                systemFields: TemplateField
    3537        ]
     
    200202                        return (!error)
    201203                })
     204                templateRelTimeFields(validator: { fields, obj, errors ->
     205                        def error = false
     206                        fields.each { key, value ->
     207                                if ( value && value.class != long ) {
     208                                        try {
     209                                                fields[key] = (value as long)
     210                                        } catch (Exception e) {
     211                                                error = true
     212                                                errors.rejectValue(
     213                                                        'templateRelTimeFields',
     214                                                        'templateEntity.typeMismatch.reltime',
     215                                                        [key, value.class] as Object[],
     216                                                        'Property {0} must be of type long and is currently of type {1}'
     217                                                )
     218                                        }
     219                                }
     220                        }
     221                        return (!error)
     222                })
    202223                templateTermFields(validator: { fields, obj, errors ->
    203224                        def error = false
     
    228249         * @throws NoSuchFieldException
    229250         */
    230         private Map getStore(TemplateFieldType fieldType) {
     251        public Map getStore(TemplateFieldType fieldType) {
    231252                switch(fieldType) {
    232253                        case TemplateFieldType.STRING:
     
    240261                        case TemplateFieldType.DATE:
    241262                                return templateDateFields
     263                        case TemplateFieldType.RELTIME:
     264                                return templateRelTimeFields
    242265                        case TemplateFieldType.FLOAT:
    243266                                return templateFloatFields
     
    353376                }
    354377
     378                // Magic setter for relative times: handle string values for relTime fields
     379                //
     380                // The relative time may be set as a string, using the following format
     381                //
     382                //    #w #d #h #m #s
     383                //
     384                // Where w = weeks, d = days, h = hours, m = minutes, s = seconds
     385                //
     386                // The spaces between the values are optional. Every timespan
     387                // (w, d, h, m, s) must appear at most once. You can also omit
     388                // timespans if needed or use a different order.
     389                // Other characters are disregarded, allthough results may not
     390                // always be as expected.
     391                // 
     392                // If an incorrect format is used, which can't be parsed
     393                // an IllegalArgumentException is thrown.
     394                //
     395                // An empty span is treated as zero seconds.
     396                //
     397                // Examples:
     398                // ---------
     399                //    5d 3h 20m     // 5 days, 3 hours and 20 minutes
     400                //    6h 2d         // 2 days, 6 hours
     401                //    10m 200s      // 13 minutes, 20 seconds (200s == 3m + 20s)
     402                //    5w4h15m       // 5 weeks, 4 hours, 15 minutes
     403                //
     404                //    16x14w10d     // Incorrect. 16x is disregarded, so the
     405                //                  // result is 15 weeks, 3 days
     406                //    13days        // Incorrect: days should be d, but this is
     407                //                  // parsed as 13d, 0 seconds
     408                //
     409                if (field.type == TemplateFieldType.RELTIME && value.class == String) {
     410                        // A string was given, attempt to transform it into a timespan
     411
     412                        // An empty string should be parsed as 0
     413                        if( value.trim() == "" ) {
     414                            value = 0;
     415                        } else {
     416                            // Find all parts that contain numbers with
     417                            // a character w, d, h, m or s after it
     418                            def periodMatch = value =~ /([0-9]+)([wdhms])/
     419                            if (periodMatch.size() > 0 ) {
     420                                    def seconds = 0L;
     421
     422                                    // Now check if every part contains data for
     423                                    // the time interval
     424                                    periodMatch.each {
     425                                        def partValue
     426
     427                                        println it
     428
     429                                        if( it[1].isLong() ) {
     430                                            partValue = Long.parseLong( it[1] );
     431                                        } else {
     432                                            partValue = 0;
     433                                        }
     434
     435                                        switch( it[ 2 ] ) {
     436                                            case 'w':
     437                                                seconds += 7L * 24 * 60 * 60 * partValue;
     438                                                break;
     439                                            case 'd':
     440                                                seconds += 24L * 60 * 60 * partValue;
     441                                                break;
     442                                            case 'h':
     443                                                seconds += 60L * 60 * partValue;
     444                                                break;
     445                                            case 'm':
     446                                                seconds += 60L * partValue;
     447                                                break;
     448                                            case 's':
     449                                                seconds += partValue;
     450                                                break;
     451                                            default:
     452                                                adf.error.warn( 'Parsing relative time: ' + it[0] + it[1] + ' is not understood and disregarded' );
     453                                                break;
     454                                        }
     455                                    }
     456
     457                                    // Continue with the computed value
     458                                    value = seconds;
     459                            } else {
     460                                throw new IllegalArgumentException( "String " + value + " cannot be parsed as a relative time. Use format #w #d #h #m #s." );
     461                            }
     462                        }
     463                }
     464
    355465                // Magic setter for ontology terms: handle string values
    356466                if (field.type == TemplateFieldType.ONTOLOGYTERM && value && value.class == String) {
     
    384494                        // If that is ever changed, the results are pretty much unpredictable (random Java object pointers?)!
    385495                        def store = getStore(field.type)
    386                         if (!value && store[fieldName]) {
    387                                 println ".unsetting [" + ((super) ? super.class : '??') + "] template field: [" + fieldName + "]"
    388 
    389                                 // remove the item from the Map (if present)
    390                                 store.remove(fieldName)
    391                         } else if (value) {
    392                                 println ".setting [" + ((super) ? super.class : '??') + "] template field: [" + fieldName + "] ([" + value.toString() + "] of type [" + value.class + "])"
    393 
    394                                 // set value
    395                                 store[fieldName] = value
    396                         }
     496
     497                        // If some value is entered (or 0), then save the value
     498                        // otherwise, it should not be present in the store, so
     499                        // it is unset if it is.
     500                        if ( value || value == 0 ) {
     501                            println ".setting [" + ((super) ? super.class : '??') + "] template field: [" + fieldName + "] ([" + value.toString() + "] of type [" + value.class + "])"
     502
     503                            // set value
     504                            store[fieldName] = value
     505                        } else if ( store[fieldName] ) {
     506                            println ".unsetting [" + ((super) ? super.class : '??') + "] template field: [" + fieldName + "]"
     507
     508                            // remove the item from the Map (if present)
     509                            store.remove(fieldName)
     510                        }
    397511                }
    398512
     
    420534        /**
    421535         * Return all fields defined in the underlying template and the built-in
    422      * domain fields of this entity
     536         * domain fields of this entity
    423537         */
    424538        def List<TemplateField> giveFields() {
  • trunk/grails-app/domain/dbnp/studycapturing/TemplateFieldType.groovy

    r482 r487  
    1717        ONTOLOGYTERM('Ontology Reference'),
    1818        DATE('Date'),
    19     RELDATE('Relative date') // relative date, e.g. days since start of study
     19        RELTIME('Relative time') // relative date, e.g. days since start of study
    2020
    2121        String name
     
    2626
    2727        static list() {
    28                 [STRING, TEXT, INTEGER, FLOAT, DOUBLE, STRINGLIST, ONTOLOGYTERM, DATE, RELDATE]
     28                [STRING, TEXT, INTEGER, FLOAT, DOUBLE, STRINGLIST, ONTOLOGYTERM, DATE, RELTIME]
    2929        }
    3030
     
    4545                        case DATE:
    4646                                return null
    47             case RELDATE:
    48                 return null
     47                        case RELTIME:
     48                                return null
    4949                        default:
    5050                                throw new NoSuchFieldException("Field type ${fieldType} not recognized")
  • trunk/test/integration/gscf/StudyTests.groovy

    r412 r487  
    2323        final Date testStudyStartDate = Date.parse('yyyy-MM-dd','2007-12-11')
    2424        final Date testStudyStartDate2 = Date.parse('yyyy-MM-dd','2008-05-11')
    25         final String testStudyStartDateString2 = "11-5-2010"
     25        final String testStudyStartDateString2 = "11-5-2008"
    2626        // The following dates do net yet work:
    2727        //final String testStudyStartDateString2 = "11-05-2010"
  • trunk/test/unit/dbnp/data/OntologyControllerTests.groovy

    r448 r487  
     1package dbnp.data
     2
    13import grails.test.*
    24
  • trunk/test/unit/dbnp/data/TermControllerTests.groovy

    r448 r487  
     1package dbnp.data
     2
    13import grails.test.*
    24
  • trunk/test/unit/dbnp/importer/ImporterTagLibTests.groovy

    r448 r487  
    1 package importer
     1package dbnp.importer
    22
    33import grails.test.*
  • trunk/test/unit/dbnp/query/QueryControllerTests.groovy

    r448 r487  
     1package dbnp.query
    12import grails.test.*
    23
  • trunk/test/unit/dbnp/studycapturing/StudyControllerTests.groovy

    r448 r487  
     1package dbnp.studycapturing
    12import grails.test.*
    23
  • trunk/test/unit/dbnp/studycapturing/TemplateFieldTests.groovy

    r84 r487  
    44
    55class TemplateFieldTests extends GrailsUnitTestCase {
     6    def testEvent;
    67    protected void setUp() {
    78        super.setUp()
     9
     10        // Create the template itself
     11        def testTemplate = new Template(
     12                name: 'Template for testing relative date fields',
     13                entity: dbnp.studycapturing.Event,
     14                fields: [
     15                    new TemplateField(
     16                        name: 'testStartDate',
     17                        type: TemplateFieldType.DATE
     18                    ),
     19                    new TemplateField(
     20                        name: 'testRelTime',
     21                        type: TemplateFieldType.RELTIME
     22                    )
     23                ]
     24            );
     25
     26        this.testEvent = new Event(
     27                template: testTemplate,
     28                startTime: Date.parse('yyyy-MM-dd','2008-01-02'),
     29                endTime: Date.parse('yyyy-MM-dd','2008-01-05')
     30        )
    831    }
    932
     
    1235    }
    1336
    14     void testSomething() {
     37    void testRelTimeFieldCreation() {
     38        def RelTimeField = new TemplateField(
     39                name: 'RelTime',
     40                type: TemplateFieldType.RELTIME
     41        );
     42    }
     43
     44    void testRelTimeSetValue() {
     45        // Check whether the field exists
     46        assert this.testEvent.fieldExists( 'testRelTime' );
     47
     48        // See that it is not a domain field
     49        assert !this.testEvent.isDomainField( 'testRelTime' );
     50        println( this.testEvent.getStore( TemplateFieldType.RELTIME ) );
     51       
     52        this.testEvent.setFieldValue( 'testRelTime', 10 );
     53        assert this.testEvent.getFieldValue( 'testRelTime' ) == 10;
     54
     55        this.testEvent.setFieldValue( 'testRelTime', 0 );
     56        assert this.testEvent.getFieldValue( 'testRelTime' ) == 0;
     57
     58        this.testEvent.setFieldValue( 'testRelTime', -130 );
     59        assert this.testEvent.getFieldValue( 'testRelTime' ) == -130;
     60
     61        // RelTime must be able to handle 100 years
     62        long hundredYears = 100L * 365 * 24 * 3600;
     63        this.testEvent.setFieldValue( 'testRelTime', hundredYears );
     64        assert this.testEvent.getFieldValue( 'testRelTime' ) ==  hundredYears;
     65    }
     66
     67    // Tests the parsing of a string for relative dates
     68    // @see TemplateEntity.setFieldValue
     69    //
     70        // The relative date may be set as a string, using the following format
     71        //
     72        //    #w #d #h #m #s
     73        //
     74        // Where w = weeks, d = days, h = hours, m = minutes, s = seconds
     75        //
     76        // The spaces between the values are optional. Every timespan
     77        // (w, d, h, m, s) must appear at most once. You can also omit
     78        // timespans if needed or use a different order.
     79        // Other characters are disregarded, allthough results may not
     80        // always be as expected.
     81        //
     82        // If an incorrect format is used, which can't be parsed
     83        // an IllegalFormatException is thrown.
     84        //
     85        // An empty span is treated as zero seconds.
     86        //
     87        // Examples:
     88        // ---------
     89        //    5d 3h 20m     // 5 days, 3 hours and 20 minutes
     90        //    6h 2d         // 2 days, 6 hours
     91        //    10m 200s      // 13 minutes, 20 seconds (200s == 3m + 20s)
     92        //    5w4h15m       // 5 weeks, 4 hours, 15 minutes
     93        //
     94        //    16x14w10d     // Incorrect. 16x is disregarded, so the
     95        //                  // result is 15 weeks, 3 days
     96        //    13days        // Incorrect: days should be d, but this is
     97        //                  // parsed as 13d, 0 seconds
     98        //
     99    void testRelTimeParser() {
     100        def s = 1L;
     101        def m = 60L * s;
     102        def h = 60L * m;
     103        def d = 24L * h;
     104        def w = 7L * d;
     105
     106        this.testEvent.setFieldValue( 'testRelTime', '' );
     107        assert this.testEvent.getFieldValue( 'testRelTime' ) == 0;
     108
     109        this.testEvent.setFieldValue( 'testRelTime', '   ' );
     110        assert this.testEvent.getFieldValue( 'testRelTime' ) == 0;
     111
     112        this.testEvent.setFieldValue( 'testRelTime', '5d 3h 20m' );
     113        assert this.testEvent.getFieldValue( 'testRelTime' ) == 5 * d + 3 * h + 20 * m;
     114
     115        this.testEvent.setFieldValue( 'testRelTime', '6h 2d' );
     116        assert this.testEvent.getFieldValue( 'testRelTime' ) == 2 * d + 6 * h;
     117
     118        this.testEvent.setFieldValue( 'testRelTime', '10m 200s' );
     119        assert this.testEvent.getFieldValue( 'testRelTime' ) == 10 * m  + 200 * s;
     120
     121        this.testEvent.setFieldValue( 'testRelTime', '5w4h15m' );
     122        assert this.testEvent.getFieldValue( 'testRelTime' ) == 5 * w + 4 * h + 15 * m;
     123
     124        // Should parse correctly, allthough it is not completely correct
     125        this.testEvent.setFieldValue( 'testRelTime', '16x14w10d' );
     126        assert this.testEvent.getFieldValue( 'testRelTime' ) == 14 * w + 10 * d;
     127
     128        this.testEvent.setFieldValue( 'testRelTime', '13days' );
     129        assert this.testEvent.getFieldValue( 'testRelTime' ) == 13 * d;
     130
     131        // Test whether an IllegalFormatException is thrown
     132        try {
     133            this.testEvent.setFieldValue( 'testRelTime', 'nonexistent relative date' );
     134            fail();
     135        } catch(IllegalArgumentException ex) {
     136        } catch( Exception ex ) {
     137            fail();
     138        }
    15139
    16140    }
Note: See TracChangeset for help on using the changeset viewer.