From e002197269c05b6ef4d16719601585d87ee670ac Mon Sep 17 00:00:00 2001 From: rdubois Date: Thu, 1 Aug 2024 14:08:16 +0200 Subject: [PATCH 01/10] feat(ci/cd): use our own runner image DEVOPS-77 --- .github/workflows/deb-test.yml | 28 ++++++++++++++++++++++++++++ .github/workflows/deb.yml | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/deb-test.yml create mode 100644 .github/workflows/deb.yml diff --git a/.github/workflows/deb-test.yml b/.github/workflows/deb-test.yml new file mode 100644 index 0000000..d56ccd4 --- /dev/null +++ b/.github/workflows/deb-test.yml @@ -0,0 +1,28 @@ +name: Deb build push notify +on: + push: + branches: + - dev-test + workflow_dispatch: + +jobs: + deb: + runs-on: gha-runners-teleservices + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set package version variable + shell: bash + run: | + echo VERSION=$(cat setup.py | grep "version =" | cut -d '"' -f 2 && echo "-" && date "+%Y%m%d%H%M%S" && echo "~" && echo ${{ github.sha }} | cut -c1-7 ) | tr -d [:space:] >> $GITHUB_ENV + - name: Deb build push and notify + uses: IMIO/gha/deb-build-push-notify@v3.7.2 + with: + REPOSITORY_URL: ${{ secrets.NEXUS_BOOKWORM_TEST_URL }} + REPOSITORY_LOGIN: ${{ secrets.NEXUS_LOGIN }} + REPOSITORY_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + PACKAGE_NAME: ${{ github.event.repository.name }} + PACKAGE_VERSION: ${{ env.VERSION }} + SIGNER_KEY_PASSPHRASE: ${{ secrets.DEB_SIGNER_PASSPHRASE }} + SIGNER_KEY: ${{ secrets.DEB_SIGNER_KEY }} + MATTERMOST_WEBHOOK_URL: ${{ secrets.TELESERVICES_MATTERMOST_WEBHOOK_URL }} diff --git a/.github/workflows/deb.yml b/.github/workflows/deb.yml new file mode 100644 index 0000000..029c13c --- /dev/null +++ b/.github/workflows/deb.yml @@ -0,0 +1,28 @@ +name: Deb build push notify +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + deb: + runs-on: gha-runners-teleservices + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set package version variable + shell: bash + run: | + echo VERSION=$(cat setup.py | grep "version =" | cut -d '"' -f 2 && echo "-" && date "+%Y%m%d%H%M%S" && echo "~" && echo ${{ github.sha }} | cut -c1-7 ) | tr -d [:space:] >> $GITHUB_ENV + - name: Deb build push and notify + uses: IMIO/gha/deb-build-push-notify@v3.7.2 + with: + REPOSITORY_URL: ${{ secrets.NEXUS_BOOKWORM_URL }} + REPOSITORY_LOGIN: ${{ secrets.NEXUS_LOGIN }} + REPOSITORY_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + PACKAGE_NAME: ${{ github.event.repository.name }} + PACKAGE_VERSION: ${{ env.VERSION }} + SIGNER_KEY_PASSPHRASE: ${{ secrets.DEB_SIGNER_PASSPHRASE }} + SIGNER_KEY: ${{ secrets.DEB_SIGNER_KEY }} + MATTERMOST_WEBHOOK_URL: ${{ secrets.TELESERVICES_MATTERMOST_WEBHOOK_URL }} From 2e9dfc56dd25f33d77017fa86457921ad7aa42f4 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Mon, 27 May 2024 08:55:31 +0200 Subject: [PATCH 02/10] [TELE-1918]add generate_day_availability --- passerelle_imio_ia_tech/models.py | 49 +++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 4c39099..0e5291d 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -494,8 +494,7 @@ def read_dates_dispo(self, request, room, delai=0): locations = self.read_reservations_room_details(request) # définition de la date d'aujourd'hui en datetime - now = datetime.datetime.now() - today = datetime.datetime(now.year, now.month, now.day) + today = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) # convert string to dict # room = ast.literal_eval(room) @@ -512,13 +511,57 @@ def read_dates_dispo(self, request, room, delai=0): return {"data": locations} + @endpoint( + name="generate-day-availability", + perm="can_access", + description="Générer la source de données des jours de disponibilité", + long_description="Générer la source de données des jours de disponibilité pour une salle donnée dans ATAL.", + display_category="Location de Salles", + display_order=8, + methods=["get"], + parameters={ + "room": { + "description": "salle", + "type": "int", + "example_value": 2732, + }, + "start": { + "description": "délai minimum à l'introduction de la demande", + "type": "int", + "example_value": 90, + }, + "end": { + "description": "délai maximum à l'introduction de la demande", + "type": "int", + "example_value": 365, + }, + }, + ) + def generate_day_availability(self, request, room, start=0, end=365): + indisponibilites = self.read_dates_dispo(request, room, start)["data"] + start_date = datetime.date.today() + datetime.timedelta(days=start) + end_date = datetime.date.today() + datetime.timedelta(days=end) + delta = end_date - start_date + days = [start_date + datetime.timedelta(days=x) for x in range(delta.days + 1)] + free_days = [] + for day in days: + text = day.strftime("%d/%m/%Y") + id = day.strftime("%Y-%m-%d") + disabled = True in [ + string_to_datetime(x["StartDate"]) <= day <= string_to_datetime(x["EndDate"]) + for x in indisponibilites + ] + free_days.append({"text": text, "id": id, "disabled": disabled}) + return {"data": free_days} + + @endpoint( name="post-reservation-room", perm="can_access", description="Inscrit une réservation de salle.", long_description="Inscrit une réservation de salle dans ATAL.", display_category="Location de Salles", - display_order=8, + display_order=9, methods=["get"], parameters={ "date_debut": { From 360eae472ae843b1f79cbf37867496a7435add8a Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Mon, 27 May 2024 21:19:51 +0200 Subject: [PATCH 03/10] [TELE-1918]add booking_room and update generate_day_availability --- passerelle_imio_ia_tech/models.py | 137 ++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 37 deletions(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 0e5291d..77a75ee 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -177,9 +177,9 @@ def post_attachment(self, request): }, }, long_description=( - "Actuellement employé dans Townstreet. Ce endpoint est plus " - "complet, mais devrait être refactoré, néttoyé (code obsolète) et " - "converti en GET" + "Actuellement employé dans Townstreet. Ce endpoint est plus " + "complet, mais devrait être refactoré, néttoyé (code obsolète) et " + "converti en GET" ), display_category="Demandes de travaux", ) @@ -230,10 +230,10 @@ def get_work_request_details(self, request, *args, **kwargs): }, }, long_description=( - "Pas employé dans Townstreet. Devrait être employé en mergeant " - "avec get_work_request_details. L'améliorer pour gérer " - "efficacement les exceptions (voir le code du endpoint) " - "'get-natures'). Adapter le workflow de Townstreet." + "Pas employé dans Townstreet. Devrait être employé en mergeant " + "avec get_work_request_details. L'améliorer pour gérer " + "efficacement les exceptions (voir le code du endpoint) " + "'get-natures'). Adapter le workflow de Townstreet." ), display_category="Demandes de travaux", ) @@ -453,10 +453,10 @@ def read_rooms_dispo(self, request, date_debut, date_fin, heure_debut, heure_fin # tri des salles en fonction des dates de locations if ( - debut_location <= datetime_debut <= fin_location - or debut_location <= datetime_fin <= fin_location - or datetime_debut <= debut_location <= datetime_fin - or datetime_debut <= fin_location <= datetime_fin + debut_location <= datetime_debut <= fin_location + or debut_location <= datetime_fin <= fin_location + or datetime_debut <= debut_location <= datetime_fin + or datetime_debut <= fin_location <= datetime_fin ): room_non_dispo.append(location.get("RoomId")) @@ -504,9 +504,9 @@ def read_dates_dispo(self, request, room, delai=0): x for x in locations if "RoomId" in x - and x["RoomId"] == int(room) - and (today + datetime.timedelta(days=delai)) - < string_to_datetime(x["StartDate"]) + and x["RoomId"] == int(room) + and (today + datetime.timedelta(days=delai)) + < string_to_datetime(x["StartDate"]) ] return {"data": locations} @@ -547,13 +547,76 @@ def generate_day_availability(self, request, room, start=0, end=365): for day in days: text = day.strftime("%d/%m/%Y") id = day.strftime("%Y-%m-%d") + start_date = day.strftime("%Y-%m-%d") + end_date = day.strftime("%Y-%m-%d") + start_time = "00:00" + end_time = "23:59" disabled = True in [ - string_to_datetime(x["StartDate"]) <= day <= string_to_datetime(x["EndDate"]) + string_to_datetime(x["StartDate"]).date() <= day <= string_to_datetime(x["EndDate"]).date() for x in indisponibilites ] - free_days.append({"text": text, "id": id, "disabled": disabled}) + free_days.append( + {"text": text, "id": id, "start_date": start_date, "end_date": end_date, "start_time": start_time, + "end_time": end_time, "disabled": disabled}) return {"data": free_days} + @endpoint( + name="bookings-room", + perm="can_access", + description="Réserver une salle", + long_description="Réserver une salle dans ATAL avec plusieurs plages horaires.", + display_category="Location de Salles", + display_order=9, + methods=["post"], + parameters={ + "room": { + "description": "salle", + "type": "int", + "example_value": 2732, + }, + "nombre_personne_prevue": { + "description": "Nombre de personne prévue", + "type": "int", + "example_value": 0, + }, + "nombre_personne_reel": { + "description": "Nombre de personne réel", + "type": "int", + "example_value": 0, + }, + "id_tier": { + "description": "Aucune idée", + "type": "int", + "example_value": 63, + }, + } + ) + def bookings_room(self, request, room, nombre_personne_prevue=0, nombre_personne_reel=0, id_tier=63): + post_data = json.loads(request.body) + booking_dates = post_data.get("booking_dates", []) + delta = datetime.timedelta(minutes=1) + merged_intervals = [] + current_interval = booking_dates[0] + + for i in range(1, len(booking_dates)): + current_end = string_to_datetime(f'{current_interval["end_date"]}T{current_interval["end_time"]}') + next_start = string_to_datetime(f'{booking_dates[i]["start_date"]}T{booking_dates[i]["start_time"]}') + + # Vérifier s'il n'y a qu'une minute d'écart + if next_start == current_end + delta: + current_interval["end_date"] = booking_dates[i]["end_date"] + current_interval["end_time"] = booking_dates[i]["end_time"] + else: + merged_intervals.append(current_interval) + current_interval = booking_dates[i] + + # Ajouter le dernier intervalle + merged_intervals.append(current_interval) + + for interval in merged_intervals: + self.write_reservation_room(request, interval["start_date"], interval["end_date"], interval["start_time"], + interval["end_time"], room, nombre_personne_prevue, nombre_personne_reel, + id_tier) @endpoint( name="post-reservation-room", @@ -561,7 +624,7 @@ def generate_day_availability(self, request, room, start=0, end=365): description="Inscrit une réservation de salle.", long_description="Inscrit une réservation de salle dans ATAL.", display_category="Location de Salles", - display_order=9, + display_order=10, methods=["get"], parameters={ "date_debut": { @@ -607,16 +670,16 @@ def generate_day_availability(self, request, room, start=0, end=365): }, ) def write_reservation_room( - self, - request, - date_debut, - date_fin, - heure_debut, - heure_fin, - room, - nombre_personne_prevue=0, - nombre_personne_reel=0, - id_tier=63, + self, + request, + date_debut, + date_fin, + heure_debut, + heure_fin, + room, + nombre_personne_prevue=0, + nombre_personne_reel=0, + id_tier=63, ): url = f"{self.base_url}/api/RoomLoans" headers = { @@ -863,15 +926,15 @@ def read_materiel_loans_details(self, request): }, ) def post_material_location( - self, - request, - date_debut, - date_fin, - heure_debut, - heure_fin, - material, - quantity, - id_tier, + self, + request, + date_debut, + date_fin, + heure_debut, + heure_fin, + material, + quantity, + id_tier, ): url = f"{self.base_url}/api/MaterialLoans" headers = { @@ -936,7 +999,7 @@ def post_material_location( display_category="Utilitaires", ) def get_atal_thematics( - self, request=None, primary_only=False, secondary_only=False, parent_id=None + self, request=None, primary_only=False, secondary_only=False, parent_id=None ): url = f"{self.base_url}/api/Thematics" headers = { From 2cc6662d3b9b10d76adbc5db54e3722ec11a276e Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Mon, 27 May 2024 21:35:16 +0200 Subject: [PATCH 04/10] [TELE-1918]fix min delai for generate_day_availability --- passerelle_imio_ia_tech/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 77a75ee..4d6b3a2 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -542,7 +542,7 @@ def generate_day_availability(self, request, room, start=0, end=365): start_date = datetime.date.today() + datetime.timedelta(days=start) end_date = datetime.date.today() + datetime.timedelta(days=end) delta = end_date - start_date - days = [start_date + datetime.timedelta(days=x) for x in range(delta.days + 1)] + days = [start_date + datetime.timedelta(days=x) for x in range(1, delta.days + 1)] free_days = [] for day in days: text = day.strftime("%d/%m/%Y") From d2cb2ae595f0955311ab5235b444afea118ae470 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Mon, 27 May 2024 21:46:34 +0200 Subject: [PATCH 05/10] [TELE-1918]revert fix min delai for generate_day_availability --- passerelle_imio_ia_tech/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 4d6b3a2..77a75ee 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -542,7 +542,7 @@ def generate_day_availability(self, request, room, start=0, end=365): start_date = datetime.date.today() + datetime.timedelta(days=start) end_date = datetime.date.today() + datetime.timedelta(days=end) delta = end_date - start_date - days = [start_date + datetime.timedelta(days=x) for x in range(1, delta.days + 1)] + days = [start_date + datetime.timedelta(days=x) for x in range(delta.days + 1)] free_days = [] for day in days: text = day.strftime("%d/%m/%Y") From 95d56c9921f2f7930aff7446beb77b9aa2388259 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Tue, 28 May 2024 13:54:39 +0200 Subject: [PATCH 06/10] [TELE-1918]add generate data source for hours --- passerelle_imio_ia_tech/models.py | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 77a75ee..f7aa9f6 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -560,6 +560,56 @@ def generate_day_availability(self, request, room, start=0, end=365): "end_time": end_time, "disabled": disabled}) return {"data": free_days} + @endpoint( + name="generate-hour-availability", + perm="can_access", + description="Générer la source de données des heures de disponibilité", + long_description="Générer la source de données des heures de disponibilité pour une salle donnée dans ATAL.", + display_category="Location de Salles", + display_order=8, + methods=["get"], + parameters={ + "room": { + "description": "salle", + "type": "int", + "example_value": 2732, + }, + "start": { + "description": "délai minimum à l'introduction de la demande", + "type": "int", + "example_value": 90, + }, + "end": { + "description": "délai maximum à l'introduction de la demande", + "type": "int", + "example_value": 365, + }, + }, + ) + def generate_hour_availability(self, request, room, start=0, end=365): + indisponibilites = self.read_dates_dispo(request, room, start)["data"] + start_datetime = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time()) + datetime.timedelta(days=start) + end_datetime = datetime.datetime.combine(datetime.date.today(), datetime.time(23, 0)) + datetime.timedelta(days=end) + delta = int((end_datetime - start_datetime).total_seconds() / 3600) + hours = [start_datetime + datetime.timedelta(hours=x) for x in range(delta + 1)] + free_hours = [] + for hour in hours: + text = hour.strftime("%d/%m/%Y %H:%M") + id = hour.strftime("%Y-%m-%dT%H:%M") + start_date = hour.strftime("%Y-%m-%d") + end_date = hour.strftime("%Y-%m-%d") + start_time = hour.strftime("%H:%M") + end_time = hour.strftime("%H:59") + disabled = True in [ + string_to_datetime(x["StartDate"]).date() <= hour <= string_to_datetime(x["EndDate"]).date() and + string_to_datetime(x["StartDate"]).hour <= hour.hour <= string_to_datetime(x["EndDate"]).hour + for x in indisponibilites + ] + free_hours.append( + {"text": text, "id": id, "start_date": start_date, "end_date": end_date, "start_time": start_time, + "end_time": end_time, "disabled": disabled}) + return {"data": free_hours} + @endpoint( name="bookings-room", perm="can_access", From 07e91f6f2f7aab6b4d22dcbb4eaa975ca14acf69 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Tue, 28 May 2024 14:07:29 +0200 Subject: [PATCH 07/10] [TELE-1918]fix generate data source for hours --- passerelle_imio_ia_tech/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index f7aa9f6..4745683 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -601,7 +601,7 @@ def generate_hour_availability(self, request, room, start=0, end=365): start_time = hour.strftime("%H:%M") end_time = hour.strftime("%H:59") disabled = True in [ - string_to_datetime(x["StartDate"]).date() <= hour <= string_to_datetime(x["EndDate"]).date() and + string_to_datetime(x["StartDate"]).date() <= hour.date() <= string_to_datetime(x["EndDate"]).date() and string_to_datetime(x["StartDate"]).hour <= hour.hour <= string_to_datetime(x["EndDate"]).hour for x in indisponibilites ] From afbca6191a0fdac840d9410bc3bbeb114e98012d Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Tue, 2 Jul 2024 10:56:15 +0200 Subject: [PATCH 08/10] [TELE-1918]update return of bookings room --- passerelle_imio_ia_tech/models.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 4745683..8d741ce 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -663,10 +663,15 @@ def bookings_room(self, request, room, nombre_personne_prevue=0, nombre_personne # Ajouter le dernier intervalle merged_intervals.append(current_interval) + responses = {"data": []} + for interval in merged_intervals: - self.write_reservation_room(request, interval["start_date"], interval["end_date"], interval["start_time"], + response = self.write_reservation_room(request, interval["start_date"], interval["end_date"], interval["start_time"], interval["end_time"], room, nombre_personne_prevue, nombre_personne_reel, id_tier) + responses["data"].append(response) + + return responses @endpoint( name="post-reservation-room", From 327d85c8481d18723b3df8ce65171dfbbf87cc32 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Thu, 4 Jul 2024 13:27:15 +0200 Subject: [PATCH 09/10] [TELE-1918]add patch to booking room --- passerelle_imio_ia_tech/models.py | 65 +++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 8d741ce..6c96fc4 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -13,6 +13,7 @@ from passerelle.base.models import BaseResource from passerelle.utils.api import endpoint from passerelle.utils.jsonresponse import APIError +from requests import RequestException # TODO : we should rename this class name with something like AtalConnector @@ -781,6 +782,70 @@ def write_reservation_room( return response.json() + @endpoint( + name="patch-booking-room", + perm="can_access", + description="Modifie le statut d'une réservation de salle.", + long_description="Modifie le statut d'une réservation de salle dans ATAL.", + display_category="Location de Salles", + display_order=11, + methods=["patch"], + parameters={ + "room_loan_id": { + "description": "id de la réservation de salle", + "type": "int", + "example_value": 2573, + }, + "request_state": { + "description": "statut de la réservation : 0 = Pending, 2 = Accepted, 3 = Refused, 4 = Cancelled", + "type": "int", + "example_value": 0, + }, + }, + ) + def update_booking_room( + self, + request, + room_loan_id, + request_state, + ): + url = f"{self.base_url}/api/RoomLoans?id={room_loan_id}" + headers = { + "accept": "application/json", + "X-API-Key": self.api_key, + "Content-Type": "application/json", + } + payload = json.dumps({"RequestState": request_state}) + + try: + response = requests.patch( + url, + headers=headers, + data=payload, + verify=False, + ) + except RequestException as e: + self.logger.warning(f'ATAL Error: {e}') + raise APIError(f'ATAL Error: {e}') + + if response.headers.get('Content-Type') == 'application/json': + json_response = None + try: + json_response = response.json() + except ValueError: + self.logger.warning('ATAL Error: bad JSON response') + raise APIError('ATAL Error: bad JSON response') + + return json_response + + try: + response.raise_for_status() + except RequestException as e: + self.logger.warning(f'ATAL Error: {e}') + raise APIError(f'ATAL Error: {e}') + self.logger.info(f'ATAL PATCH Booking Room {room_loan_id} successful') + return + ############################# ### Location de matériels ### ############################# From b68446aca3ac3244763f811778f08afcc27f5655 Mon Sep 17 00:00:00 2001 From: Nicolas Selva Date: Mon, 30 Sep 2024 13:36:41 +0200 Subject: [PATCH 10/10] update return of read reservation room --- passerelle_imio_ia_tech/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passerelle_imio_ia_tech/models.py b/passerelle_imio_ia_tech/models.py index 6c96fc4..5618cc8 100644 --- a/passerelle_imio_ia_tech/models.py +++ b/passerelle_imio_ia_tech/models.py @@ -360,7 +360,7 @@ def read_reservation_room(self, request, id): response.raise_for_status() - return response.json() # must return dict + return {"data": response.json()} # must return dict @endpoint( name="get-reservation-room-detail",