Skip to content

Commit

Permalink
avniproject/avni-webapp#1225 - Introduced AddressLevelTypes collectio…
Browse files Browse the repository at this point in the history
…n class. Moved logic support registration location types to Subject Type. Changed some exceptions to RuntimeException.
  • Loading branch information
petmongrels committed May 14, 2024
1 parent 65c3c7c commit 13a6b5e
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.avni.server.dao;

import org.avni.server.domain.AddressLevelType;
import org.avni.server.domain.AddressLevelTypes;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
Expand Down Expand Up @@ -36,4 +37,8 @@ public interface AddressLevelTypeRepository extends ReferenceDataRepository<Addr
AddressLevelType findByParent(AddressLevelType parent);

List<AddressLevelType> findByIsVoidedFalseAndNameIgnoreCaseContains(String name);

default AddressLevelTypes getAllAddressLevelTypes() {
return new AddressLevelTypes(this.findAllByIsVoidedFalse());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,12 @@ public Boolean isVoidable() {
public interface AddressLevelTypeProjection extends BaseProjection {
String getName();
}

@Override
public String toString() {
return "{" +
"name='" + name + '\'' +
", level=" + level +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.avni.server.domain;

import java.util.*;

public class AddressLevelTypes extends ArrayList<AddressLevelType> {
public AddressLevelTypes(AddressLevelType ... addressLevelTypes) {
super(Arrays.asList(addressLevelTypes));
}

public AddressLevelTypes(List<AddressLevelType> addressLevelTypes) {
super(addressLevelTypes);
}

public AddressLevelTypes getLowToHigh() {
AddressLevelTypes temp = new AddressLevelTypes(this);
temp.sort(Comparator.comparingDouble(AddressLevelType::getLevel));
return temp;
}

public AddressLevelTypes getHighToLow() {
AddressLevelTypes temp = this.getLowToHigh();
Collections.reverse(temp);
return temp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@


import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.type.TypeReference;
import org.avni.server.application.KeyType;
import org.avni.server.application.OrganisationConfigSettingKey;
import org.avni.server.domain.framework.BaseJsonObject;
import org.avni.server.util.ObjectMapperSingleton;
import org.avni.server.web.request.webapp.SubjectTypeSetting;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Type;

Expand Down Expand Up @@ -71,6 +75,15 @@ public Boolean isFeatureEnabled(String feature) {
return (Boolean) getSettings().getOrDefault(feature, false);
}

@JsonIgnore
public List<SubjectTypeSetting> getCustomRegistrationLocations() {
return ObjectMapperSingleton.getObjectMapper().convertValue(this.getSettings().getOrDefault(KeyType.customRegistrationLocations.toString(), Collections.EMPTY_LIST), new TypeReference<List<SubjectTypeSetting>>() {});
}

public SubjectTypeSetting getRegistrationSetting(SubjectType subjectType) {
return this.getCustomRegistrationLocations().stream().filter(subjectTypeSetting -> subjectTypeSetting.getSubjectTypeUUID().equals(subjectType.getUuid())).findFirst().orElse(null);
}

public class Settings {
private final JsonObject settings;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public List<GroupRoleContract> getGroupRolesContract() {
.collect(Collectors.toList());
}

// Used from projections
@JsonIgnore
public List<String> getMemberSubjectUUIDs() {
return isGroup() ? groupRoles.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public Step importStep(FlatFileItemReader<Row> csvFileItemReader,
.writer(csvFileItemWriter)
.faultTolerant()
.skip(Exception.class)
.skip(RuntimeException.class)
.noSkip(FileNotFoundException.class)
.noSkip(FlatFileParseException.class)
.noSkip(FlatFileFormatException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.avni.server.dao.LocationRepository;
import org.avni.server.domain.AddressLevel;
import org.avni.server.domain.AddressLevelType;
import org.avni.server.domain.AddressLevelTypes;
import org.avni.server.importer.batch.model.Row;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
Expand All @@ -13,49 +14,48 @@

@Service
public class AddressLevelCreator {

private LocationRepository locationRepository;
private final LocationRepository locationRepository;

@Autowired
public AddressLevelCreator(LocationRepository locationRepository) {
this.locationRepository = locationRepository;
}

public AddressLevel findAddressLevel(Row row,
List<AddressLevelType> locationTypes) throws Exception {
AddressLevelType lowestAddressLevelType = locationTypes.get(locationTypes.size() - 1);

String lowestInputAddressLevel = row.get(lowestAddressLevelType.getName());
if (lowestInputAddressLevel == null) {
throw new Exception(String.format("Missing '%s'", lowestAddressLevelType.getName()));
}

List<AddressLevel> matchingAddressLevels = locationRepository.findByTitleAndType(lowestInputAddressLevel, lowestAddressLevelType, PageRequest.of(0, 2));
AddressLevelTypes addressLevelTypes) throws Exception {
AddressLevelTypes orderedLocationTypes = addressLevelTypes.getLowToHigh();
AddressLevelType firstMatch = orderedLocationTypes.stream().filter(addressLevelType -> row.get(addressLevelType.getName()) != null)
.findFirst()
.orElseThrow(() -> new RuntimeException("No matching location types found. If subject type has registration locations then only those will be used for matching."));

String title = row.get(firstMatch.getName());
List<AddressLevel> matchingAddressLevels = locationRepository.findByTitleAndType(title, firstMatch, PageRequest.of(0, 2));
switch (matchingAddressLevels.size()) {
case 0:
throw new Exception(("Address not found: " + lowestInputAddressLevel));
throw new RuntimeException(("Address not found: " + title));
case 1:
return matchingAddressLevels.get(0);
default:
return getAddressLevelByLineage(row, locationTypes);
return getAddressLevelByLineage(row, addressLevelTypes);
}
}

private AddressLevel getAddressLevelByLineage(Row row,
List<AddressLevelType> locationTypes) throws Exception {
AddressLevelTypes locationTypes) {
AddressLevelTypes highToLow = locationTypes.getHighToLow();
List<String> inputLocations = new ArrayList<>();
for (AddressLevelType addressLevelType : locationTypes) {
for (AddressLevelType addressLevelType : highToLow) {
String _location = row.get(addressLevelType.getName());
if (_location != null)
inputLocations.add(_location);
}

if (inputLocations.size() == 0)
throw new Exception("Invalid address");
if (inputLocations.isEmpty())
throw new RuntimeException("No locations matching their location types found.");

String lineage = String.join(", ", inputLocations);

return locationRepository.findByTitleLineageIgnoreCase(lineage)
.orElseThrow(() -> new Exception("'Address' not found: " + lineage));
.orElseThrow(() -> new RuntimeException("'Address' not found: " + lineage));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.avni.server.service.OrganisationConfigService;

public abstract class EntityWriter {
private final OrganisationConfigService organisationConfigService;
protected final OrganisationConfigService organisationConfigService;

protected EntityWriter(OrganisationConfigService organisationConfigService) {
this.organisationConfigService = organisationConfigService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;


@Component
public class SubjectWriter extends EntityWriter implements ItemWriter<Row>, Serializable {
private final AddressLevelTypeRepository addressLevelTypeRepository;
private final LocationRepository locationRepository;
private final IndividualRepository individualRepository;
private final GenderRepository genderRepository;
private final SubjectTypeCreator subjectTypeCreator;
Expand All @@ -45,8 +42,9 @@ public class SubjectWriter extends EntityWriter implements ItemWriter<Row>, Seri
private final IndividualService individualService;
private final S3Service s3Service;
private final EntityApprovalStatusWriter entityApprovalStatusWriter;
private AddressLevelCreator addressLevelCreator;
private final AddressLevelCreator addressLevelCreator;
private final SubjectMigrationService subjectMigrationService;
private final SubjectTypeService subjectTypeService;

private static final Logger logger = LoggerFactory.getLogger(SubjectWriter.class);

Expand All @@ -64,10 +62,8 @@ public SubjectWriter(AddressLevelTypeRepository addressLevelTypeRepository,
ObservationCreator observationCreator, IndividualService individualService, EntityApprovalStatusWriter entityApprovalStatusWriter,
S3Service s3Service,
OrganisationConfigService organisationConfigService,
AddressLevelCreator addressLevelCreator, SubjectMigrationService subjectMigrationService) {
AddressLevelCreator addressLevelCreator, SubjectMigrationService subjectMigrationService, SubjectTypeService subjectTypeService) {
super(organisationConfigService);
this.addressLevelTypeRepository = addressLevelTypeRepository;
this.locationRepository = locationRepository;
this.individualRepository = individualRepository;
this.genderRepository = genderRepository;
this.subjectTypeCreator = subjectTypeCreator;
Expand All @@ -81,6 +77,7 @@ public SubjectWriter(AddressLevelTypeRepository addressLevelTypeRepository,
this.entityApprovalStatusWriter = entityApprovalStatusWriter;
this.addressLevelCreator = addressLevelCreator;
this.subjectMigrationService = subjectMigrationService;
this.subjectTypeService = subjectTypeService;
this.locationCreator = new LocationCreator();
this.s3Service = s3Service;
}
Expand All @@ -92,9 +89,6 @@ public void write(List<? extends Row> rows) throws Exception {

private void write(Row row) throws Exception {
try {
List<AddressLevelType> locationTypes = addressLevelTypeRepository.findAllByIsVoidedFalse();
locationTypes.sort(Comparator.comparingDouble(AddressLevelType::getLevel).reversed());

Individual individual = getOrCreateIndividual(row);
AddressLevel oldAddressLevel = individual.getAddressLevel();
ObservationCollection oldObservations = individual.getObservations();
Expand All @@ -111,7 +105,10 @@ private void write(Row row) throws Exception {
individual.setDateOfBirthVerified(row.getBool(SubjectHeaders.dobVerified));
setRegistrationDate(individual, row, allErrorMsgs);
individual.setRegistrationLocation(locationCreator.getLocation(row, SubjectHeaders.registrationLocation, allErrorMsgs));
individual.setAddressLevel(addressLevelCreator.findAddressLevel(row, locationTypes));

AddressLevelTypes registrationLocationTypes = subjectTypeService.getRegistrableLocationTypes(subjectType);
individual.setAddressLevel(addressLevelCreator.findAddressLevel(row, registrationLocationTypes));

if (individual.getSubjectType().getType().equals(Subject.Person)) setGender(individual, row);
FormMapping formMapping = formMappingRepository.getRegistrationFormMapping(subjectType);
individual.setVoided(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,13 @@ public OrganisationConfig updateOrganisationConfig(OrganisationConfigRequest req
}

public Object getSettingsByKey(String key) {
OrganisationConfig currentOrganisationConfig = this.getCurrentOrganisationConfig();
return currentOrganisationConfig.getSettings().getOrDefault(key, Collections.EMPTY_LIST);
}

public OrganisationConfig getCurrentOrganisationConfig() {
Long organisationId = UserContextHolder.getUserContext().getOrganisationId();
OrganisationConfig organisationConfig = organisationConfigRepository.findByOrganisationId(organisationId);
return organisationConfig.getSettings().getOrDefault(key, Collections.EMPTY_LIST);
return organisationConfigRepository.findByOrganisationId(organisationId);
}

public void saveCustomRegistrationLocations(List<String> locationTypeUUIDs, SubjectType subjectType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.avni.server.application.Subject;
import org.avni.server.application.SubjectTypeSettingKey;
import org.avni.server.dao.AddressLevelTypeRepository;
import org.avni.server.dao.AvniJobRepository;
import org.avni.server.dao.OperationalSubjectTypeRepository;
import org.avni.server.dao.SubjectTypeRepository;
Expand All @@ -10,6 +11,7 @@
import org.avni.server.web.request.OperationalSubjectTypeContract;
import org.avni.server.web.request.SubjectTypeContract;
import org.avni.server.web.request.syncAttribute.UserSyncAttributeAssignmentRequest;
import org.avni.server.web.request.webapp.SubjectTypeSetting;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -34,25 +36,30 @@ public class SubjectTypeService implements NonScopeAwareService {

private final Logger logger;
private final OperationalSubjectTypeRepository operationalSubjectTypeRepository;
private SubjectTypeRepository subjectTypeRepository;
private Job syncAttributesJob;
private JobLauncher syncAttributesJobLauncher;
private AvniJobRepository avniJobRepository;
private final SubjectTypeRepository subjectTypeRepository;
private final Job syncAttributesJob;
private final JobLauncher syncAttributesJobLauncher;
private final AvniJobRepository avniJobRepository;
private final ConceptService conceptService;
private final OrganisationConfigService organisationConfigService;
private final AddressLevelTypeRepository addressLevelTypeRepository;

@Autowired
public SubjectTypeService(SubjectTypeRepository subjectTypeRepository,
OperationalSubjectTypeRepository operationalSubjectTypeRepository,
Job syncAttributesJob,
JobLauncher syncAttributesJobLauncher,
AvniJobRepository avniJobRepository,
ConceptService conceptService) {
ConceptService conceptService, OrganisationConfigService organisationConfigService,
AddressLevelTypeRepository addressLevelTypeRepository) {
this.subjectTypeRepository = subjectTypeRepository;
this.operationalSubjectTypeRepository = operationalSubjectTypeRepository;
this.syncAttributesJob = syncAttributesJob;
this.syncAttributesJobLauncher = syncAttributesJobLauncher;
this.avniJobRepository = avniJobRepository;
this.conceptService = conceptService;
this.organisationConfigService = organisationConfigService;
this.addressLevelTypeRepository = addressLevelTypeRepository;
logger = LoggerFactory.getLogger(this.getClass());
}

Expand Down Expand Up @@ -205,4 +212,19 @@ public JsonObject getDefaultSettings() {
defaultSettings.put(String.valueOf(SubjectTypeSettingKey.displayRegistrationDetails), true);
return defaultSettings;
}

public AddressLevelTypes getRegistrableLocationTypes(SubjectType subjectType) {
OrganisationConfig organisationConfig = this.organisationConfigService.getCurrentOrganisationConfig();
SubjectTypeSetting registrationSetting = organisationConfig.getRegistrationSetting(subjectType);
AddressLevelTypes locationTypes = addressLevelTypeRepository.getAllAddressLevelTypes();
if (locationTypes.isEmpty()) {
throw new RuntimeException("No address level types found");
}

if (registrationSetting == null) {
return new AddressLevelTypes(locationTypes);
} else {
return registrationSetting.getAddressLevelTypes(locationTypes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ public Page<IndividualWebProjection> search(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "subjectTypeUUID", required = false) String subjectTypeUUID,
Pageable pageable) {
IndividualRepository repo = this.individualRepository;
IndividualRepository repo = this.individualRepository;
return repo.findAll(
where(repo.getFilterSpecForName(name))
.and(repo.getFilterSpecForSubjectTypeId(subjectTypeUUID))
.and(repo.getFilterSpecForVoid(false))
, pageable)
.map(t -> projectionFactory.createProjection(IndividualWebProjection.class, t));
where(repo.getFilterSpecForName(name))
.and(repo.getFilterSpecForSubjectTypeId(subjectTypeUUID))
.and(repo.getFilterSpecForVoid(false))
, pageable)
.map(t -> projectionFactory.createProjection(IndividualWebProjection.class, t));
}

@PostMapping(value = "/web/searchAPI/v2")
Expand Down
Loading

0 comments on commit 13a6b5e

Please sign in to comment.