Import of CSV-Data and data structure

This commit is contained in:
Matthias Engelien
2024-09-09 21:13:43 +02:00
parent 6985e0ea87
commit 97d86cd11a
18 changed files with 266 additions and 110 deletions

View File

@@ -5,18 +5,14 @@ import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
import org.springframework.stereotype.Component;
@Component
public class DataBaseConfiguration extends AbstractJdbcConfiguration {
@Override
protected List<?> userConverters() {
@@ -28,7 +24,7 @@ public class DataBaseConfiguration extends AbstractJdbcConfiguration {
@Override
public Long convert(Duration duration) {
return duration.toNanos();
return duration.toSeconds();
}
}
@@ -37,7 +33,7 @@ public class DataBaseConfiguration extends AbstractJdbcConfiguration {
@Override
public Duration convert(Long duration) {
return Duration.of(duration, ChronoUnit.NANOS);
return Duration.of(duration, ChronoUnit.SECONDS);
}
}

View File

@@ -1,29 +1,22 @@
package de.etecture.ga.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.etecture.ga.model.Garage;
import de.etecture.ga.service.GarageImportService;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@AllArgsConstructor
public class Setup {
@Autowired
private GarageImportService importService;
private final GarageImportService importService;
@PostConstruct
private void setupData() {
List<Garage> garages = importService.importGarageData();
// Daten in DB übertragen
private void importData() {
importService.importGarageData();
}
}

View File

@@ -4,20 +4,29 @@ import java.time.Duration;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.relational.core.mapping.Column;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(fluent = true, chain = true)
public class Appointment {
@Id
private Long id;
@Column("GARAGE_ID")
private AggregateReference<Garage, Long> garageId;
private String serviceCode;
private String serviceName;
private Date appointmentTime;
private Duration duration;
private Integer slot = 1;
private Duration duration = Duration.ZERO;
}

View File

@@ -1,48 +1,63 @@
package de.etecture.ga.model;
import java.util.ArrayList;
import java.util.List;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.relational.core.mapping.MappedCollection;
import org.springframework.util.Assert;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(fluent = true, chain = true)
public class Garage {
@Id
private Long id;
private String code;
private String name;
private List<Appointment> appointments;
private Integer maxAppointments = 1;
private Set<GarageServices> garageServices;
@MappedCollection(idColumn = "GARAGE_ID")
private Set<Appointment> appointments = new HashSet<>();
@MappedCollection(idColumn = "GARAGE_ID")
private Set<GarageServices> garageServices = new HashSet<>();
public Garage addAppointment(Appointment appointment) {
if (this.appointments == null) {
this.appointments = new ArrayList<>();
boolean added = this.appointments.add(appointment);
if(!added) {
appointment.slot(appointment.slot() + 1);
this.addAppointment(appointment);
}
this.appointments.add(appointment);
return this;
}
public void addService(MDService service) {
garageServices.add(createGarageService(service));
public Garage addService(MDService service) {
garageServices.add(createGarageService(service, null));
return this;
}
private GarageServices createGarageService(MDService service) {
public void addService(MDService service, Duration duration) {
garageServices.add(createGarageService(service, duration));
}
private GarageServices createGarageService(MDService service, Duration duration) {
Assert.notNull(service, "Service must not be null");
Assert.notNull(service.getId(), "Service id, must not be null");
Assert.notNull(service.id(), "Service id, must not be null");
GarageServices garageService = new GarageServices();
garageService.setMdService(service.getId());
duration = duration == null ? service.duration() : duration;
return garageService;
return new GarageServices().garageId(AggregateReference.to(this.id()))
.serviceId(AggregateReference.to(service.id())).duration(duration);
}
}

View File

@@ -1,9 +1,23 @@
package de.etecture.ga.model;
import java.time.Duration;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.relational.core.mapping.Column;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(fluent = true, chain = true)
public class GarageServices {
private Long mdService;
@Column("GARAGE_ID")
private AggregateReference<Garage, Long> garageId;
@Column("SERVICE_ID")
private AggregateReference<MDService, Long> serviceId;
private Duration duration;
}

View File

@@ -5,8 +5,10 @@ import java.time.Duration;
import org.springframework.data.annotation.Id;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(fluent = true, chain = true)
public class MDService {
@Id
@@ -16,6 +18,6 @@ public class MDService {
private String name;
private Duration duration;
private Duration duration = Duration.ZERO;
}

View File

@@ -0,0 +1,9 @@
package de.etecture.ga.repository;
import org.springframework.data.repository.CrudRepository;
import de.etecture.ga.model.Appointment;
public interface AppointmentRepository extends CrudRepository<Appointment, Long> {
}

View File

@@ -0,0 +1,12 @@
package de.etecture.ga.repository;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import de.etecture.ga.model.Garage;
public interface GarageRepository extends CrudRepository<Garage, Long> {
public Optional<Garage> findByCode(String code);
}

View File

@@ -0,0 +1,20 @@
package de.etecture.ga.repository;
import java.util.Optional;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import de.etecture.ga.model.GarageServices;
import de.etecture.ga.model.MDService;
public interface GarageServiceRepository extends CrudRepository<GarageServices, Long> {
@Query("select s.ID, s.CODE, s.NAME, ifNull(gs.DURATION, s.DURATION) as DURATION "
+ "from GARAGE_SERVICES gs "
+ "join MD_SERVICE s on s.ID = gs.SERVICE_ID "
+ "join GARAGE g on g.ID = gs.GARAGE_ID "
+ "where s.CODE = :serviceCode and g.CODE = :garageCode")
public Optional<MDService> findByServiceCodeAndGarage(String serviceCode, String garageCode);
}

View File

@@ -0,0 +1,12 @@
package de.etecture.ga.repository;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import de.etecture.ga.model.MDService;
public interface MDServiceRepository extends CrudRepository<MDService, Long> {
public Optional<MDService> findByCode(String code);
}

View File

@@ -5,7 +5,6 @@ import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -13,6 +12,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.MappingIterator;
@@ -21,42 +21,54 @@ import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import de.etecture.ga.model.Appointment;
import de.etecture.ga.model.Garage;
import de.etecture.ga.model.MDService;
import de.etecture.ga.repository.AppointmentRepository;
import de.etecture.ga.repository.GarageRepository;
import de.etecture.ga.repository.GarageServiceRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@AllArgsConstructor
public class GarageImportService {
private static final String IMPORT_FOLDER = "import";
public List<Garage> importGarageData() {
private final GarageRepository garageRepository;
List<Garage> importData = new ArrayList<>();
private final GarageServiceRepository garageServiceRepository;
private final AppointmentRepository appointmentRepository;
public void importGarageData() {
try (Stream<Path> files = Files
.list(Paths.get(getClass().getClassLoader().getResource(IMPORT_FOLDER).toURI()))) {
files.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".csv"))
.map(this::loadGarageData).<Garage>mapMulti(Optional::ifPresent).forEach(importData::add);
.forEach(this::loadGarageData);
} catch (IOException | URISyntaxException e) {
log.error("Can't read file", e);
}
return importData;
}
public Optional<Garage> loadGarageData(final Path file) {
Garage garage = new Garage();
garage.setName(getGarageNameFromFile(file));
Optional<Garage> garage = garageRepository.findByCode(getGarageNameFromFile(file));
List<CSVData> appointments = loadObjectsFromFile(file);
appointments.stream().map(this::fromCSVData).filter(Objects::nonNull)
.forEach(garage::addAppointment);
if (garage.isPresent()) {
List<CSVData> appointmentData = loadObjectsFromFile(file);
return Optional.of(garage);
appointmentData.stream().filter(Objects::nonNull)
.forEach(data -> addAppointmentToGarage(data, garage.get()));
// bestehende Termine speichern
garage.get().appointments().forEach(appointmentRepository::save);
}
return garage;
}
private String getGarageNameFromFile(final Path fileName) {
@@ -87,16 +99,18 @@ public class GarageImportService {
}
}
private Appointment fromCSVData(CSVData data) {
private void addAppointmentToGarage(CSVData data, Garage garage) {
if (data == null)
return null;
Optional<MDService> garageService = garageServiceRepository.findByServiceCodeAndGarage(data.SERVICE,
garage.code());
Appointment appointment = new Appointment();
appointment.setAppointmentTime(data.APP_DATE);
appointment.setServiceCode(data.SERVICE);
garageService.map(service -> getAppointmentForService(service, data.APP_DATE, garage.id()))
.ifPresent(garage::addAppointment);
}
return appointment;
private Appointment getAppointmentForService(MDService service, Date date, Long garageId) {
return new Appointment().appointmentTime(date).serviceCode(service.code()).serviceName(service.name())
.duration(service.duration()).garageId(AggregateReference.to(garageId));
}
private record CSVData(Date APP_DATE, String SERVICE) {

View File

@@ -0,0 +1,39 @@
package de.etecture.ga.service;
import java.security.InvalidParameterException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import de.etecture.ga.model.MDService;
import de.etecture.ga.repository.MDServiceRepository;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class MDServiceService {
private final MDServiceRepository serviceRepository;
public MDService storeMDService(String serviceCode) {
if (StringUtils.isBlank(serviceCode))
throw new InvalidParameterException("serviceCode should not been empty");
MDService service = serviceRepository.findByCode(serviceCode).orElse(new MDService().code(serviceCode));
return serviceRepository.save(service);
}
public MDService storeMDService(MDService serviceToSafe) {
Assert.notNull(serviceToSafe, "Service must not be null");
Assert.notNull(serviceToSafe.code(), "Service code must not be null");
Assert.isTrue(serviceToSafe.duration().isPositive(), "Service duration must must be bigger then 0");
MDService service = serviceRepository.findByCode(serviceToSafe.code()).orElse(serviceToSafe);
return serviceRepository.save(service);
}
}