/******************************************************************************* * Copyright (c) 2008 Dennis Schenk, Peter Siska. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Dennis Schenk - initial implementation * Peter Siska - initial implementation *******************************************************************************/ package ch.unibe.iam.scg.archie.samples; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import ch.elexis.core.data.activator.CoreHub; import ch.elexis.data.Konsultation; import ch.elexis.data.Patient; import ch.elexis.data.Query; import ch.rgw.tools.Money; import ch.unibe.iam.scg.archie.annotations.GetProperty; import ch.unibe.iam.scg.archie.annotations.SetProperty; import ch.unibe.iam.scg.archie.model.AbstractTimeSeries; import ch.unibe.iam.scg.archie.model.Cohort; import ch.unibe.iam.scg.archie.model.SetDataException; import ch.unibe.iam.scg.archie.samples.i18n.Messages; import ch.unibe.iam.scg.archie.ui.widgets.WidgetTypes; /** * <p> * Provides statistics about consultations. The resulting dataset contains * information about costs, profits and the total number of consultations for a * given age group. * </p> * * $Id: ConsultationStats.java 766 2009-07-24 11:28:14Z peschehimself $ * * @author Peter Siska * @author Dennis Schenk * @version $Rev: 766 $ */ public class ConsultationStats extends AbstractTimeSeries { private int cohortSize; private boolean currentMandatorOnly; private static final String DATE_DB_FORMAT = "yyyyMMdd"; /** Constructor */ public ConsultationStats() { super(Messages.CONSULTATION_STATS_TITLE); this.cohortSize = 5; this.currentMandatorOnly = true; } /** {@inheritDoc} */ @Override protected List<String> createHeadings() { final ArrayList<String> headings = new ArrayList<String>(6); headings.add(Messages.CONSULTATION_STATS_AGE_GROUP); headings.add(Messages.CONSULTATION_STATS_NUMBER_OF_CONSULTATIONS); headings.add(Messages.CONSULTATION_STATS_TOTAL_COSTS); headings.add(Messages.CONSULTATION_STATS_AVERAGE_COSTS); headings.add(Messages.CONSULTATION_STATS_TOTAL_PROFITS); headings.add(Messages.CONSULTATION_STATS_AVERAGE_PROFITS); return headings; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override protected IStatus createContent(IProgressMonitor monitor) { final SimpleDateFormat databaseFormat = new SimpleDateFormat(DATE_DB_FORMAT); // Prepare DB query final Query<Konsultation> query = new Query<Konsultation>(Konsultation.class); query.add("Datum", ">=", databaseFormat.format(this.getStartDate().getTime())); query.add("Datum", "<=", databaseFormat.format(this.getEndDate().getTime())); if (this.currentMandatorOnly) { query.add("MandantID", "=", CoreHub.actMandant.getId()); } // Get all Consultation which happened in the specified date range. final List<Konsultation> consultations = query.execute(); monitor.beginTask(Messages.CALCULATING, consultations.size()); // monitoring // Create a list of cohorts which we will be using as main data holder. // Key is cohort name, entry is cohort itself TreeMap<Cohort, Cohort> cohorts = new TreeMap<Cohort, Cohort>(); monitor.subTask("Grouping Consultations"); for (Konsultation consultation : consultations) { // Check for cancellation if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } // Get the patient which is linked to this consultation Patient patient = consultation.getFall().getPatient(); int age = 0; // We get age as a string (thankyouverymuch) so we have to parse it try { age = Integer.parseInt(patient.getAlter()); } catch (NumberFormatException exception) { // If the age of a patient was not formatted right, we just // ignore him. continue; // gets us out of the loop... } // In which cohort does this patient belong to? // gets rounded down int lowerBound = ((age / this.cohortSize) * this.cohortSize); int upperBound = lowerBound + this.cohortSize - 1; // Initialize empty cohort content, which we will fill with // consultation costs and profits. // cohortContent is an Array with two ArrayLists<Double> in it. ArrayList<Double>[] cohortContent = new ArrayList[2]; // Prepare Cohort. Cohort cohort = new Cohort(lowerBound, upperBound, cohortContent); ArrayList<Double> cohortContentCosts = new ArrayList<Double>(consultations.size()); ArrayList<Double> cohortContentProfits = new ArrayList<Double>(consultations.size()); cohortContent[0] = cohortContentCosts; cohortContent[1] = cohortContentProfits; // Cohort is not in cohort list yet, add it. if (!cohorts.containsKey(cohort)) { // Cast int to double. cohortContentCosts.add(((Integer) consultation.getKosten()).doubleValue()); cohortContentProfits.add(consultation.getGewinn()); cohorts.put(cohort, cohort); } // Cohort is already in cohort list. Add consultation costs and // profits to it's content. else { ArrayList<Double>[] cohortContentOld = (ArrayList<Double>[]) cohorts.get(cohort).getValue(); ((ArrayList<Double>) cohortContentOld[0]).add(((Integer) consultation.getKosten()).doubleValue()); ((ArrayList<Double>) cohortContentOld[1]).add(consultation.getGewinn()); } monitor.worked(1); // monitor } // Create dataset result ArrayList<Comparable<?>[]> result = new ArrayList<Comparable<?>[]>(); // Go over all cohorts we stored and create actual dataset. monitor.subTask("Computing Results"); for (final Entry<Cohort, Cohort> entry : cohorts.entrySet()) { // Check for cancellation if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } final Comparable<?>[] row = new Comparable[this.dataSet.getHeadings().size()]; ArrayList<Double>[] cohortContent = (ArrayList<Double>[]) entry.getValue().getValue(); double totalCosts = 0.0; double totalProfits = 0.0; // Go through all consultation costs and add them together for (Double costs : ((ArrayList<Double>) cohortContent[0])) { totalCosts += costs; } // Go through all consultation profits and add them together for (Double profits : ((ArrayList<Double>) cohortContent[1])) { totalProfits += profits; } row[0] = entry.getValue(); // cohortName row[1] = ((Integer) cohortContent[0].size()); // numberOfConsultations // CAVEAT: Money(int) is constructed with cents, Money(double) // isn't. We have to divide by 100 :( row[2] = new Money(totalCosts / 100); // totalCosts row[3] = new Money(totalCosts / cohortContent[0].size() / 100); // averageCosts row[4] = new Money(totalProfits / 100); // totalProfits row[5] = new Money(totalProfits / cohortContent[1].size() / 100); // averageProfits result.add(row); } // set content this.dataSet.setContent(result); monitor.done(); return Status.OK_STATUS; } /** {@inheritDoc} */ @Override public String getDescription() { return Messages.CONSULTATION_STATS_DESCRIPTION; } /** * Returns the cohort size. * * @return cohortSize */ @GetProperty(name = "Cohort Size", index = 2, widgetType = WidgetTypes.TEXT_NUMERIC, validationRegex = "^([1-9]){1}\\d{0,2}", validationMessage = "This field has to consist of at least one, at most three numbers.") public int getCohortSize() { return this.cohortSize; } /** * Sets the cohort size. * * @param cohortSize * Size of the cohort. * @throws SetDataException * Thrown when a cohort size is smaller than 1. */ @SetProperty(name = "Cohort Size") public void setCohortSize(final int cohortSize) throws SetDataException { try { if (cohortSize < 1) { throw new Exception("Cohort size must be at least 1!"); } this.cohortSize = cohortSize; } catch (final Exception e) { throw new SetDataException(Messages.CONSULTATION_STATS_COHORT_SIZE_EXCEPTION); } } /** * Returns true if the statistic is run for the current mandator only. * * @return currentMandatorOnly True if the statistic is run for the current * mandator only, false else. */ @GetProperty(name = "Active Mandator Only", index = 3, widgetType = WidgetTypes.BUTTON_CHECKBOX, description = "Compute statistics only for the current mandator. If unchecked, the statistics will be computed for all mandator.") public boolean getCurrentMandatorOnly() { return this.currentMandatorOnly; } /** * Sets whether the statistics should be run for the current mandator only. * * @param currentMandatorOnly * True if the statistic needs to be run for the current mandator * only, false else. */ @SetProperty(name = "Active Mandator Only") public void setCurrentMandatorOnly(final boolean currentMandatorOnly) { this.currentMandatorOnly = currentMandatorOnly; } }