Browse Source

first working commit

tripeur 4 years ago
parent
commit
02d532a813
24 changed files with 421 additions and 152 deletions
  1. 2 1
      bdlgplanner.code-workspace
  2. 62 62
      pom.xml
  3. 18 12
      src/main/java/fr/jaquin/bdlg/planner/PlanningController.java
  4. 13 1
      src/main/java/fr/jaquin/bdlg/planner/domain/Assignement.java
  5. 20 0
      src/main/java/fr/jaquin/bdlg/planner/domain/AssignementPair.java
  6. 12 3
      src/main/java/fr/jaquin/bdlg/planner/domain/MealAssignement.java
  7. 23 1
      src/main/java/fr/jaquin/bdlg/planner/domain/MealSlot.java
  8. 14 9
      src/main/java/fr/jaquin/bdlg/planner/domain/Planning.java
  9. 114 39
      src/main/java/fr/jaquin/bdlg/planner/domain/PlanningInput.java
  10. 51 0
      src/main/java/fr/jaquin/bdlg/planner/domain/PlanningSolution.java
  11. 2 9
      src/main/java/fr/jaquin/bdlg/planner/domain/Timeslot.java
  12. 11 6
      src/main/java/fr/jaquin/bdlg/planner/domain/TimeslotRaw.java
  13. 8 7
      src/main/java/fr/jaquin/bdlg/planner/solver/PlanningConstraintProvider.java
  14. 15 2
      src/main/resources/application.properties
  15. 0 0
      src/main/resources/static/css/app.49ff1949.css
  16. BIN
      src/main/resources/static/favicon.ico
  17. 0 0
      src/main/resources/static/img/bdlg.min.ea622d66.svg
  18. 1 0
      src/main/resources/static/index.html
  19. 0 0
      src/main/resources/static/js/app.7763bc88.js
  20. 0 0
      src/main/resources/static/js/app.7763bc88.js.map
  21. 0 0
      src/main/resources/static/js/app.e1837404.js
  22. 0 0
      src/main/resources/static/js/app.e1837404.js.map
  23. 55 0
      src/main/resources/static/js/chunk-vendors.4d096b3a.js
  24. 0 0
      src/main/resources/static/js/chunk-vendors.4d096b3a.js.map

+ 2 - 1
bdlgplanner.code-workspace

@@ -5,6 +5,7 @@
 		}
 	],
 	"settings": {
-		"editor.tabSize": 2
+    "editor.tabSize": 2,
+    "java.configuration.updateBuildConfiguration": "automatic"
 	}
 }

+ 62 - 62
pom.xml

@@ -1,69 +1,69 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>org.springframework.boot</groupId>
-		<artifactId>spring-boot-starter-parent</artifactId>
-		<version>2.3.1.RELEASE</version>
-		<relativePath/> <!-- lookup parent from repository -->
-	</parent>
-	<groupId>fr.jaquin</groupId>
-	<artifactId>bdlg.planner</artifactId>
-	<version>0.0.1-SNAPSHOT</version>
-	<packaging>war</packaging>
-	<name>bdlg.planner</name>
-	<description>Plannification automatique des créneaux bénévoles pour </description>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.springframework.boot</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.4.5</version>
+    <relativePath /> <!-- lookup parent from repository -->
+  </parent>
+  <groupId>fr.jaquin</groupId>
+  <artifactId>bdlg.planner</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>war</packaging>
+  <name>bdlg.planner</name>
+  <description>Plannification automatique des créneaux bénévoles pour </description>
 
-	<properties>
-		<java.version>14</java.version>
-	</properties>
+  <properties>
+    <java.version>14</java.version>
+  </properties>
 
-	<dependencies>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web</artifactId>
-		</dependency>
-        <dependency>
-            <groupId>org.optaplanner</groupId>
-            <artifactId>optaplanner-spring-boot-starter</artifactId>
-        </dependency>
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.optaplanner</groupId>
+      <artifactId>optaplanner-spring-boot-starter</artifactId>
+    </dependency>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-tomcat</artifactId>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-test</artifactId>
-			<scope>test</scope>
-			<exclusions>
-				<exclusion>
-					<groupId>org.junit.vintage</groupId>
-					<artifactId>junit-vintage-engine</artifactId>
-				</exclusion>
-			</exclusions>
-		</dependency>
-	</dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-tomcat</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.junit.vintage</groupId>
+          <artifactId>junit-vintage-engine</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
 
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.optaplanner</groupId>
-                <artifactId>optaplanner-spring-boot-starter</artifactId>
-                <version>7.43.1.Final</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-	
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-maven-plugin</artifactId>
-			</plugin>
-		</plugins>
-	</build>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.optaplanner</groupId>
+        <artifactId>optaplanner-spring-boot-starter</artifactId>
+        <version>8.6.0.Final</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
 
 </project>

+ 18 - 12
src/main/java/fr/jaquin/bdlg/planner/PlanningController.java

@@ -5,8 +5,9 @@ import java.util.concurrent.ExecutionException;
 
 import fr.jaquin.bdlg.planner.domain.Planning;
 import fr.jaquin.bdlg.planner.domain.PlanningInput;
-import org.optaplanner.core.api.solver.Solver;
-import org.optaplanner.core.api.solver.SolverFactory;
+import fr.jaquin.bdlg.planner.domain.PlanningSolution;
+import org.optaplanner.core.api.score.ScoreManager;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
 import org.optaplanner.core.api.solver.SolverJob;
 import org.optaplanner.core.api.solver.SolverManager;
 
@@ -19,24 +20,29 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/planning")
 public class PlanningController {
-/*
+
   @Autowired
   private SolverManager<Planning, UUID> solverManager;
-*/
+  @Autowired
+    private ScoreManager<Planning, HardSoftScore> scoreManager;
+
   @PostMapping("/solve")
-  public Planning solve(@RequestBody PlanningInput inputs) {
+  public PlanningSolution solve(@RequestBody PlanningInput inputs) {
     Planning problem = inputs.generatePlanningProblem();
-    /*
+
     UUID problemId = UUID.randomUUID();
     // Submit the problem to start solving
     SolverJob<Planning, UUID> solverJob = solverManager.solve(problemId, problem);
-    */
-    SolverFactory<Planning> solverFactory = SolverFactory
-        .createFromXmlResource("main/java/fr/jasuin/bdlg/planner/solver/AssigmentSolverConfig.xml");
-    Solver<Planning> solver = solverFactory.buildSolver();
-    Planning solution = solver.solve(problem);
 
-    return solution;
+    Planning solution;
+    try {
+      // Wait until the solving ends
+      solution = solverJob.getFinalBestSolution();
+    } catch (InterruptedException | ExecutionException e) {
+      throw new IllegalStateException("Solving failed.", e);
+    }
+    System.out.println(scoreManager.explainScore(solution));
+    return PlanningSolution.from(solution);
   }
 
 }

+ 13 - 1
src/main/java/fr/jaquin/bdlg/planner/domain/Assignement.java

@@ -16,6 +16,8 @@ public class Assignement {
   @PlanningVariable(valueRangeProviderRefs = "volonteerRange")
   private Volonteer volonteer;
 
+  public Assignement() {}
+
   public Assignement(Timeslot slot) {
     this.slot = slot;
     this.isFixed = false;
@@ -28,7 +30,8 @@ public class Assignement {
   }
 
 
-  public Assignement(final Timeslot slot, final Volonteer volonteer) {
+  public Assignement(int id, final Timeslot slot, final Volonteer volonteer) {
+    this.id = id;
     this.slot = slot;
     this.volonteer = volonteer;
     this.isFixed = true;
@@ -39,6 +42,14 @@ public class Assignement {
     this.volonteer = volonteer;
     this.isFixed = isFixed;
   }
+
+  public Assignement(int id, final Timeslot slot, final Volonteer volonteer,
+      final boolean isFixed) {
+    this.id = id;
+    this.slot = slot;
+    this.volonteer = volonteer;
+    this.isFixed = isFixed;
+  }
   // ********************************
   // Getters and setters
   // ********************************
@@ -54,6 +65,7 @@ public class Assignement {
   public Timeslot getSlot() {
     return this.slot;
   }
+
   public Volonteer getVolonteer() {
     return this.volonteer;
   }

+ 20 - 0
src/main/java/fr/jaquin/bdlg/planner/domain/AssignementPair.java

@@ -0,0 +1,20 @@
+package fr.jaquin.bdlg.planner.domain;
+
+public class AssignementPair {
+  public final String slotId;
+  public final Long volonteerId;
+
+  public AssignementPair(String slotId, Long volonteerId) {
+    this.slotId = slotId;
+    this.volonteerId = volonteerId;
+  }
+
+  public String getSlotId() {
+    return this.slotId;
+  }
+
+  public Long getVolonteerId() {
+    return this.volonteerId;
+  }
+
+}

+ 12 - 3
src/main/java/fr/jaquin/bdlg/planner/domain/MealAssignement.java

@@ -2,11 +2,10 @@ package fr.jaquin.bdlg.planner.domain;
 
 import java.time.LocalDateTime;
 
-import org.optaplanner.core.api.domain.entity.PlanningEntity;
 import org.optaplanner.core.api.domain.variable.PlanningVariable;
 
-@PlanningEntity
 public class MealAssignement {
+  private int id;
   private Volonteer volonteer;
 
   @PlanningVariable(valueRangeProviderRefs = "mealslotRange")
@@ -16,7 +15,8 @@ public class MealAssignement {
     this.volonteer = volonteer;
   }
 
-  public MealAssignement(Long id, Volonteer volonteer, MealSlot MealSlot) {
+  public MealAssignement(int id, MealSlot MealSlot, Volonteer volonteer) {
+    this.id = id;
     this.volonteer = volonteer;
     this.MealSlot = MealSlot;
   }
@@ -24,6 +24,9 @@ public class MealAssignement {
   // Getters and setters
   // ********************************
 
+  public int getId() {
+    return this.id;
+  }
 
   public Volonteer getVolonteer() {
     return this.volonteer;
@@ -42,10 +45,16 @@ public class MealAssignement {
   }
 
   public LocalDateTime getStartDateTime() {
+    if(this.MealSlot==null){
+      return LocalDateTime.MIN;
+    }
     return this.MealSlot.getStartTime();
   }
 
   public LocalDateTime getEndDateTime() {
+    if(this.MealSlot==null){
+      return LocalDateTime.MIN;
+    }
     return this.MealSlot.getEndTime();
   }
 

+ 23 - 1
src/main/java/fr/jaquin/bdlg/planner/domain/MealSlot.java

@@ -1,14 +1,20 @@
 package fr.jaquin.bdlg.planner.domain;
 
 import java.time.LocalDateTime;
+import java.util.Objects;
+
 public class MealSlot {
   private String id;
 
   private LocalDateTime startTime;
   private LocalDateTime endTime;
-  
+
   private int maxAttendee;
 
+  public MealSlot(String id) {
+    this.id = id;
+  }
+
   public MealSlot(String id, LocalDateTime startTime, LocalDateTime endTime, int maxAttendee) {
     this.id = id;
     this.startTime = startTime;
@@ -48,4 +54,20 @@ public class MealSlot {
     this.maxAttendee = maxAttendee;
   }
 
+  @Override
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof MealSlot)) {
+            return false;
+        }
+        MealSlot mealSlot = (MealSlot) o;
+        return id.equals( mealSlot.id) ;
+      }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(id);
+  }
+
 }

+ 14 - 9
src/main/java/fr/jaquin/bdlg/planner/domain/Planning.java

@@ -23,17 +23,20 @@ public class Planning {
   @ProblemFactCollectionProperty
   private ArrayList<Timeslot> timeslots;
 
-  @PlanningEntityCollectionProperty
-  private ArrayList<Assignement> assignements;
+  @ProblemFactCollectionProperty
+  private ArrayList<MealAssignement> mealAssignements;
 
   @PlanningEntityCollectionProperty
-  private ArrayList<MealAssignement> mealAssignements;
+  private ArrayList<Assignement> assignements;
 
   @PlanningScore
   private HardSoftScore score;
 
+  public Planning() {}
 
-  public Planning(ArrayList<Volonteer> volonteerList, ArrayList<MealSlot> mealSlots, ArrayList<Timeslot> timeslots, ArrayList<Assignement> assignements, ArrayList<MealAssignement> mealAssignements) {
+  public Planning(ArrayList<Volonteer> volonteerList, ArrayList<MealSlot> mealSlots,
+      ArrayList<Timeslot> timeslots, ArrayList<Assignement> assignements,
+      ArrayList<MealAssignement> mealAssignements) {
     this.volonteerList = volonteerList;
     this.mealSlots = mealSlots;
     this.timeslots = timeslots;
@@ -44,11 +47,7 @@ public class Planning {
   // ********************************
   // Getters and setters
   // ********************************
-
-  public ArrayList<MealAssignement> getMealAssignements() {
-    return mealAssignements;
-  }
-
+  
   public ArrayList<Volonteer> getVolonteerList() {
     return this.volonteerList;
   }
@@ -60,7 +59,13 @@ public class Planning {
   public ArrayList<Timeslot> getTimeslots() {
     return this.timeslots;
   }
+  public ArrayList<Assignement> getAssignements() {
+    return assignements;
+  }
 
+  public ArrayList<MealAssignement> getMealAssignements() {
+    return mealAssignements;
+  }
   public HardSoftScore getScore() {
     return this.score;
   }

+ 114 - 39
src/main/java/fr/jaquin/bdlg/planner/domain/PlanningInput.java

@@ -3,7 +3,6 @@ package fr.jaquin.bdlg.planner.domain;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.HashMap;
-import java.util.OptionalInt;
 
 public class PlanningInput {
   private ArrayList<VolonteerRaw> volonteerList;
@@ -12,17 +11,26 @@ public class PlanningInput {
 
   private ArrayList<TimeslotRaw> timeslots;
 
-  private ArrayList<Assignement> assignements;
+  private ArrayList<AssignementPair> assignements;
 
-  private ArrayList<MealAssignement> mealAssignements;
+  private ArrayList<AssignementPair> mealAssignements;
 
   private ArrayList<Skill> constraints;
 
   private int currentMaxId = 1;
 
+  public PlanningInput() {
+    this.volonteerList = new ArrayList<VolonteerRaw>();
+    this.mealSlots = new ArrayList<MealSlot>();
+    this.timeslots = new ArrayList<TimeslotRaw>();
+    this.assignements = new ArrayList<AssignementPair>();
+    this.mealAssignements = new ArrayList<AssignementPair>();
+    this.constraints = new ArrayList<Skill>();
+  }
+
   public PlanningInput(ArrayList<VolonteerRaw> volonteerList, ArrayList<MealSlot> mealSlots,
-      ArrayList<TimeslotRaw> timeslots, ArrayList<Assignement> assignements,
-      ArrayList<MealAssignement> mealAssignements) {
+      ArrayList<TimeslotRaw> timeslots, ArrayList<AssignementPair> assignements,
+      ArrayList<AssignementPair> mealAssignements) {
     this.volonteerList = volonteerList;
     this.mealSlots = mealSlots;
     this.timeslots = timeslots;
@@ -30,27 +38,34 @@ public class PlanningInput {
     this.mealAssignements = mealAssignements;
   }
 
-  private void addMissingAssigment(ArrayList<Timeslot> timeslots) {
+  private ArrayList<Assignement> addMissingAssigment(ArrayList<Assignement> assignements,
+      ArrayList<Timeslot> timeslots) {
     for (Timeslot slot : timeslots) {
       int minAssignement = slot.getMinAttendee();
       Long existingAssignementCount = assignements.stream()
           .filter(predicate -> predicate.getSlot().getId().equals(slot.getId())).count();
       Long missingAssignementCount = minAssignement - existingAssignementCount;
-      if (missingAssignementCount > 0) {
-        this.assignements.add(new Assignement(this.currentMaxId, slot));
+
+      while (missingAssignementCount > 0) {
+        assignements.add(new Assignement(this.currentMaxId, slot));
         this.currentMaxId++;
+        missingAssignementCount--;
       }
     }
+    return assignements;
   }
 
-  private void addMissingMealAssigment(ArrayList<Volonteer> volonteerList) {
+  private ArrayList<MealAssignement> addMissingMealAssigment(
+      ArrayList<MealAssignement> mealAssignements, ArrayList<Volonteer> volonteerList) {
     for (Volonteer volonteer : volonteerList) {
       Long missingAssignementCount = 1 - mealAssignements.stream()
           .filter(predicate -> predicate.getVolonteer().getId().equals(volonteer.getId())).count();
-      if (missingAssignementCount > 0) {
-        this.mealAssignements.add(new MealAssignement(volonteer));
+      while (missingAssignementCount > 0) {
+        mealAssignements.add(new MealAssignement(volonteer));
+        missingAssignementCount--;
       }
     }
+    return mealAssignements;
   }
 
   private ArrayList<Timeslot> getMappedTimeslots(HashMap<Integer, Skill> skillsMap) {
@@ -60,8 +75,8 @@ public class PlanningInput {
       HashSet<Skill> hardCompetencies = new HashSet<Skill>();
       HashSet<Skill> softCompetencies = new HashSet<Skill>();
 
-      for (int i = 0; i < timeslot.getRawSkills().length; i++) {
-        Skill e = skillsMap.get(timeslot.getRawSkills()[i]);
+      for (int i = 0; i < timeslot.getSkillsId().length; i++) {
+        Skill e = skillsMap.get(timeslot.getSkillsId()[i]);
 
         if (e.getIsPreference()) {
           preferences.add(e);
@@ -73,9 +88,10 @@ public class PlanningInput {
           }
         }
       }
-      Timeslot newTimeslot = new Timeslot(timeslot.getId(), timeslot.getStartTime(),
-          timeslot.getStartTime(), softCompetencies, hardCompetencies, preferences,
-          timeslot.getMinAttendee(), timeslot.getMaxAttendee());
+      Timeslot newTimeslot =
+          new Timeslot(timeslot.getId(), timeslot.getStartTime(), timeslot.getStartTime(),
+              softCompetencies, hardCompetencies, preferences, timeslot.getMinAttendee());
+
       output.add(newTimeslot);
     }
     return output;
@@ -101,22 +117,7 @@ public class PlanningInput {
   }
 
   public Planning generatePlanningProblem() {
-    // Fix null list ??
-    if (this.assignements == null) {
-      this.assignements = new ArrayList<Assignement>();
-    }
-    // Add ids to assignements that don't have any
-    OptionalInt existingMaxId = this.assignements.stream().mapToInt(o -> o.getId()).max();
 
-    if (existingMaxId.isPresent()) {
-      this.currentMaxId = existingMaxId.getAsInt();
-    }
-    for (Assignement assignement : this.assignements) {
-      if (assignement.getId() == 0) {
-        assignement.setId(currentMaxId);
-        this.currentMaxId++;
-      }
-    }
     // Create skills map
     HashMap<Integer, Skill> skillMap = new HashMap<Integer, Skill>();
     if (this.constraints != null) {
@@ -126,13 +127,74 @@ public class PlanningInput {
     }
     // Map skills to timseslots
     ArrayList<Timeslot> timeslots = this.getMappedTimeslots(skillMap);
-    this.addMissingAssigment(timeslots);
+
+    // Create slots map
+    HashMap<String, Timeslot> slotMap = new HashMap<String, Timeslot>();
+    for (Timeslot v : timeslots) {
+      slotMap.put(v.getId(), v);
+    }
+
     // Map skills to volonteer
     ArrayList<Volonteer> volonteerList = this.getMappedVolonteers(skillMap);
-    this.addMissingMealAssigment(volonteerList);
+
+    // Create skills map
+    HashMap<Long, Volonteer> volonteerMap = new HashMap<Long, Volonteer>();
+    for (Volonteer v : volonteerList) {
+      volonteerMap.put(v.getId(), v);
+    }
+    Volonteer volonteer;
+    Timeslot timeslot;
+    // Create Assignements Objects
+    ArrayList<Assignement> assignements = new ArrayList<Assignement>();
+    if (this.assignements != null) {
+      for (AssignementPair pair : this.assignements) {
+        if (volonteerMap.containsKey(pair.volonteerId)) {
+          volonteer = volonteerMap.get(pair.volonteerId);
+        } else {
+          volonteer = null;
+        }
+        if (slotMap.containsKey(pair.slotId)) {
+          timeslot = slotMap.get(pair.slotId);
+        } else {
+          timeslot = null;
+        }
+        if (volonteer == null || timeslot == null) {
+          throw new NullPointerException(
+              "Impossible to find the corresponding assignement object." + pair.slotId);
+        } else {
+          assignements.add(new Assignement(currentMaxId, timeslot, volonteer));
+          currentMaxId++;
+        }
+      }
+    }
+    assignements = this.addMissingAssigment(assignements, timeslots);
+
+    // Create MealAssignements Objects
+    ArrayList<MealAssignement> mealAssignements = new ArrayList<MealAssignement>();
+    MealSlot slot;
+    if (this.mealAssignements != null) {
+      for (AssignementPair pair : this.mealAssignements) {
+        if (volonteerMap.containsKey(pair.volonteerId)) {
+          volonteer = volonteerMap.get(pair.volonteerId);
+        } else {
+          volonteer = null;
+        }
+        int idx = this.mealSlots.indexOf(new MealSlot(pair.slotId));
+        if (idx < 0 || volonteer == null) {
+          throw new NullPointerException(
+              "Impossible to find the corresponding assignement objects." + pair.slotId);
+        } else {
+          slot = this.mealSlots.get(idx);
+          mealAssignements.add(new MealAssignement(currentMaxId, slot, volonteer));
+          currentMaxId++;
+        }
+      }
+    }
+    mealAssignements = addMissingMealAssigment(mealAssignements, volonteerList);
+    
     // Create Planning problem to be optimized
-    Planning outPlanning = new Planning(volonteerList, this.mealSlots, timeslots, this.assignements,
-        this.mealAssignements);
+    Planning outPlanning =
+        new Planning(volonteerList, this.mealSlots, timeslots, assignements, mealAssignements);
     return outPlanning;
   }
 
@@ -163,22 +225,35 @@ public class PlanningInput {
     this.timeslots = timeslots;
   }
 
-  public ArrayList<Assignement> getAssignements() {
+  public ArrayList<AssignementPair> getAssignements() {
     return this.assignements;
   }
 
-  public void setAssignements(ArrayList<Assignement> assignements) {
+  public void setAssignements(ArrayList<AssignementPair> assignements) {
     this.assignements = assignements;
   }
 
-  public ArrayList<MealAssignement> getMealAssignements() {
+  public ArrayList<AssignementPair> getMealAssignements() {
     return this.mealAssignements;
   }
 
-  public void setMealAssignements(ArrayList<MealAssignement> mealAssignements) {
+  public void setMealAssignements(ArrayList<AssignementPair> mealAssignements) {
     this.mealAssignements = mealAssignements;
   }
 
+  public ArrayList<Skill> getConstraints() {
+    return this.constraints;
+  }
+
+  public void setConstraints(ArrayList<Skill> constraints) {
+    this.constraints = constraints;
+  }
 
+  public int getCurrentMaxId() {
+    return this.currentMaxId;
+  }
 
+  public void setCurrentMaxId(int currentMaxId) {
+    this.currentMaxId = currentMaxId;
+  }
 }

+ 51 - 0
src/main/java/fr/jaquin/bdlg/planner/domain/PlanningSolution.java

@@ -0,0 +1,51 @@
+package fr.jaquin.bdlg.planner.domain;
+
+import java.util.ArrayList;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+
+public class PlanningSolution {
+  private ArrayList<AssignementPair> assignements;
+  private String message = "";
+  private HardSoftScore score;
+
+  public PlanningSolution() {
+    assignements = new ArrayList<AssignementPair>();
+  }
+
+  public static PlanningSolution from(Planning planning) {
+    PlanningSolution output = new PlanningSolution();
+    output.setScore(planning.getScore());
+    for (Assignement assignement : planning.getAssignements()) {
+      output.assignements.add(
+          new AssignementPair(assignement.getSlot().getId(), assignement.getVolonteer().getId()));
+    }
+    for (MealAssignement assignement : planning.getMealAssignements()) {
+      if (assignement.getMealSlot() == null) {
+        output.message +=
+            "Error: No meal for volunteer " + assignement.getVolonteer().getId().toString() + "\n";
+      } else {
+        output.assignements.add(new AssignementPair(assignement.getMealSlot().getId(),
+            assignement.getVolonteer().getId()));
+      }
+    }
+    return output;
+  }
+
+
+  public HardSoftScore getScore() {
+    return score;
+  }
+
+  public void setScore(HardSoftScore score) {
+    this.score = score;
+  }
+
+  public String getMessage() {
+    return this.message;
+  }
+
+  public ArrayList<AssignementPair> getAssignements() {
+    return this.assignements;
+  }
+
+}

+ 2 - 9
src/main/java/fr/jaquin/bdlg/planner/domain/Timeslot.java

@@ -13,12 +13,10 @@ public class Timeslot {
   private Set<Skill> hardCompetencies;
   private Set<Skill> preferences;
   private int minAttendee;
-  private int maxAttendee;
 
-
-  public Timeslot(String id, LocalDateTime startTime, LocalDateTime endTime,
+   public Timeslot(String id, LocalDateTime startTime, LocalDateTime endTime,
       Set<Skill> softCompetencies, Set<Skill> hardCompetencies, Set<Skill> preferences,
-      int minAttendee, int maxAttendee) {
+      int minAttendee) {
     this.id = id;
     this.startTime = startTime;
     this.endTime = endTime;
@@ -26,7 +24,6 @@ public class Timeslot {
     this.hardCompetencies = hardCompetencies;
     this.preferences = preferences;
     this.minAttendee = minAttendee;
-    this.maxAttendee = maxAttendee;
   }
 
 
@@ -67,10 +64,6 @@ public class Timeslot {
     return this.minAttendee;
   }
 
-  public int getMaxAttendee() {
-    return this.maxAttendee;
-  }
-
   @Override
   public boolean equals(Object o) {
     if (o == this)

+ 11 - 6
src/main/java/fr/jaquin/bdlg/planner/domain/TimeslotRaw.java

@@ -12,11 +12,12 @@ public class TimeslotRaw {
   private int minAttendee;
   private int maxAttendee;
 
-  public TimeslotRaw(String id, LocalDateTime startTime, LocalDateTime endTime, int[] skillsID, int minAttendee, int maxAttendee) {
+  public TimeslotRaw(String id, LocalDateTime startTime, LocalDateTime endTime, int[] skillsID,
+      int minAttendee, int maxAttendee) {
     this.id = id;
     this.startTime = startTime;
     this.endTime = endTime;
-    this.setRawSkills(skillsID);
+    this.skillsId = skillsID;
     this.minAttendee = minAttendee;
     this.maxAttendee = maxAttendee;
   }
@@ -49,12 +50,16 @@ public class TimeslotRaw {
   public int getMaxAttendee() {
     return this.maxAttendee;
   }
-  
-  public int[] getRawSkills() {
-    return skillsId;
+
+  public int[] getSkillsId() {
+    if (skillsId == null) {
+      return new int[0];
+    } else {
+      return skillsId;
+    }
   }
 
-  public void setRawSkills(int[] skillsID) {
+  public void setSkillsId(int[] skillsID) {
     this.skillsId = skillsID;
   }
 

+ 8 - 7
src/main/java/fr/jaquin/bdlg/planner/solver/PlanningConstraintProvider.java

@@ -13,7 +13,7 @@ import org.optaplanner.core.api.score.stream.uni.UniConstraintStream;
 
 public class PlanningConstraintProvider implements ConstraintProvider {
 
-  private static UniConstraintStream<Assignement> getAssignedShiftConstraintStream(
+  private static UniConstraintStream<Assignement> getAssignedSlotStream(
       ConstraintFactory constraintFactory) {
     return constraintFactory.fromUnfiltered(Assignement.class) // To match DRL
         .filter(shift -> shift.getVolonteer() != null);
@@ -48,7 +48,7 @@ public class PlanningConstraintProvider implements ConstraintProvider {
     // a volonteer cannot go to 2 timeslot at the same time.
 
     // Select a assignement ...
-    return getAssignedShiftConstraintStream(constraintFactory)
+    return getAssignedSlotStream(constraintFactory)
         // ... and pair it with another assignement ...
         .join(Assignement.class,
             // ... in the same volunteer ...
@@ -66,7 +66,7 @@ public class PlanningConstraintProvider implements ConstraintProvider {
     // a volonteer cannot go to 2 timeslot at the same time.
 
     // Select a assignement ...
-    return getAssignedShiftConstraintStream(constraintFactory)
+    return getAssignedSlotStream(constraintFactory)
         // ... and pair it with another assignement ...
         .join(MealAssignement.class,
             // ... in the same volunteer ...
@@ -80,27 +80,27 @@ public class PlanningConstraintProvider implements ConstraintProvider {
 
   private Constraint competencyConflict(ConstraintFactory constraintFactory) {
     // a volonteer must match required competencies.
-    return getAssignedShiftConstraintStream(constraintFactory).penalize("Competence conflict",
+    return getAssignedSlotStream(constraintFactory).penalize("Competence conflict",
         HardSoftScore.ONE_HARD, Assignement::getCompetenceLackScore);
   }
 
   private Constraint competencyTeachingEffort(ConstraintFactory constraintFactory) {
     // We favor volonteer that already have required competencies.
-    return getAssignedShiftConstraintStream(constraintFactory).penalize(
+    return getAssignedSlotStream(constraintFactory).penalize(
         "Avoid teaching competences to volunteer", HardSoftScore.ONE_SOFT,
         Assignement::getSoftCompetenceLackScore);
   }
 
   private Constraint preferenceApplication(ConstraintFactory constraintFactory) {
     // The system should favor timeslots selected by user.
-    return getAssignedShiftConstraintStream(constraintFactory).reward("Preference application",
+    return getAssignedSlotStream(constraintFactory).reward("Preference application",
         HardSoftScore.ONE_SOFT, Assignement::getPreferenceScore);
   }
 
   private Constraint balanceLoad(ConstraintFactory constraintFactory) {
     // Distribut fairly the timeslot per volunter
 
-    return getAssignedShiftConstraintStream(constraintFactory)
+    return getAssignedSlotStream(constraintFactory)
         // ... look at volunteer and count nulber of associated slots ...
         .groupBy(Assignement::getVolonteer, ConstraintCollectors.count())
         // then penalize each pair with a hard weight.
@@ -111,6 +111,7 @@ public class PlanningConstraintProvider implements ConstraintProvider {
   private Constraint mealMaxAttendee(ConstraintFactory constraintFactory) {
     // Each Meal slot can only accomodate a maximum of attendee
     return constraintFactory.from(MealAssignement.class)
+        .filter(assignement -> assignement.getMealSlot() != null)
         .groupBy(MealAssignement::getMealSlot, ConstraintCollectors.count())
         .filter((slot, volonteerCount) -> slot.getMaxAttendee() < volonteerCount)
         // then penalize each pair with a hard weight.

+ 15 - 2
src/main/resources/application.properties

@@ -1,7 +1,20 @@
 
 
 
-# The solver runs only for 5 seconds to avoid a HTTP timeout in this simple implementation.
-# It's recommended to run for at least 5 minutes ("5m") otherwise.
+########################
+# OptaPlanner properties
+########################
+
+# The solver runs for 30 seconds. To run for 5 minutes use "5m" and for 2 hours use "2h".
 optaplanner.solver.termination.spent-limit=5s
 
+# To change how many solvers to run in parallel
+# optaplanner.solver-manager.parallel-solver-count=4
+# To run increase CPU cores usage per solver
+# optaplanner.solver.move-thread-count=2
+
+# To detect common bugs in your code
+# optaplanner.solver.environment-mode=FULL_ASSERT
+
+# XML file for power tweaking, defaults to solverConfig.xml (directly under src/main/resources)
+# optaplanner.solver-config-xml=org/.../timeTableSolverConfig.xml

File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/css/app.49ff1949.css


BIN
src/main/resources/static/favicon.ico


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/img/bdlg.min.ea622d66.svg


+ 1 - 0
src/main/resources/static/index.html

@@ -0,0 +1 @@
+<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>bdlg-scheduler</title><link href="/css/app.49ff1949.css" rel="preload" as="style"><link href="/js/app.e1837404.js" rel="preload" as="script"><link href="/js/chunk-vendors.4d096b3a.js" rel="preload" as="script"><link href="/css/app.49ff1949.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but bdlg-scheduler doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.4d096b3a.js"></script><script src="/js/app.e1837404.js"></script></body></html>

File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/app.7763bc88.js


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/app.7763bc88.js.map


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/app.e1837404.js


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/app.e1837404.js.map


File diff suppressed because it is too large
+ 55 - 0
src/main/resources/static/js/chunk-vendors.4d096b3a.js


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/chunk-vendors.4d096b3a.js.map


Some files were not shown because too many files changed in this diff