/** * The contents of this file are subject to the OpenMRS Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.web.taglib; import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeSet; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.Concept; import org.openmrs.ConceptName; import org.openmrs.Obs; import org.openmrs.api.ConceptService; import org.openmrs.api.context.Context; public class ObsTableWidget extends TagSupport { private static final long serialVersionUID = 14352344444L; private final Log log = LogFactory.getLog(getClass()); /* * pipe-separated list that could be: * a concept id (e.g. "5089") * a concept name (e.g. "name:CD4 COUNT") * a concept set, by id or name (e.g. "set:5089" or "set.en:LAB TESTS") */ private String concepts; private Collection<Obs> observations; private boolean sortDescending = true; private boolean orientVertical = true; private Boolean showEmptyConcepts = true; private Boolean showConceptHeader = true; private Boolean showDateHeader = true; private Boolean combineEqualResults = true; private String id; private String cssClass; private Date fromDate; private Date toDate; private Integer limit = 0; private String conceptLink = null; //private String combineBy = "day"; public ObsTableWidget() { } public String getConcepts() { return concepts; } public void setConcepts(String concepts) { if (concepts == null || concepts.length() == 0) return; this.concepts = concepts; } public String getConceptLink() { return conceptLink; } public void setConceptLink(String conceptLink) { this.conceptLink = conceptLink; } public String getCssClass() { return cssClass; } public void setCssClass(String cssClass) { if (cssClass == null || cssClass.length() == 0) return; this.cssClass = cssClass; } public String getId() { return id; } public void setId(String id) { if (id == null || id.length() == 0) return; this.id = id; } public Boolean getShowEmptyConcepts() { return showEmptyConcepts; } public void setShowEmptyConcepts(Boolean showEmptyConcepts) { if (showEmptyConcepts == null) return; this.showEmptyConcepts = showEmptyConcepts; } public Boolean getShowConceptHeader() { return showConceptHeader; } public void setShowConceptHeader(Boolean showHeader) { if (showHeader == null) return; this.showConceptHeader = showHeader; } public Boolean getShowDateHeader() { return showDateHeader; } public void setShowDateHeader(Boolean showDateHeader) { if (showDateHeader == null) return; this.showDateHeader = showDateHeader; } public String getSort() { return sortDescending ? "desc" : "asc"; } public void setSort(String sort) { if (sort == null || sort.length() == 0) return; sortDescending = !sort.equals("asc"); } public String getOrientation() { return orientVertical ? "vertical" : "horizontal"; } public void setOrientation(String orientation) { if (orientation == null || orientation.length() == 0) return; orientVertical = !orientation.equals("horizontal"); } public Collection<Obs> getObservations() { return observations; } public void setObservations(Collection<Obs> observations) { this.observations = observations; } public Date getFromDate() { return fromDate; } public void setFromDate(Date fromDate) { if (fromDate == null) return; this.fromDate = fromDate; } public Date getToDate() { return toDate; } public void setToDate(Date toDate) { if (toDate == null) return; this.toDate = toDate; } public Integer getLimit() { return limit; } public void setLimit(Integer limit) { if (limit == null) return; this.limit = limit; } public Boolean getCombineEqualResults() { return combineEqualResults; } public void setCombineEqualResults(Boolean combineEqualResults) { this.combineEqualResults = combineEqualResults; } public int doStartTag() { Locale loc = Context.getLocale(); DateFormat df = Context.getDateFormat(); //DateFormat.getDateInstance(DateFormat.SHORT, loc); // determine which concepts we care about List<Concept> conceptList = new ArrayList<Concept>(); Set<Integer> conceptIds = new HashSet<Integer>(); ConceptService cs = Context.getConceptService(); for (StringTokenizer st = new StringTokenizer(concepts, "|"); st.hasMoreTokens();) { String s = st.nextToken().trim(); log.debug("looking at " + s); boolean isSet = s.startsWith("set:"); if (isSet) s = s.substring(4).trim(); Concept c = null; if (s.startsWith("name:")) { String name = s.substring(5).trim(); c = cs.getConceptByName(name); } else { try { c = cs.getConcept(Integer.valueOf(s.trim())); } catch (Exception ex) {} } if (c != null) { if (isSet) { List<Concept> inSet = cs.getConceptsByConceptSet(c); for (Concept con : inSet) { if (!conceptIds.contains(con.getConceptId())) { conceptList.add(con); conceptIds.add(con.getConceptId()); } } } else { if (!conceptIds.contains(c.getConceptId())) { conceptList.add(c); conceptIds.add(c.getConceptId()); } } } log.debug("conceptList == " + conceptList); } // organize obs of those concepts by Date and Concept Set<Integer> conceptsWithObs = new HashSet<Integer>(); SortedSet<Date> dates = new TreeSet<Date>(); Map<String, List<Obs>> groupedObs = new HashMap<String, List<Obs>>(); // key is conceptId + "." + date for (Obs o : observations) { Integer conceptId = o.getConcept().getConceptId(); if (conceptIds.contains(conceptId)) { Date thisDate = o.getObsDatetime(); // TODO: allow grouping by day/week/month/etc if ((fromDate != null && thisDate.compareTo(fromDate) < 0) || (toDate != null && thisDate.compareTo(toDate) > 0)) { continue; } dates.add(thisDate); String key = conceptId + "." + thisDate; List<Obs> group = groupedObs.get(key); if (group == null) { group = new ArrayList<Obs>(); groupedObs.put(key, group); } group.add(o); conceptsWithObs.add(conceptId); } } if (!showEmptyConcepts) { for (Iterator<Concept> i = conceptList.iterator(); i.hasNext();) { if (!conceptsWithObs.contains(i.next().getConceptId())) i.remove(); } } List<Date> dateOrder = new ArrayList<Date>(dates); if (sortDescending) Collections.reverse(dateOrder); if (limit > 0 && limit < dateOrder.size()) { if (!sortDescending) { dateOrder = dateOrder.subList(dateOrder.size() - limit, dateOrder.size()); } else { dateOrder = dateOrder.subList(0, limit); } } StringBuilder ret = new StringBuilder(); ret.append("<table"); if (id != null) ret.append(" id=\"" + id + "\""); if (cssClass != null) ret.append(" class=\"" + cssClass + "\""); ret.append(">"); if (orientVertical) { if (showConceptHeader) { ret.append("<tr>"); ret.append("<th></th>"); for (Concept c : conceptList) { ConceptName cn = c.getShortestName(loc, false); String name = cn.getName(); ret.append("<th>"); if (conceptLink != null) { ret.append("<a href=\"" + conceptLink + "conceptId=" + c.getConceptId() + "\">"); } ret.append(name); if (conceptLink != null) { ret.append("</a>"); } ret.append("</th>"); } ret.append("</tr>"); } for (Date date : dateOrder) { ret.append("<tr>"); if (showDateHeader) ret.append("<th>" + df.format(date) + "</th>"); for (Concept c : conceptList) { ret.append("<td align=\"center\">"); String key = c.getConceptId() + "." + date; List<Obs> list = groupedObs.get(key); if (list != null) { if (combineEqualResults) { Collection<String> unique = new LinkedHashSet<String>(); for (Obs obs : list) unique.add(obs.getValueAsString(loc)); for (String s : unique) ret.append(s).append("<br/>"); } else { for (Obs obs : list) ret.append(obs.getValueAsString(loc)).append("<br/>"); } } ret.append("</td>"); } ret.append("</tr>"); } } else { // horizontal if (showDateHeader) { ret.append("<tr>"); ret.append("<th></th>"); for (Date date : dateOrder) { ret.append("<th>" + df.format(date) + "</th>"); } } for (Concept c : conceptList) { ret.append("<tr>"); if (showConceptHeader) { ConceptName cn = c.getShortestName(loc, false); String name = cn.getName(); ret.append("<th>"); if (conceptLink != null) { ret.append("<a href=\"" + conceptLink + "conceptId=" + c.getConceptId() + "\">"); } ret.append(name); if (conceptLink != null) { ret.append("</a>"); } ret.append("</th>"); } for (Date date : dateOrder) { ret.append("<td align=\"center\">"); String key = c.getConceptId() + "." + date; List<Obs> list = groupedObs.get(key); if (list != null) { if (combineEqualResults) { Collection<String> unique = new LinkedHashSet<String>(); for (Obs obs : list) unique.add(obs.getValueAsString(loc)); for (String s : unique) ret.append(s).append("<br/>"); } else { for (Obs obs : list) ret.append(obs.getValueAsString(loc)).append("<br/>"); } } ret.append("</td>"); } ret.append("</tr>"); } } ret.append("</table>"); try { JspWriter w = pageContext.getOut(); w.println(ret); } catch (IOException ex) { log.error("Error while starting ObsTableWidget tag", ex); } return SKIP_BODY; } public int doEndTag() { concepts = null; observations = null; sortDescending = true; orientVertical = true; showEmptyConcepts = true; showConceptHeader = true; showDateHeader = true; combineEqualResults = true; id = null; cssClass = null; showEmptyConcepts = true; showConceptHeader = true; fromDate = null; toDate = null; limit = 0; conceptLink = null; return EVAL_PAGE; } }