Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sort & Filter attribute change logs [DHIS2-18475] #19331

Merged
merged 12 commits into from
Dec 3, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.hisp.dhis.changelog.ChangeLogType;
import org.hisp.dhis.common.IdentifiableObject;
import org.hisp.dhis.common.UID;
Expand Down Expand Up @@ -173,6 +174,11 @@ public Set<String> getOrderableFields() {
return hibernateTrackedEntityChangeLogStore.getOrderableFields();
}

@Override
public Set<Pair<String, Class<?>>> getFilterableFields() {
return hibernateTrackedEntityChangeLogStore.getFilterableFields();
}

private Program validateProgram(String programUid) throws NotFoundException {
Program program = programService.getProgram(programUid);
if (program == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.Pair;
import org.hibernate.Session;
import org.hisp.dhis.changelog.ChangeLogType;
import org.hisp.dhis.common.QueryFilter;
import org.hisp.dhis.common.SortDirection;
import org.hisp.dhis.common.UID;
import org.hisp.dhis.program.UserInfoSnapshot;
Expand All @@ -53,11 +56,22 @@
@Repository("org.hisp.dhis.tracker.export.trackedentity.HibernateTrackedEntityChangeLogStore")
public class HibernateTrackedEntityChangeLogStore {
private static final String COLUMN_CHANGELOG_CREATED = "tecl.created";
private static final String COLUMN_CHANGELOG_USER = "tecl.createdByUsername";
private static final String COLUMN_CHANGELOG_DATA_ELEMENT = "tea.uid";

private static final String DEFAULT_ORDER =
COLUMN_CHANGELOG_CREATED + " " + SortDirection.DESC.getValue();

private static final Map<String, String> ORDERABLE_FIELDS =
Map.ofEntries(entry("createdAt", COLUMN_CHANGELOG_CREATED));
Map.ofEntries(
entry("createdAt", COLUMN_CHANGELOG_CREATED),
entry("username", COLUMN_CHANGELOG_USER),
entry("attribute", COLUMN_CHANGELOG_DATA_ELEMENT));

private static final Map<Pair<String, Class<?>>, String> FILTERABLE_FIELDS =
Map.ofEntries(
entry(Pair.of("username", String.class), COLUMN_CHANGELOG_USER),
entry(Pair.of("attribute", UID.class), COLUMN_CHANGELOG_DATA_ELEMENT));

private final EntityManager entityManager;
private final Session session;
Expand All @@ -78,6 +92,8 @@ public Page<TrackedEntityChangeLog> getTrackedEntityChangeLogs(
@Nonnull TrackedEntityChangeLogOperationParams operationParams,
@Nonnull PageParams pageParams) {

Pair<String, QueryFilter> filter = operationParams.getFilter();
muilpp marked this conversation as resolved.
Show resolved Hide resolved

String hql =
"""
select tecl.trackedEntity,
Expand Down Expand Up @@ -119,6 +135,17 @@ and tea.uid in (:attributes)
""";
}

if (filter != null) {
String filterField =
FILTERABLE_FIELDS.entrySet().stream()
.filter(entry -> entry.getKey().getLeft().equals(filter.getKey()))
.findFirst()
.map(Entry::getValue)
.get();

hql += String.format(" and %s = :filterValue ", filterField);
}

hql += String.format("order by %s".formatted(sortExpressions(operationParams.getOrder())));

Query query = entityManager.createQuery(hql);
Expand All @@ -136,6 +163,10 @@ and tea.uid in (:attributes)
query.setFirstResult((pageParams.getPage() - 1) * pageParams.getPageSize());
query.setMaxResults(pageParams.getPageSize() + 1);

if (filter != null) {
query.setParameter("filterValue", filter.getValue().getFilter());
}

List<Object[]> results = query.getResultList();
List<TrackedEntityChangeLog> trackedEntityChangeLogs =
results.stream()
Expand Down Expand Up @@ -188,6 +219,10 @@ public Set<String> getOrderableFields() {
return ORDERABLE_FIELDS.keySet();
}

public Set<Pair<String, Class<?>>> getFilterableFields() {
return FILTERABLE_FIELDS.keySet();
}

private static String sortExpressions(Order order) {
if (order == null) {
return DEFAULT_ORDER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.apache.commons.lang3.tuple.Pair;
import org.hisp.dhis.common.QueryFilter;
import org.hisp.dhis.common.SortDirection;
import org.hisp.dhis.tracker.export.Order;

Expand All @@ -39,6 +41,7 @@
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class TrackedEntityChangeLogOperationParams {
private Order order;
private Pair<String, QueryFilter> filter;

public static class TrackedEntityChangeLogOperationParamsBuilder {

Expand All @@ -50,10 +53,23 @@ private TrackedEntityChangeLogOperationParamsBuilder order(Order order) {
return this;
}

muilpp marked this conversation as resolved.
Show resolved Hide resolved
// Do not remove this unused method. This hides the filter field from the builder which Lombok
// does not support. The repeated filter field and private filter method prevent access to
// filter via the builder.
// Filter should be added via the filterBy builder methods.
private TrackedEntityChangeLogOperationParamsBuilder filter(Pair<String, QueryFilter> filter) {
Dismissed Show dismissed Hide dismissed
return this;
}

public TrackedEntityChangeLogOperationParamsBuilder orderBy(
String field, SortDirection direction) {
this.order = new Order(field, direction);
return this;
}

public TrackedEntityChangeLogOperationParamsBuilder filterBy(String field, QueryFilter filter) {
this.filter = Pair.of(field, filter);
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.Pair;
import org.hisp.dhis.changelog.ChangeLogType;
import org.hisp.dhis.common.UID;
import org.hisp.dhis.feedback.BadRequestException;
Expand Down Expand Up @@ -86,4 +87,13 @@ Page<TrackedEntityChangeLog> getTrackedEntityChangeLog(
* PageParams)}.
*/
Set<String> getOrderableFields();

/**
* Fields the {@link #getTrackedEntityChangeLog(UID, UID, TrackedEntityChangeLogOperationParams,
* PageParams)} can filter attribute change logs by. Filtering by fields other than these, is
* considered a programmer error. Validation of user provided field names should occur before
* calling {@link #getTrackedEntityChangeLog(UID, UID, TrackedEntityChangeLogOperationParams,
* PageParams)}.
*/
Set<Pair<String, Class<?>>> getFilterableFields();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2000,6 +2000,21 @@
"id": "ja8NY4PW7Xm"
},
"valueType": "TEXT"
},
{
"displayInList": true,
"displayName": "Person Given name",
"displayShortName": "null Given name",
"id": "EIVt4l5vIOa",
"name": "Person Given name",
"searchable": true,
"trackedEntityAttribute": {
"id": "toUpdate000"
},
"trackedEntityType": {
"id": "ja8NY4PW7Xm"
},
"valueType": "TEXT"
}
],
"user": {
Expand Down
Loading
Loading