/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; /** * This class represents a list of patientIds. */ public class Cohort extends BaseOpenmrsData { public static final long serialVersionUID = 0L; private Integer cohortId; private String name; private String description; private Collection<CohortMembership> memberships; public Cohort() { memberships = new TreeSet<CohortMembership>(); } /** * Convenience constructor to create a Cohort object that has an primarykey/internal identifier * of <code>cohortId</code> * * @param cohortId the internal identifier for this cohort */ public Cohort(Integer cohortId) { this(); this.cohortId = cohortId; } /** * This constructor does not check whether the database contains patients with the given ids, * but {@link org.openmrs.api.CohortService#saveCohort(Cohort)} will. * * @param name * @param description optional description * @param ids option array of Integer ids */ public Cohort(String name, String description, Integer[] ids) { this(); this.name = name; this.description = description; if (ids != null) { Arrays.stream(ids).forEach(id -> addMember(id)); } } /** * This constructor does not check whether the database contains patients with the given ids, * but {@link org.openmrs.api.CohortService#saveCohort(Cohort)} will. * * @param name * @param description optional description * @param patients optional array of patients */ public Cohort(String name, String description, Patient[] patients) { this(name, description, (Integer[]) null); if (patients != null) { Arrays.stream(patients).forEach(p -> addMembership(new CohortMembership(p.getPatientId()))); } } /** * This constructor does not check whether the database contains patients with the given ids, * but {@link org.openmrs.api.CohortService#saveCohort(Cohort)} will. * * @param patientsOrIds optional collection which may contain Patients, or patientIds which may * be Integers, Strings, or anything whose toString() can be parsed to an Integer. */ public Cohort(Collection<?> patientsOrIds) { this(null, null, patientsOrIds); } /** * This constructor does not check whether the database contains patients with the given ids, * but {@link org.openmrs.api.CohortService#saveCohort(Cohort)} will. * * @param name * @param description optional description * @param patientsOrIds optional collection which may contain Patients, or patientIds which may * be Integers, Strings, or anything whose toString() can be parsed to an Integer. */ public Cohort(String name, String description, Collection<?> patientsOrIds) { this(name, description, (Integer[]) null); if (patientsOrIds != null) { for (Object o : patientsOrIds) { if (o instanceof Patient) { addMembership(new CohortMembership(((Patient) o).getPatientId())); } else if (o instanceof Integer) { addMembership(new CohortMembership((Integer) o)); } } } } /** * Convenience constructor taking in a string that is a list of comma separated patient ids This * constructor does not check whether the database contains patients with the given ids, but * {@link org.openmrs.api.CohortService#saveCohort(Cohort)} will. * * @param commaSeparatedIds */ public Cohort(String commaSeparatedIds) { this(); String[] ids = StringUtils.split(commaSeparatedIds, ','); Arrays.stream(ids).forEach(id -> addMembership(new CohortMembership(Integer.valueOf(id.trim())))); } /** * @deprecated since 2.1.0 cohorts are more complex than just a set of patient ids, so there is no one-line replacement * @return Returns a comma-separated list of patient ids in the cohort. */ @Deprecated public String getCommaSeparatedPatientIds() { return StringUtils.join(getMemberIds(), ','); } public boolean contains(Integer patientId) { return getMemberships() != null && getMemberships().stream().anyMatch(m -> m.getPatientId().equals(patientId) && m.isActive()); } @Override public String toString() { StringBuilder sb = new StringBuilder("Cohort id=" + getCohortId()); if (getName() != null) { sb.append(" name=").append(getName()); } if (getMemberships() != null) { sb.append(" size=").append(getMemberships().size()); } return sb.toString(); } public boolean addMember(Integer memberId) { return this.addMembership(new CohortMembership(memberId)); } /** * @since 2.1.0 */ public boolean addMembership(CohortMembership cohortMembership) { if (cohortMembership != null && !this.contains(cohortMembership.getPatientId())) { cohortMembership.setCohort(this); return getMemberships().add(cohortMembership); } return false; } /** * @since 2.1.0 */ public boolean removeMembership(CohortMembership cohortMembership) { return getMemberships().remove(cohortMembership); } /** * @since 2.1.0 * @param includeVoided boolean true/false to include/exclude voided memberships * @return Collection of cohort memberships */ public Collection<CohortMembership> getMemberships(boolean includeVoided) { if (includeVoided) { return getMemberships(); } return getMemberships().stream().filter(m -> m.getVoided() == includeVoided).collect(Collectors.toList()); } /** * @since 2.1.0 */ public Collection<CohortMembership> getMemberships() { if (memberships == null) { memberships = new TreeSet<CohortMembership>(); } return memberships; } /** * @since 2.1.0 * @param asOfDate date used to return active memberships * @return Collection of cohort memberships */ public Collection<CohortMembership> getActiveMemberships(Date asOfDate) { return getMemberships().stream().filter(m -> m.isActive(asOfDate)).collect(Collectors.toList()); } public Collection<CohortMembership> getActiveMemberships() { return getActiveMemberships(new Date()); } /** * @since 2.1.0 */ public CohortMembership getActiveMembership(Patient patient) { return getMemberships().stream().filter(m -> m.isActive() && m.getPatientId().equals(patient.getPatientId())) .collect(Collectors.toList()).get(0); } public int size() { return getMemberships().stream().filter(m -> !m.getVoided() && m.getEndDate() == null).collect(Collectors.toList()) .size(); } /** * @deprecated use {@link #size()} */ @Deprecated public int getSize() { return size(); } public boolean isEmpty() { return size() == 0; } // static utility methods /** * Returns the union of two cohorts * * @param a The first Cohort * @param b The second Cohort * @return Cohort */ public static Cohort union(Cohort a, Cohort b) { Cohort ret = new Cohort(); if (a != null) { ret.getMemberships().addAll(a.getMemberships()); } if (b != null) { ret.getMemberships().addAll(b.getMemberships()); } if (a != null && b != null) { ret.setName("(" + a.getName() + " + " + b.getName() + ")"); } return ret; } /** * Returns the intersection of two cohorts, treating null as an empty cohort * * @param a The first Cohort * @param b The second Cohort * @return Cohort */ public static Cohort intersect(Cohort a, Cohort b) { Cohort ret = new Cohort(); ret.setName("(" + (a == null ? "NULL" : a.getName()) + " * " + (b == null ? "NULL" : b.getName()) + ")"); if (a != null && b != null) { ret.getMemberships().addAll(a.getMemberships()); ret.getMemberships().retainAll(b.getMemberships()); } return ret; } /** * Subtracts a cohort from a cohort * * @param a the original Cohort * @param b the Cohort to subtract * @return Cohort */ public static Cohort subtract(Cohort a, Cohort b) { Cohort ret = new Cohort(); if (a != null) { ret.getMemberships().addAll(a.getMemberships()); if (b != null) { ret.getMemberships().removeAll(b.getMemberships()); ret.setName("(" + a.getName() + " - " + b.getName() + ")"); } } return ret; } // getters and setters public Integer getCohortId() { return cohortId; } public void setCohortId(Integer cohortId) { this.cohortId = cohortId; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * @deprecated since 2.1.0 cohorts are more complex than just a set of patient ids, so there is no one-line replacement */ @Deprecated public Set<Integer> getMemberIds() { Set<Integer> memberIds = new TreeSet<Integer>(); for (CohortMembership member : getMemberships()) { memberIds.add(member.getPatientId()); } return memberIds; } /** * @deprecated since 2.1.0 cohorts are more complex than just a set of patient ids, so there is no one-line replacement * @param memberIds */ @Deprecated public void setMemberIds(Set<Integer> memberIds) { if (getMemberships().size() == 0) { for (Integer id : memberIds) { addMembership(new CohortMembership(id)); } } else { throw new IllegalArgumentException("since 2.1.0 cohorts are more complex than just a set of patient ids"); } } public void setMemberships(Collection<CohortMembership> members) { this.memberships = members; } /** * @since 1.5 * @see org.openmrs.OpenmrsObject#getId() */ @Override public Integer getId() { return getCohortId(); } /** * @since 1.5 * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer) */ @Override public void setId(Integer id) { setCohortId(id); } }