source: extensions/net.sf.basedb.reggie/trunk/resources/persinfo.jsp @ 1300

Last change on this file since 1300 was 1300, checked in by Nicklas Nordborg, 11 years ago

References #291: Personal information registration

Better handling of cases where laterality information is missing on specimen tubes or when it is not consistent.

Cleaning up some validation code.

File size: 19.1 KB
Line 
1<%@ page
2  pageEncoding="UTF-8"
3  session="false"
4  import="net.sf.basedb.core.Application"
5  import="net.sf.basedb.core.User"
6  import="net.sf.basedb.core.DbControl"
7  import="net.sf.basedb.core.SessionControl"
8  import="net.sf.basedb.clients.web.Base"
9  import="net.sf.basedb.clients.web.util.HTML"
10  import="net.sf.basedb.util.Values"
11%>
12<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
13<%@ taglib prefix="p" uri="/WEB-INF/path.tld" %>
14<%
15final SessionControl sc = Base.getExistingSessionControl(request, true);
16final String ID = sc.getId();
17final float scale = Base.getScale(sc);
18DbControl dc = null;
19try
20{
21  dc = sc.newDbControl();
22  final User user = User.getById(dc, sc.getLoggedInUserId());
23%>
24<base:page type="default" >
25<base:head scripts="ajax.js" styles="path.css">
26<script language="JavaScript">
27
28var currentStep = 1;
29var pnrIsValid = false;
30var caseIsValid = false;
31var lateralityIsValid = false;
32
33var patientInfo = null;
34var caseInfo = null;
35
36function init()
37{
38  var frm = document.forms['reggie'];
39  frm.caseName.focus();
40}
41
42function step1IsValid()
43{
44  return pnrIsValid && caseIsValid;
45}
46
47function step2IsValid()
48{
49  var formOk = true;
50  var frm = document.forms['reggie'];
51
52  // New patient only
53  if (!patientInfo.id)
54  {
55    // Validate 'New patient' form
56    if (frm.allFirstNames.value == '')
57    {
58      setInputStatus('allFirstNames', 'Missing', 'invalid');
59      frm.allFirstNames.focus();
60      formOk = false;
61    }
62    else
63    {
64      setInputStatus('allFirstNames', '', 'valid');
65    }
66   
67    if (frm.familyName.value == '')
68    {
69      setInputStatus('familyName', 'Missing', 'invalid');
70      frm.familyName.focus();
71      formOk = false;
72    }
73    else
74    {
75      setInputStatus('familyName', '', 'valid');
76    }
77
78    if (frm.patientCode.value == '')
79    {
80      setInputStatus('patientCode', 'Missing', 'invalid');
81      frm.patientCode.focus();
82      formOk = false;
83    }
84    else
85    {
86      setInputStatus('patientCode', '', 'valid');
87    }
88  }
89  return formOk;
90}
91
92function step3IsValid()
93{
94  return lateralityIsValid;
95}
96
97function goNext()
98{
99  if (currentStep == 1)
100  {
101    if (step1IsValid()) gotoStep2();
102  }
103  else if (currentStep == 2)
104  {
105    if (step2IsValid()) gotoStep3();
106  }
107}
108
109function caseNameOnChange()
110{
111  var frm = document.forms['reggie'];
112  var caseName = frm.caseName.value;
113  if (caseName == '')
114  {
115    setInputStatus('case', 'Missing', 'invalid');
116    return;
117  }
118  setInputStatus('case', '', 'valid');
119  caseIsValid = true;
120}
121
122function personalNumberOnChange()
123{
124  var frm = document.forms['reggie'];
125  var pnr = frm.personalNumber.value;
126  pnrIsValid = false;
127 
128  if (pnr.length < 12)
129  {
130    setInputStatus('pnr', 'Too short', 'invalid');
131    return;
132  }
133 
134  if (!Dates.isDate(pnr.substring(0, 8), 'yyyyMMdd'))
135  {
136    setInputStatus('pnr', 'Not a valid date', 'invalid');
137    return;
138  }
139 
140  var sum = 0;
141  var factor = 2;
142  var x = '';
143  for (var i = 2; i < 11; i++)
144  {
145    var digit = parseInt(pnr.substr(i, 1));
146    var tmp = factor * digit;
147    x += '(' + factor + '*' + digit+')';
148    sum += tmp >= 10 ? tmp - 9: tmp;
149    factor = factor == 2 ? 1 : 2;
150  }
151 
152  var control = 10 - (sum % 10);
153  if (control != parseInt(pnr.substr(11, 1)))
154  {
155    setInputStatus('pnr', 'Invalid control digit', 'invalid');
156    return;
157  }
158 
159  setInputStatus('pnr', '', 'valid');
160  pnrIsValid = true;
161}
162
163function lateralityOnChange()
164{
165  lateralityIsValid = false;
166 
167  // Check selected laterality against specimen tubes
168  var frm = document.forms['reggie'];
169  var selectedLaterality = Forms.getCheckedRadio(frm.laterality);
170 
171  // No laterality/case selected
172  if (selectedLaterality == null)
173  {
174    setInputStatus('laterality', 'Not selected', 'invalid');
175    return;
176  }
177  setInputStatus('laterality', '', 'valid');
178  lateralityIsValid = true;
179 
180  var laterality = selectedLaterality.value;
181  if (laterality != 'LEFT' && laterality != 'RIGHT')
182  {
183    // It is the ID of an existing case
184    for (var i = 0; i < patientInfo.cases.length; i++)
185    {
186      var cse = patientInfo.cases[i];
187      if (cse.id == laterality)
188      {
189        laterality = cse.laterality;
190        break;
191      }
192    }
193  }
194 
195  if (!laterality) return;
196 
197  // No specimen tubes
198  if (!caseInfo.specimen || caseInfo.specimen.length == 0) return;
199  for (var i = 0; i < caseInfo.specimen.length; i++)
200  {
201    var specimen = caseInfo.specimen[i];
202    if (specimen.laterality && specimen.laterality != laterality)
203    {
204      setInputStatus('laterality', 'Not same laterality as specimen tubes', 'warning');
205      return;
206    }
207  }
208}
209
210function goNextOnTab(event)
211{
212  if (event.keyCode == 9) setTimeout('goNext()', 200);
213  return true;
214}
215
216function gotoStep2()
217{
218  // Check entered case and pnr with AJAX
219  var frm = document.forms['reggie'];
220  frm.caseName.disabled = true;
221  frm.personalNumber.disabled = true;
222  currentStep = 2;
223 
224  var pnr = frm.personalNumber.value;
225  var caseName = frm.caseName.value;
226  var request = Ajax.getXmlHttpRequest();
227  var url = 'PersonalRegistration.servlet?ID=<%=ID%>&cmd=CheckPersonalNumberAndCaseName';
228  url += '&personalNumber=' + pnr;
229  url += '&caseName=' + caseName;
230  request.open("GET", url, false);
231  request.send(null);
232 
233  //setInnerHTML('debug', request.responseText);
234 
235  var response = JSON.parse(request.responseText);
236  if (response.status != 'ok')
237  {
238    setFatalError(response.message);
239    return false;
240  }
241 
242  // Get biosource information from the AJAX response
243  patientInfo = response.patientInfo;
244  caseInfo = response.caseInfo;
245 
246  if (!patientInfo.id)
247  {
248    Main.show('newPatientSection');
249    frm.patientCode.value=patientInfo.name;
250    setInnerHTML('new.dateOfBirth', patientInfo.dateOfBirth);
251    setInnerHTML('new.gender', patientInfo.gender);
252    frm.familyName.focus();
253  }
254  else
255  {
256    Main.show('existingPatientSection');
257    setInnerHTML('existing.patientCode', patientInfo.name);
258    setInnerHTML('existing.familyName', patientInfo.familyName);
259    setInnerHTML('existing.allFirstNames', patientInfo.allFirstNames);
260    setInnerHTML('existing.dateOfBirth', patientInfo.dateOfBirth);
261    setInnerHTML('existing.gender', patientInfo.gender);
262    gotoStep3();
263  }
264}
265
266function gotoStep3()
267{
268  // Check entered case and pnr with AJAX
269  var frm = document.forms['reggie'];
270 
271  if (!patientInfo.id)
272  {
273    frm.patientCode.disabled = true;
274    frm.familyName.disabled = true;
275    frm.allFirstNames.disabled = true;
276    patientInfo.familyName = frm.familyName.value;
277    patientInfo.allFirstNames = frm.allFirstNames.value;
278  }
279  currentStep = 3;
280 
281  // Generate list of specimen tubes
282  var thisCaseLaterality;
283  if (caseInfo.specimen && caseInfo.specimen.length > 0)
284  {
285    var specimenTubes = '';
286    Main.hide('reasonIfNoSpecimenSection');
287    for (var i = 0; i < caseInfo.specimen.length; i++)
288    {
289      var specimen = caseInfo.specimen[i];
290      specimenTubes += specimen.name ;
291      if (specimen.laterality) 
292      {
293        specimenTubes += ' ('+specimen.laterality + ')';
294        Forms.checkRadio(frm.laterality, specimen.laterality);
295       
296        if (thisCaseLaterality && thisCaseLaterality != specimen.laterality)
297        {
298          setInputStatus('specimenTubes', 'Specimen tubes with different laterality', 'warning');
299        }
300        thisCaseLaterality = specimen.laterality;
301      }
302      else
303      {
304        specimenTubes += ' (<i>unknown laterality</i>)';
305      }
306      specimenTubes += '<br>';
307    }
308    setInnerHTML('specimenTubes', specimenTubes);
309  }
310
311 
312  Main.show('caseSection');
313 
314  // Existing cases for this patient
315  var hasLeftCase = false;
316  var hasRightCase = false;
317  if (patientInfo.cases && patientInfo.cases.length > 0)
318  {
319    var cases = '';
320    for (var i = 0; i < patientInfo.cases.length; i++)
321    {
322      var cc = patientInfo.cases[i];
323      cases += '<input type="radio" name="laterality" value="' + cc.id + '"';
324      if (cc.laterality == 'LEFT') 
325      {
326        if (hasLeftCase) setInputStatus('laterality', 'Two cases with LEFT', 'warning');
327        hasLeftCase = true;
328      }
329      if (cc.laterality == 'RIGHT') 
330      {
331        if (hasRightCase) setInputStatus('laterality', 'Two cases with RIGHT', 'warning');
332        hasRightCase = true;
333      }
334      if (cc.laterality == thisCaseLaterality) cases += ' checked';
335      cases += ' onclick="lateralityOnChange()">' + cc.name;
336      if (cc.laterality)
337      {
338        cases += ' (' + cc.laterality + ')<br>';
339      }
340      else
341      {
342        cases += ' (<i>unknown laterality</i>)<br>';
343      }
344    }
345   
346    if (patientInfo.cases.length == 1)
347    {
348      if (!hasLeftCase)
349      {
350        cases += '<input type="radio" name="laterality" value="LEFT" ';
351        if (thisCaseLaterality == 'LEFT') cases += ' checked';
352        cases += ' onclick="lateralityOnChange()"><i>new case</i> (LEFT)<br>';
353      }
354      if (!hasRightCase)
355      {
356        cases += '<input type="radio" name="laterality" value="RIGHT"';
357        if (thisCaseLaterality == 'RIGHT') cases += ' checked';
358        cases += ' onclick="lateralityOnChange()"><i>new case</i> (RIGHT)<br>';
359      }
360    }
361   
362    if (patientInfo.cases.length == 2)
363    {
364      setInnerHTML('laterality.prompt', 'Merge with case');
365    }
366    else
367    {
368      setInnerHTML('laterality.prompt', 'Merge/create new case');
369    }
370   
371    setInnerHTML('laterality.input', cases);
372  }
373  lateralityOnChange();
374
375  Main.hide('gonext');
376  Main.show('gocreate');
377}
378
379function goCreate()
380{
381  if (!step3IsValid()) return;
382 
383  Main.hide('gocreate');
384  var frm = document.forms['reggie'];
385
386  caseInfo.laterality = Forms.getCheckedRadio(frm.laterality).value;
387  caseInfo.reasonIfNoSpecimen = frm.reasonIfNoSpecimen.value;
388
389  for (var i = 0; i < frm.laterality.length; i++)
390  {
391    frm.laterality[i].disabled = true;
392  }
393  frm.reasonIfNoSpecimen.disabled = true;
394
395  var submitInfo = new Object();
396  submitInfo.patientInfo = patientInfo;
397  submitInfo.caseInfo = caseInfo;
398
399  var request = Ajax.getXmlHttpRequest();
400  var url = 'PersonalRegistration.servlet?ID=<%=ID%>&cmd=Create';
401  request.open("POST", url, false);
402  request.setRequestHeader("Content-Type", "application/json");
403  request.send(JSON.stringify(submitInfo));
404
405  //setInnerHTML('debug', request.responseText);
406 
407  var response = JSON.parse(request.responseText);
408  if (response.status != 'ok')
409  {
410    setFatalError(response.message);
411    return false;
412  }
413 
414  var msg = '<ul>';
415  for (var i = 0; i < response.messages.length; i++)
416  {
417    msg += '<li>' + response.messages[i];
418  }
419  msg += '</ul>';
420  setInnerHTML('done', msg);
421  Main.show('done');
422  Main.show('gorestart');
423
424}
425
426
427function setInnerHTML(id, html)
428{
429  var tag = document.getElementById(id);
430  if (!tag) alert('No tag with id='+id);
431  tag.innerHTML = html;
432}
433
434function setInputStatus(prefix, message, clazz)
435{
436  var tag = document.getElementById(prefix + '.status');
437  tag.className = 'status ' + clazz;
438 
439  setInnerHTML(prefix + '.message', message);
440  if (message)
441  {
442    Main.showInline(prefix + '.message');
443  }
444  else
445  {
446    Main.hide(prefix + '.message');
447  }
448}
449
450function setFatalError(message)
451{
452  setInnerHTML('errorMessage', message);
453  Main.show('errorMessage');
454  Main.hide('gonext');
455  Main.show('gorestart');
456}
457
458function goRestart()
459{
460  location.href = location.href;
461}
462</script>
463<style>
464
465.stepform
466{
467  margin-left: 20px;
468  border: 1px solid #999999;
469  width: 800px;
470  table-layout: fixed;
471}
472
473.stepno
474{
475  width: 20px;
476  font-size: 20px;
477  font-weight: bold;
478  color: #E0E0E0;
479  background: #555577;
480  vertical-align: top;
481  text-align: center;
482}
483
484.steptitle
485{
486  width: 780px;
487  color: #333377;
488  background: #E0E0E0;
489  font-weight: bold;
490  padding: 1px 4px 1px 4px;
491  border-bottom: 1px solid #999999;
492}
493
494.nextstep
495{
496  width: 780px;
497  color: #333377;
498  background: #E0E0E0;
499  font-weight: bold;
500  padding: 1px 4px 1px 4px;
501  border-top: 1px solid #999999;
502}
503
504.stepfields
505{
506  width: 780px;
507}
508
509.prompt
510{
511  width: 150px;
512  font-weight: bold;
513  padding: 1px 2px 1px 2px;
514}
515
516.input
517{
518  width: 250px;
519  padding: 1px 2px 1px 2px;
520}
521
522.status
523{
524  width: 30px;
525  padding: 1px 2px 1px 2px;
526}
527
528.help
529{
530  background: #e0e0e0;
531  width: 350px;
532  font-style: italic;
533  padding: 1px 2px 1px 2px;
534}
535
536.message
537{
538  color: #cc0000;
539  font-weight: bold;
540  padding-right: 6px;
541}
542
543.status.invalid:before
544{
545  content: url('../../images/error.gif');
546}
547.status.warning:before
548{
549  content: url('../../images/warning.gif');
550}
551.status.valid:before
552{
553  content: url('../../images/ok.gif');
554}
555.success ul
556{
557  list-style-image: url('../../images/ok.gif');
558}
559</style>
560</base:head>
561<base:body onload="init()">
562
563  <p:path style="margin-top: 20px; margin-bottom: 10px;">
564    <p:pathelement title="Reggie" href="<%="index.jsp?ID="+ID%>" />
565    <p:pathelement title="Personal information registration" />
566  </p:path>
567
568  <form name="reggie" onsubmit="return false;">
569 
570  <!-- 1. Case + Personal number -->
571  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
572  <tr>
573    <td rowspan="3" class="stepno">1</td>
574    <td class="steptitle">Enter Case Name and Personal Number</td>
575  </tr>
576  <tr>
577    <td class="stepfields">
578      <table border="0" cellspacing="0" cellpadding="0" width="100%">
579      <tr valign="top">
580        <td class="prompt">Case name</td>
581        <td class="input"><input type="text" name="caseName" 
582          size="18" maxlength="12" onblur="caseNameOnChange()"></td>
583        <td class="status" id="case.status"></td>
584        <td class="help"><span id="case.message" class="message" style="display: none;"></span>The case (barcode) associated with this patient.</td>
585      </tr>
586      <tr>
587        <td class="prompt">Personal number</td>
588        <td class="input"><input type="text" name="personalNumber" 
589          size="18" maxlength="12" onkeyup="personalNumberOnChange()" onkeypress="goNextOnTab(event)"></td>
590        <td class="status" id="pnr.status"></td>
591        <td class="help"><span id="pnr.message" class="message" style="display: none;"></span>(YYYYMMDDZZZZ)</td>
592      </tr>
593      </table>
594    </td>
595  </tr>
596  </table>
597
598  <!-- 2. New patient registration -->
599  <div id="newPatientSection" style="display: none;">
600  <p>
601  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
602  <tr>
603    <td rowspan="2" class="stepno">2</td>
604    <td class="steptitle">New patient: Enter all names</td>
605  </tr>
606  <tr>
607    <td class="stepfields">
608      <table border="0" cellspacing="0" cellpadding="0" width="100%">
609      <tr>
610        <td class="prompt">Patient code</td>
611        <td class="input"><input type="text" name="patientCode" value="" size="18" maxlength="12"></td>
612        <td class="status" id="patientCode.status"></td>
613        <td class="help"><span id="patientCode.message" class="message" style="display: none;"></span></td>
614      </tr>
615      <tr valign="top">
616        <td class="prompt">Family name</td>
617        <td class="input"><input type="text" name="familyName" 
618          size="35" maxlength="255"></td>
619        <td class="status" id="familyName.status"></td>
620        <td class="help"><span id="familyName.message" class="message" style="display: none;"></span>Keep hyphens, keep åäö, replace all special accented letters [e.g. éèü etc] with standard alphabet character.</td>
621      </tr>
622      <tr valign="top">
623        <td class="prompt">All first names</td>
624        <td class="input"><input type="text" name="allFirstNames" 
625          size="35" maxlength="255" onkeypress="goNextOnTab(event)"></td>
626        <td class="status" id="allFirstNames.status"></td>
627        <td class="help"><span id="allFirstNames.message" class="message" style="display: none;"></span>Type all names, see FamilyName comment on valid characters.</td>
628      </tr>
629      <tr>
630        <td class="prompt">Gender</td>
631        <td class="input" id="new.gender"></td>
632        <td class="status"></td>
633        <td class="help"></td>
634      </tr>
635      <tr>
636        <td class="prompt">Date of birth</td>
637        <td class="input" id="new.dateOfBirth"></td>
638        <td class="status"></td>
639        <td class="help"></td>
640      </tr>
641      </table>
642    </td>
643  </tr>
644  </table>
645  </div>
646 
647  <!-- 2b. Existing patient -->
648  <div id="existingPatientSection" style="display: none;">
649  <p>
650  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
651  <tr>
652    <td rowspan="2" class="stepno">2</td>
653    <td class="steptitle">Existing patient: Verify names</td>
654  </tr>
655  <tr>
656    <td class="stepfields">
657      <table border="0" cellspacing="0" cellpadding="0" width="100%">
658      <tr>
659        <td class="prompt">Patient code</td>
660        <td class="input" id="existing.patientCode"></td>
661        <td class="status"></td>
662        <td class="help"></td>
663      </tr>
664      <tr>
665        <td class="prompt">Family name</td>
666        <td class="input" id="existing.familyName"></td>
667        <td class="status"></td>
668        <td class="help"></td>
669      </tr>
670      <tr>
671        <td class="prompt">All first names</td>
672        <td class="input" id="existing.allFirstNames"></td>
673        <td class="status"></td>
674        <td class="help"></td>
675      </tr>
676      <tr>
677        <td class="prompt">Gender</td>
678        <td class="input" id="existing.gender"></td>
679        <td class="status"></td>
680        <td class="help"></td>
681      </tr>
682      <tr>
683        <td class="prompt">Date of birth</td>
684        <td class="input" id="existing.dateOfBirth"></td>
685        <td class="status"></td>
686        <td class="help"></td>
687      </tr>
688      </table>
689    </td>
690  </tr>
691  </table>
692  </div>
693
694
695  <!-- 3. Case registration -->
696  <div id="caseSection" style="display: none;">
697  <p>
698  <table border="0" cellspacing="0" cellpadding="0" class="stepform">
699  <tr>
700    <td rowspan="2" class="stepno">3</td>
701    <td class="steptitle">About this case</td>
702  </tr>
703  <tr>
704    <td class="stepfields">
705      <table border="0" cellspacing="0" cellpadding="0" width="100%">
706      <tr valign="top">
707        <td class="prompt" id="laterality.prompt">Laterality</td>
708        <td class="input" id="laterality.input">
709          <input type="radio" name="laterality" value="LEFT" onclick="lateralityOnChange()">LEFT
710          <input type="radio" name="laterality" value="RIGHT" onclick="lateralityOnChange()">RIGHT
711        </td>
712        <td class="status" id="laterality.status"></td>
713        <td class="help"><span id="laterality.message" class="message" style="display: none;"></span></td>
714      </tr>
715      <tr valign="top">
716        <td class="prompt">Specimen tubes</td>
717        <td class="input" id="specimenTubes"><i>not found</i></td>
718        <td class="status" id="specimenTubes.status"></td>
719        <td class="help"><span id="specimenTubes.message" class="message" style="display: none;"></span>The specimen tube(s) associated with this case.</td>
720      </tr>
721      <tr id="reasonIfNoSpecimenSection" valign="top">
722        <td class="prompt">Reason if no specimen</td>
723        <td class="input"><textarea rows="3" cols="30" name="reasonIfNoSpecimen" value=""></textarea></td>
724        <td class="status"></td>
725        <td class="help">Comment why there was no specimen tubes in the delivery.</td>
726      </tr>
727      </table>
728    </td>
729  </tr>
730  </table>
731  </div>
732
733  <div class="error" id="errorMessage" style="display: none; width: 800px; margin-left: 20px; margin-bottom: 0px;"></div>
734
735  <div id="done" class="success" style="display: none; width: 800px; margin-left: 20px; margin-top: 20px;"></div>
736
737  <table style="margin-left: 20px; margin-top: 10px;">
738    <tr><td><base:button id="gonext" title="Next" image="gonext.gif" onclick="goNext()"/></td></tr>
739    <tr><td><base:button id="gocreate" title="Create" image="gonext.gif" onclick="goCreate()" style="display: none;"/></td></tr>
740    <tr><td><base:button id="gorestart" title="Restart" image="goback.gif" onclick="goRestart()" style="display: none;"/></td></tr>
741  </table>
742  </form>
743
744  <pre>
745  <div id="debug"></div>
746  </pre>
747 
748</base:body>
749</base:page>
750<%
751}
752finally
753{
754  if (dc != null) dc.close();
755}
756%>
Note: See TracBrowser for help on using the repository browser.