From 38d88e480a2dd7e1a408378f46d6e1f722a504f5 Mon Sep 17 00:00:00 2001 From: erickmob Date: Sun, 12 Jul 2020 19:02:17 -0300 Subject: [PATCH] main method refactored, covering more corner cases --- README.md | 10 +- .../erickmob/schedulingjob/Dto/JobDTO.java | 2 +- .../controller/JobController.java | 5 +- .../com/erickmob/schedulingjob/model/Job.java | 2 +- .../schedulingjob/service/JobService.java | 2 +- .../service/SchedulingService.java | 55 ++++++--- .../service/SchedulingServiceTest.java | 110 ++++++++++++++++-- 7 files changed, 150 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 2a37c02..77f8486 100644 --- a/README.md +++ b/README.md @@ -152,19 +152,19 @@ mvn test "inicioJanelaDeExecucao": "2019-11-10T09:00:00.483Z", "jobList": [ { - "dataMaximaDeDuracao": "2019-11-10T12:00:00.483Z", + "dataMaximaDeConclusao": "2019-11-10T12:00:00.483Z", "descricao": "Importação de arquivos de fundos", "id": 1, "tempoEstimado": 2 }, { - "dataMaximaDeDuracao": "2019-11-11T12:00:00.483Z", + "dataMaximaDeConclusao": "2019-11-11T12:00:00.483Z", "descricao": "Importação de dados da Base Legada", "id": 2, "tempoEstimado": 4 }, { - "dataMaximaDeDuracao": "2019-11-11T08:00:00.483Z", + "dataMaximaDeConclusao": "2019-11-11T08:00:00.483Z", "descricao": "Importação de dados de integração", "id": 3, "tempoEstimado": 6 @@ -196,12 +196,12 @@ Simply run on cli: LocalHost: ```zsh -curl -X POST "http://localhost:8080/sequenceJobs" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"fimJanelaDeExecucao\": \"2019-11-11T12:00:00.483Z\", \"inicioJanelaDeExecucao\": \"2019-11-10T09:00:00.483Z\", \"jobList\": [ { \"dataMaximaDeDuracao\": \"2019-11-10T12:00:00.483Z\", \"descricao\": \"Importação de arquivos de fundos\", \"id\": 1, \"tempoEstimado\": 2 }, { \"dataMaximaDeDuracao\": \"2019-11-11T12:00:00.483Z\", \"descricao\": \"Importação de dados da Base Legada\", \"id\": 2, \"tempoEstimado\": 4 }, { \"dataMaximaDeDuracao\": \"2019-11-11T08:00:00.483Z\", \"descricao\": \"Importação de dados de integração\", \"id\": 3, \"tempoEstimado\": 6 } ]}" +curl -X POST "http://localhost:8080/sequenceJobs" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"fimJanelaDeExecucao\": \"2019-11-11T12:00:00.483Z\", \"inicioJanelaDeExecucao\": \"2019-11-10T09:00:00.483Z\", \"jobList\": [ { \"dataMaximaDeConclusao\": \"2019-11-10T12:00:00.483Z\", \"descricao\": \"Importação de arquivos de fundos\", \"id\": 1, \"tempoEstimado\": 2 }, { \"dataMaximaDeConclusao\": \"2019-11-11T12:00:00.483Z\", \"descricao\": \"Importação de dados da Base Legada\", \"id\": 2, \"tempoEstimado\": 4 }, { \"dataMaximaDeConclusao\": \"2019-11-11T08:00:00.483Z\", \"descricao\": \"Importação de dados de integração\", \"id\": 3, \"tempoEstimado\": 6 } ]}" ``` Or Heroku: ```zsh -curl -X POST "https://scheduling-job-777.herokuapp.com/sequenceJobs" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"fimJanelaDeExecucao\": \"2019-11-11T12:00:00.483Z\", \"inicioJanelaDeExecucao\": \"2019-11-10T09:00:00.483Z\", \"jobList\": [ { \"dataMaximaDeDuracao\": \"2019-11-10T12:00:00.483Z\", \"descricao\": \"Importação de arquivos de fundos\", \"id\": 1, \"tempoEstimado\": 2 }, { \"dataMaximaDeDuracao\": \"2019-11-11T12:00:00.483Z\", \"descricao\": \"Importação de dados da Base Legada\", \"id\": 2, \"tempoEstimado\": 4 }, { \"dataMaximaDeDuracao\": \"2019-11-11T08:00:00.483Z\", \"descricao\": \"Importação de dados de integração\", \"id\": 3, \"tempoEstimado\": 6 } ]}" +curl -X POST "https://scheduling-job-777.herokuapp.com/sequenceJobs" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"fimJanelaDeExecucao\": \"2019-11-11T12:00:00.483Z\", \"inicioJanelaDeExecucao\": \"2019-11-10T09:00:00.483Z\", \"jobList\": [ { \"dataMaximaDeConclusao\": \"2019-11-10T12:00:00.483Z\", \"descricao\": \"Importação de arquivos de fundos\", \"id\": 1, \"tempoEstimado\": 2 }, { \"dataMaximaDeConclusao\": \"2019-11-11T12:00:00.483Z\", \"descricao\": \"Importação de dados da Base Legada\", \"id\": 2, \"tempoEstimado\": 4 }, { \"dataMaximaDeConclusao\": \"2019-11-11T08:00:00.483Z\", \"descricao\": \"Importação de dados de integração\", \"id\": 3, \"tempoEstimado\": 6 } ]}" ``` And see the output response: diff --git a/src/main/java/com/erickmob/schedulingjob/Dto/JobDTO.java b/src/main/java/com/erickmob/schedulingjob/Dto/JobDTO.java index 6c09e27..a6ff3fa 100644 --- a/src/main/java/com/erickmob/schedulingjob/Dto/JobDTO.java +++ b/src/main/java/com/erickmob/schedulingjob/Dto/JobDTO.java @@ -11,6 +11,6 @@ public class JobDTO { private long id; private String descricao; - private LocalDateTime dataMaximaDeDuracao; + private LocalDateTime dataMaximaDeConclusao; private long tempoEstimado; } diff --git a/src/main/java/com/erickmob/schedulingjob/controller/JobController.java b/src/main/java/com/erickmob/schedulingjob/controller/JobController.java index fc7da2c..705f3a5 100644 --- a/src/main/java/com/erickmob/schedulingjob/controller/JobController.java +++ b/src/main/java/com/erickmob/schedulingjob/controller/JobController.java @@ -33,8 +33,11 @@ public class JobController { public List sequenceJobs(@RequestBody JobsDTO jobsDTO) { log.info("Received request with body:"+jobsDTO.toString()); List result; + List jobsList = new ArrayList<>(); try{ - List jobsList = jobService.createFromJobListDto(jobsDTO.getJobList()); + if(!jobsDTO.getJobList().isEmpty()){ + jobsList = jobService.createFromJobListDto(jobsDTO.getJobList()); + } result = schedulingService.sortJobsForScheduling(jobsList, jobsDTO.getInicioJanelaDeExecucao(), jobsDTO.getFimJanelaDeExecucao()); return result; }catch (TimeWindowException e){ diff --git a/src/main/java/com/erickmob/schedulingjob/model/Job.java b/src/main/java/com/erickmob/schedulingjob/model/Job.java index 5bcca6d..b4addd8 100644 --- a/src/main/java/com/erickmob/schedulingjob/model/Job.java +++ b/src/main/java/com/erickmob/schedulingjob/model/Job.java @@ -11,6 +11,6 @@ public class Job { private long id; private String descricao; - private LocalDateTime dataMaximaDeDuracao; + private LocalDateTime dataMaximaDeConclusao; private Duration tempoEstimado; } diff --git a/src/main/java/com/erickmob/schedulingjob/service/JobService.java b/src/main/java/com/erickmob/schedulingjob/service/JobService.java index f3f35bc..63ddd14 100644 --- a/src/main/java/com/erickmob/schedulingjob/service/JobService.java +++ b/src/main/java/com/erickmob/schedulingjob/service/JobService.java @@ -19,7 +19,7 @@ public List createFromJobListDto(List jobDTOList){ new Job( jobDTO.getId(), jobDTO.getDescricao(), - jobDTO.getDataMaximaDeDuracao(), + jobDTO.getDataMaximaDeConclusao(), Duration.ofHours(jobDTO.getTempoEstimado()) ) ) diff --git a/src/main/java/com/erickmob/schedulingjob/service/SchedulingService.java b/src/main/java/com/erickmob/schedulingjob/service/SchedulingService.java index 3bed3e4..cb74288 100644 --- a/src/main/java/com/erickmob/schedulingjob/service/SchedulingService.java +++ b/src/main/java/com/erickmob/schedulingjob/service/SchedulingService.java @@ -67,47 +67,70 @@ List sortListByDateAndFilter(List jobsList, LocalDateTime inicioJanel return jobsList.stream() .filter(job -> job.getTempoEstimado().toHours() < maxDurationHour.toHours()) .filter(job -> conlusionInsideTimeWindow(job, inicioJanelaDeExecucao, fimJanelaDeExecucao)) + .filter(job -> maxEstimatedDateInsideTimeWindow(job, inicioJanelaDeExecucao)) .sorted( - Comparator.comparing(Job::getDataMaximaDeDuracao) + Comparator.comparing(Job::getDataMaximaDeConclusao) ) .collect(Collectors.toList()); } boolean conlusionInsideTimeWindow(Job job, LocalDateTime inicioJanelaDeExecucao, LocalDateTime fimJanelaDeExecucao) { - LocalDateTime estimateConcludion = executionTimeWindowStart.plusHours(job.getTempoEstimado().toHours()); - Duration duration = Duration.between(estimateConcludion, executionTimeWindowEnd); + LocalDateTime estimateConcludion = inicioJanelaDeExecucao.plusHours(job.getTempoEstimado().toHours()); + Duration duration = Duration.between(estimateConcludion, fimJanelaDeExecucao); return duration.toHours() > 0; } + boolean maxEstimatedDateInsideTimeWindow(Job job, LocalDateTime inicioJanelaDeExecucao) { + return job.getDataMaximaDeConclusao().isAfter(inicioJanelaDeExecucao); + } + private List createJobExecuteSequence(List jobsList, LocalDateTime inicioJanelaDeExecucao, LocalDateTime fimJanelaDeExecucao) { - ArrayList expectedList = new ArrayList<>(); - ArrayList longArray = new ArrayList<>(); - long totalHoursForArray = 0; + + ArrayList resultList = new ArrayList<>(); + ArrayList eightHoursArray = new ArrayList<>(); + + long countTotalHoursForArray = 0; + + LocalDateTime currentDateTime = inicioJanelaDeExecucao; + LocalDateTime estimateJobConclusionDate; for(Job job : jobsList){ - if(jobFitsInsideArray(totalHoursForArray, job)){ - longArray.add(job.getId()); - totalHoursForArray += job.getTempoEstimado().toHours(); + + estimateJobConclusionDate = currentDateTime.plusHours(job.getTempoEstimado().toHours()); + + if(estimateJobConclusionDate.isAfter(fimJanelaDeExecucao)){ + break; + } + if(estimateJobConclusionDate.isAfter(job.getDataMaximaDeConclusao())){ + break; + } + + if(jobFitsInsideEightHoursArray(countTotalHoursForArray, job)){ + eightHoursArray.add(job.getId()); + countTotalHoursForArray += job.getTempoEstimado().toHours(); }else{ - addArrayToExpectedList(expectedList, longArray); - longArray = new ArrayList<>(); - longArray.add(job.getId()); + addEightHourArrayToResultList(resultList, eightHoursArray); + eightHoursArray = new ArrayList<>(); + eightHoursArray.add(job.getId()); + countTotalHoursForArray = job.getTempoEstimado().toHours(); } + currentDateTime = currentDateTime.plusHours(job.getTempoEstimado().toHours()); + } - addArrayToExpectedList(expectedList, longArray); + addEightHourArrayToResultList(resultList, eightHoursArray); - return expectedList; + return resultList; } - private void addArrayToExpectedList(ArrayList expectedList, ArrayList longArray) { + private void addEightHourArrayToResultList(ArrayList expectedList, ArrayList longArray) { if (longArray.size() > 0) { expectedList.add(longArray); } } - private boolean jobFitsInsideArray(long totalHoursForArray, Job job) { + private boolean jobFitsInsideEightHoursArray(long totalHoursForArray, Job job) { return (totalHoursForArray + job.getTempoEstimado().toHours()) <= maxDurationHour.toHours(); } } diff --git a/src/test/java/com/erickmob/schedulingjob/service/SchedulingServiceTest.java b/src/test/java/com/erickmob/schedulingjob/service/SchedulingServiceTest.java index 3c9635e..e664687 100644 --- a/src/test/java/com/erickmob/schedulingjob/service/SchedulingServiceTest.java +++ b/src/test/java/com/erickmob/schedulingjob/service/SchedulingServiceTest.java @@ -151,9 +151,9 @@ void whenJobListSameDateThenSort() throws Exception { //when List result = schedulingService.sortListByDateAndFilter(jobsList, executionTimeWindowStart, executionTimeWindowEnd); assertEquals(1,result.get(0).getId()); - assertEquals(3,result.get(1).getId()); - assertEquals(4,result.get(2).getId()); - assertEquals(5,result.get(3).getId()); + assertEquals(4,result.get(1).getId()); + assertEquals(5,result.get(2).getId()); + assertEquals(3,result.get(3).getId()); assertEquals(2,result.get(4).getId()); } @@ -200,26 +200,114 @@ void givenTestCaseFromDocTest() throws TimeWindowException { } @Test - void givenListWithNoValidJob() throws TimeWindowException { + void givenTestCaseFromDocTestOnShortWindown() throws TimeWindowException { //given + jobsList = getJobsArrayMocked(); LocalDateTime jobDateTime = LocalDateTime.of(2019, Month.NOVEMBER, 10, - 12, + 20, 0, 0); - jobsList = new ArrayList(Arrays.asList( - new Job(4,"Importação de arquivos de fundos 2", jobDateTime, Duration.ofHours(27)) - )); + //when + List received = schedulingService.sortJobsForScheduling(jobsList, null, jobDateTime); + + //then + assertEquals("[[1, 3]]", received.toString()); + } + + + @Test + void givenJobWithEstimatedBeforeStartWindowTime() throws TimeWindowException { + //given + jobsList = getJobsArrayMocked(); + + LocalDateTime jobDateTime = LocalDateTime.of(2019, + Month.JANUARY, + 11, + 8, + 0, + 0); + + jobsList.add( + new Job( + 4, + "job com data max conclusao antes do inicio da janela", + jobDateTime, + Duration.ofHours(6) + ) + ); //when List received = schedulingService.sortJobsForScheduling(jobsList, null, null); //then - assertEquals("[]", received.toString()); + assertEquals("[[1, 3], [2]]", received.toString()); } + @Test + void givenMultipleJobsWithSameMaxEstimated() throws TimeWindowException { + //given + LocalDateTime dataMaximaDeConclusao = LocalDateTime.of( + 2019, + Month.NOVEMBER, + 10, + 19, + 0, + 0); + jobsList = new ArrayList<>(); + jobsList.add( + new Job( + 1, + "asd", + dataMaximaDeConclusao, + Duration.ofHours(6) + ) + ); + jobsList.add( + new Job( + 2, + "asd", + dataMaximaDeConclusao, + Duration.ofHours(6) + ) + ); + + jobsList.add( + new Job( + 3, + "asd", + dataMaximaDeConclusao, + Duration.ofHours(6) + ) + ); + + jobsList.add( + new Job( + 4, + "asd", + dataMaximaDeConclusao, + Duration.ofHours(6) + ) + ); + + jobsList.add( + new Job( + 5, + "asd", + dataMaximaDeConclusao, + Duration.ofHours(6) + ) + ); + + + //when + List received = schedulingService.sortJobsForScheduling(jobsList, null, null); + + //then + assertEquals("[[1]]", received.toString()); + } private List getJobsArrayMocked() { jobsList = new ArrayList(Arrays.asList( @@ -254,8 +342,8 @@ private Job getJob2Mocked() { private Job getJob3Mocked() { LocalDateTime jobDateTime = LocalDateTime.of(2019, Month.NOVEMBER, - 10, - 12, + 11, + 8, 0, 0); return new Job(3,"Importação de dados de integração", jobDateTime, Duration.ofHours(6));