Repository: LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas
Pull request #283: Trabajos automatizados backend
Author: login: Javiruizg
Labels: name: Feature
description: This issue is a Feature
name: backend
name: Review effort 3/5
Created: 2025-04-06T22:45:50Z | Merged: 2025-04-07T21:47:52Z

### **User description**
Ahora mismo lanzando los siguientes comandos se haria una limpieza de la base de datos borrando todos los objetos archivados que ya pueden ser borrados:

- docker run -p 6379:6379 redis
- celery -A pawtel worker -P solo --loglevel=info
- celery -A pawtel beat --loglevel=info


___

### **PR Type**
Enhancement, Configuration changes


___

### **Description**
- Integrated Celery for task automation and scheduling.
  - Added tasks for cleaning up old and inactive data.
  - Configured periodic task schedules using Celery Beat.

- Added Redis as the Celery broker and result backend.

- Updated Django settings to support Celery and task scheduling.

- Introduced cleanup tasks for bookings, booking holds, hotels, and room types.


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>6 files</summary><table>
<tr>
  <td><strong>__init__.py</strong><dd><code>Expose Celery app for project-wide usage</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-9fc2f490adc8ff57428389a8c8469a5aa29d6754f691311af0655aa84645f33c">+3/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tasks.py</strong><dd><code>Add task to delete inactive booking holds</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-cb7f5f61ff27dfd2562f1a01e2bfd25ab5e0766ab04bdac9787672d87f66245e">+9/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tasks.py</strong><dd><code>Add task to delete old bookings</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-8edb0724801135b8ee8a21e276e195b3d2de68f58b42e9453408c4510a8d8057">+17/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>celery.py</strong><dd><code>Configure Celery app with Django settings</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-5a59ce4666473a5c23be83d91eb87d617b29300b61e7c9cdf1507a204fa5d774">+14/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tasks.py</strong><dd><code>Add task to delete archived hotels without recent bookings</code></dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-95da12f29844c841d5b69819ac310fb8590f04a79bd54a5b7c64a286b372af31">+29/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tasks.py</strong><dd><code>Add task to delete archived room types without recent bookings</code></dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-78607024481a4187326529bf0358943739817fd4063dcf9cb55bee6a12db60e7">+24/-0</a>&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>3 files</summary><table>
<tr>
  <td><strong>settings.py</strong><dd><code>Configure Celery and periodic task schedules</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-cc0eee54c7138abed89014c7760014b3701bc42973715f6a9522311f8d47395a">+28/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>celerybeat-schedule.bak</strong><dd><code>Add Celery Beat schedule backup file</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-876176ad5243e5d4fffef0e38a17542c9683f7429a3e753f75e8ebe76255412e">+4/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>celerybeat-schedule.dir</strong><dd><code>Add Celery Beat schedule directory file</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
  <td><a href="https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-06075bb89f2ed02f96643195375869e8f3ba33a921297eeab23e962ae8c737cb">+4/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about Qodo Merge usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


Diff:
diff --git a/backend/celerybeat-schedule.bak b/backend/celerybeat-schedule.bak
new file mode 100644
index 00000000..177bcb55
--- /dev/null
+++ b/backend/celerybeat-schedule.bak
@@ -0,0 +1,4 @@
+'entries', (3072, 1305)
+'__version__', (512, 20)
+'tz', (1024, 18)
+'utc_enabled', (1536, 4)
diff --git a/backend/celerybeat-schedule.dat b/backend/celerybeat-schedule.dat
new file mode 100644
index 00000000..6574104f
Binary files /dev/null and b/backend/celerybeat-schedule.dat differ
diff --git a/backend/celerybeat-schedule.dir b/backend/celerybeat-schedule.dir
new file mode 100644
index 00000000..8bfdf770
--- /dev/null
+++ b/backend/celerybeat-schedule.dir
@@ -0,0 +1,4 @@
+'entries', (3072, 1143)
+'__version__', (512, 20)
+'tz', (1024, 18)
+'utc_enabled', (1536, 4)
diff --git a/backend/pawtel/__init__.py b/backend/pawtel/__init__.py
index e69de29b..53f4ccb1 100644
--- a/backend/pawtel/__init__.py
+++ b/backend/pawtel/__init__.py
@@ -0,0 +1,3 @@
+from .celery import app as celery_app
+
+__all__ = ("celery_app",)
diff --git a/backend/pawtel/booking_holds/tasks.py b/backend/pawtel/booking_holds/tasks.py
new file mode 100644
index 00000000..c5d31aeb
--- /dev/null
+++ b/backend/pawtel/booking_holds/tasks.py
@@ -0,0 +1,9 @@
+from celery import shared_task
+from django.utils import timezone
+from pawtel.booking_holds.models import BookingHold
+
+
+@shared_task
+def delete_inactive_bookingholds():
+    count, _ = BookingHold.objects.filter(hold_expires_at__lt=timezone.now()).delete()
+    return f"Eliminados {count} booking_holds inactivos."
diff --git a/backend/pawtel/bookings/tasks.py b/backend/pawtel/bookings/tasks.py
new file mode 100644
index 00000000..e7839051
--- /dev/null
+++ b/backend/pawtel/bookings/tasks.py
@@ -0,0 +1,17 @@
+from datetime import timedelta
+
+from celery import shared_task
+from django.utils import timezone
+from pawtel.bookings.models import Booking
+
+THREE_YEARS = timedelta(days=3 * 365)
+
+
+@shared_task
+def delete_old_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
+
+    count, _ = Booking.objects.filter(creation_date__lt=threshold_date).delete()
+
+    return f"Eliminadas {count} bookings con creation_date anterior a 3 años."
diff --git a/backend/pawtel/celery.py b/backend/pawtel/celery.py
new file mode 100644
index 00000000..b5e50553
--- /dev/null
+++ b/backend/pawtel/celery.py
@@ -0,0 +1,14 @@
+import os
+
+from celery import Celery
+
+# Set the environment variable so Django uses the correct settings file
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pawtel.settings")
+
+app = Celery("pawtel")
+
+# Load Celery configuration from settings.py using the CELERY namespace
+app.config_from_object("django.conf:settings", namespace="CELERY")
+
+# Automatically discover tasks in installed apps
+app.autodiscover_tasks()
diff --git a/backend/pawtel/customers/tasks.py b/backend/pawtel/customers/tasks.py
new file mode 100644
index 00000000..1d98d564
--- /dev/null
+++ b/backend/pawtel/customers/tasks.py
@@ -0,0 +1,26 @@
+from datetime import timedelta
+
+from celery import shared_task
+from django.utils import timezone
+from pawtel.bookings.models import Booking
+from pawtel.customers.models import Customer
+
+THREE_YEARS = timedelta(days=3 * 365)
+
+
+@shared_task
+def delete_inactive_customers_without_recent_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
+    total_deleted = 0
+
+    customers = Customer.objects.select_related("user").all()
+    for customer in customers:
+        if not customer.user.is_active:
+            if not Booking.objects.filter(
+                customer=customer, creation_date__gte=threshold_date
+            ).exists():
+                customer.user.delete()
+                total_deleted += 1
+
+    return f"Eliminados {total_deleted} customers inactivos."
diff --git a/backend/pawtel/hotel_owners/tasks.py b/backend/pawtel/hotel_owners/tasks.py
new file mode 100644
index 00000000..68aea17d
--- /dev/null
+++ b/backend/pawtel/hotel_owners/tasks.py
@@ -0,0 +1,26 @@
+from datetime import timedelta
+
+from celery import shared_task
+from django.utils import timezone
+from pawtel.bookings.models import Booking
+from pawtel.hotel_owners.models import HotelOwner
+
+THREE_YEARS = timedelta(days=3 * 365)
+
+
+@shared_task
+def delete_inactive_hotel_owners_without_recent_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
+    total_deleted = 0
+
+    hotel_owners = HotelOwner.objects.select_related("user").all()
+    for owner in hotel_owners:
+        if not owner.user.is_active:
+            if not Booking.objects.filter(
+                room_type__hotel__hotel_owner=owner, start_date__gte=threshold_date
+            ).exists():
+                owner.user.delete()
+                total_deleted += 1
+
+    return f"Eliminados {total_deleted} hotel owners inactivos."
diff --git a/backend/pawtel/hotels/tasks.py b/backend/pawtel/hotels/tasks.py
new file mode 100644
index 00000000..4f80993f
--- /dev/null
+++ b/backend/pawtel/hotels/tasks.py
@@ -0,0 +1,30 @@
+from datetime import timedelta
+
+from celery import shared_task
+from django.utils import timezone
+from pawtel.bookings.models import Booking
+from pawtel.hotels.models import Hotel
+
+THREE_YEARS = timedelta(days=3 * 365)
+
+
+@shared_task
+def delete_archived_hotels_without_recent_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
+
+    total_deleted = 0
+    archived_hotels = Hotel.objects.filter(is_archived=True)
+    for hotel in archived_hotels:
+        roomtypes = hotel.roomtype_set.all()
+        has_recent_booking = any(
+            Booking.objects.filter(
+                room_type=rt, start_date__gte=threshold_date
+            ).exists()
+            for rt in roomtypes
+        )
+        if not has_recent_booking:
+            count, _ = hotel.delete()
+            total_deleted += count
+
+    return f"Cleanup de Hotels archivados completado. Eliminados: {total_deleted}"
diff --git a/backend/pawtel/room_types/tasks.py b/backend/pawtel/room_types/tasks.py
new file mode 100644
index 00000000..6581a7c1
--- /dev/null
+++ b/backend/pawtel/room_types/tasks.py
@@ -0,0 +1,25 @@
+from datetime import timedelta
+
+from celery import shared_task
+from django.utils import timezone
+from pawtel.bookings.models import Booking
+from pawtel.room_types.models import RoomType
+
+THREE_YEARS = timedelta(days=3 * 365)
+
+
+@shared_task
+def delete_archived_room_types_without_recent_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
+
+    total_deleted = 0
+    archived_roomtypes = RoomType.objects.filter(is_archived=True)
+    for rt in archived_roomtypes:
+        if not Booking.objects.filter(
+            room_type=rt, start_date__gte=threshold_date
+        ).exists():
+            count, _ = rt.delete()
+            total_deleted += count
+
+    return f"Cleanup de RoomTypes archivados completado. Eliminados: {total_deleted}"
diff --git a/backend/pawtel/settings.py b/backend/pawtel/settings.py
index 20b99948..1c7a7cc6 100644
--- a/backend/pawtel/settings.py
+++ b/backend/pawtel/settings.py
@@ -14,6 +14,7 @@
 from datetime import timedelta
 from pathlib import Path
 
+from celery.schedules import crontab
 from dotenv import load_dotenv
 
 load_dotenv()
@@ -174,6 +175,40 @@
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
 
+CELERY_BROKER_URL = "redis://localhost:6379/0"
+CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
+CELERY_ACCEPT_CONTENT = ["json"]
+CELERY_TASK_SERIALIZER = "json"
+CELERY_RESULT_SERIALIZER = "json"
+
+
+CELERY_BEAT_SCHEDULE = {
+    "delete-inactive-bookingholds": {
+        "task": "pawtel.booking_holds.tasks.delete_inactive_bookingholds",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-archived-roomtypes": {
+        "task": "pawtel.room_types.tasks.delete_archived_room_types_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-archived-hotels": {
+        "task": "pawtel.hotels.tasks.delete_archived_hotels_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-old-bookings": {
+        "task": "pawtel.bookings.tasks.delete_old_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-inactive-customers": {
+        "task": "pawtel.customers.tasks.delete_inactive_customers_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-inactive-hotelowners": {
+        "task": "pawtel.hotel_owners.tasks.delete_inactive_hotel_owners_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+}
+
 
 def bool_env(env_var: str) -> bool:
     return os.getenv(env_var, "False").lower() == "true"
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 0edd9533..a087e11d 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -54,4 +54,6 @@ uritemplate==4.1.1
 urllib3==2.3.0
 uvicorn==0.34.0
 virtualenv==20.29.3
-coverage==7.4.0
+coverage==7.4.0
+celery>=5.3
+redis>=4.0


Reviews:
user: login: rafcasceb
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:30:56Z
user: login: rafcasceb
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-07T21:47:43Z

Review comments:
diff_hunk: @@ -0,0 +1,14 @@
+import os
+
+from celery import Celery
+
+# Establece la variable de entorno para que Django use el archivo de settings correcto
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pawtel.settings")
+
+app = Celery("pawtel")
+
+# Carga la configuración de Celery desde el settings.py usando el namespace CELERY
+app.config_from_object("django.conf:settings", namespace="CELERY")
+
+# Descubre automáticamente las tareas en las apps instaladas
+app.autodiscover_tasks()
path: backend/pawtel/celery.py
user: login: rafcasceb
body: Comentarios en inglés.
created_at: 2025-04-07T16:14:54Z
author_association: COLLABORATOR
diff_hunk: @@ -174,6 +174,40 @@
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
 
+CELERY_BROKER_URL = "redis://localhost:6379/0"
+CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
+CELERY_ACCEPT_CONTENT = ["json"]
+CELERY_TASK_SERIALIZER = "json"
+CELERY_RESULT_SERIALIZER = "json"
+
+
+CELERY_BEAT_SCHEDULE = {
+    "delete-inactive-bookingholds-daily": {
+        "task": "pawtel.booking_holds.tasks.delete_inactive_bookingholds",
+        "schedule": 60.0,
path: backend/pawtel/settings.py
user: login: rafcasceb
body: Sacado de chatgpt:

from celery.schedules import crontab

"cleanup-inactive-customers": {
    "task": "pawtel.customers.tasks.delete_inactive_customers_without_recent_bookings",
    "schedule": crontab(hour=3, minute=0),  # runs every day at 3:00 AM
},


Este por ejemplo:

crontab(hour=3, minute=0, day_of_week=1)  # el lunes a las 03:00

Yo creo que el lunes a las 3 es la hora ideal.

created_at: 2025-04-07T16:28:40Z
author_association: COLLABORATOR
diff_hunk: @@ -174,6 +174,40 @@
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
 
+CELERY_BROKER_URL = "redis://localhost:6379/0"
+CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
+CELERY_ACCEPT_CONTENT = ["json"]
+CELERY_TASK_SERIALIZER = "json"
+CELERY_RESULT_SERIALIZER = "json"
+
+
+CELERY_BEAT_SCHEDULE = {
+    "delete-inactive-bookingholds-daily": {
+        "task": "pawtel.booking_holds.tasks.delete_inactive_bookingholds",
+        "schedule": 60.0,
+    },
+    "cleanup-archived-roomtypes-daily": {
path: backend/pawtel/settings.py
user: login: rafcasceb
body: Quítale la palabra daily a los métodos.
created_at: 2025-04-07T16:30:44Z
author_association: COLLABORATOR

PR comments:
user: login: codiumai-pr-agent-free[bot]
created_at: 2025-04-06T22:46:09Z
body: ## PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 3 🔵🔵🔵⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-95da12f29844c841d5b69819ac310fb8590f04a79bd54a5b7c64a286b372af31R20-R22'><strong>Missing Import</strong></a>

The Booking model is used but not imported. This will cause a NameError when the task is executed.
</summary>

```python
Booking.objects.filter(
    room_type=rt, start_date__gte=threshold_date
).exists()
```

</details>

<details><summary><a href='https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-78607024481a4187326529bf0358943739817fd4063dcf9cb55bee6a12db60e7R18-R20'><strong>Missing Import</strong></a>

The Booking model is used but not imported. This will cause a NameError when the task is executed.
</summary>

```python
if not Booking.objects.filter(
    room_type=rt, start_date__gte=threshold_date
).exists():
```

</details>

<details><summary><a href='https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-cc0eee54c7138abed89014c7760014b3701bc42973715f6a9522311f8d47395aR189-R202'><strong>Task Frequency</strong></a>

All scheduled tasks run every 60 seconds (schedule: 60.0), which seems too frequent for cleanup operations. Consider using daily or weekly schedules for these maintenance tasks.
</summary>

```python
    "schedule": 60.0,
},
"cleanup-archived-roomtypes-daily": {
    "task": "pawtel.room_types.tasks.delete_archived_room_types_without_recent_bookings",
    "schedule": 60.0,
},
"cleanup-archived-hotels-daily": {
    "task": "pawtel.hotels.tasks.delete_archived_hotels_without_recent_bookings",
    "schedule": 60.0,
},
"cleanup-old-bookings-daily": {
    "task": "pawtel.bookings.tasks.delete_old_bookings",
    "schedule": 60.0,
},
```

</details>

</td></tr>
</table>

author_association: NONE
user: login: codiumai-pr-agent-free[bot]
created_at: 2025-04-06T22:47:16Z
body: ## PR Code Suggestions ✨

<!-- 6cea39f -->

Explore these optional code suggestions:

<table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>Possible issue</td>
<td>



<details><summary>Missing Booking model import</summary>

___


**The <code>Booking</code> model is being used but not imported. Add the missing import to <br>avoid a NameError when the task runs.**

[backend/pawtel/hotels/tasks.py [10-27]](https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-95da12f29844c841d5b69819ac310fb8590f04a79bd54a5b7c64a286b372af31R10-R27)

```diff
+@shared_task
+def delete_archived_hotels_without_recent_bookings():
+    today = timezone.now().date()
+    threshold_date = today - THREE_YEARS
 
+    total_deleted = 0
+    archived_hotels = Hotel.objects.filter(is_archived=True)
+    for hotel in archived_hotels:
+        roomtypes = hotel.roomtype_set.all()
+        has_recent_booking = any(
+            Booking.objects.filter(
+                room_type=rt, start_date__gte=threshold_date
+            ).exists()
+            for rt in roomtypes
+        )
+        if not has_recent_booking:
+            count, _ = hotel.delete()
+            total_deleted += count
```

- [ ] **Apply this suggestion** <!-- /improve --apply_suggestion=0 -->

<details><summary>Suggestion importance[1-10]: 9</summary>

__

Why: The task uses the Booking model in lines 20-22 but doesn't import it, which would cause a NameError when the task runs. This is a critical issue that would prevent the task from functioning properly.


</details></details></td><td align=center>High

</td></tr><tr><td rowspan=1>General</td>
<td>



<details><summary>✅ 
Excessive task frequency<!-- not_implemented -->
</summary>

___

<details><summary><b>Suggestion Impact:</b></summary>The commit implemented the suggestion's intent by changing the task frequency from 60 seconds to a weekly schedule using crontab (running at 3:00 AM every Monday) instead of the suggested 86400 seconds. The implementation is even better than the suggestion as it uses crontab for more precise scheduling.

code diff:

```diff
-    "delete-inactive-bookingholds-daily": {
+    "delete-inactive-bookingholds": {
         "task": "pawtel.booking_holds.tasks.delete_inactive_bookingholds",
-        "schedule": 60.0,
-    },
-    "cleanup-archived-roomtypes-daily": {
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-archived-roomtypes": {
         "task": "pawtel.room_types.tasks.delete_archived_room_types_without_recent_bookings",
-        "schedule": 60.0,
-    },
-    "cleanup-archived-hotels-daily": {
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-archived-hotels": {
         "task": "pawtel.hotels.tasks.delete_archived_hotels_without_recent_bookings",
-        "schedule": 60.0,
-    },
-    "cleanup-old-bookings-daily": {
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-old-bookings": {
         "task": "pawtel.bookings.tasks.delete_old_bookings",
-        "schedule": 60.0,
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-inactive-customers": {
+        "task": "pawtel.customers.tasks.delete_inactive_customers_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
+    },
+    "cleanup-inactive-hotelowners": {
+        "task": "pawtel.hotel_owners.tasks.delete_inactive_hotel_owners_without_recent_bookings",
+        "schedule": crontab(hour=3, minute=0, day_of_week=1),
     },
```

</details>


___


**The tasks are scheduled to run every 60 seconds (60.0) which is very frequent <br>for cleanup operations. Consider using a more appropriate schedule like daily <br>(86400 seconds) or using crontab for specific times, especially for <br>resource-intensive database operations.**

[backend/pawtel/settings.py [186-202]](https://github.com/LuisMelladoDiaz/Pawtel-ComparadorDeHotelesParaMascotas/pull/283/files#diff-cc0eee54c7138abed89014c7760014b3701bc42973715f6a9522311f8d47395aR186-R202)

```diff
 CELERY_BEAT_SCHEDULE = {
     "delete-inactive-bookingholds-daily": {
         "task": "pawtel.booking_holds.tasks.delete_inactive_bookingholds",
-        "schedule": 60.0,
+        "schedule": 86400.0,  # Run once per day
     },
     "cleanup-archived-roomtypes-daily": {
         "task": "pawtel.room_types.tasks.delete_archived_room_types_without_recent_bookings",
-        "schedule": 60.0,
+        "schedule": 86400.0,  # Run once per day
     },
     "cleanup-archived-hotels-daily": {
         "task": "pawtel.hotels.tasks.delete_archived_hotels_without_recent_bookings",
-        "schedule": 60.0,
+        "schedule": 86400.0,  # Run once per day
     },
     "cleanup-old-bookings-daily": {
         "task": "pawtel.bookings.tasks.delete_old_bookings",
-        "schedule": 60.0,
+        "schedule": 86400.0,  # Run once per day
     },
 }
```


`[To ensure code accuracy, apply this suggestion manually]`

<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: Running cleanup tasks every 60 seconds (as configured) is unnecessarily frequent for database operations that should typically run daily. This could cause significant database load and performance issues in production.


</details></details></td><td align=center>Medium

</td></tr>
<tr><td align="center" colspan="2">

- [ ] Update <!-- /improve --more_suggestions=true -->

</td><td></td></tr></tbody></table>
author_association: NONE