/*******************************************************************************
* 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
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.core.data.interfaces.IVerrechenbar;
import ch.elexis.data.Konsultation;
import ch.elexis.data.Query;
import ch.elexis.data.Verrechnet;
import ch.rgw.tools.Money;
import ch.rgw.tools.StringTool;
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.samples.i18n.Messages;
import ch.unibe.iam.scg.archie.ui.widgets.WidgetTypes;
/**
* <p>
* Generates an overview of the services provided in a given timeframe.
* </p>
*
* $Id: ServiceStats.java 766 2009-07-24 11:28:14Z peschehimself $
*
* @author Peter Siska
* @author Dennis Schenk
* @version $Rev: 766 $
*/
public class ServiceStats extends AbstractTimeSeries {
private static final String DATE_DB_FORMAT = "yyyyMMdd";
private boolean currentMandatorOnly;
private boolean groupByCodeSystem;
/**
* Costructs ServiceStats
*/
public ServiceStats() {
super(Messages.SERVICES_TITLE);
this.currentMandatorOnly = true;
this.groupByCodeSystem = false;
}
/** {@inheritDoc} */
@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
final HashMap<IVerrechenbar, ServiceCounter> services = new HashMap<IVerrechenbar, ServiceCounter>();
// Go through all consultations.
monitor.subTask("Grouping Consultations");
for (Konsultation consultation : consultations) {
// Check for cancellation.
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
// Go through all services.
List<Verrechnet> consServices = consultation.getLeistungen();
for (Verrechnet service : consServices) {
// Check for cancellation.
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
IVerrechenbar serviceBase = service.getVerrechenbar();
ServiceCounter counter = services.get(serviceBase);
if (counter == null) {
counter = new ServiceCounter(service);
services.put(serviceBase, counter);
} else {
counter.add(service);
}
}
monitor.worked(1);
}
// Create dataset result
final ArrayList<Comparable<?>[]> result = new ArrayList<Comparable<?>[]>();
final ArrayList<ServiceCounter> counters = this.isGroupByCodeSystem() ? this.groupServiceCounters(services
.values()) : new ArrayList<ServiceCounter>(services.values());
// sort the counters
Collections.sort(counters);
// Go over all services we stored and create actual dataset.
monitor.subTask("Computing Results");
for (ServiceCounter counter : counters) {
// Check for cancellation
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
final Comparable<?>[] row = new Comparable[this.dataSet.getHeadings().size()];
int i = 0;
row[i++] = counter.getVerrechenbar().getCodeSystemName();
// add label if we don't group by code system
if (!this.isGroupByCodeSystem()) {
row[i++] = counter.getService().getLabel();
}
row[i++] = counter.getServiceCount();
row[i++] = new Money(counter.getCost());
row[i++] = new Money(counter.getIncome());
row[i++] = new Money(counter.getIncome().subtractMoney(counter.getCost()));
result.add(row);
}
// Set content.
this.dataSet.setContent(result);
monitor.done();
return Status.OK_STATUS;
}
/**
* This methods groups the given collection containing
* <code>ServiceCounter</code> objects by the underlying code system name
* their service objects belong to.
*
* @return An <code>ArrayList</code> containing the grouped service
* counters.
*/
private ArrayList<ServiceCounter> groupServiceCounters(Collection<ServiceCounter> counters) {
TreeMap<String, ServiceCounter> groupedCounters = new TreeMap<String, ServiceCounter>();
for (ServiceCounter counter : counters) {
String codeSystem = counter.getVerrechenbar().getCodeSystemName();
ServiceCounter groupedCounter = groupedCounters.get(codeSystem);
// if there's no counter with that code system name
if (groupedCounter == null) {
groupedCounters.put(codeSystem, counter);
} else {
// else we start summing up
groupedCounter.add(counter);
}
}
return new ArrayList<ServiceCounter>(groupedCounters.values());
}
// /////////////////////////////////////////////////////////////////////////////
// ANNOTATION METHODS
// /////////////////////////////////////////////////////////////////////////////
/** {@inheritDoc} */
@Override
protected List<String> createHeadings() {
final ArrayList<String> headings = new ArrayList<String>(2);
headings.add(Messages.SERVICES_HEADING_CODESYSTEM);
// if we don't group by code system, add service name
if (!this.isGroupByCodeSystem()) {
headings.add(Messages.SERVICES_HEADING_SERVICE);
}
headings.add(Messages.SERVICES_HEADING_AMOUNT);
headings.add(Messages.SERVICES_HEADING_COSTS);
headings.add(Messages.SERVICES_HEADING_INCOME);
headings.add(Messages.SERVICES_HEADING_PROFITS);
return headings;
}
/** {@inheritDoc} */
@Override
public String getDescription() {
return Messages.SERVICES_DESCRIPTION;
}
/**
* @return currentMandatorOnly
*/
@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 mandators.")
public boolean getCurrentMandatorOnly() {
return this.currentMandatorOnly;
}
/**
* @param currentMandatorOnly
*/
@SetProperty(name = "Active Mandator Only")
public void setCurrentMandatorOnly(final boolean currentMandatorOnly) {
this.currentMandatorOnly = currentMandatorOnly;
}
/**
* @return groupByCodeSystem
*/
@GetProperty(name = "Groupy By Codesystem", index = 5, widgetType = WidgetTypes.BUTTON_CHECKBOX, description = "Groups services by code system.")
public boolean isGroupByCodeSystem() {
return this.groupByCodeSystem;
}
/**
* @param groupByCodeSystem
*/
@SetProperty(name = "Groupy By Codesystem")
public void setGroupByCodeSystem(final boolean groupByCodeSystem) {
this.groupByCodeSystem = groupByCodeSystem;
}
// /////////////////////////////////////////////////////////////////////////////
// PRIVATE HELPER CLASSES
// /////////////////////////////////////////////////////////////////////////////
/**
* <p>
* Simple service counter class. This counter takes a given service and adds
* its values to the current values of this counter. It sums up the income
* and costs for the given service.
* </p>
*
* $Id: ServiceStats.java 766 2009-07-24 11:28:14Z peschehimself $
*
* @author Peter Siska
* @author Dennis Schenk
* @version $Rev: 766 $
*/
private class ServiceCounter implements Comparable<ServiceCounter> {
private Verrechnet service;
private Money income;
private Money cost;
private int totalServices;
/**
* Public constructor.
*
* @param service
*/
public ServiceCounter(Verrechnet service) {
int serviceCount = service.getZahl();
this.service = service;
this.income = service.getNettoPreis().multiply(serviceCount);
this.cost = service.getKosten().multiply(serviceCount);
this.totalServices = serviceCount;
}
/**
* Compares one service counter with an other. First the service group
* name is compared, if that's equal, the service code is compared, if
* that's equal, the total income in this service counter is compared.
*
* @param other
* ServiceCounter
* @return int
*/
public int compareTo(ServiceCounter other) {
// compare service group
int serviceGroup = StringTool.compareWithNull(this.getVerrechenbar().getCodeSystemName(), other
.getVerrechenbar().getCodeSystemName());
if (serviceGroup != 0) {
return serviceGroup;
}
// compare service code
int serviceCode = StringTool.compareWithNull(this.getVerrechenbar().getCode(), other.getVerrechenbar()
.getCode());
if (serviceCode != 0) {
return serviceCode;
}
return this.getIncome().getCents() - other.getIncome().getCents();
}
/**
* Adds a service to this counter. This means that this counter will sum
* up the cost and income values of the given service and the values of
* the counter. It also increments the internal counter for the service
* type.
*
* @param service
*/
protected void add(Verrechnet service) {
// increment counter
int serviceCount = service.getZahl();
this.totalServices += serviceCount;
// sum up moneys
Money totalIncome = service.getNettoPreis().multiply(serviceCount);
Money totalCost = service.getKosten().multiply(serviceCount);
this.cost.addMoney(totalCost);
this.income.addMoney(totalIncome);
}
/**
* Adds a <code>ServiceCounter</code> counter object to this counter.
* This means suming up the values of the given counter with these of
* this object.
*
* @param counter
*/
protected void add(ServiceCounter counter) {
int serviceCount = counter.getServiceCount();
this.totalServices += serviceCount;
// sum up money values
Money totalIncome = counter.getIncome();
Money totalCost = counter.getCost();
this.cost.addMoney(totalCost);
this.income.addMoney(totalIncome);
}
/**
* Returns the
* <code>IVerrechenbar<code> object for the service in this counter.
*
* @return The <code>IVerrechenbar<code> object for the service in this
* counter.
*/
protected IVerrechenbar getVerrechenbar() {
return this.service.getVerrechenbar();
}
/**
* Returns the service in this counter.
*
* @return Returns the service in this counter.
*/
protected Verrechnet getService() {
return this.service;
}
/**
* Returns the income.
*
* @return Returns the income.
*/
protected Money getIncome() {
return this.income;
}
/**
* Returns the cost.
*
* @return Returns the cost.
*/
protected Money getCost() {
return this.cost;
}
/**
* Returns the value of the internal service counter.
*
* @return Total number of services for this counter.
*/
protected int getServiceCount() {
return this.totalServices;
}
}
}