Browse Source

implement api for persistence

tripeur 4 years ago
parent
commit
afac8a26e9
26 changed files with 202 additions and 94 deletions
  1. 56 25
      src/main/java/fr/jaquin/bdlg/planner/controller/ApiController.java
  2. 9 4
      src/main/java/fr/jaquin/bdlg/planner/controller/PageController.java
  3. 13 9
      src/main/java/fr/jaquin/bdlg/planner/controller/SolverController.java
  4. 7 2
      src/main/java/fr/jaquin/bdlg/planner/domain/PlanningSolution.java
  5. 4 3
      src/main/java/fr/jaquin/bdlg/planner/persistence/CustomUser.java
  6. 25 0
      src/main/java/fr/jaquin/bdlg/planner/persistence/Evenement.java
  7. 19 0
      src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementData.java
  8. 6 0
      src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementRepository.java
  9. 5 5
      src/main/java/fr/jaquin/bdlg/planner/security/CustomUserDetailService.java
  10. 3 1
      src/main/java/fr/jaquin/bdlg/planner/security/LoadFixedUser.java
  11. 32 29
      src/main/java/fr/jaquin/bdlg/planner/security/WebSecurityConfig.java
  12. 21 15
      src/main/java/fr/jaquin/bdlg/planner/solver/PlanningConstraintProvider.java
  13. 0 0
      src/main/resources/static/css/app.1112d078.css
  14. 0 0
      src/main/resources/static/css/index.1d043421.css
  15. 0 0
      src/main/resources/static/css/login.a752cf82.css
  16. 0 1
      src/main/resources/static/index.html
  17. 0 0
      src/main/resources/static/js/app.70178e53.js
  18. 0 0
      src/main/resources/static/js/app.70178e53.js.map
  19. 0 0
      src/main/resources/static/js/chunk-vendors.121bdf9d.js
  20. 0 0
      src/main/resources/static/js/chunk-vendors.121bdf9d.js.map
  21. 0 0
      src/main/resources/static/js/index.ecd2c675.js
  22. 0 0
      src/main/resources/static/js/index.ecd2c675.js.map
  23. 0 0
      src/main/resources/static/js/login.1fcb48c0.js
  24. 0 0
      src/main/resources/static/js/login.1fcb48c0.js.map
  25. 1 0
      src/main/resources/static/login.html
  26. 1 0
      src/main/resources/static/planner/index.html

+ 56 - 25
src/main/java/fr/jaquin/bdlg/planner/controller/ApiController.java

@@ -1,14 +1,19 @@
 package fr.jaquin.bdlg.planner.controller;
 
+import java.security.Principal;
 import java.time.LocalDateTime;
 import java.util.List;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import java.util.Optional;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
 import fr.jaquin.bdlg.planner.persistence.Evenement;
 import fr.jaquin.bdlg.planner.persistence.EvenementAlreadyExistException;
 import fr.jaquin.bdlg.planner.persistence.EvenementData;
@@ -17,69 +22,95 @@ import fr.jaquin.bdlg.planner.persistence.EvenementLobRepository;
 import fr.jaquin.bdlg.planner.persistence.EvenementNotFoundException;
 import fr.jaquin.bdlg.planner.persistence.EvenementRepository;
 import fr.jaquin.bdlg.planner.persistence.CustomUser;
+import fr.jaquin.bdlg.planner.persistence.CustomUserRepository;
 
-@RestController("/api")
+@RestController
+@RequestMapping("/api")
 public class ApiController {
 
   private final EvenementRepository repository;
 
   private final EvenementLobRepository repositoryLob;
 
-  public ApiController(EvenementRepository repository, EvenementLobRepository repositoryLob) {
+  private final CustomUserRepository repositoryUser;
+
+  public ApiController(EvenementRepository repository, EvenementLobRepository repositoryLob,
+      CustomUserRepository repositoryUser) {
     this.repository = repository;
     this.repositoryLob = repositoryLob;
+    this.repositoryUser = repositoryUser;
   }
 
+  @CrossOrigin(origins = "http://localhost:8081")
   @GetMapping("/evenements")
   public List<Evenement> getEvenements() {
-    return (List<Evenement>) repository.findAll();
+    return (List<Evenement>) repository.findByLastVersionIsTrue();
   }
 
   // Single evenement data
+  @CrossOrigin(origins = "http://localhost:8081")
   @GetMapping("/evenements/{uuid}")
   String one(@PathVariable String uuid) {
-    List<Evenement> allVersion = repository.findByUuid(uuid);
-    if (allVersion.size() < 1) {
+    List<Evenement> allVersions = repository.findFirst1ByUuidOrderByLastModifiedDesc(uuid);
+    if (allVersions.size() < 1) {
       throw new EvenementNotFoundException(uuid);
     }
-    Evenement lastVersion = allVersion.get(0);
-    for (Evenement evenement : allVersion) {
-      if (evenement.getLastModified().isAfter(evenement.getLastModified())) {
-        lastVersion = evenement;
-      }
-    }
-    return repositoryLob.findById(lastVersion.getId()).orElseThrow(() -> new EvenementNotFoundException(uuid))
-        .getJsonContent();
+    return repositoryLob.findById(allVersions.get(0).getId())
+        .orElseThrow(() -> new EvenementNotFoundException(uuid)).getJsonContent();
   }
 
+  @CrossOrigin(origins = "http://localhost:8081")
   @GetMapping("/evenements/history/{uuid}")
   List<Evenement> history(@PathVariable String uuid) {
-    return repository.findByUuid(uuid);
+    return repository.findByUuidOrderByLastModifiedDesc(uuid);
+  }
+
+  @GetMapping("/evenements/history/{uuid}/content/{id}")
+  String historyContent(@PathVariable String uuid, @PathVariable Long id) {
+    Optional<EvenementLob> lob = repositoryLob.findById(id);
+    if (lob.isPresent() && lob.get().getEvenement().getUuid().equals(uuid)) {
+      return lob.get().getJsonContent();
+    } else {
+      throw new EvenementNotFoundException(uuid);
+    }
   }
 
   @PostMapping("/evenements")
-  Evenement newEvenement(@AuthenticationPrincipal CustomUser customUser, @RequestBody EvenementData evt) {
-    if (repository.findByUuid(evt.getUuid()).size() > 0) {
-      throw new EvenementAlreadyExistException(evt.getUuid());
+  Evenement newEvenement(Principal principal, @RequestBody EvenementData evt) {
+    if (repository.findFirst1ByUuidOrderByLastModifiedDesc(evt.getUuid()).size() > 0) {
+      throw new ResponseStatusException(HttpStatus.CONFLICT, "",
+          new EvenementAlreadyExistException(evt.getUuid()));
     }
-    return saveEvenementData(evt, customUser);
+    return saveEvenementData(evt, principal.getName());
   }
 
   @PutMapping("/evenements/{uuid}")
-  Evenement newEvenement(@AuthenticationPrincipal CustomUser customUser, @RequestBody EvenementData evt,
-      @PathVariable String id) {
-    if (repository.findByUuid(evt.getUuid()).size() == 0) {
-      throw new EvenementNotFoundException(evt.getUuid());
+  Evenement updateEvenement(Principal principal, @RequestBody EvenementData evt,
+      @PathVariable String uuid) {
+    List<Evenement> previous = repository.findFirst1ByUuidOrderByLastModifiedDesc(uuid);
+    if (previous.size() == 0) {
+      throw new ResponseStatusException(HttpStatus.NOT_FOUND, "",
+          new EvenementNotFoundException(uuid));
+    }
+    Evenement lastversioEvenement = previous.get(0);
+    if (lastversioEvenement.getContentHash().equals(evt.getContentHash())) {
+      return lastversioEvenement;
+    } else {
+      lastversioEvenement.setLastVersion(false);
+      repository.save(lastversioEvenement);
+      return saveEvenementData(evt, principal.getName());
     }
-    return saveEvenementData(evt, customUser);
   }
 
-  private Evenement saveEvenementData(EvenementData evt, CustomUser customUser) {
+  private Evenement saveEvenementData(EvenementData evt, String username) {
+    CustomUser customUser = repositoryUser.findByUsername(username);
     Evenement newEvenement = new Evenement();
     newEvenement.setName(evt.getName());
     newEvenement.setUuid(evt.getUuid());
     newEvenement.setLastModified(LocalDateTime.now());
     newEvenement.setLastEditor(customUser);
+    newEvenement.setLastVersion(true);
+    newEvenement.setContentHash(evt.getContentHash());
     newEvenement = repository.save(newEvenement);
     EvenementLob blob = new EvenementLob();
     blob.setEvenement(newEvenement);

+ 9 - 4
src/main/java/fr/jaquin/bdlg/planner/controller/PageController.java

@@ -6,8 +6,13 @@ import org.springframework.web.bind.annotation.GetMapping;
 @Controller
 class PageController {
 
-  @GetMapping(value = "/{path:[^\\.]*}")
-  public String redirect() {
-    return "forward:/";
+  @GetMapping(value = {"/planner", "/planner/{path:[^\\.]*}"})
+  public String redirectPlanner() {
+    return "forward:/planner/index.html";
   }
-}
+
+  @GetMapping(value = "/")
+  public String redirectHome() {
+    return "redirect:/planner/";
+  }
+}

+ 13 - 9
src/main/java/fr/jaquin/bdlg/planner/controller/SolverController.java

@@ -17,6 +17,7 @@ import org.optaplanner.core.api.solver.SolverJob;
 import org.optaplanner.core.api.solver.SolverManager;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -35,7 +36,8 @@ public class SolverController {
 
   @CrossOrigin(origins = "http://localhost:8081")
   @PostMapping("/solve")
-  public PlanningSolution solve(@RequestBody PlanningInput inputs) {
+  public PlanningSolution solve(@AuthenticationPrincipal String username,
+      @RequestBody PlanningInput inputs) {
     Planning problem = inputs.generatePlanningProblem();
 
     UUID problemId = UUID.randomUUID();
@@ -50,10 +52,11 @@ public class SolverController {
       throw new IllegalStateException("Solving failed.", e);
     }
 
-    ScoreExplanation<Planning, HardMediumSoftScore> explanation = scoreManager.explainScore(solution);
+    ScoreExplanation<Planning, HardMediumSoftScore> explanation =
+        scoreManager.explainScore(solution);
     PlanningSolution output = PlanningSolution.from(solution);
     output.setExplanation(stringifyExplanation(explanation));
-    System.out.println(explanation.getSummary());
+    // System.out.println(explanation.getSummary());
     return output;
   }
 
@@ -66,8 +69,9 @@ public class SolverController {
         // Populate the value with an array of justification objects
             + constraint.getConstraintMatchSet().stream()
                 // Filter pair that have a negative impact on the score
-                .filter(matchElt -> matchElt.getScore().getHardScore() < 0).map(match -> match.getJustificationList()
-                    .stream().map(elt -> stringifyConstraint(elt)).collect(listCollector))
+                .filter(matchElt -> matchElt.getScore().getHardScore() < 0)
+                .map(match -> match.getJustificationList().stream()
+                    .map(elt -> stringifyConstraint(elt)).collect(listCollector))
                 .collect(listCollector))
         .collect(Collectors.joining(",", "{", "}"));
   }
@@ -75,13 +79,13 @@ public class SolverController {
   private String stringifyConstraint(Object val) {
     if (val instanceof Assignement) {
       Assignement casted = (Assignement) val;
-      return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId() + "\",\"volonteerId\":"
-          + casted.getVolonteer().getId().toString() + "}";
+      return "{\"type\":\"Assignement\",\"slotId\":\"" + casted.getSlot().getId()
+          + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
     }
     if (val instanceof MealAssignement) {
       MealAssignement casted = (MealAssignement) val;
-      return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId() + "\",\"volonteerId\":"
-          + casted.getVolonteer().getId().toString() + "}";
+      return "{\"type\":\"MealAssignement\",\"slotId\":\"" + casted.getMealSlot().getId()
+          + "\",\"volonteerId\":" + casted.getVolonteer().getId().toString() + "}";
     }
     if (val instanceof MealSlot) {
       MealSlot casted = (MealSlot) val;

+ 7 - 2
src/main/java/fr/jaquin/bdlg/planner/domain/PlanningSolution.java

@@ -17,8 +17,13 @@ public class PlanningSolution {
     PlanningSolution output = new PlanningSolution();
     output.setScore(planning.getScore());
     for (Assignement assignement : planning.getAssignements()) {
-      output.assignements.add(
-          new AssignementPair(assignement.getSlot().getId(), assignement.getVolonteer().getId()));
+      if (assignement.getVolonteer() == null) {
+        output.message += "Error: No volunteer for slot " + assignement.getSlot().getId() + "\n";
+      } else {
+
+        output.assignements.add(
+            new AssignementPair(assignement.getSlot().getId(), assignement.getVolonteer().getId()));
+      }
     }
     for (MealAssignement assignement : planning.getMealAssignements()) {
       if (assignement.getMealSlot() == null) {

+ 4 - 3
src/main/java/fr/jaquin/bdlg/planner/persistence/CustomUser.java

@@ -6,7 +6,7 @@ import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
-
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
 @Entity
@@ -21,14 +21,14 @@ public class CustomUser {
   private String email;
   private String password;
 
-  public CustomUser() {
-  }
+  public CustomUser() {}
 
   public CustomUser(String username, String password) {
     this.username = username;
     this.setPassword(password);
   }
 
+  @JsonIgnore
   public Long getId() {
     return this.id;
   }
@@ -53,6 +53,7 @@ public class CustomUser {
     this.email = email;
   }
 
+  @JsonIgnore
   public String getPassword() {
     return this.password;
   }

+ 25 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/Evenement.java

@@ -3,23 +3,48 @@ package fr.jaquin.bdlg.planner.persistence;
 import java.time.LocalDateTime;
 import java.util.Objects;
 import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 
 @Entity
 public class Evenement {
   @Id
+  @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   private String uuid;
   private String name;
+  private String contentHash;
   private LocalDateTime lastModified;
+  private Boolean lastVersion;
   @ManyToOne
+  @JoinColumn(name = "user_id", nullable = false)
   private CustomUser lastEditor;
 
   public Long getId() {
     return this.id;
   }
 
+  public Boolean getLastVersion() {
+    return lastVersion;
+  }
+
+  public void setLastVersion(Boolean lastVersion) {
+    this.lastVersion = lastVersion;
+  }
+
+  @JsonIgnore
+  public String getContentHash() {
+    return contentHash;
+  }
+
+  public void setContentHash(String contentHash) {
+    this.contentHash = contentHash;
+  }
+
   public void setId(Long id) {
     this.id = id;
   }

+ 19 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementData.java

@@ -1,10 +1,14 @@
 package fr.jaquin.bdlg.planner.persistence;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
 public class EvenementData {
 
   private String uuid;
   private String name;
   private String content;
+  private String _hash;
 
   public EvenementData() {}
 
@@ -31,4 +35,19 @@ public class EvenementData {
   public void setContent(String content) {
     this.content = content;
   }
+
+  public String getContentHash() {
+    if (_hash == null) {
+      MessageDigest messageDigest;
+      try {
+        messageDigest = MessageDigest.getInstance("SHA-256");
+        messageDigest.update(content.getBytes());
+        _hash = new String(messageDigest.digest());
+      } catch (NoSuchAlgorithmException e) {
+        // Should never happen
+        e.printStackTrace();
+      }
+    }
+    return _hash;
+  }
 }

+ 6 - 0
src/main/java/fr/jaquin/bdlg/planner/persistence/EvenementRepository.java

@@ -7,6 +7,12 @@ import org.springframework.data.repository.CrudRepository;
 public interface EvenementRepository extends CrudRepository<Evenement, Long> {
   List<Evenement> findByUuid(String uuid);
 
+  List<Evenement> findByUuidOrderByLastModifiedDesc(String uuid);
+
+  List<Evenement> findFirst1ByUuidOrderByLastModifiedDesc(String uuid);
+
   Optional<Evenement> findById(Long id);
 
+  List<Evenement> findByLastVersionIsTrue();
+
 }

+ 5 - 5
src/main/java/fr/jaquin/bdlg/planner/security/CustomUserDetailService.java

@@ -16,13 +16,13 @@ public class CustomUserDetailService implements UserDetailsService {
   private CustomUserRepository userRepository;
 
   @Override
-  public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
-    final CustomUser customUser = userRepository.findByUsername(login);
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    final CustomUser customUser = userRepository.findByUsername(username);
     if (customUser == null) {
-      throw new UsernameNotFoundException(login);
+      throw new UsernameNotFoundException(username);
     }
-    UserDetails user = User.withUsername(customUser.getEmail()).password(customUser.getPassword())
-        .authorities("ROLE_USER").build();
+    UserDetails user = User.withUsername(customUser.getUsername())
+        .password(customUser.getPassword()).authorities("ROLE_USER").build();
     return user;
   }
 }

+ 3 - 1
src/main/java/fr/jaquin/bdlg/planner/security/LoadFixedUser.java

@@ -16,7 +16,9 @@ public class LoadFixedUser {
   @Bean
   CommandLineRunner initDatabase(CustomUserRepository repository) {
     return args -> {
-      log.info("Preloading " + repository.save(new CustomUser("admin", "notAnAdmin")));
+      CustomUser admin = new CustomUser("admin", "notAnAdmin");
+      log.info("admin pwd " + admin.getPassword());
+      log.info("Preloading " + repository.save(admin));
       log.info("Preloading " + repository.save(new CustomUser("clovis", "M1m2pBP.")));
     };
   }

+ 32 - 29
src/main/java/fr/jaquin/bdlg/planner/security/WebSecurityConfig.java

@@ -1,5 +1,6 @@
 package fr.jaquin.bdlg.planner.security;
 
+
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpMethod;
@@ -11,7 +12,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
-
 import javax.annotation.Resource;
 
 @Configuration
@@ -21,42 +21,45 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
   @Resource
   private UserDetailsService userDetailsService;
 
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    return new BCryptPasswordEncoder();
+  }
+
+  @Override
+  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+    authProvider.setUserDetailsService(userDetailsService);
+    authProvider.setPasswordEncoder(passwordEncoder());
+    auth.authenticationProvider(authProvider);
+  }
+
   @Override
   protected void configure(HttpSecurity http) throws Exception {
     // @formatter:off
       http
-        .authorizeRequests()
+        .csrf().disable()
+        .authorizeRequests()  
+          .antMatchers(HttpMethod.GET,"/planning/display").permitAll()    
+          .antMatchers(HttpMethod.GET, "/api/evenements/history/**").hasRole("USER") 
+          .antMatchers(HttpMethod.GET, "/api/evenements/**").permitAll()   
+          .antMatchers(HttpMethod.PUT, "/api/evenements/**").hasRole("USER")  
           .antMatchers(HttpMethod.POST, "/api/evenements").hasRole("USER")
-          .antMatchers(HttpMethod.PUT, "/api/evenements/**").hasRole("USER")
-          .antMatchers(HttpMethod.GET,"/**").permitAll()
-          .antMatchers(HttpMethod.POST, "/planning/solve").permitAll()
+          .antMatchers(HttpMethod.POST, "/planning/solve").hasRole("USER")     
+          .antMatchers(HttpMethod.GET,"/planner/**").hasRole("USER")
+          .antMatchers(HttpMethod.GET,"/**").permitAll() 
           .and()
-       .formLogin() 
-         .loginPage("/login") 
-         .permitAll()
-         .and()
-      .logout() 
-        .permitAll()
-        .and()
-      .httpBasic(); 
+        .formLogin()
+          .loginProcessingUrl("/login.html")
+          .failureUrl("/login.html?error=true")
+          .loginPage("/login.html").permitAll()
+          .defaultSuccessUrl("/planner/index.html")
+          .and() // if user provided expired session id
+        .logout() 
+          .permitAll()
+          .and();
     // @formatter:on
   }
 
-  @Bean
-  public DaoAuthenticationProvider authProvider() {
-    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
-    authProvider.setUserDetailsService(userDetailsService);
-    authProvider.setPasswordEncoder(passwordEncoder());
-    return authProvider;
-  }
 
-  @Bean
-  public PasswordEncoder passwordEncoder() {
-    return new BCryptPasswordEncoder();
-  }
-
-  @Override
-  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-    auth.authenticationProvider(authProvider());
-  }
 }

+ 21 - 15
src/main/java/fr/jaquin/bdlg/planner/solver/PlanningConstraintProvider.java

@@ -13,9 +13,10 @@ import org.optaplanner.core.api.score.stream.uni.UniConstraintStream;
 
 public class PlanningConstraintProvider implements ConstraintProvider {
 
-  private static UniConstraintStream<Assignement> getAssignedSlotStream(ConstraintFactory constraintFactory) {
-    return constraintFactory.fromUnfiltered(Assignement.class) // To match DRL
-        .filter(shift -> shift.getVolonteer() != null);
+  private static UniConstraintStream<Assignement> getAssignedSlotStream(
+      ConstraintFactory constraintFactory) {
+    return constraintFactory.fromUnfiltered(Assignement.class)
+        .filter(assignement -> assignement.getVolonteer() != null);
   }
 
   @Override
@@ -28,16 +29,18 @@ public class PlanningConstraintProvider implements ConstraintProvider {
         // Soft constraints are only implemented in the "complete"
         // implementation
         volunteerMinRestTime(constraintFactory), competencyTeachingEffort(constraintFactory),
-        preferenceApplication(constraintFactory), balanceLoad(constraintFactory) };
+        preferenceApplication(constraintFactory), balanceLoad(constraintFactory)};
   }
 
   private Constraint completeAllTimeslot(ConstraintFactory constraintFactory) {
-    return constraintFactory.from(Assignement.class).filter((assignment) -> assignment.getVolonteer() == null)
+    return constraintFactory.from(Assignement.class)
+        .filter((assignment) -> assignment.getVolonteer() == null)
         .penalize("Timeslot not initialized", HardMediumSoftScore.ONE_HARD);
   }
 
   private Constraint feedAllVolunteer(ConstraintFactory constraintFactory) {
-    return constraintFactory.from(MealAssignement.class).filter((assignment) -> assignment.getMealSlot() == null)
+    return constraintFactory.from(MealAssignement.class)
+        .filter((assignment) -> assignment.getMealSlot() == null)
         .penalize("Meal not initialized", HardMediumSoftScore.ONE_HARD);
   }
 
@@ -94,22 +97,23 @@ public class PlanningConstraintProvider implements ConstraintProvider {
 
   private Constraint competencyConflict(ConstraintFactory constraintFactory) {
     // a volonteer must match required competencies.
-    return getAssignedSlotStream(constraintFactory).penalize("Competence conflict", HardMediumSoftScore.ONE_HARD,
-        Assignement::getCompetenceLackScore);
+    return getAssignedSlotStream(constraintFactory).penalize("Competence conflict",
+        HardMediumSoftScore.ONE_HARD, Assignement::getCompetenceLackScore);
   }
 
   private Constraint competencyTeachingEffort(ConstraintFactory constraintFactory) {
     // We favor volonteer that already have required competencies.
     // TODO: Idealy you should group by per volonteer and computed missing teachable
     // competencies
-    return getAssignedSlotStream(constraintFactory).penalize("Avoid teaching competences to volunteer",
-        HardMediumSoftScore.ONE_SOFT, Assignement::getSoftCompetenceLackScore);
+    return getAssignedSlotStream(constraintFactory).penalize(
+        "Avoid teaching competences to volunteer", HardMediumSoftScore.ONE_SOFT,
+        Assignement::getSoftCompetenceLackScore);
   }
 
   private Constraint preferenceApplication(ConstraintFactory constraintFactory) {
     // The system should favor timeslots selected by user.
-    return getAssignedSlotStream(constraintFactory).reward("Preference application", HardMediumSoftScore.ONE_SOFT,
-        Assignement::getPreferenceScore);
+    return getAssignedSlotStream(constraintFactory).reward("Preference application",
+        HardMediumSoftScore.ONE_SOFT, Assignement::getPreferenceScore);
   }
 
   private Constraint balanceLoad(ConstraintFactory constraintFactory) {
@@ -125,11 +129,13 @@ 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)
+    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.
-        .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD, (slot,
-            volonteerCount) -> (slot.getMaxAttendee() - volonteerCount) * (slot.getMaxAttendee() - volonteerCount));
+        .penalize("Meal max attendee", HardMediumSoftScore.ONE_HARD,
+            (slot, volonteerCount) -> (slot.getMaxAttendee() - volonteerCount)
+                * (slot.getMaxAttendee() - volonteerCount));
   }
 }

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


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/css/index.1d043421.css


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/css/login.a752cf82.css


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

@@ -1 +0,0 @@
-<!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.1112d078.css" rel="preload" as="style"><link href="/js/app.70178e53.js" rel="preload" as="script"><link href="/js/chunk-vendors.2f5ea497.js" rel="preload" as="script"><link href="/css/app.1112d078.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.2f5ea497.js"></script><script src="/js/app.70178e53.js"></script></body></html>

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


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


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


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


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


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


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/login.1fcb48c0.js


File diff suppressed because it is too large
+ 0 - 0
src/main/resources/static/js/login.1fcb48c0.js.map


+ 1 - 0
src/main/resources/static/login.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 planner Connexion</title><link href="/css/login.a752cf82.css" rel="preload" as="style"><link href="/js/chunk-vendors.121bdf9d.js" rel="preload" as="script"><link href="/js/login.1fcb48c0.js" rel="preload" as="script"><link href="/css/login.a752cf82.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but BDLG planner Connexion doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.121bdf9d.js"></script><script src="/js/login.1fcb48c0.js"></script></body></html>

+ 1 - 0
src/main/resources/static/planner/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 planner</title><link href="/css/index.1d043421.css" rel="preload" as="style"><link href="/js/chunk-vendors.121bdf9d.js" rel="preload" as="script"><link href="/js/index.ecd2c675.js" rel="preload" as="script"><link href="/css/index.1d043421.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but BDLG planner doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.121bdf9d.js"></script><script src="/js/index.ecd2c675.js"></script></body></html>

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