Skip to content

Commit

Permalink
Merge pull request #13215 from SORMAS-Foundation/feature-13190_add_ra…
Browse files Browse the repository at this point in the history
…nge_filter_for_birthdate

#13190 - Add date range filter for birthdate (from-to) to Persons, Ca…
  • Loading branch information
leventegal-she authored Dec 17, 2024
2 parents aef93ea + 82cb706 commit 996f110
Show file tree
Hide file tree
Showing 17 changed files with 431 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ public class CaseCriteria extends CriteriaWithDateType implements ExternalShareC
private Date newCaseDateTo;
private Date creationDateFrom;
private Date creationDateTo;
private Date birthdateFrom;
private Date birthdateTo;
private boolean includePartialMatch;
private CriteriaDateType newCaseDateType;
// Used to re-construct whether users have filtered by epi weeks or dates
private DateFilterOption dateFilterOption = DateFilterOption.DATE;
Expand Down Expand Up @@ -552,6 +555,31 @@ public CaseCriteria creationDateTo(Date creationDateTo) {
return this;
}

public Date getBirthdateFrom() {
return birthdateFrom;
}

public void setBirthdateFrom(Date birthdateFrom) {
this.birthdateFrom = birthdateFrom;
}

public Date getBirthdateTo() {
return birthdateTo;
}

public void setBirthdateTo(Date birthdateTo) {
this.birthdateTo = birthdateTo;
}

@IgnoreForUrl
public boolean isIncludePartialMatch() {
return includePartialMatch;
}

public void setIncludePartialMatch(boolean includePartialMatch) {
this.includePartialMatch = includePartialMatch;
}

public Date getQuarantineTo() {
return quarantineTo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ public class ContactCriteria extends BaseCriteria implements Serializable {
private Boolean onlyContactsFromOtherInstances;
private Date creationDateFrom;
private Date creationDateTo;
private Date birthdateFrom;
private Date birthdateTo;
private boolean includePartialMatch;
private String reportingUserLike;
private String personLike;
private boolean excludeLimitedSyncRestrictions;
Expand Down Expand Up @@ -665,6 +668,31 @@ public ContactCriteria creationDateTo(Date creationDateTo) {
return this;
}

public Date getBirthdateFrom() {
return birthdateFrom;
}

public void setBirthdateFrom(Date birthdateFrom) {
this.birthdateFrom = birthdateFrom;
}

public Date getBirthdateTo() {
return birthdateTo;
}

public void setBirthdateTo(Date birthdateTo) {
this.birthdateTo = birthdateTo;
}

@IgnoreForUrl
public boolean isIncludePartialMatch() {
return includePartialMatch;
}

public void setIncludePartialMatch(boolean includePartialMatch) {
this.includePartialMatch = includePartialMatch;
}

public String getReportingUserLike() {
return reportingUserLike;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,7 @@ public interface Captions {
String importSkips = "importSkips";
String importValueSeparator = "importValueSeparator";
String inaccessibleValue = "inaccessibleValue";
String includePartialBirthdates = "includePartialBirthdates";
String info = "info";
String infrastructureImportAllowOverwrite = "infrastructureImportAllowOverwrite";
String lastName = "lastName";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface Descriptions {
String aefiDashboardDiseaseFilter = "aefiDashboardDiseaseFilter";
String aefiDashboardDistrictFilter = "aefiDashboardDistrictFilter";
String aefiDashboardRegionFilter = "aefiDashboardRegionFilter";
String birthdateFilterPartialMatchDescription = "birthdateFilterPartialMatchDescription";
String Campaign_calculatedBasedOn = "Campaign.calculatedBasedOn";
String Campaign_campaignPhase = "Campaign.campaignPhase";
String CaseData_caseClassification = "CaseData.caseClassification";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ public interface Strings {
String headingImportSelfReports = "headingImportSelfReports";
String headingImportSubcontinents = "headingImportSubcontinents";
String headingImportTravelEntries = "headingImportTravelEntries";
String headingIncorrectDateRange = "headingIncorrectDateRange";
String headingInformationSource = "headingInformationSource";
String headingInfrastructureLocked = "headingInfrastructureLocked";
String headingIntroduction = "headingIntroduction";
Expand Down Expand Up @@ -1419,6 +1420,7 @@ public interface Strings {
String messageImportSuccessful = "messageImportSuccessful";
String messageImportSuccessfulWithSkips = "messageImportSuccessfulWithSkips";
String messageIncompleteGpsCoordinates = "messageIncompleteGpsCoordinates";
String messageIncorrectDateRange = "messageIncorrectDateRange";
String messageInfrastructureLocked = "messageInfrastructureLocked";
String messageInvalidDatesLineListing = "messageInvalidDatesLineListing";
String messageLaboratoriesArchived = "messageLaboratoriesArchived";
Expand Down Expand Up @@ -1680,6 +1682,8 @@ public interface Strings {
String promptAllDistricts = "promptAllDistricts";
String promptAllRegions = "promptAllRegions";
String promptArea = "promptArea";
String promptBirthdateFrom = "promptBirthdateFrom";
String promptBirthdateTo = "promptBirthdateTo";
String promptCampaign = "promptCampaign";
String promptCampaignSearch = "promptCampaignSearch";
String promptCaseOrContactEventSearchField = "promptCaseOrContactEventSearchField";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.symeda.sormas.api.person;

import java.util.Date;
import java.util.Set;

import de.symeda.sormas.api.infrastructure.community.CommunityReferenceDto;
Expand Down Expand Up @@ -37,6 +38,9 @@ public class PersonCriteria extends BaseCriteria implements Cloneable {
private CommunityReferenceDto community;
private PersonAssociation personAssociation;
private Set<String> uuids;
private Date birthdateFrom;
private Date birthdateTo;
private boolean includePartialMatch;

public PersonCriteria() {

Expand Down Expand Up @@ -127,6 +131,31 @@ public PersonCriteria personAssociation(PersonAssociation personAssociation) {
return this;
}

public Date getBirthdateFrom() {
return birthdateFrom;
}

public void setBirthdateFrom(Date birthdateFrom) {
this.birthdateFrom = birthdateFrom;
}

public Date getBirthdateTo() {
return birthdateTo;
}

public void setBirthdateTo(Date birthdateTo) {
this.birthdateTo = birthdateTo;
}

@IgnoreForUrl
public boolean isIncludePartialMatch() {
return includePartialMatch;
}

public void setIncludePartialMatch(boolean includePartialMatch) {
this.includePartialMatch = includePartialMatch;
}

@IgnoreForUrl
public Set<String> getUuids() {
return uuids;
Expand Down
1 change: 1 addition & 0 deletions sormas-api/src/main/resources/captions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ creationDate=Creation date
changeDate=Date of last change
notAvailableShort=NA
inaccessibleValue=Confidential
includePartialBirthdates = Include partial birthdates
numberOfCharacters=Number of characters: %d / %d
remove=Remove
notTestedYet=Not tested yet
Expand Down
1 change: 1 addition & 0 deletions sormas-api/src/main/resources/descriptions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ descContactOnlyWithReducedQuarantine = Only list contacts whose quarantine perio
descContactIncludeContactsFromOtherJurisdictions = Include all contacts from other jurisdictions that you have access to, e.g. because you created them or their source case is in your jurisdiction
descGdpr = Reminder: All comments entered must comply with GDPR rules as described during connection.
discardDescription = Discards any unsaved changes
birthdateFilterPartialMatchDescription = If checked the search will include also the persons that have incomplete birthdate and have only higher level match eg. only year or only year and month

# EpiData
EpiData.bats = Did you have contact with live or dead bats or their excreta during the incubation period?
Expand Down
4 changes: 4 additions & 0 deletions sormas-api/src/main/resources/strings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ headingImportRegions= Import Regions
headingImportTravelEntries = Import Travel Entries
headingImportEnvironments = Import Environments
headingImportSelfReports = Import Self Reports
headingIncorrectDateRange = Incorrect date range
headingInformationSource = Source of Information
headingInfrastructureLocked = Infrastructure locked
headingIntroduction = Introduction
Expand Down Expand Up @@ -1314,6 +1315,7 @@ messageImportSuccessful = <b>Import successful!</b><br/>All rows have been impor
messageImportSuccessfulWithSkips = <b>Import successful!</b><br/>The import has been successful, but some of the rows were skipped. You can now close this window.
messageUploadSuccessful = Upload successful! You can now close this window.
messageIncompleteGpsCoordinates = GPS coordinates are incomplete
messageIncorrectDateRange = Date from is after date to
messageExternalMessagesAssigned = The assignee has been changed for all selected messages
messageLoginFailed = Please check your username and password and try again
messageMissingCases = Please generate some cases before generating contacts
Expand Down Expand Up @@ -1651,6 +1653,8 @@ promptActionChangeDateFrom = Date of action change from...
promptActionChangeDateTo = ... to
promptActionChangeEpiWeekFrom = Date of action change from epi week...
promptActionChangeEpiWeekTo = ... to epi week
promptBirthdateFrom = Birthdate from
promptBirthdateTo = ... to
promptCampaignSearch = ID, name
promptCasesDateFrom = New cases from...
promptCasesEpiWeekFrom = New cases from epi week...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
import de.symeda.sormas.backend.user.User;
import de.symeda.sormas.backend.user.UserRole;
import de.symeda.sormas.backend.user.UserService;
import de.symeda.sormas.backend.util.BirthdateRangeFilterPredicate;
import de.symeda.sormas.backend.util.ExternalDataUtil;
import de.symeda.sormas.backend.util.IterableHelper;
import de.symeda.sormas.backend.util.JurisdictionHelper;
Expand Down Expand Up @@ -912,6 +913,15 @@ public <T extends AbstractDomainObject> Predicate createCriteriaFilter(CaseCrite
filter = CriteriaBuilderHelper.and(cb, filter, likeFilters);
}
}

filter = BirthdateRangeFilterPredicate.createBirthdateRangeFilter(
caseCriteria.getBirthdateFrom(),
caseCriteria.getBirthdateTo(),
caseCriteria.isIncludePartialMatch(),
cb,
joins.getPerson(),
filter);

if (caseCriteria.getBirthdateYYYY() != null) {
filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(joins.getPerson().get(Person.BIRTHDATE_YYYY), caseCriteria.getBirthdateYYYY()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
import de.symeda.sormas.backend.user.User;
import de.symeda.sormas.backend.user.UserRole;
import de.symeda.sormas.backend.user.UserService;
import de.symeda.sormas.backend.util.BirthdateRangeFilterPredicate;
import de.symeda.sormas.backend.util.ExternalDataUtil;
import de.symeda.sormas.backend.util.IterableHelper;
import de.symeda.sormas.backend.util.JurisdictionHelper;
Expand Down Expand Up @@ -1419,6 +1420,15 @@ public Predicate buildCriteriaFilter(ContactCriteria contactCriteria, ContactQue
if (contactCriteria.getPerson() != null) {
filter = CriteriaBuilderHelper.and(cb, filter, cb.equal(joins.getPerson().get(Person.UUID), contactCriteria.getPerson().getUuid()));
}

filter = BirthdateRangeFilterPredicate.createBirthdateRangeFilter(
contactCriteria.getBirthdateFrom(),
contactCriteria.getBirthdateTo(),
contactCriteria.isIncludePartialMatch(),
cb,
joins.getPerson(),
filter);

if (contactCriteria.getBirthdateYYYY() != null) {
filter =
CriteriaBuilderHelper.and(cb, filter, cb.equal(joins.getPerson().get(Person.BIRTHDATE_YYYY), contactCriteria.getBirthdateYYYY()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
import de.symeda.sormas.backend.travelentry.services.TravelEntryService;
import de.symeda.sormas.backend.user.User;
import de.symeda.sormas.backend.user.UserService;
import de.symeda.sormas.backend.util.BirthdateRangeFilterPredicate;
import de.symeda.sormas.backend.util.ExternalDataUtil;
import de.symeda.sormas.backend.util.IterableHelper;
import de.symeda.sormas.backend.util.JurisdictionHelper;
Expand Down Expand Up @@ -386,6 +387,15 @@ public Predicate buildCriteriaFilter(PersonCriteria personCriteria, PersonQueryC
filter = andEquals(cb, personFrom, filter, personCriteria.getBirthdateYYYY(), Person.BIRTHDATE_YYYY);
filter = andEquals(cb, personFrom, filter, personCriteria.getBirthdateMM(), Person.BIRTHDATE_MM);
filter = andEquals(cb, personFrom, filter, personCriteria.getBirthdateDD(), Person.BIRTHDATE_DD);

filter = BirthdateRangeFilterPredicate.createBirthdateRangeFilter(
personCriteria.getBirthdateFrom(),
personCriteria.getBirthdateTo(),
personCriteria.isIncludePartialMatch(),
cb,
personFrom,
filter);

if (personCriteria.getNameAddressPhoneEmailLike() != null) {

String[] textFilters = personCriteria.getNameAddressPhoneEmailLike().split("\\s+");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package de.symeda.sormas.backend.util;

import java.util.Calendar;
import java.util.Date;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Predicate;

import de.symeda.sormas.backend.common.CriteriaBuilderHelper;
import de.symeda.sormas.backend.person.Person;

public class BirthdateRangeFilterPredicate {

public static Predicate createBirthdateRangeFilter(
Date birthdateFrom,
Date birthdateTo,
boolean includePartialMatch,
CriteriaBuilder cb,
From<?, Person> personFrom,
Predicate filter) {
if (birthdateFrom != null) {
Calendar calendarBirthdateFrom = Calendar.getInstance();
calendarBirthdateFrom.setTime(birthdateFrom);
int birthdateFromCriteriaYear = calendarBirthdateFrom.get(Calendar.YEAR);
int birthdateFromCriteriaMonth = calendarBirthdateFrom.get(Calendar.MONTH) + 1;
int birthdateFromCriteriaDay = calendarBirthdateFrom.get(Calendar.DAY_OF_MONTH);

Predicate yearPredicate = cb.greaterThan(personFrom.get(Person.BIRTHDATE_YYYY), birthdateFromCriteriaYear);

Predicate monthPredicate = cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateFromCriteriaYear);
monthPredicate = cb.and(monthPredicate, cb.greaterThan(personFrom.get(Person.BIRTHDATE_MM), birthdateFromCriteriaMonth));

Predicate dayPredicate = cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateFromCriteriaYear);
dayPredicate = cb.and(dayPredicate, cb.equal(personFrom.get(Person.BIRTHDATE_MM), birthdateFromCriteriaMonth));
dayPredicate = cb.and(dayPredicate, cb.greaterThanOrEqualTo(personFrom.get(Person.BIRTHDATE_DD), birthdateFromCriteriaDay));

if (includePartialMatch) {
Predicate sameYearPartialMatchPredicate = cb
.and(cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateFromCriteriaYear), cb.isNull(personFrom.get(Person.BIRTHDATE_MM)));
Predicate sameYearMonthPartialMatchPredicate = cb.and(
cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateFromCriteriaYear),
cb.equal(personFrom.get(Person.BIRTHDATE_MM), birthdateFromCriteriaMonth),
cb.isNull(personFrom.get(Person.BIRTHDATE_DD)));
filter = CriteriaBuilderHelper.and(
cb,
filter,
cb.or(yearPredicate, monthPredicate, dayPredicate, sameYearPartialMatchPredicate, sameYearMonthPartialMatchPredicate));
} else {
filter = CriteriaBuilderHelper.and(cb, filter, cb.or(yearPredicate, monthPredicate, dayPredicate));
}
}

if (birthdateTo != null) {
Calendar calendarBirthdateTo = Calendar.getInstance();
calendarBirthdateTo.setTime(birthdateTo);
int birthdateToCriteriaYear = calendarBirthdateTo.get(Calendar.YEAR);
int birthdateToCriteriaMonth = calendarBirthdateTo.get(Calendar.MONTH) + 1;
int birthdateToCriteriaDay = calendarBirthdateTo.get(Calendar.DAY_OF_MONTH);

Predicate yearPredicate = cb.lessThan(personFrom.get(Person.BIRTHDATE_YYYY), birthdateToCriteriaYear);

Predicate monthPredicate = cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateToCriteriaYear);
monthPredicate = cb.and(monthPredicate, cb.lessThan(personFrom.get(Person.BIRTHDATE_MM), birthdateToCriteriaMonth));

Predicate dayPredicate = cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateToCriteriaYear);
dayPredicate = cb.and(dayPredicate, cb.equal(personFrom.get(Person.BIRTHDATE_MM), birthdateToCriteriaMonth));
dayPredicate = cb.and(dayPredicate, cb.lessThanOrEqualTo(personFrom.get(Person.BIRTHDATE_DD), birthdateToCriteriaDay));

if (includePartialMatch) {
Predicate sameYearPartialMatchPredicate =
cb.and(cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateToCriteriaYear), cb.isNull(personFrom.get(Person.BIRTHDATE_MM)));
Predicate sameYearMonthPartialMatchPredicate = cb.and(
cb.equal(personFrom.get(Person.BIRTHDATE_YYYY), birthdateToCriteriaYear),
cb.equal(personFrom.get(Person.BIRTHDATE_MM), birthdateToCriteriaMonth),
cb.isNull(personFrom.get(Person.BIRTHDATE_DD)));
filter = CriteriaBuilderHelper.and(
cb,
filter,
cb.or(yearPredicate, monthPredicate, dayPredicate, sameYearPartialMatchPredicate, sameYearMonthPartialMatchPredicate));
} else {
filter = CriteriaBuilderHelper.and(cb, filter, cb.or(yearPredicate, monthPredicate, dayPredicate));
}
}
return filter;
}
}
Loading

0 comments on commit 996f110

Please sign in to comment.