avant changement UI
This commit is contained in:
parent
793393b294
commit
a9bc1e47db
122
chat.json
122
chat.json
@ -1,41 +1,111 @@
|
||||
{
|
||||
"employees": [
|
||||
{
|
||||
"name": "Employee1",
|
||||
"skills": ["Skill1", "Skill2"],
|
||||
"unavailableDates": ["2025-01-01", "2025-01-02"],
|
||||
"undesiredDates": ["2025-01-03"],
|
||||
"desiredDates": ["2025-01-04"]
|
||||
"name": "Aurélie Antoine",
|
||||
"skills": ["INFIRMIER", "PRELEVEMENT"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Employee2",
|
||||
"skills": ["Skill2", "Skill3"],
|
||||
"unavailableDates": ["2025-01-02"],
|
||||
"undesiredDates": ["2025-01-05"],
|
||||
"desiredDates": ["2025-01-06"]
|
||||
"name": "Cathy Coucou",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Sophie Bernard-Dupont",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Jean Leroy",
|
||||
"skills": ["TRANSPORT", "PRELEVEMENT"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Anne Moreau",
|
||||
"skills": ["MEDECIN", "INFIRMIER"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": ["2025-12-21", "2025-12-29"]
|
||||
},
|
||||
{
|
||||
"name": "Luc Petit",
|
||||
"skills": ["ACCUEIL", "TRANSPORT"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": ["2024-12-25", "2024-12-26"]
|
||||
},
|
||||
{
|
||||
"name": "Marie Dubois",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "Pierre Martin",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": ["2024-12-21"],
|
||||
"desiredDates": ["2024-12-29", "2024-12-30"]
|
||||
}
|
||||
],
|
||||
"shifts": [
|
||||
"collectes": [
|
||||
{
|
||||
"id": "Shift1",
|
||||
"start": "2025-01-01T08:00:00",
|
||||
"end": "2025-01-01T16:00:00",
|
||||
"location": "Location1",
|
||||
"requiredSkill": "Skill1",
|
||||
"employee": {
|
||||
"name": "Employee1"
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Shift2",
|
||||
"start": "2025-01-02T08:00:00",
|
||||
"end": "2025-01-02T16:00:00",
|
||||
"location": "Location2",
|
||||
"requiredSkill": "Skill2",
|
||||
"employee": {
|
||||
"name": "Employee2"
|
||||
"id": "collecte_blagnac_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_purpan_20241222",
|
||||
"start": "2024-12-22T08:00:00",
|
||||
"end": "2024-12-22T16:00:00",
|
||||
"location": "Hôpital Purpan - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_rangueil_20241223",
|
||||
"start": "2024-12-23T08:00:00",
|
||||
"end": "2024-12-23T16:00:00",
|
||||
"location": "Hôpital Rangueil - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"PRELEVEMENT": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"score": null,
|
||||
"solverStatus": null
|
||||
}
|
||||
|
||||
|
||||
114
chat2.json
Normal file
114
chat2.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"employees": [
|
||||
{
|
||||
"name": "Aurélie Antoine",
|
||||
"skills": ["INFIRMIER", "PRELEVEMENT"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Cathy Coucou",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
},
|
||||
{
|
||||
"name": "Sophie Bernard-Dupont",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": [],
|
||||
"undesiredDates": [],
|
||||
"desiredDates": []
|
||||
}
|
||||
],
|
||||
"collectes": [
|
||||
{
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_blagnac_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 1,
|
||||
"MEDECIN": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"shifts": [
|
||||
{
|
||||
"id": "shift_toulouse_1",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkill": "INFIRMIER",
|
||||
"collecte": {
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shift_toulouse_2",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkill": "INFIRMIER",
|
||||
"collecte": {
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shift_toulouse_3",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkill": "MEDECIN",
|
||||
"collecte": {
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shift_blagnac_1",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkill": "INFIRMIER",
|
||||
"collecte": {
|
||||
"id": "collecte_blagnac_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shift_blagnac_2",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkill": "MEDECIN",
|
||||
"collecte": {
|
||||
"id": "collecte_blagnac_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
268
chat3.json
Normal file
268
chat3.json
Normal file
@ -0,0 +1,268 @@
|
||||
{
|
||||
"employees": [
|
||||
{
|
||||
"name": "Aurélie Antoine",
|
||||
"skills": ["INFIRMIER", "PRELEVEMENT"],
|
||||
"unavailableDates": ["2024-12-25", "2024-12-26", "2024-12-31"],
|
||||
"undesiredDates": ["2024-12-24", "2024-12-30"],
|
||||
"desiredDates": ["2024-12-20", "2024-12-21"]
|
||||
},
|
||||
{
|
||||
"name": "Cathy Coucou",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": ["2024-12-30", "2024-12-31"],
|
||||
"undesiredDates": ["2024-12-29", "2024-12-28"],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "Sophie Bernard-Dupont",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": ["2024-12-27", "2024-12-28"],
|
||||
"undesiredDates": ["2024-12-25", "2024-12-26"],
|
||||
"desiredDates": ["2024-12-24", "2024-12-29"]
|
||||
},
|
||||
{
|
||||
"name": "Jean Leroy",
|
||||
"skills": ["TRANSPORT", "PRELEVEMENT"],
|
||||
"unavailableDates": ["2024-12-24", "2024-12-25"],
|
||||
"undesiredDates": ["2024-12-23", "2024-12-22"],
|
||||
"desiredDates": ["2024-12-27", "2024-12-30"]
|
||||
},
|
||||
{
|
||||
"name": "Anne Moreau",
|
||||
"skills": ["MEDECIN", "INFIRMIER"],
|
||||
"unavailableDates": ["2024-12-26", "2024-12-27"],
|
||||
"undesiredDates": ["2024-12-31", "2024-12-20"],
|
||||
"desiredDates": ["2024-12-21", "2024-12-29"]
|
||||
},
|
||||
{
|
||||
"name": "Luc Petit",
|
||||
"skills": ["ACCUEIL", "TRANSPORT"],
|
||||
"unavailableDates": ["2024-12-20", "2024-12-21"],
|
||||
"undesiredDates": ["2024-12-22", "2024-12-23"],
|
||||
"desiredDates": ["2024-12-25", "2024-12-26"]
|
||||
},
|
||||
{
|
||||
"name": "Marie Dubois",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": ["2024-12-27", "2024-12-28"],
|
||||
"undesiredDates": ["2024-12-29", "2024-12-30"],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "Pierre Martin",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": ["2024-12-20", "2024-12-21"],
|
||||
"undesiredDates": ["2024-12-22", "2024-12-23"],
|
||||
"desiredDates": ["2024-12-29", "2024-12-30"]
|
||||
},
|
||||
{
|
||||
"name": "Claire Dupuis",
|
||||
"skills": ["INFIRMIER", "PRELEVEMENT"],
|
||||
"unavailableDates": ["2024-12-25", "2024-12-26"],
|
||||
"undesiredDates": ["2024-12-27", "2024-12-28"],
|
||||
"desiredDates": ["2024-12-20", "2024-12-21"]
|
||||
},
|
||||
{
|
||||
"name": "Thomas Lambert",
|
||||
"skills": ["TRANSPORT", "ACCUEIL"],
|
||||
"unavailableDates": ["2024-12-30", "2024-12-31"],
|
||||
"undesiredDates": ["2024-12-24", "2024-12-25"],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "Julie Lefèvre",
|
||||
"skills": ["INFIRMIER", "MEDECIN"],
|
||||
"unavailableDates": ["2024-12-22", "2024-12-23"],
|
||||
"undesiredDates": ["2024-12-24", "2024-12-25"],
|
||||
"desiredDates": ["2024-12-27", "2024-12-28"]
|
||||
},
|
||||
{
|
||||
"name": "Nicolas Bernard",
|
||||
"skills": ["PRELEVEMENT", "SUPERVISION"],
|
||||
"unavailableDates": ["2024-12-29", "2024-12-30"],
|
||||
"undesiredDates": ["2024-12-20", "2024-12-21"],
|
||||
"desiredDates": ["2024-12-25", "2024-12-26"]
|
||||
},
|
||||
{
|
||||
"name": "Isabelle Moreau",
|
||||
"skills": ["ACCUEIL", "INFIRMIER"],
|
||||
"unavailableDates": ["2024-12-24", "2024-12-25"],
|
||||
"undesiredDates": ["2024-12-26", "2024-12-27"],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "François Dubois",
|
||||
"skills": ["MEDECIN", "TRANSPORT"],
|
||||
"unavailableDates": ["2024-12-27", "2024-12-28"],
|
||||
"undesiredDates": ["2024-12-29", "2024-12-30"],
|
||||
"desiredDates": ["2024-12-20", "2024-12-21"]
|
||||
},
|
||||
{
|
||||
"name": "Élodie Martin",
|
||||
"skills": ["INFIRMIER", "ACCUEIL"],
|
||||
"unavailableDates": ["2024-12-31"],
|
||||
"undesiredDates": ["2024-12-25", "2024-12-26"],
|
||||
"desiredDates": ["2024-12-27", "2024-12-28"]
|
||||
},
|
||||
{
|
||||
"name": "Guillaume Lefèvre",
|
||||
"skills": ["PRELEVEMENT", "TRANSPORT"],
|
||||
"unavailableDates": ["2024-12-20", "2024-12-21"],
|
||||
"undesiredDates": ["2024-12-22", "2024-12-23"],
|
||||
"desiredDates": ["2024-12-29", "2024-12-30"]
|
||||
},
|
||||
{
|
||||
"name": "Caroline Lambert",
|
||||
"skills": ["MEDECIN", "SUPERVISION"],
|
||||
"unavailableDates": ["2024-12-25", "2024-12-26"],
|
||||
"undesiredDates": ["2024-12-27", "2024-12-28"],
|
||||
"desiredDates": ["2024-12-20", "2024-12-21"]
|
||||
},
|
||||
{
|
||||
"name": "Olivier Bernard",
|
||||
"skills": ["INFIRMIER", "PRELEVEMENT"],
|
||||
"unavailableDates": ["2024-12-22", "2024-12-23"],
|
||||
"undesiredDates": ["2024-12-24", "2024-12-25"],
|
||||
"desiredDates": ["2024-12-29", "2024-12-30"]
|
||||
},
|
||||
{
|
||||
"name": "Sandrine Moreau",
|
||||
"skills": ["ACCUEIL", "TRANSPORT"],
|
||||
"unavailableDates": ["2024-12-27", "2024-12-28"],
|
||||
"undesiredDates": ["2024-12-29", "2024-12-30"],
|
||||
"desiredDates": ["2024-12-20", "2024-12-21"]
|
||||
},
|
||||
{
|
||||
"name": "David Lefèvre",
|
||||
"skills": ["MEDECIN", "INFIRMIER"],
|
||||
"unavailableDates": ["2024-12-30", "2024-12-31"],
|
||||
"undesiredDates": ["2024-12-24", "2024-12-25"],
|
||||
"desiredDates": ["2024-12-22", "2024-12-23"]
|
||||
},
|
||||
{
|
||||
"name": "Céline Martin",
|
||||
"skills": ["SUPERVISION", "ACCUEIL"],
|
||||
"unavailableDates": ["2024-12-24", "2024-12-25"],
|
||||
"undesiredDates": ["2024-12-26", "2024-12-27"],
|
||||
"desiredDates": ["2024-12-29", "2024-12-30"]
|
||||
}
|
||||
],
|
||||
"collectes": [
|
||||
{
|
||||
"id": "collecte_toulouse_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_blagnac_20241220",
|
||||
"start": "2024-12-20T08:00:00",
|
||||
"end": "2024-12-20T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_purpan_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Hôpital Purpan - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_rangueil_20241221",
|
||||
"start": "2024-12-21T08:00:00",
|
||||
"end": "2024-12-21T16:00:00",
|
||||
"location": "Hôpital Rangueil - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"PRELEVEMENT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_toulouse_20241222",
|
||||
"start": "2024-12-22T08:00:00",
|
||||
"end": "2024-12-22T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_blagnac_20241222",
|
||||
"start": "2024-12-22T08:00:00",
|
||||
"end": "2024-12-22T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_purpan_20241223",
|
||||
"start": "2024-12-23T08:00:00",
|
||||
"end": "2024-12-23T16:00:00",
|
||||
"location": "Hôpital Purpan - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_rangueil_20241223",
|
||||
"start": "2024-12-23T08:00:00",
|
||||
"end": "2024-12-23T16:00:00",
|
||||
"location": "Hôpital Rangueil - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"PRELEVEMENT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_toulouse_20241224",
|
||||
"start": "2024-12-24T08:00:00",
|
||||
"end": "2024-12-24T16:00:00",
|
||||
"location": "Centre de collecte - Toulouse",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 3,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1,
|
||||
"TRANSPORT": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collecte_blagnac_20241224",
|
||||
"start": "2024-12-24T08:00:00",
|
||||
"end": "2024-12-24T16:00:00",
|
||||
"location": "Centre de collecte - Blagnac",
|
||||
"requiredSkills": {
|
||||
"INFIRMIER": 2,
|
||||
"MEDECIN": 1,
|
||||
"ACCUEIL": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
75
claude.puml
Normal file
75
claude.puml
Normal file
@ -0,0 +1,75 @@
|
||||
@startuml BloodCollectionSimple
|
||||
|
||||
!define PLANNING_ENTITY class
|
||||
!define PLANNING_SOLUTION class
|
||||
!define PROBLEM_FACT class
|
||||
|
||||
' Main solution class (même qu'avant)
|
||||
PLANNING_SOLUTION BloodCollectionSchedule {
|
||||
- List<Employee> employees
|
||||
- List<BloodCollection> bloodCollections
|
||||
- HardSoftBigDecimalScore score
|
||||
- SolverStatus solverStatus
|
||||
+ getAllShifts() : List<Shift>
|
||||
}
|
||||
|
||||
' Employee (inchangé)
|
||||
PROBLEM_FACT Employee {
|
||||
@PlanningId
|
||||
- String name
|
||||
- Set<String> skills
|
||||
- Set<LocalDate> unavailableDates
|
||||
- Set<LocalDate> undesiredDates
|
||||
- Set<LocalDate> desiredDates
|
||||
}
|
||||
|
||||
' Collecte de sang
|
||||
PROBLEM_FACT BloodCollection {
|
||||
@PlanningId
|
||||
- String id
|
||||
- String name
|
||||
- LocalDate date
|
||||
- String location
|
||||
- List<Shift> shifts
|
||||
+ getShifts() : List<Shift>
|
||||
+ isTeamComplete() : boolean
|
||||
}
|
||||
|
||||
' Shift (légèrement modifié)
|
||||
PLANNING_ENTITY Shift {
|
||||
@PlanningId
|
||||
- String id
|
||||
- LocalDateTime start
|
||||
- LocalDateTime end
|
||||
- String requiredSkill
|
||||
- BloodCollection parentCollection
|
||||
@PlanningVariable
|
||||
- Employee employee
|
||||
+ getParentCollection() : BloodCollection
|
||||
}
|
||||
|
||||
' Relations
|
||||
BloodCollectionSchedule ||--o{ Employee : contains
|
||||
BloodCollectionSchedule ||--o{ BloodCollection : contains
|
||||
|
||||
BloodCollection ||--o{ Shift : contains
|
||||
Shift }o--|| BloodCollection : belongs_to
|
||||
Shift }o--o| Employee : assigned_to
|
||||
|
||||
note right of BloodCollection
|
||||
Une collecte contient exactement:
|
||||
- 2 shifts INFIRMIER
|
||||
- 1 shift MEDECIN
|
||||
- 1 shift CHAUFFEUR
|
||||
- 1 shift ACCUEIL
|
||||
|
||||
Chacun avec ses propres horaires
|
||||
end note
|
||||
|
||||
note left of Shift
|
||||
Même concept qu'avant,
|
||||
mais maintenant lié à
|
||||
une collecte parente
|
||||
end note
|
||||
|
||||
@enduml
|
||||
64
gemini.puml
Normal file
64
gemini.puml
Normal file
@ -0,0 +1,64 @@
|
||||
@startuml
|
||||
!define DARK_BLUE #264653
|
||||
!define ORANGE #F4A261
|
||||
!define YELLOW #E9C46A
|
||||
!define LIGHT_BLUE #2A9D8F
|
||||
|
||||
skinparam class {
|
||||
BackgroundColor DARK_BLUE
|
||||
ArrowColor DARK_BLUE
|
||||
BorderColor DARK_BLUE
|
||||
FontColor WHITE
|
||||
}
|
||||
|
||||
skinparam arrow {
|
||||
Color DARK_BLUE
|
||||
}
|
||||
|
||||
' Définition des classes
|
||||
class Employee {
|
||||
+ name : String
|
||||
+ skills : Set<Skill>
|
||||
}
|
||||
|
||||
class BloodDrive {
|
||||
+ id : UUID
|
||||
+ location : String
|
||||
+ date : LocalDate
|
||||
--
|
||||
+ <<transient>> shifts : List<Shift>
|
||||
}
|
||||
|
||||
class Shift {
|
||||
+ id : UUID
|
||||
+ start : LocalDateTime
|
||||
+ end : LocalDateTime
|
||||
+ requiredSkill : Skill
|
||||
}
|
||||
|
||||
enum Skill {
|
||||
+ NURSE
|
||||
+ DOCTOR
|
||||
+ DRIVER
|
||||
+ RECEPTIONIST
|
||||
}
|
||||
|
||||
' Définition des relations
|
||||
BloodDrive "1" --> "1..*" Shift : <<compose>>
|
||||
Shift "1" --> "1" Employee : <<assigned to>>
|
||||
Shift "1" --> "1" Skill : <<requires>>
|
||||
Employee "1" -- "0..*" Shift : <<is assigned to>>
|
||||
Employee "1" -- "0..*" Skill : <<has>>
|
||||
|
||||
' Notes explicatives
|
||||
note top of BloodDrive
|
||||
Nouvelle entité pour regrouper
|
||||
tous les shifts d'une même collecte.
|
||||
end note
|
||||
|
||||
note top of Shift
|
||||
Chaque rôle (infirmier, médecin)
|
||||
est un shift distinct avec ses propres horaires.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -0,0 +1,69 @@
|
||||
package org.acme.employeescheduling.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class Collecte {
|
||||
private String id;
|
||||
private LocalDateTime start;
|
||||
private LocalDateTime end;
|
||||
private String location;
|
||||
private Map<String, Integer> requiredSkills;
|
||||
private List<Shift> shifts;
|
||||
|
||||
public Collecte() {
|
||||
this.requiredSkills = new HashMap<>();
|
||||
this.shifts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Collecte(String id, LocalDateTime start, LocalDateTime end, String location, Map<String, Integer> requiredSkills) {
|
||||
this.id = id;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.location = location;
|
||||
this.requiredSkills = requiredSkills != null ? requiredSkills : new HashMap<>();
|
||||
this.shifts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void generateShifts() {
|
||||
if (this.requiredSkills == null) {
|
||||
this.requiredSkills = new HashMap<>();
|
||||
}
|
||||
|
||||
this.shifts = this.requiredSkills.entrySet().stream()
|
||||
.flatMap(entry -> {
|
||||
String skill = entry.getKey();
|
||||
int quantity = entry.getValue();
|
||||
return IntStream.range(0, quantity)
|
||||
.mapToObj(i -> new Shift(
|
||||
this.id + "_" + skill + "_" + i,
|
||||
this.start,
|
||||
this.end,
|
||||
this.location,
|
||||
skill,
|
||||
null, // employee
|
||||
this // collecte
|
||||
));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public LocalDateTime getStart() { return start; }
|
||||
public void setStart(LocalDateTime start) { this.start = start; }
|
||||
public LocalDateTime getEnd() { return end; }
|
||||
public void setEnd(LocalDateTime end) { this.end = end; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public Map<String, Integer> getRequiredSkills() { return requiredSkills; }
|
||||
public void setRequiredSkills(Map<String, Integer> requiredSkills) { this.requiredSkills = requiredSkills; }
|
||||
public List<Shift> getShifts() { return shifts; }
|
||||
public void setShifts(List<Shift> shifts) { this.shifts = shifts; }
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
package org.acme.employeescheduling.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import ai.timefold.solver.core.api.domain.solution.PlanningEntityCollectionProperty;
|
||||
import ai.timefold.solver.core.api.domain.solution.PlanningScore;
|
||||
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
|
||||
@ -12,61 +13,50 @@ import ai.timefold.solver.core.api.solver.SolverStatus;
|
||||
|
||||
@PlanningSolution
|
||||
public class EmployeeSchedule {
|
||||
@ProblemFactCollectionProperty
|
||||
@ValueRangeProvider(id = "employeeRange")
|
||||
private List<Employee> employees;
|
||||
|
||||
@ProblemFactCollectionProperty
|
||||
@ValueRangeProvider
|
||||
private List<Employee> employees;
|
||||
private List<Collecte> collectes;
|
||||
|
||||
@PlanningEntityCollectionProperty
|
||||
private List<Shift> shifts;
|
||||
|
||||
@PlanningScore
|
||||
private HardSoftBigDecimalScore score;
|
||||
|
||||
private SolverStatus solverStatus;
|
||||
|
||||
// No-arg constructor required for Timefold
|
||||
public EmployeeSchedule() {}
|
||||
|
||||
public EmployeeSchedule(List<Employee> employees, List<Shift> shifts) {
|
||||
this.employees = employees;
|
||||
this.shifts = shifts;
|
||||
// Constructeur vide requis par Timefold
|
||||
public EmployeeSchedule() {
|
||||
this.employees = new ArrayList<>();
|
||||
this.collectes = new ArrayList<>();
|
||||
this.shifts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public EmployeeSchedule(HardSoftBigDecimalScore score, SolverStatus solverStatus) {
|
||||
this.score = score;
|
||||
this.solverStatus = solverStatus;
|
||||
// Constructeur principal
|
||||
public EmployeeSchedule(List<Employee> employees, List<Collecte> collectes) {
|
||||
this.employees = employees != null ? employees : new ArrayList<>();
|
||||
this.collectes = collectes != null ? collectes : new ArrayList<>();
|
||||
|
||||
// Générer les shifts à partir des collectes
|
||||
this.shifts = collectes != null ?
|
||||
collectes.stream()
|
||||
.peek(Collecte::generateShifts)
|
||||
.flatMap(collecte -> collecte.getShifts().stream())
|
||||
.collect(Collectors.toList()) :
|
||||
new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<Employee> getEmployees() {
|
||||
return employees;
|
||||
}
|
||||
|
||||
public void setEmployees(List<Employee> employees) {
|
||||
this.employees = employees;
|
||||
}
|
||||
|
||||
public List<Shift> getShifts() {
|
||||
return shifts;
|
||||
}
|
||||
|
||||
public void setShifts(List<Shift> shifts) {
|
||||
this.shifts = shifts;
|
||||
}
|
||||
|
||||
public HardSoftBigDecimalScore getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(HardSoftBigDecimalScore score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public SolverStatus getSolverStatus() {
|
||||
return solverStatus;
|
||||
}
|
||||
|
||||
public void setSolverStatus(SolverStatus solverStatus) {
|
||||
this.solverStatus = solverStatus;
|
||||
}
|
||||
// Getters et setters
|
||||
public List<Employee> getEmployees() { return employees; }
|
||||
public void setEmployees(List<Employee> employees) { this.employees = employees; }
|
||||
public List<Collecte> getCollectes() { return collectes; }
|
||||
public void setCollectes(List<Collecte> collectes) { this.collectes = collectes; }
|
||||
public List<Shift> getShifts() { return shifts; }
|
||||
public void setShifts(List<Shift> shifts) { this.shifts = shifts; }
|
||||
public HardSoftBigDecimalScore getScore() { return score; }
|
||||
public void setScore(HardSoftBigDecimalScore score) { this.score = score; }
|
||||
public SolverStatus getSolverStatus() { return solverStatus; }
|
||||
public void setSolverStatus(SolverStatus solverStatus) { this.solverStatus = solverStatus; }
|
||||
}
|
||||
|
||||
@ -20,28 +20,30 @@ public class Shift {
|
||||
|
||||
private String location;
|
||||
private String requiredSkill;
|
||||
private Collecte collecte;
|
||||
|
||||
@PlanningVariable
|
||||
@PlanningVariable(valueRangeProviderRefs = "employeeRange")
|
||||
private Employee employee;
|
||||
|
||||
public Shift() {
|
||||
}
|
||||
|
||||
public Shift(LocalDateTime start, LocalDateTime end, String location, String requiredSkill) {
|
||||
this(start, end, location, requiredSkill, null);
|
||||
this(null, start, end, location, requiredSkill, null, null);;
|
||||
}
|
||||
|
||||
public Shift(LocalDateTime start, LocalDateTime end, String location, String requiredSkill, Employee employee) {
|
||||
this(null, start, end, location, requiredSkill, employee);
|
||||
this(null, start, end, location, requiredSkill, employee, null);
|
||||
}
|
||||
|
||||
public Shift(String id, LocalDateTime start, LocalDateTime end, String location, String requiredSkill, Employee employee) {
|
||||
public Shift(String id, LocalDateTime start, LocalDateTime end, String location, String requiredSkill, Employee employee, Collecte collecte) {
|
||||
this.id = id;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.location = location;
|
||||
this.requiredSkill = requiredSkill;
|
||||
this.employee = employee;
|
||||
this.collecte = collecte;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@ -88,6 +90,9 @@ public class Shift {
|
||||
return employee;
|
||||
}
|
||||
|
||||
public Collecte getCollecte() { return collecte; }
|
||||
public void setCollecte(Collecte collecte) { this.collecte = collecte; }
|
||||
|
||||
public void setEmployee(Employee employee) {
|
||||
this.employee = employee;
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
@ -17,14 +16,12 @@ import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import ai.timefold.solver.core.api.score.analysis.ScoreAnalysis;
|
||||
import ai.timefold.solver.core.api.score.buildin.hardsoftbigdecimal.HardSoftBigDecimalScore;
|
||||
import ai.timefold.solver.core.api.solver.ScoreAnalysisFetchPolicy;
|
||||
import ai.timefold.solver.core.api.solver.SolutionManager;
|
||||
import ai.timefold.solver.core.api.solver.SolverManager;
|
||||
import ai.timefold.solver.core.api.solver.SolverStatus;
|
||||
|
||||
import org.acme.employeescheduling.domain.EmployeeSchedule;
|
||||
import org.acme.employeescheduling.rest.exception.EmployeeScheduleSolverException;
|
||||
import org.acme.employeescheduling.rest.exception.ErrorInfo;
|
||||
@ -42,13 +39,9 @@ import org.slf4j.LoggerFactory;
|
||||
@Tag(name = "Employee Schedules", description = "Employee Schedules service for assigning employees to shifts.")
|
||||
@Path("schedules")
|
||||
public class EmployeeScheduleResource {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeScheduleResource.class);
|
||||
|
||||
SolverManager<EmployeeSchedule, String> solverManager;
|
||||
SolutionManager<EmployeeSchedule, HardSoftBigDecimalScore> solutionManager;
|
||||
|
||||
// TODO: Without any "time to live", the map may eventually grow out of memory.
|
||||
private final ConcurrentMap<String, Job> jobIdToJob = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
@ -77,20 +70,36 @@ public class EmployeeScheduleResource {
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String solve(EmployeeSchedule problem) {
|
||||
String jobId = UUID.randomUUID().toString();
|
||||
jobIdToJob.put(jobId, Job.ofSchedule(problem));
|
||||
solverManager.solveBuilder()
|
||||
.withProblemId(jobId)
|
||||
.withProblemFinder(jobId_ -> jobIdToJob.get(jobId).schedule)
|
||||
.withBestSolutionConsumer(solution -> jobIdToJob.put(jobId, Job.ofSchedule(solution)))
|
||||
.withExceptionHandler((jobId_, exception) -> {
|
||||
jobIdToJob.put(jobId, Job.ofException(exception));
|
||||
LOGGER.error("Failed solving jobId ({}).", jobId, exception);
|
||||
})
|
||||
.run();
|
||||
return jobId;
|
||||
}
|
||||
public String solve(EmployeeSchedule problem) {
|
||||
String jobId = UUID.randomUUID().toString();
|
||||
LOGGER.info("Submitting problem with jobId: {}", jobId);
|
||||
LOGGER.info("Problem details - Employees: {}, Collectes: {}",
|
||||
problem.getEmployees().size(),
|
||||
problem.getCollectes().size());
|
||||
|
||||
jobIdToJob.put(jobId, Job.ofSchedule(problem));
|
||||
|
||||
LOGGER.info("Starting solver for jobId: {}", jobId);
|
||||
solverManager.solveBuilder()
|
||||
.withProblemId(jobId)
|
||||
.withProblemFinder(jobId_ -> {
|
||||
Job job = jobIdToJob.get(jobId);
|
||||
LOGGER.debug("Problem finder called for jobId: {}, problem: {}", jobId, job != null ? job.schedule : null);
|
||||
return job != null ? job.schedule : null;
|
||||
})
|
||||
.withBestSolutionConsumer(solution -> {
|
||||
LOGGER.info("New best solution for jobId: {}", jobId);
|
||||
jobIdToJob.put(jobId, Job.ofSchedule(solution));
|
||||
})
|
||||
.withExceptionHandler((jobId_, exception) -> {
|
||||
LOGGER.error("Exception during solving jobId {}: {}", jobId, exception.getMessage(), exception);
|
||||
jobIdToJob.put(jobId, Job.ofException(exception));
|
||||
})
|
||||
.run();
|
||||
|
||||
LOGGER.info("Solver started for jobId: {}", jobId);
|
||||
return jobId;
|
||||
}
|
||||
|
||||
@Operation(summary = "Submit a schedule to analyze its score.")
|
||||
@APIResponses(value = {
|
||||
@ -131,17 +140,6 @@ public class EmployeeScheduleResource {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private EmployeeSchedule getEmployeeScheduleAndCheckForExceptions(String jobId) {
|
||||
Job job = jobIdToJob.get(jobId);
|
||||
if (job == null) {
|
||||
throw new EmployeeScheduleSolverException(jobId, Response.Status.NOT_FOUND, "No schedule found.");
|
||||
}
|
||||
if (job.exception != null) {
|
||||
throw new EmployeeScheduleSolverException(jobId, job.exception);
|
||||
}
|
||||
return job.schedule;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Terminate solving for a given job ID. Returns the best solution of the schedule so far, as it might still be running or not even started.")
|
||||
@APIResponses(value = {
|
||||
@ -160,7 +158,6 @@ public class EmployeeScheduleResource {
|
||||
@Path("{jobId}")
|
||||
public EmployeeSchedule terminateSolving(
|
||||
@Parameter(description = "The job ID returned by the POST method.") @PathParam("jobId") String jobId) {
|
||||
// TODO: Replace with .terminateEarlyAndWait(... [, timeout]); see https://github.com/TimefoldAI/timefold-solver/issues/77
|
||||
solverManager.terminateEarly(jobId);
|
||||
return getEmployeeSchedule(jobId);
|
||||
}
|
||||
@ -170,7 +167,7 @@ public class EmployeeScheduleResource {
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "The schedule status and the best score so far.",
|
||||
content = @Content(mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = EmployeeSchedule.class))),
|
||||
schema = @Schema(implementation = ScheduleStatus.class))),
|
||||
@APIResponse(responseCode = "404", description = "No schedule found.",
|
||||
content = @Content(mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = ErrorInfo.class))),
|
||||
@ -181,21 +178,50 @@ public class EmployeeScheduleResource {
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("{jobId}/status")
|
||||
public EmployeeSchedule getStatus(
|
||||
public Response getStatus(
|
||||
@Parameter(description = "The job ID returned by the POST method.") @PathParam("jobId") String jobId) {
|
||||
EmployeeSchedule schedule = getEmployeeScheduleAndCheckForExceptions(jobId);
|
||||
SolverStatus solverStatus = solverManager.getSolverStatus(jobId);
|
||||
return new EmployeeSchedule(schedule.getScore(), solverStatus);
|
||||
|
||||
return Response.ok(new ScheduleStatus(schedule.getScore(), solverStatus)).build();
|
||||
}
|
||||
|
||||
private EmployeeSchedule getEmployeeScheduleAndCheckForExceptions(String jobId) {
|
||||
Job job = jobIdToJob.get(jobId);
|
||||
if (job == null) {
|
||||
throw new EmployeeScheduleSolverException(jobId, Response.Status.NOT_FOUND, "No schedule found.");
|
||||
}
|
||||
if (job.exception != null) {
|
||||
throw new EmployeeScheduleSolverException(jobId, job.exception);
|
||||
}
|
||||
return job.schedule;
|
||||
}
|
||||
|
||||
private record Job(EmployeeSchedule schedule, Throwable exception) {
|
||||
|
||||
static Job ofSchedule(EmployeeSchedule schedule) {
|
||||
return new Job(schedule, null);
|
||||
}
|
||||
|
||||
static Job ofException(Throwable error) {
|
||||
return new Job(null, error);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScheduleStatus {
|
||||
private HardSoftBigDecimalScore score;
|
||||
private SolverStatus solverStatus;
|
||||
|
||||
public ScheduleStatus(HardSoftBigDecimalScore score, SolverStatus solverStatus) {
|
||||
this.score = score;
|
||||
this.solverStatus = solverStatus;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public HardSoftBigDecimalScore getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public SolverStatus getSolverStatus() {
|
||||
return solverStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package org.acme.employeescheduling.solver;
|
||||
|
||||
import ai.timefold.solver.core.api.score.buildin.hardsoftbigdecimal.HardSoftBigDecimalScore;
|
||||
import ai.timefold.solver.core.api.score.stream.Constraint;
|
||||
import ai.timefold.solver.core.api.score.stream.ConstraintFactory;
|
||||
import ai.timefold.solver.core.api.score.stream.ConstraintProvider;
|
||||
import ai.timefold.solver.core.api.score.stream.ConstraintCollectors;
|
||||
// import org.acme.employeescheduling.domain.Collecte;
|
||||
import org.acme.employeescheduling.domain.Shift;
|
||||
|
||||
public class CollecteConstraintProvider implements ConstraintProvider {
|
||||
|
||||
@Override
|
||||
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
|
||||
return new Constraint[] {
|
||||
requiredSkills(constraintFactory)
|
||||
};
|
||||
}
|
||||
|
||||
private Constraint requiredSkills(ConstraintFactory constraintFactory) {
|
||||
return constraintFactory.forEach(Shift.class)
|
||||
.groupBy(Shift::getCollecte, Shift::getRequiredSkill, ConstraintCollectors.count())
|
||||
.penalize(HardSoftBigDecimalScore.ONE_HARD,
|
||||
(collecte, skill, count) -> {
|
||||
int requiredQuantity = collecte.getRequiredSkills().getOrDefault(skill, 0);
|
||||
return Math.max(0, requiredQuantity - count);
|
||||
})
|
||||
.asConstraint("Insufficient employees with required skill");
|
||||
}
|
||||
}
|
||||
@ -48,19 +48,20 @@ public class EmployeeSchedulingConstraintProvider implements ConstraintProvider
|
||||
};
|
||||
}
|
||||
|
||||
Constraint requiredSkill(ConstraintFactory constraintFactory) {
|
||||
return constraintFactory.forEach(Shift.class)
|
||||
.filter(shift -> !shift.getEmployee().getSkills().contains(shift.getRequiredSkill()))
|
||||
.penalize(HardSoftBigDecimalScore.ONE_HARD)
|
||||
.asConstraint("Missing required skill");
|
||||
private Constraint requiredSkill(ConstraintFactory constraintFactory) {
|
||||
return constraintFactory.forEach(Shift.class)
|
||||
.filter(shift -> shift.getEmployee() != null &&
|
||||
!shift.getEmployee().getSkills().contains(shift.getRequiredSkill()))
|
||||
.penalize(HardSoftBigDecimalScore.ONE_HARD)
|
||||
.asConstraint("Missing required skill");
|
||||
}
|
||||
|
||||
Constraint noOverlappingShifts(ConstraintFactory constraintFactory) {
|
||||
return constraintFactory.forEachUniquePair(Shift.class, equal(Shift::getEmployee),
|
||||
overlapping(Shift::getStart, Shift::getEnd))
|
||||
.penalize(HardSoftBigDecimalScore.ONE_HARD,
|
||||
EmployeeSchedulingConstraintProvider::getMinuteOverlap)
|
||||
.asConstraint("Overlapping shift");
|
||||
private Constraint noOverlappingShifts(ConstraintFactory constraintFactory) {
|
||||
return constraintFactory.forEachUniquePair(Shift.class,
|
||||
equal(Shift::getEmployee),
|
||||
overlapping(shift -> shift.getStart(), shift -> shift.getEnd()))
|
||||
.penalize(HardSoftBigDecimalScore.ONE_HARD)
|
||||
.asConstraint("Overlapping shift");
|
||||
}
|
||||
|
||||
Constraint atLeast10HoursBetweenTwoShifts(ConstraintFactory constraintFactory) {
|
||||
|
||||
@ -41,3 +41,6 @@ quarkus.swagger-ui.always-include=true
|
||||
########################
|
||||
|
||||
%test.quarkus.timefold.solver.termination.spent-limit=10s
|
||||
|
||||
quarkus.log.category."ai.timefold.solver".level=DEBUG
|
||||
quarkus.log.category."org.acme.employeescheduling".level=DEBUG
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -41,3 +41,6 @@ quarkus.swagger-ui.always-include=true
|
||||
########################
|
||||
|
||||
%test.quarkus.timefold.solver.termination.spent-limit=10s
|
||||
|
||||
quarkus.log.category."ai.timefold.solver".level=DEBUG
|
||||
quarkus.log.category."org.acme.employeescheduling".level=DEBUG
|
||||
|
||||
BIN
target/classes/org/acme/employeescheduling/domain/Collecte.class
Normal file
BIN
target/classes/org/acme/employeescheduling/domain/Collecte.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
org/acme/employeescheduling/domain/EmployeeSchedule.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$CountDistribution.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$DemoDataParameters.class
|
||||
org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverException.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleDemoResource.class
|
||||
org/acme/employeescheduling/solver/EmployeeSchedulingConstraintProvider.class
|
||||
org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverExceptionMapper.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResource$Job.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResource.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator.class
|
||||
org/acme/employeescheduling/domain/Shift.class
|
||||
org/acme/employeescheduling/domain/Employee.class
|
||||
org/acme/employeescheduling/rest/exception/ErrorInfo.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$DemoData.class
|
||||
@ -1,10 +0,0 @@
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/domain/Employee.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/domain/EmployeeSchedule.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/domain/Shift.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/DemoDataGenerator.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/EmployeeScheduleDemoResource.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/EmployeeScheduleResource.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverException.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverExceptionMapper.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/rest/exception/ErrorInfo.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/main/java/org/acme/employeescheduling/solver/EmployeeSchedulingConstraintProvider.java
|
||||
@ -0,0 +1,16 @@
|
||||
org/acme/employeescheduling/domain/EmployeeSchedule.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$CountDistribution.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$DemoDataParameters.class
|
||||
org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverException.class
|
||||
org/acme/employeescheduling/domain/Collecte.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleDemoResource.class
|
||||
org/acme/employeescheduling/solver/EmployeeSchedulingConstraintProvider.class
|
||||
org/acme/employeescheduling/rest/exception/EmployeeScheduleSolverExceptionMapper.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResource$Job.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResource.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator.class
|
||||
org/acme/employeescheduling/domain/Shift.class
|
||||
org/acme/employeescheduling/domain/Employee.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResource$ScheduleStatus.class
|
||||
org/acme/employeescheduling/rest/exception/ErrorInfo.class
|
||||
org/acme/employeescheduling/rest/DemoDataGenerator$DemoData.class
|
||||
@ -1,3 +1,4 @@
|
||||
/home/virt/timefold-quickstarts/java/collect-sang/src/main/java/org/acme/employeescheduling/domain/Collecte.java
|
||||
/home/virt/timefold-quickstarts/java/collect-sang/src/main/java/org/acme/employeescheduling/domain/Employee.java
|
||||
/home/virt/timefold-quickstarts/java/collect-sang/src/main/java/org/acme/employeescheduling/domain/EmployeeSchedule.java
|
||||
/home/virt/timefold-quickstarts/java/collect-sang/src/main/java/org/acme/employeescheduling/domain/Shift.java
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
org/acme/employeescheduling/solver/EmployeeSchedulingConstraintProviderTest.class
|
||||
org/acme/employeescheduling/rest/EmployeeScheduleResourceTest.class
|
||||
org/acme/employeescheduling/rest/EmployeeSchedulingEnvironmentTest.class
|
||||
org/acme/employeescheduling/rest/EmployeeSchedulingResourceIT.class
|
||||
@ -1,4 +0,0 @@
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/test/java/org/acme/employeescheduling/rest/EmployeeScheduleResourceTest.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/test/java/org/acme/employeescheduling/rest/EmployeeSchedulingEnvironmentTest.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/test/java/org/acme/employeescheduling/rest/EmployeeSchedulingResourceIT.java
|
||||
/home/virt/timefold-quickstarts/java/employee-scheduling/src/test/java/org/acme/employeescheduling/solver/EmployeeSchedulingConstraintProviderTest.java
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user