001package org.hl7.fhir.r5.renderers; 002 003import java.io.File; 004import java.io.IOException; 005import java.io.UnsupportedEncodingException; 006import java.util.ArrayList; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010import java.util.UUID; 011 012import org.hl7.fhir.exceptions.DefinitionException; 013import org.hl7.fhir.exceptions.FHIRException; 014import org.hl7.fhir.exceptions.FHIRFormatError; 015import org.hl7.fhir.r5.model.Address; 016import org.hl7.fhir.r5.model.Attachment; 017import org.hl7.fhir.r5.model.CodeableConcept; 018import org.hl7.fhir.r5.model.ContactPoint; 019import org.hl7.fhir.r5.model.DataType; 020import org.hl7.fhir.r5.model.DateType; 021import org.hl7.fhir.r5.model.Enumeration; 022import org.hl7.fhir.r5.model.Extension; 023import org.hl7.fhir.r5.model.HumanName; 024import org.hl7.fhir.r5.model.HumanName.NameUse; 025import org.hl7.fhir.r5.model.Identifier; 026import org.hl7.fhir.r5.model.Identifier.IdentifierUse; 027import org.hl7.fhir.r5.model.Patient; 028import org.hl7.fhir.r5.model.Period; 029import org.hl7.fhir.r5.model.PrimitiveType; 030import org.hl7.fhir.r5.model.Reference; 031import org.hl7.fhir.r5.model.Resource; 032import org.hl7.fhir.r5.model.StructureDefinition; 033import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; 034import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper; 035import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 036import org.hl7.fhir.r5.renderers.utils.RenderingContext; 037import org.hl7.fhir.r5.utils.EOperationOutcome; 038import org.hl7.fhir.utilities.TextFile; 039import org.hl7.fhir.utilities.Utilities; 040import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 041import org.hl7.fhir.utilities.xhtml.XhtmlNode; 042 043public class PatientRenderer extends ResourceRenderer { 044 045 public class NamedReferance { 046 047 private String name; 048 private Reference reference; 049 private BaseWrapper wrapper; 050 051 public NamedReferance(String name, Reference ref, BaseWrapper refw) { 052 this.name = name; 053 this.reference = ref; 054 this.wrapper = refw; 055 } 056 057 public String getName() { 058 return name; 059 } 060 061 public Reference getReference() { 062 return reference; 063 } 064 065 public BaseWrapper getWrapper() { 066 return wrapper; 067 } 068 069 } 070 071 private static final int MAX_IMAGE_LENGTH = 2*1024*1024; 072 073 public PatientRenderer(RenderingContext context) { 074 super(context); 075 } 076 077 public boolean render(XhtmlNode x, Resource dr) throws UnsupportedEncodingException, IOException { 078 describe(x, dr); 079 return false; 080 } 081 082 // name gender DoB (MRN) 083 public String display(Resource dr) { 084 Patient pat = (Patient) dr; 085 Identifier id = null; 086 for (Identifier t : pat.getIdentifier()) { 087 id = chooseId(id, t); 088 } 089 HumanName n = null; 090 for (HumanName t : pat.getName()) { 091 n = chooseName(n, t); 092 } 093 return display(n, pat.hasGender() ? context.getTranslatedCode(pat.getGenderElement(), "http://hl7.org/fhir/administrative-gender") : null, pat.getBirthDateElement(), id); 094 } 095 096 private Identifier chooseId(Identifier oldId, Identifier newId) { 097 if (oldId == null) { 098 return newId; 099 } 100 if (newId == null) { 101 return oldId; 102 } 103 return isPreferred(newId.getUse(), oldId.getUse()) ? newId : oldId; 104 } 105 106 private boolean isPreferred(IdentifierUse newUse, IdentifierUse oldUse) { 107 if (newUse == null && oldUse == null || newUse == oldUse) { 108 return false; 109 } 110 if (newUse == null) { 111 return true; 112 } 113 switch (newUse) { 114 case NULL: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.USUAL); 115 case OFFICIAL: return !existsInList(oldUse, IdentifierUse.USUAL); 116 case OLD: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.SECONDARY, IdentifierUse.USUAL); 117 case SECONDARY: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.USUAL); 118 case TEMP: return !existsInList(oldUse, IdentifierUse.OFFICIAL, IdentifierUse.SECONDARY, IdentifierUse.USUAL); 119 case USUAL: return true; 120 default: return false; 121 } 122 } 123 124 private boolean existsInList(IdentifierUse oldUse, IdentifierUse... values) { 125 for (IdentifierUse value : values) { 126 if (value == oldUse) { 127 return true; 128 } 129 } 130 return false; 131 } 132 133 private HumanName chooseName(HumanName oldName, HumanName newName) { 134 if (oldName == null) { 135 return newName; 136 } 137 if (newName == null) { 138 return oldName; 139 } 140 return isPreferred(newName.getUse(), oldName.getUse()) ? newName : oldName; 141 } 142 143 144 private boolean isPreferred(NameUse newUse, NameUse oldUse) { 145 if (newUse == null && oldUse == null || newUse == oldUse) { 146 return false; 147 } 148 if (newUse == null) { 149 return true; 150 } 151 if (oldUse == null) { 152 return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 153 } 154 switch (oldUse) { 155 case ANONYMOUS: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 156 case MAIDEN: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 157 case NICKNAME: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 158 case NULL: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 159 case OFFICIAL: return existsInList(newUse, NameUse.USUAL); 160 case OLD: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 161 case TEMP: return existsInList(newUse, NameUse.OFFICIAL, NameUse.USUAL); 162 case USUAL: return false; 163 } 164 return false; 165 } 166 167 private boolean existsInList(NameUse oldUse, NameUse... values) { 168 for (NameUse value : values) { 169 if (value == oldUse) { 170 return true; 171 } 172 } 173 return false; 174 } 175 176 @Override 177 public String display(ResourceWrapper pat) throws UnsupportedEncodingException, IOException { 178 Identifier id = null; 179 PropertyWrapper pw = getProperty(pat, "identifier"); 180 for (BaseWrapper t : pw.getValues()) { 181 id = chooseId(id, (Identifier) t.getBase()); 182 } 183 pw = getProperty(pat, "name"); 184 HumanName n = null; 185 for (BaseWrapper t : pw.getValues()) { 186 n = chooseName(n, (HumanName) t); 187 } 188 String gender = null; 189 pw = getProperty(pat, "gender"); 190 if (valued(pw)) { 191 gender = context.getTranslatedCode(pw.value().getBase(), "http://hl7.org/fhir/administrative-gender"); 192 } 193 DateType dt = null; 194 pw = getProperty(pat, "birthDate"); 195 if (valued(pw)) { 196 dt = (DateType) pw.value().getBase(); 197 } 198 return display(n, gender, dt, id); 199 } 200 201 public void describe(XhtmlNode x, ResourceWrapper pat) throws UnsupportedEncodingException, IOException { 202 Identifier id = null; 203 PropertyWrapper pw = getProperty(pat, "identifier"); 204 for (BaseWrapper t : pw.getValues()) { 205 id = chooseId(id, (Identifier) t.getBase()); 206 } 207 pw = getProperty(pat, "name"); 208 HumanName n = null; 209 for (BaseWrapper t : pw.getValues()) { 210 n = chooseName(n, (HumanName) t.getBase()); 211 } 212 String gender = null; 213 pw = getProperty(pat, "gender"); 214 if (valued(pw)) { 215 gender = context.getTranslatedCode(pw.value().getBase(), "http://hl7.org/fhir/administrative-gender"); 216 } 217 DateType dt = null; 218 pw = getProperty(pat, "birthDate"); 219 if (valued(pw)) { 220 dt = (DateType) pw.value().getBase(); 221 } 222 describe(x, n, gender, dt, id); 223 } 224 225 226 private String display(HumanName name, String gender, DateType dob, Identifier id) { 227 StringBuilder b = new StringBuilder(); 228 if (name == null) { 229 b.append(display(name)); 230 } else { 231 b.append(context.formatPhrase(RenderingContext.PAT_NO_NAME)); 232 } 233 b.append(" "); 234 if (dob == null) { 235 b.append(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 236 } else { 237 b.append(gender); 238 } 239 b.append(", "); 240 if (dob == null) { 241 b.append(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 242 } else { 243 b.append(context.formatPhrase(RenderingContext.PAT_DOB, display(dob))); 244 } 245 if (id != null) { 246 b.append(" ( "); 247 b.append(display(id)); 248 b.append(")"); 249 } 250 return b.toString(); 251 } 252 253 public void describe(XhtmlNode x, HumanName name, String gender, DateType dob, Identifier id) throws UnsupportedEncodingException, IOException { 254 if (name == null) { 255 x.b().tx(context.formatPhrase(RenderingContext.PAT_NO_NAME)); // todo: is this appropriate? 256 } else { 257 render(x.b(), name); 258 } 259 x.tx(" "); 260 if (gender == null) { 261 x.tx(context.formatPhrase(RenderingContext.PAT_NO_GENDER)); 262 } else { 263 x.tx(gender); 264 } 265 x.tx(", "); 266 if (dob == null) { 267 x.tx(context.formatPhrase(RenderingContext.PAT_NO_DOB)); 268 } else { 269 x.tx(context.formatPhrase(RenderingContext.PAT_DOB, display(dob))); 270 } 271 if (id != null) { 272 x.tx(" ( "); 273 render(x, id); 274 x.tx(")"); 275 } 276 } 277 278 @Override 279 public boolean render(XhtmlNode x, ResourceWrapper r) throws IOException, FHIRException, EOperationOutcome { 280 // banner 281 describe(makeBanner(x.para()), r); 282 x.hr(); 283 XhtmlNode tbl; 284 if (hasRenderablePhoto(r)) { 285 tbl = x.table("none"); 286 XhtmlNode tr = tbl.tr(); 287 tbl = tr.td().table("grid"); 288 renderPhoto(tr.td(), r); 289 } else { 290 tbl = x.table("grid"); 291 } 292 293 // the table has 4 columns 294 addStatus(tbl, r); 295 addIdentifiers(tbl, r); 296 addNames(tbl, r); 297 addComms(tbl, r); 298 addLangs(tbl, r); 299 addNOKs(tbl, r); 300 addLinks(tbl, r); 301 addExtensions(tbl, r); 302 if (tbl.isEmpty()) { 303 x.remove(tbl); 304 } 305 if (r.has("contained") && context.isTechnicalMode()) { 306 x.hr(); 307 x.para().b().tx(context.formatMessagePlural(r.getContained().size(), RenderingContext.PAT_CONTAINED)); 308 addContained(x, r.getContained()); 309 } 310 return false; 311 } 312 313 private void addContained(XhtmlNode x, List<ResourceWrapper> list) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { 314 for (ResourceWrapper c : list) { 315 x.hr(); 316 x.an(c.getId()); 317 new RendererFactory().factory(c, context).render(x, c); 318 } 319 } 320 321 private void addExtensions(XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 322 Map<String, List<Extension>> extensions = new HashMap<>(); 323 PropertyWrapper pw = getProperty(r, "extension"); 324 for (BaseWrapper t : pw.getValues()) { 325 Extension ext = (Extension) t.getBase(); 326 if (!extensions.containsKey(ext.getUrl())) { 327 extensions.put(ext.getUrl(), new ArrayList<>()); 328 } 329 extensions.get(ext.getUrl()).add(ext); 330 } 331 for (String url : extensions.keySet()) { 332 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, url); 333 if (sd != null) { 334 List<Extension> list = extensions.get(url); 335 boolean anyComplex = false; 336 for (Extension ext : list) { 337 anyComplex = anyComplex || ext.hasExtension(); 338 } 339 if (!anyComplex) { 340 XhtmlNode tr = tbl.tr(); 341 nameCell(tr, getContext().getTranslated(sd.getTitleElement()), sd.getDescription(), sd.getWebPath()); 342 XhtmlNode td = tr.td(); 343 td.colspan("3"); 344 if (list.size() == 1) { 345 XhtmlNode ul = td.ul(); 346 for (Extension s : list) { 347 XhtmlNode li = ul.li(); 348 render(r, li, s.getValue()); 349 } 350 } else { 351 render(r, td, list.get(0).getValue()); 352 } 353 } else { 354 for (Extension ext : list) { 355 XhtmlNode tr = tbl.tr(); 356 nameCell(tr, sd.getTitle()+":", sd.getDescription()); 357 XhtmlNode td = tr.td(); 358 td.colspan("3"); 359 if (ext.hasExtension()) { 360 XhtmlNode ul = td.ul(); 361 for (Extension s : ext.getExtension()) { 362 XhtmlNode li = ul.li(); 363 li.tx(s.getUrl()+": "); 364 if (s.hasExtension()) { 365 boolean first = true; 366 for (Extension t : s.getExtension()) { 367 if (first) first = false; else li.tx("; "); 368 li.tx(t.getUrl()+"="); 369 render(r, li, t.getValue()); 370 } 371 } else { 372 render(r, li, s.getValue()); 373 } 374 } 375 } else { 376 render(r, td, ext.getValue()); 377 } 378 } 379 } 380 } 381 } 382 383 384 } 385 386 private void addIdentifiers(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 387 List<Identifier> ids = new ArrayList<>(); 388 PropertyWrapper pw = getProperty(r, "identifier"); 389 for (BaseWrapper t : pw.getValues()) { 390 ids.add((Identifier) t.getBase()); 391 } 392 Identifier id = null; 393 for (Identifier i : ids) { 394 id = chooseId(id, i); 395 } 396 if (id != null) { 397 ids.remove(id); 398 }; 399 if (ids.size() > 0) { 400 XhtmlNode tr = tbl.tr(); 401 nameCell(tr, context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID),context.formatMessagePlural(ids.size(), RenderingContext.PAT_OTHER_ID_HINT)); 402 XhtmlNode td = tr.td(); 403 td.colspan("3"); 404 if (ids.size() == 1) { 405 render(r, td, ids.get(0)); 406 } else { 407 XhtmlNode ul = td.ul(); 408 for (Identifier i : ids) { 409 render(r, ul.li(), i); 410 } 411 } 412 } 413 } 414 415 private void addLangs(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 416 List<CodeableConcept> langs = new ArrayList<>(); 417 PropertyWrapper pw = getProperty(r, "communication"); 418 CodeableConcept prefLang = null; 419 for (BaseWrapper t : pw.getValues()) { 420 PropertyWrapper l = getProperty(t, "language"); 421 if (l != null && l.hasValues()) { 422 CodeableConcept lang = (CodeableConcept) l.getValues().get(0).getBase(); 423 langs.add(lang); 424 l = getProperty(t, "preferred"); 425 if (l != null && l.hasValues() && "true".equals(l.getValues().get(0).getBase().primitiveValue())) { 426 prefLang = lang; 427 } 428 } 429 } 430 if (langs.size() > 0) { 431 XhtmlNode tr = tbl.tr(); 432 nameCell(tr, context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG), context.formatMessagePlural(langs.size(), RenderingContext.PAT_LANG_HINT)); 433 XhtmlNode td = tr.td(); 434 td.colspan("3"); 435 if (langs.size() == 1) { 436 render(r, td, langs.get(0)); 437 if (prefLang != null) { 438 td.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED)); 439 } 440 } else if (langs.size() > 1) { 441 XhtmlNode ul = td.ul(); 442 for (CodeableConcept i : langs) { 443 XhtmlNode li = ul.li(); 444 render(r, li, i); 445 if (i == prefLang) { 446 li.tx(" "+context.formatPhrase(RenderingContext.PAT_LANG_PREFERRED));; 447 } 448 } 449 } 450 } 451 } 452 453 private void addLinks(XhtmlNode tbl, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 454 List<NamedReferance> refs = new ArrayList<>(); 455 PropertyWrapper pw = getProperty(r, "generalPractitioner"); 456 if (pw != null) { 457 for (BaseWrapper t : pw.getValues()) { 458 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_GP), (Reference) t.getBase(), t)); 459 } 460 } 461 pw = getProperty(r, "managingOrganization"); 462 if (pw != null) { 463 for (BaseWrapper t : pw.getValues()) { 464 refs.add(new NamedReferance(context.formatPhrase(RenderingContext.PAT_MO), (Reference) t.getBase(), t)); 465 } 466 } 467 pw = getProperty(r, "link"); 468 for (BaseWrapper t : pw.getValues()) { 469 PropertyWrapper l = getProperty(t, "other"); 470 Reference ref = null; 471 BaseWrapper refw = null; 472 for (BaseWrapper v : l.getValues()) { 473 ref = (Reference) v.getBase(); 474 refw = v; 475 } 476 String type = null; 477 l = getProperty(t, "type"); 478 for (BaseWrapper v : l.getValues()) { 479 type = v.getBase().primitiveValue(); 480 } 481 if (type != null && ref != null) { 482 refs.add(new NamedReferance(describeLinkedRecord(type), ref, refw)); 483 } 484 } 485 486 if (refs.size() > 0) { 487 XhtmlNode tr = tbl.tr(); 488 nameCell(tr, context.formatPhrase(RenderingContext.PAT_LINKS), context.formatPhrase(RenderingContext.PAT_LINKS_HINT)); 489 XhtmlNode td = tr.td(); 490 td.colspan("3"); 491 XhtmlNode ul = td.ul(); 492 for (NamedReferance ref : refs) { 493 XhtmlNode li = ul.li(); 494 li.tx(ref.getName()); 495 li.tx(": "); 496 renderReference(r, li, ref.getReference()); 497 } 498 } 499 } 500 501 private String describeLinkedRecord(String type) { 502 switch (type) { 503 case "replaced-by" : return context.formatPhrase(RenderingContext.PAT_LINK_REPLBY); 504 case "replaces": return context.formatPhrase(RenderingContext.PAT_LINK_REPL); 505 case "refer": return context.formatPhrase(RenderingContext.PAT_LINK_REFER); 506 case "seealso": return context.formatPhrase(RenderingContext.PAT_LINK_SEE); 507 } 508 return "Unknown"; 509 } 510 511 private void addNOKs(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 512 PropertyWrapper pw = getProperty(r, "contact"); 513 for (BaseWrapper t : pw.getValues()) { 514 addNOK(tbl,r, t); 515 } 516 } 517 518 private void addNOK(XhtmlNode tbl, ResourceWrapper r, BaseWrapper bw) throws FHIRFormatError, DefinitionException, IOException { 519 List<CodeableConcept> rels = new ArrayList<>(); 520 HumanName name = null; 521 Address add = null; 522 String gender = null; 523 Period period = null; 524 Reference organization = null; 525 List<ContactPoint> tels = new ArrayList<>(); 526 527 PropertyWrapper pw = getProperty(bw, "relationship"); 528 for (BaseWrapper t : pw.getValues()) { 529 CodeableConcept v = (CodeableConcept) t.getBase(); 530 rels.add(v); 531 } 532 533 pw = getProperty(bw, "name"); 534 if (pw.hasValues()) { 535 name = (HumanName) pw.getValues().get(0).getBase(); 536 } 537 pw = getProperty(bw, "telecom"); 538 for (BaseWrapper t : pw.getValues()) { 539 ContactPoint v = (ContactPoint) t.getBase(); 540 tels.add(v); 541 } 542 543 pw = getProperty(bw, "address"); 544 if (pw.hasValues()) { 545 add = (Address) pw.getValues().get(0).getBase(); 546 } 547 548 pw = getProperty(bw, "gender"); 549 if (pw.hasValues()) { 550 gender = context.getTranslatedCode(pw.getValues().get(0).getBase(), "http://hl7.org/fhir/administrative-gender"); 551 } 552 553 pw = getProperty(bw, "organization"); 554 if (pw.hasValues()) { 555 organization = (Reference) pw.getValues().get(0).getBase(); 556 } 557 558 pw = getProperty(bw, "period"); 559 if (pw.hasValues()) { 560 period = (Period) pw.getValues().get(0).getBase(); 561 } 562 563 if (rels.size() < 2 && name == null && add == null && gender == null && period == null && organization == null && tels.size() == 0) { 564 return; // nothing to render 565 } 566 XhtmlNode tr = tbl.tr(); 567 if (rels.size() == 1) { 568 nameCell(tr, (rels.get(0).getCodingFirstRep().hasDisplay() ? rels.get(0).getCodingFirstRep().getDisplay() : display(rels.get(0)))+":", context.formatPhrase(RenderingContext.PAT_NOM_CONTACT)+" "+display(rels.get(0))); 569 } else { 570 nameCell(tr, context.formatPhrase(RenderingContext.GENERAL_CONTACT), context.formatPhrase(RenderingContext.PAT_NOK_CONTACT_HINT)); 571 } 572 XhtmlNode td = tr.td(); 573 td.colspan("3"); 574 XhtmlNode ul = td.ul(); 575 XhtmlNode li; 576 if (name != null) { 577 li = ul.li(); 578 render(r, li, name); 579 if (gender != null) { 580 li.tx(" "+"("+gender+")"); 581 } 582 } else if (gender != null) { 583 li = ul.li(); 584 li.tx(context.formatPhrase(RenderingContext.PAT_GENDER, gender)); 585 } 586 if (rels.size() > 1) { 587 li = ul.li(); 588 li.tx(context.formatPhrase(RenderingContext.PAT_RELN)); 589 boolean first = true; 590 for (CodeableConcept rel : rels) { 591 if (first) first = false; else li.tx(", "); 592 render(r, li, rel); 593 } 594 } 595 if (add != null) { 596 render(r, ul.li(), add); 597 } 598 for (ContactPoint cp : tels) { 599 render(r, ul.li(), cp); 600 } 601 if (organization != null) { 602 li = ul.li(); 603 li.tx(context.formatPhrase(RenderingContext.PAT_ORG)); 604 render(r, li, organization); 605 } 606 if (period != null) { 607 li = ul.li(); 608 li.tx(context.formatPhrase(RenderingContext.PAT_PERIOD)); 609 render(r, li, period); 610 } 611 } 612 613 private void addNames(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 614 List<HumanName> names = new ArrayList<>(); 615 PropertyWrapper pw = getProperty(r, "name"); 616 for (BaseWrapper t : pw.getValues()) { 617 names.add((HumanName) t.getBase()); 618 } 619 HumanName name = null; 620 for (HumanName n : names) { 621 name = chooseName(name, n); 622 } 623 if (name != null) { 624 names.remove(name); 625 }; 626 if (names.size() == 1) { 627 XhtmlNode tr = tbl.tr(); 628 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ALT_NAME), context.formatPhrase(RenderingContext.PAT_ALT_NAME_HINT)); 629 XhtmlNode td = tr.td(); 630 td.colspan("3"); 631 if (names.size() == 1) { 632 render(r, td, names.get(0)); 633 } else { 634 XhtmlNode ul = td.ul(); 635 for (HumanName n : names) { 636 render(r, ul.li(), n); 637 } 638 } 639 } 640 } 641 642 private void addComms(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 643 List<ContactPoint> tels = new ArrayList<>(); 644 PropertyWrapper pw = getProperty(r, "telecom"); 645 for (BaseWrapper t : pw.getValues()) { 646 tels.add((ContactPoint) t.getBase()); 647 } 648 List<Address> adds = new ArrayList<>(); 649 pw = getProperty(r, "address"); 650 for (BaseWrapper t : pw.getValues()) { 651 adds.add((Address) t.getBase()); 652 } 653 if (tels.size() + adds.size() > 0) { 654 XhtmlNode tr = tbl.tr(); 655 nameCell(tr, context.formatPhrase(RenderingContext.PAT_CONTACT), context.formatPhrase(RenderingContext.PAT_CONTACT_HINT)); 656 XhtmlNode td = tr.td(); 657 td.colspan("3"); 658 if (tels.size() + adds.size() == 1) { 659 if (adds.isEmpty()) { 660 render(r, td, tels.get(0)); 661 } else { 662 render(r, td, adds.get(0)); 663 } 664 } else { 665 XhtmlNode ul = td.ul(); 666 for (ContactPoint n : tels) { 667 render(r, ul.li(), n); 668 } 669 for (Address n : adds) { 670 render(r, ul.li(), n); 671 } 672 } 673 } 674 } 675 676 private void addStatus(XhtmlNode tbl, ResourceWrapper r) throws FHIRFormatError, DefinitionException, UnsupportedEncodingException, FHIRException, IOException { 677 // TODO Auto-generated method stub 678 int count = 0; 679 if (r.has("active")) { 680 count++; 681 } 682 if (r.has("deceased")) { 683 count++; 684 } 685 if (r.has("maritalStatus")) { 686 count++; 687 } 688 if (r.has("multipleBirth")) { 689 count++; 690 } 691 if (count > 0) { 692 XhtmlNode tr = tbl.tr(); 693 int pos = 0; 694 if (r.has("active")) { 695 PropertyWrapper a = r.getChildByName("active"); 696 if (a.hasValues()) { 697 pos++; 698 nameCell(tr, context.formatPhrase(RenderingContext.PAT_ACTIVE), context.formatPhrase(RenderingContext.PAT_ACTIVE_HINT)); 699 XhtmlNode td = tr.td(); 700 if (pos == count) { 701 td.colspan("3"); 702 } 703 render(r, td, (DataType) a.value().getBase()); 704 } 705 } 706 if (r.has("deceased[x]")) { 707 PropertyWrapper a = r.getChildByName("deceased[x]"); 708 if (a.hasValues()) { 709 pos++; 710 nameCell(tr, context.formatPhrase(RenderingContext.PAT_DECEASED), context.formatPhrase(RenderingContext.PAT_DECEASED_HINT)); 711 XhtmlNode td = tr.td(); 712 if (pos == count) { 713 td.colspan("3"); 714 } 715 render(r, td, (DataType) a.value().getBase()); 716 } 717 } 718 if (r.has("maritalStatus")) { 719 PropertyWrapper a = r.getChildByName("maritalStatus"); 720 if (a.hasValues()) { 721 pos++; 722 if (pos == 3) { 723 tr = tbl.tr(); 724 } 725 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MARITAL), context.formatPhrase(RenderingContext.PAT_MARITAL_HINT)); 726 XhtmlNode td = tr.td(); 727 if (pos == count) { 728 td.colspan("3"); 729 } 730 render(r, td, (DataType) a.value().getBase()); 731 } 732 } 733 if (r.has("multipleBirth[x]")) { 734 PropertyWrapper a = r.getChildByName("multipleBirth[x]"); 735 if (a.hasValues()) { 736 pos++; 737 if (pos == 3) { 738 tr = tbl.tr(); 739 } 740 nameCell(tr, context.formatPhrase(RenderingContext.PAT_MUL_BIRTH), context.formatPhrase(RenderingContext.PAT_MUL_BIRTH_HINT)); 741 XhtmlNode td = tr.td(); 742 if (pos == count) { 743 td.colspan("3"); 744 } 745 render(r, td, (DataType) a.value().getBase()); 746 } 747 } 748 } 749 } 750 751 private void nameCell(XhtmlNode tr, String text, String title) { 752 XhtmlNode td = tr.td(); 753 td.setAttribute("title", title); 754 td.tx(text); 755 td.style("background-color: #f3f5da"); 756 } 757 758 private void nameCell(XhtmlNode tr, String text, String title, String link) { 759 XhtmlNode td = tr.td(); 760 td.setAttribute("title", title); 761 if (link != null) { 762 td.ah(link).tx(text); 763 } else { 764 td.tx(text); 765 } 766 td.style("background-color: #f3f5da"); 767 } 768 769 private void renderPhoto(XhtmlNode td, ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 770 if (r.has("photo")) { 771 PropertyWrapper a = r.getChildByName("photo"); 772 for (BaseWrapper v : a.getValues()) { 773 Attachment att = (Attachment) v.getBase(); 774 if (att.getContentType().startsWith("image/") && 775 att.getData() != null && (!context.isInlineGraphics() || (att.getData().length > 0 && att.getData().length < MAX_IMAGE_LENGTH))) { 776 String ext = extensionForType(att.getContentType()); 777 if (context.isInlineGraphics() || Utilities.noString(context.getDestDir()) || ext == null) { 778 td.img("data:"+att.getContentType()+";base64,"+att.getDataElement().asStringValue(), "patient photo"); 779 } else { 780 String n = UUID.randomUUID().toString().toLowerCase()+ext; 781 TextFile.bytesToFile(att.getData(), ManagedFileAccess.file(Utilities.path(context.getDestDir(), n))); 782 context.registerFile(n); 783 td.img(n, context.formatPhrase(RenderingContext.PAT_PHOTO)); 784 } 785 return; 786 } 787 } 788 } 789 return; 790 } 791 792 private String extensionForType(String contentType) { 793 if (contentType.equals("image/gif")) { 794 return ".gif"; 795 } 796 if (contentType.equals("image/png")) { 797 return ".png"; 798 } 799 if (contentType.equals("image/jpeg")) { 800 return ".jpg"; 801 } 802 return null; 803 } 804 805 private boolean hasRenderablePhoto(ResourceWrapper r) throws UnsupportedEncodingException, FHIRException, IOException { 806 if (r.has("photo")) { 807 PropertyWrapper a = r.getChildByName("photo"); 808 for (BaseWrapper v : a.getValues()) { 809 Attachment att = (Attachment) v.getBase(); 810 if (att.hasContentType() && att.getContentType().startsWith("image/") && 811 att.getData() != null && (!context.isInlineGraphics() || (att.getData().length > 0 && att.getData().length < MAX_IMAGE_LENGTH))) { 812 return true; 813 } 814 } 815 } 816 return false; 817 } 818 819 private XhtmlNode makeBanner(XhtmlNode para) { 820 para.style("border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"); 821 return para; 822 } 823}