Changeset 487

Show
Ignore:
Timestamp:
27-05-10 16:03:31 (4 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 removed
4 modified
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

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

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

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

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

    r21 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    }