Changeset 1608
- Timestamp:
- Apr 20, 2012, 9:57:46 AM (11 years ago)
- Location:
- extensions/net.sf.basedb.reggie/trunk
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/net.sf.basedb.reggie/trunk/resources/persinfo.jsp
r1600 r1608 609 609 else 610 610 { 611 // No specimen tubes... if there is only one PAD on the case... use that 612 if (caseInfo.pad && caseInfo.pad.length == 1) 613 { 614 hasPAD = true; 615 thisCasePAD = caseInfo.pad[0]; 616 } 617 if (caseInfo.samplingDate) 618 { 619 if (caseInfo.samplingDate.length == 1) 620 { 621 thisCaseSamplingDate = caseInfo.samplingDate[0]; 622 } 623 else 624 { 625 frm.samplingDate.disabled = true; 626 frm.samplingTime.disabled = true; 627 setInputStatus('samplingDate', 'Multiple dates have already been registered for this case.', 'warning'); 628 } 611 if (caseInfo.noSpecimen) 612 { 613 // This case is registered with 'NoSpecimen' 614 thisCasePAD = caseInfo.noSpecimen.pad; 615 thisCaseSamplingDate = caseInfo.noSpecimen.samplingDate; 616 frm.reasonIfNoSpecimen.value = caseInfo.noSpecimen.reasonIfNoSpecimen; 629 617 } 630 618 … … 653 641 rnaLaterDateTimeOnChange(); 654 642 } 655 656 643 657 644 frm.pad.value = thisCasePAD; 658 caseInfo.oldPad = thisCasePAD;659 caseInfo.oldSamplingDate = thisCaseSamplingDate;660 661 if (updateMode && !hasSpecimen)662 {663 // Try to find 'reasonIfNoSpecimen' annotation on the case664 for (var i = 0; i < caseInfo.reasonIfNoSpecimen.length; i++)665 {666 var rns = caseInfo.reasonIfNoSpecimen[i];667 if (rns.indexOf('['+caseInfo.originalName+']') == 0)668 {669 caseInfo.oldReasonIfNoSpecimen = rns;670 frm.reasonIfNoSpecimen.value = rns.substring(rns.indexOf(']')+2);671 i = 999;672 }673 }674 }675 645 676 646 Main.show('caseSection'); -
extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/Reggie.java
r1606 r1608 85 85 /** 86 86 The name of the "ReasonIfNoSpecimen" annotation type, used 87 for samples ( Case). It is a multi-valuedstring annotation.87 for samples (NoSpecimen). It is a string annotation. 88 88 */ 89 89 public static final String ANNOTATION_REASON_IF_NO_SPECIMEN = "ReasonIfNoSpecimen"; 90 90 91 91 /** 92 92 The name of the "PAD" annotation type, used … … 96 96 97 97 /** 98 The name of the "PADcase" annotation type, used99 for samples (Case). It is a multi-valued string annotation.100 */101 public static final String ANNOTATION_PAD_CASE = "PADcase";102 103 /**104 98 The name of the "PathNote" annotation type, 105 99 used for samples (Case). It is a string annotation type. … … 114 108 115 109 /** 116 The name of the "SamplingDateCase" annotation type, used117 for samples (Case). The value is basically a copy of the118 date part of the {@link #ANNOTATION_SAMPLING_DATETIME}119 annotation, but also allow registration of a date when120 no specimen has been received. It is a multi-valued date annotation.121 @since 2.4122 */123 public static final String ANNOTATION_SAMPLING_DATE_CASE = "SamplingDateCase";124 125 /**126 110 The name of the "RNALaterDateTime" annotation type, used 127 111 for samples (Specimen tube). It is a timestamp annotation. 128 112 */ 129 113 public static final String ANNOTATION_RNALATER_DATETIME = "RNALaterDateTime"; 130 131 114 132 115 /** -
extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/dao/Case.java
r1503 r1608 15 15 import net.sf.basedb.core.Sample; 16 16 import net.sf.basedb.core.Type; 17 import net.sf.basedb.core.query.Annotations;18 17 import net.sf.basedb.core.query.Expressions; 19 18 import net.sf.basedb.core.query.Hql; … … 36 35 on the name of a sample. If exactly one match is found this is the 37 36 (primary) case. More than one match is an error condition. If no 38 match is found, we look harder by trying to find a c ase with39 specimen tubesthat have names matching the case we are looking37 match is found, we look harder by trying to find a child sample with 38 'Specimen' or 'NoSpecimen' that have names matching the case we are looking 40 39 for (eg. xxx.1, xxx.2, etc.). If exactly one match is found this is 41 40 the (merged) case. More than one match is an error condition. No match … … 64 63 { 65 64 // Not found... see if we can find a different case 66 // with specimen tubes belonging to this case (merged case)65 // with 'Specimen'/'NoSpecimen' child items belonging to this case (merged case) 67 66 merged = true; 68 67 caseQuery = Sample.getQuery(); … … 71 70 caseQuery.join(Hql.innerJoin("cce", "event", "evt")); 72 71 caseQuery.join(Hql.innerJoin("evt", "bioMaterial", "sp")); // 'sp' should now reference a specimen tube 73 Subtype.SPECIMEN.addFilter(dc, caseQuery, "sp"); 72 73 // Restrict to 'Specimen' or 'NoSpecimen' child items 74 caseQuery.restrict(Restrictions.or(Subtype.SPECIMEN.restriction(dc, "sp"), Subtype.NO_SPECIMEN.restriction(dc, "sp"))); 74 75 caseQuery.restrict(Restrictions.like(Hql.property("sp", "name"), Expressions.parameter("name", name + ".%", Type.STRING))); 75 76 caseQuery.setDistinct(true); … … 86 87 } 87 88 88 if (cases.size() == 0)89 {90 // Still not found... last chance is to find a (merged) case with ReasonIfNoSpecimen comment91 // that has [caseName] as prefix...92 AnnotationType reasonIfNoSpecimenType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, true);93 caseQuery = Sample.getQuery();94 Subtype.CASE.addFilter(dc, caseQuery);95 caseQuery.restrict(Annotations.like(null, reasonIfNoSpecimenType, "[" + name + "]%", false));96 cases = caseQuery.list(dc);97 98 99 if (cases.size() > 1)100 {101 // ...something is very incorrectly registered...102 // we different cases with comments for this case103 throw new InvalidDataException(104 "Found " + cases.size() + " cases with ReasonIfNoSpecimen comments " +105 "for this case (" + name + "). This wizard can't be used until that is corrected.");106 }107 108 }109 110 89 } 111 90 -
extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/dao/Subtype.java
r1544 r1608 61 61 */ 62 62 public static final Subtype SPECIMEN = new Subtype("Specimen", Item.SAMPLE); 63 64 /** 65 The definition of the "NoSpecimen" subtype (for cases when no specimen has been received). 66 @since 2.5 67 */ 68 public static final Subtype NO_SPECIMEN = new Subtype("NoSpecimen", Item.SAMPLE); 69 63 70 64 71 /** -
extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/InstallServlet.java
r1606 r1608 124 124 jsonChecks.add(checkSubtype(dc, Subtype.BLOOD, createIfMissing, Subtype.PATIENT)); 125 125 jsonChecks.add(checkSubtype(dc, Subtype.SPECIMEN,createIfMissing, Subtype.CASE)); 126 jsonChecks.add(checkSubtype(dc, Subtype.NO_SPECIMEN,createIfMissing, Subtype.CASE)); 126 127 jsonChecks.add(checkSubtype(dc, Subtype.HISTOLOGY, createIfMissing, Subtype.SPECIMEN)); 127 128 jsonChecks.add(checkSubtype(dc, Subtype.LYSATE, createIfMissing, Subtype.SPECIMEN)); … … 169 170 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_LATERALITY, Item.SAMPLE, Type.STRING, 1, 170 171 new EnumerationOptions("LEFT", "RIGHT"), effectiveOptions, createIfMissing)); 171 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, Item.SAMPLE, Type.STRING, 0,172 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, Item.SAMPLE, Type.STRING, 1, 172 173 null, effectiveOptions, createIfMissing)); 173 174 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_PAD, Item.SAMPLE, Type.STRING, 1, 174 175 null, effectiveOptions, createIfMissing)); 176 /* 177 TODO - remove 175 178 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_PAD_CASE, Item.SAMPLE, Type.STRING, 0, 176 179 null, effectiveOptions, createIfMissing)); 180 */ 181 177 182 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_BLOOD_SAMPLING_DATETIME, Item.SAMPLE, Type.TIMESTAMP, 1, 178 183 null, effectiveOptions, createIfMissing)); … … 183 188 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_SAMPLING_DATETIME, Item.SAMPLE, Type.TIMESTAMP, 1, 184 189 null, effectiveOptions, createIfMissing)); 190 /* 191 TODO - remove 185 192 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_SAMPLING_DATE_CASE, Item.SAMPLE, Type.DATE, 0, 186 193 null, effectiveOptions, createIfMissing)); 194 */ 195 187 196 jsonChecks.add(checkAnnotationType(dc, Reggie.ANNOTATION_RNALATER_DATETIME, Item.SAMPLE, Type.TIMESTAMP, 1, 188 197 null, effectiveOptions, createIfMissing)); … … 231 240 jsonChecks.add(checkAnnotationTypeCategory(dc, Subtype.CASE, createIfMissing, 232 241 Reggie.ANNOTATION_CONSENT, Reggie.ANNOTATION_CONSENT_DATE, 233 Reggie.ANNOTATION_LATERALITY, Reggie.ANNOTATION_SAMPLING_DATE_CASE, 234 Reggie.ANNOTATION_PAD_CASE, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN)); 242 Reggie.ANNOTATION_LATERALITY)); 235 243 236 244 jsonChecks.add(checkAnnotationTypeCategory(dc, Subtype.BLOOD, createIfMissing, … … 246 254 Reggie.ANNOTATION_PAD, Reggie.ANNOTATION_RNALATER_DATETIME, 247 255 Reggie.ANNOTATION_SAMPLING_DATETIME, Reggie.ANNOTATION_SPECIMEN_TYPE)); 256 257 jsonChecks.add(checkAnnotationTypeCategory(dc, Subtype.NO_SPECIMEN, createIfMissing, 258 Reggie.ANNOTATION_PAD, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, 259 Reggie.ANNOTATION_SAMPLING_DATETIME 260 )); 261 248 262 jsonChecks.add(checkAnnotationTypeCategory(dc, Subtype.LYSATE, createIfMissing, 249 263 Reggie.ANNOTATION_PARTITION_DATE)); -
extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/servlet/PersonalRegistrationServlet.java
r1600 r1608 21 21 import net.sf.basedb.core.BioSource; 22 22 import net.sf.basedb.core.DbControl; 23 import net.sf.basedb.core.InvalidDataException; 23 24 import net.sf.basedb.core.Item; 24 25 import net.sf.basedb.core.Sample; … … 28 29 import net.sf.basedb.reggie.dao.Case; 29 30 import net.sf.basedb.reggie.dao.Consent; 31 import net.sf.basedb.reggie.dao.NoSpecimen; 30 32 import net.sf.basedb.reggie.dao.Patient; 31 33 import net.sf.basedb.reggie.dao.SpecimenTube; … … 80 82 81 83 List<SpecimenTube> specimenTubes = null; 84 NoSpecimen noSpecimen = null; 82 85 JSONObject jsonCase = null; 83 86 JSONObject jsonBlood = null; … … 95 98 specimenTubes = SpecimenTube.findByCaseName(dc, caseName); 96 99 } 100 101 if (specimenTubes.size() == 0) 102 { 103 // Still not found -- check if there are any 'NoSpecimen' items 104 List<NoSpecimen> nsList = NoSpecimen.findByCase(dc, theCase, true); 105 if (nsList.size() > 1) 106 { 107 // There should be at most one 'NoSpecimen' item 108 throw new InvalidDataException( 109 "Found " + nsList.size() + " 'NoSpecimen' samples for the case (" + caseName + 110 "). This wizard can't be used until that is corrected."); 111 } 112 else if (nsList.size() == 1) 113 { 114 noSpecimen = nsList.get(0); 115 } 116 } 97 117 98 118 // Continue to load some more info (annotations, etc...) 99 119 // ... case annotations 100 120 theCase.loadAnnotations(dc, "laterality", Reggie.ANNOTATION_LATERALITY, null); 121 /* 122 TODO - remove 101 123 theCase.loadAnnotations(dc, "pad", Reggie.ANNOTATION_PAD_CASE, null); 102 124 theCase.loadAnnotations(dc, "samplingDate", Reggie.ANNOTATION_SAMPLING_DATE_CASE, Reggie.CONVERTER_DATE_TO_STRING); 103 125 theCase.loadAnnotations(dc, "reasonIfNoSpecimen", Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, null); 126 */ 104 127 105 128 // Wrap what we have so far up into JSON objects … … 142 165 } 143 166 144 // Load specimen tubeannotations167 // Load 'Specimen'/'NoSpecimen' annotations 145 168 JSONArray jsonTubes = new JSONArray(); 169 AnnotationType padType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD, true); 170 AnnotationType samplingDateType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATETIME, true); 146 171 if (specimenTubes.size() > 0) 147 172 { 148 AnnotationType padType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD, true);149 173 AnnotationType lateralityType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_LATERALITY, true); 150 AnnotationType samplingDateType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATETIME, true);151 174 AnnotationType rnaLaterDateType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_RNALATER_DATETIME, true); 152 175 for (SpecimenTube tube : specimenTubes) … … 160 183 } 161 184 jsonCase.put("specimen", jsonTubes); 185 186 if (noSpecimen != null) 187 { 188 noSpecimen.loadAnnotations("pad", padType, null); 189 noSpecimen.loadAnnotations("samplingDate", samplingDateType, Reggie.CONVERTER_DATE_TO_STRING); 190 noSpecimen.loadAnnotations(dc, "reasonIfNoSpecimen", Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, null); 191 jsonCase.put("noSpecimen", noSpecimen.asJSONObject()); 192 } 162 193 163 194 // This is what we send back to the browser … … 337 368 AnnotationSet caseAnnotations = theCase.getAnnotationSet(); 338 369 JSONArray jsonSpecimen = (JSONArray)jsonCase.get("specimen"); 339 // PAD value 340 AnnotationType padCaseType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD_CASE, true); 370 371 // Annotations that are always used 372 AnnotationType padType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD, true); 373 AnnotationType samplingDateTimeType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATETIME, true); 374 341 375 String pad = Values.getStringOrNull((String)jsonCase.get("pad")); 342 if (pad != null) Reggie.appendAnnotationValueIfUnique(theCase, padCaseType, pad);343 344 // Sampling date on Case345 AnnotationType samplingDateCaseType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATE_CASE, true);346 Date samplingDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonCase.get("samplingDate"));347 if (samplingDate != null)348 {349 Reggie.appendAnnotationValueIfUnique(theCase, samplingDateCaseType, samplingDate);350 }351 376 352 377 if (jsonSpecimen != null && jsonSpecimen.size() > 0) 353 378 { 354 AnnotationType padType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD, true);355 AnnotationType samplingDateTimeType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATETIME, true);356 379 AnnotationType rnaLaterType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_RNALATER_DATETIME, true); 380 Date rnaLaterDateTime = Reggie.CONVERTER_STRING_TO_DATETIME.convert((String)jsonCase.get("rnaLaterDate")); 357 381 Date samplingDateTime = Reggie.CONVERTER_STRING_TO_DATETIME.convert((String)jsonCase.get("samplingDate")); 358 Date rnaLaterDateTime = Reggie.CONVERTER_STRING_TO_DATETIME.convert((String)jsonCase.get("rnaLaterDate"));359 382 360 383 for (int i = 0; i < jsonSpecimen.size(); ++i) … … 366 389 creationEvent.setSource(theCase); 367 390 // !IMPORTANT - The sampling date must be set here and as an 'SamplingDateTime' annotation 368 creationEvent.setEventDate(samplingDate );391 creationEvent.setEventDate(samplingDateTime); 369 392 370 393 // Update the specimen annotations … … 379 402 else 380 403 { 404 Date samplingDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonCase.get("samplingDate")); 405 406 // Create new 'NoSpecimen' item 407 Sample noSpecimen = Sample.getNew(dc); 408 dc.saveItem(noSpecimen); 409 noSpecimen.setItemSubtype(Subtype.NO_SPECIMEN.load(dc)); 410 noSpecimen.setName(originalCaseName+".1"); 411 BioMaterialEvent creationEvent = noSpecimen.getCreationEvent(); 412 creationEvent.setSource(theCase); 413 creationEvent.setEventDate(samplingDate); 414 381 415 AnnotationType reasonIfNoSpecimenType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, true); 382 Reggie.appendAnnotationValueIfUnique(theCase, reasonIfNoSpecimenType, "[" + originalCaseName + "] " + jsonCase.get("reasonIfNoSpecimen")); 416 Reggie.setAnnotationValue(noSpecimen, padType, pad); 417 Reggie.setAnnotationValue(noSpecimen, reasonIfNoSpecimenType, jsonCase.get("reasonIfNoSpecimen")); 418 if (samplingDate != null) Reggie.setAnnotationValue(noSpecimen, samplingDateTimeType, samplingDate); 419 420 jsonMessages.add("Virtual specimen '" + noSpecimen.getName() + "' created successfully."); 383 421 } 384 422 … … 406 444 boolean updateLaterality = Values.getInt(laterality, -1) == -1; 407 445 String pad = Values.getStringOrNull((String)jsonCase.get("pad")); 408 String oldPad = Values.getStringOrNull((String)jsonCase.get("oldPad"));409 446 JSONArray jsonSpecimen = (JSONArray)jsonCase.get("specimen"); 447 JSONObject jsonNoSpecimen = (JSONObject)jsonCase.get("noSpecimen"); 410 448 411 449 // Update laterality and PAD for the case 412 450 AnnotationType lateralityType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_LATERALITY, true); 413 AnnotationType padCaseType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD_CASE, true);414 451 if (updateLaterality) 415 452 { 416 453 Reggie.setAnnotationValue(theCase, lateralityType, laterality); 417 454 } 418 419 if (oldPad != null) Reggie.removeAnnotationValue(theCase, padCaseType, oldPad); 420 if (pad != null) Reggie.appendAnnotationValueIfUnique(theCase, padCaseType, pad); 421 422 // Sampling date on Case 423 AnnotationType samplingDateCaseType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATE_CASE, true); 424 Date samplingDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonCase.get("samplingDate")); 425 Date oldSamplingDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonCase.get("oldSamplingDate")); 426 if (oldSamplingDate != null) 427 { 428 Reggie.removeAnnotationValue(theCase, samplingDateCaseType, oldSamplingDate); 429 } 430 if (samplingDate != null) 431 { 432 Reggie.appendAnnotationValueIfUnique(theCase, samplingDateCaseType, samplingDate); 433 } 434 455 435 456 jsonMessages.add("Case '" + originalCaseName + "' updated successfully."); 436 457 … … 450 471 Sample specimen = Sample.getById(dc, ((Number)jsonSpec.get("id")).intValue()); 451 472 // !IMPORTANT - The sampling date must be set here and as an 'SamplingDateTime' annotation 452 specimen.getCreationEvent().setEventDate(samplingDate );473 specimen.getCreationEvent().setEventDate(samplingDateTime); 453 474 454 475 // Update the specimen annotations … … 462 483 463 484 } 464 else 465 { 466 // No specimen tubes...update the 'reasonIfNoSpecimen' annotation 485 else if (jsonNoSpecimen != null) 486 { 487 // Update the 'NoSpecimen' item 488 AnnotationType padType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_PAD, true); 489 AnnotationType samplingDateType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_SAMPLING_DATETIME, true); 467 490 AnnotationType reasonIfNoSpecimenType = Reggie.findAnnotationType(dc, Item.SAMPLE, Reggie.ANNOTATION_REASON_IF_NO_SPECIMEN, true); 468 469 String rns = Values.getString((String)jsonCase.get("reasonIfNoSpecimen")); 470 String oldRns = Values.getString((String)jsonCase.get("oldReasonIfNoSpecimen")); 471 if (oldRns != null) 472 { 473 Reggie.removeAnnotationValue(theCase, reasonIfNoSpecimenType, oldRns); 474 } 475 if (rns != null) 476 { 477 Reggie.appendAnnotationValueIfUnique(theCase, reasonIfNoSpecimenType, "[" + originalCaseName + "] " + rns); 478 } 479 491 492 Date samplingDate = Reggie.CONVERTER_STRING_TO_DATE.convert((String)jsonCase.get("samplingDate")); 493 494 Sample noSpecimen = Sample.getById(dc, ((Number)jsonNoSpecimen.get("id")).intValue()); 495 noSpecimen.getCreationEvent().setEventDate(samplingDate); 496 497 Reggie.setAnnotationValue(noSpecimen, padType, pad); 498 Reggie.setAnnotationValue(noSpecimen, samplingDateType, samplingDate); 499 Reggie.setAnnotationValue(noSpecimen, reasonIfNoSpecimenType, Values.getString((String)jsonCase.get("reasonIfNoSpecimen"))); 480 500 } 481 501
Note: See TracChangeset
for help on using the changeset viewer.