diff --git a/.gitignore b/.gitignore index acd6f4d..e0954d8 100644 --- a/.gitignore +++ b/.gitignore @@ -375,3 +375,5 @@ media/ /vehicles/migrations_save/ /carnet_rouge/migrations/ /collabs/migrations/ +db.sqlite3 +/Reskreen/my.cnf \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a8d2a2c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM python:alpine3.19 + +WORKDIR /app + +COPY . . +COPY crons_jobs /etc/cron.d/django_crons_jobs + +RUN chmod 0644 /etc/cron.d/django_crons_jobs && crontab /etc/cron.d/django_crons_jobs && touch /var/log/cron.log + + + +RUN apk update && apk add --no-cache mariadb-connector-c-dev && apk add --no-cache gcc musl-dev openrc +RUN pip install --no-cache-dir -r requirements.txt +RUN python manage.py makemigrations && python manage.py migrate + + +EXPOSE 8080 + +CMD ["python", "manage.py", "runserver", "0.0.0.0:8080"] +#CMD ["./startup.sh"] + diff --git a/my.cnf b/Reskreen/my.cnf similarity index 100% rename from my.cnf rename to Reskreen/my.cnf diff --git a/Reskreen/my_dev.cnf b/Reskreen/my_dev.cnf new file mode 100644 index 0000000..683374b --- /dev/null +++ b/Reskreen/my_dev.cnf @@ -0,0 +1,7 @@ +[client] +database = django_dev +user = django_usr +password = YwVHV36ovBTXWugb +default-character-set = utf8 +host =33.144.144.3 +port =3306 diff --git a/Reskreen/rest_permission.py b/Reskreen/rest_permission.py new file mode 100644 index 0000000..5f8c5be --- /dev/null +++ b/Reskreen/rest_permission.py @@ -0,0 +1,11 @@ +from rest_framework import permissions +class CustomPermission_DjangoModel_based(permissions.DjangoModelPermissions): + perms_map = { + 'GET': ['%(app_label)s.view_%(model_name)s'], + 'OPTIONS': ['%(app_label)s.view_%(model_name)s'], + 'HEAD': ['%(app_label)s.view_%(model_name)s'], + 'POST': ['%(app_label)s.add_%(model_name)s'], + 'PUT': ['%(app_label)s.change_%(model_name)s'], + 'PATCH': ['%(app_label)s.change_%(model_name)s'], + 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + } \ No newline at end of file diff --git a/Reskreen/settings.py b/Reskreen/settings.py index 629afdb..a9a3340 100644 --- a/Reskreen/settings.py +++ b/Reskreen/settings.py @@ -12,7 +12,7 @@ https://docs.djangoproject.com/en/4.0/ref/settings/ from pathlib import Path import os -from .server_config import * +from Reskreen.server_config import * # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -30,6 +30,8 @@ DEBUG = cfg_dev_mode ALLOWED_HOSTS = ["rh.ambulance-clerc.ch"] if cfg_dev_mode: ALLOWED_HOSTS.append("127.0.0.1") + ALLOWED_HOSTS.append("33.144.144.6") + ALLOWED_HOSTS.append("33.144.144.13") CSRF_TRUSTED_ORIGINS = ['https://rh.ambulance-clerc.ch'] @@ -37,6 +39,7 @@ CSRF_TRUSTED_ORIGINS = ['https://rh.ambulance-clerc.ch'] LOGIN_REDIRECT_URL = '/admin' INSTALLED_APPS = [ 'jazzmin', + 'django_cron', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -55,6 +58,20 @@ INSTALLED_APPS = [ 'django_summernote', ] +INSTALLED_APPS += ( 'apilog.apps.ApilogConfig',) +INSTALLED_APPS += ( 'corsheaders',) +'', + +#Ajout de rest_framework +INSTALLED_APPS += ('rest_framework', 'rest_framework.authtoken',) +'''REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.TokenAuthentication', #Todo prod: replace by TokenAuthentication + ), + # Autres paramètres de configuration... +}''' + + LANGUAGE_CODE = 'fr-CH' if not cfg_dev_mode: @@ -104,6 +121,7 @@ DATA_UPLOAD_MAX_NUMBER_FIELDS = 2500 MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -117,6 +135,22 @@ MIDDLEWARE = [ 'django.middleware.locale.LocaleMiddleware', ] +CRON_CLASSES = [ + "mycaldav.cron.InitTaskSync", +] + +CORS_ALLOWED_ORIGINS = [ + "http://33.144.144.13:4173", + "http://33.144.144.13:5173", + "http://33.144.144.13:3000", +] + + + + +MIDDLEWARE += ('apilog.middleware.APILogMiddleware',) + + ROOT_URLCONF = 'Reskreen.urls' TEMPLATES = [ @@ -149,7 +183,7 @@ if cfg_dev_mode: 'NAME': BASE_DIR / 'db.sqlite3', } } - ''' + ''' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', @@ -163,7 +197,7 @@ else: 'default': { 'ENGINE': 'django.db.backends.mysql', 'OPTIONS': { - 'read_default_file': 'my.cnf', + 'read_default_file': os.path.join(BASE_DIR, 'Reskreen/my.cnf'), }, } } diff --git a/Reskreen/urls.py b/Reskreen/urls.py index 5eea486..fd44fff 100644 --- a/Reskreen/urls.py +++ b/Reskreen/urls.py @@ -26,12 +26,15 @@ urlpatterns = [ path('accounts/', admin.site.urls), path('collabs/', include('collabs.urls')), path('vehicules/', include('vehicles.urls')), + path('comm-opmessage/', include('comm_op.urls')), path('caldav/', include('mycaldav.urls')), #path('collabs_hour/', include('collabs.urls')), path('carnet_rouge/', include('carnet_rouge.urls')), path('student_eval/', include('studenteval.urls')), path('summernote/', include('django_summernote.urls')), path('editor/', include('django_summernote.urls')), + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), + ] if settings.DEBUG: diff --git a/apilog/__init__.py b/apilog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apilog/admin.py b/apilog/admin.py new file mode 100644 index 0000000..08fbeb4 --- /dev/null +++ b/apilog/admin.py @@ -0,0 +1,15 @@ +from django.contrib import admin +from .models import APILog + + +@admin.register(APILog) +class APILogAdmin(admin.ModelAdmin): + list_display = ('user', 'request_url', 'request_params', 'request_method', 'request_body', 'request_timestamp') + list_filter = ('user', 'request_method') + search_fields = ('request_url',) + ordering = ('-request_timestamp',) + + actions = None + + def has_delete_permission(self, request, obj=None): + return False diff --git a/apilog/apps.py b/apilog/apps.py new file mode 100644 index 0000000..6d0b448 --- /dev/null +++ b/apilog/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApilogConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apilog' diff --git a/apilog/middleware.py b/apilog/middleware.py new file mode 100644 index 0000000..3e0fd0a --- /dev/null +++ b/apilog/middleware.py @@ -0,0 +1,27 @@ +from .models import APILog + +class APILogMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if not request.path.startswith('/admin'): + + user = request.user if request.user.is_authenticated else None + + + request_url = request.path + + request_params = request.GET if request.GET else None + + + request_method = request.method + + + request_body = request.body.decode('utf-8') + + + APILog.objects.create(user=user, request_url=request_url, request_params=request_params, request_method=request_method, request_body=request_body) + + response = self.get_response(request) + return response diff --git a/apilog/models.py b/apilog/models.py new file mode 100644 index 0000000..0374f7d --- /dev/null +++ b/apilog/models.py @@ -0,0 +1,22 @@ +from django.db import models +from django.conf import settings +from django.db.models.signals import pre_delete +from django.dispatch import receiver + + +class APILog(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True) + request_url = models.URLField() + request_params = models.CharField(max_length=255, null=True, blank=True) + request_method = models.CharField(max_length=10) + request_body = models.TextField() + request_timestamp = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.user} - {self.request_url}" + + +@receiver(pre_delete, sender=APILog) +def prevent_delete(sender, instance, **kwargs): + # Empêchez la suppression en levant une exception, par exemple ValueError + raise ValueError("Vous n'êtes pas autorisé à supprimer cet enregistrement.") \ No newline at end of file diff --git a/apilog/tests.py b/apilog/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apilog/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apilog/views.py b/apilog/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apilog/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/comm_op/serializers.py b/comm_op/serializers.py new file mode 100644 index 0000000..b9cbf30 --- /dev/null +++ b/comm_op/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers + +from.models import comm_opMessage + +class cl_comm_opMessage_Serializer(serializers.ModelSerializer): + class Meta: + model = comm_opMessage + fields = '__all__' diff --git a/comm_op/urls.py b/comm_op/urls.py new file mode 100644 index 0000000..d39c611 --- /dev/null +++ b/comm_op/urls.py @@ -0,0 +1,15 @@ +from django.urls import path, include +from rest_framework import routers + + +from . import views + +router = routers.DefaultRouter() +router.register(r'api-comm-opmessage', views.comm_opMessageViewset, basename='comm-opmessage') + +app_name = "comm_opMessage" +urlpatterns = [ + path('', include(router.urls)), + +] + diff --git a/comm_op/views.py b/comm_op/views.py index 91ea44a..96de93f 100644 --- a/comm_op/views.py +++ b/comm_op/views.py @@ -1,3 +1,12 @@ from django.shortcuts import render -# Create your views here. +from comm_op.models import * +from rest_framework import viewsets +from Reskreen.rest_permission import CustomPermission_DjangoModel_based + +from .serializers import cl_comm_opMessage_Serializer + +class comm_opMessageViewset(viewsets.ModelViewSet): + queryset = comm_opMessage.objects.all() + serializer_class = cl_comm_opMessage_Serializer + permission_classes = [CustomPermission_DjangoModel_based] \ No newline at end of file diff --git a/compile_and_start_cont.sh b/compile_and_start_cont.sh new file mode 100755 index 0000000..2ea1751 --- /dev/null +++ b/compile_and_start_cont.sh @@ -0,0 +1,5 @@ +docker build -t reskreen:dev . +docker stop reskreen-validation && docker rm reskreen-validation +docker stop reskreen-validation-cron && docker rm reskreen-validation-cron +docker run -p 8080:8080 -d --name reskreen-validation -v /home/reskreen:/app/Reskreen reskreen:dev +docker run -d --name reskreen-validation-cron -v /home/reskreen:/app/Reskreen reskreen:dev crond -f diff --git a/crons_jobs b/crons_jobs new file mode 100644 index 0000000..f365558 --- /dev/null +++ b/crons_jobs @@ -0,0 +1,2 @@ +# Exécute la tâche cron toutes les heures +* * * * * cd /app && python /app/manage.py runcrons > /var/log/runcrons.log diff --git a/mycaldav/admin.py b/mycaldav/admin.py index 8c38f3f..721204f 100644 --- a/mycaldav/admin.py +++ b/mycaldav/admin.py @@ -1,3 +1,8 @@ from django.contrib import admin +from mycaldav.models import caldav_sync_manager -# Register your models here. + +@admin.register(caldav_sync_manager) +class caldav_sync_managerAdmin(admin.ModelAdmin): + list_display = ('dtDate',) + diff --git a/mycaldav/apps.py b/mycaldav/apps.py index af900b6..e2d3c1a 100644 --- a/mycaldav/apps.py +++ b/mycaldav/apps.py @@ -4,3 +4,4 @@ from django.apps import AppConfig class CaldavConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'mycaldav' + diff --git a/mycaldav/cron.py b/mycaldav/cron.py new file mode 100644 index 0000000..89a7351 --- /dev/null +++ b/mycaldav/cron.py @@ -0,0 +1,22 @@ +from django_cron import CronJobBase, Schedule +import django + + + +class InitTaskSync(CronJobBase): + RUN_AT_TIMES = ['07:00', '19:00'] + RUN_EVERY_MIN = 1 + + schedule = Schedule(run_at_times=RUN_AT_TIMES,) + code = 'reskreen.init_sync_task' # Un code unique pour votre tâche cron + + def do(self): + django.setup() + + from mycaldav.models import caldav_sync_manager + from mycaldav.settings import CALDAV_URL,CALDAV_USER,CALDAV_PASSWORD + o_caldav_sync_management = caldav_sync_manager() + o_caldav_sync_management.init_caldav(caldav_url=CALDAV_URL, caldav_user=CALDAV_USER, caldav_password=CALDAV_PASSWORD) + o_caldav_sync_management.init_task_management() + + print('Ma tâche cron s\'exécute !') diff --git a/mycaldav/models.py b/mycaldav/models.py index d0fabdf..6f008d8 100644 --- a/mycaldav/models.py +++ b/mycaldav/models.py @@ -1,3 +1,4 @@ +from django.db import models from datetime import datetime, timedelta, time from django.contrib.auth.models import User import datetime as Datetime @@ -17,9 +18,82 @@ import recurring_ical_events import caldav from mycaldav.settings import * +import pytz +from dateutil.parser import parse Key_separator = "--" + +class caldav_sync_manager(models.Model): + dtDate = models.DateField('Date_synchronized', auto_now_add=True, unique=True) + bDone = models.BooleanField("Effectuée", default=False) + dtUpdated = models.DateTimeField('date updated', auto_now=True) + dtCreated = models.DateTimeField('date published', auto_now_add=True) + + def __str__(self): + return f"{self.dtDate}" + + def init_caldav(self, caldav_url, caldav_user, caldav_password): + client = caldav.DAVClient(url=caldav_url, username=caldav_user, password=caldav_password) + data = client.principal() + + self.a_task = data.calendar(cal_id=caldav_id["task"]) + + def set_today_as_synced(self): + o_new_manager = caldav_sync_manager() + o_new_manager.save() + + def init_task_management(self): + today = datetime.now() + o_caldav_sync_management = caldav_sync_manager.objects.filter(dtDate=today.date()) + if not o_caldav_sync_management.exists(): + self.copy_caldav_data() + print(f"synced events") + else: + print("pas de copy, sync déjà fait") + + + + + + def copy_caldav_data(self,): + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + today = datetime.now() + seeked_date = today + relativedelta(months=1) + sync_date = today + relativedelta(months=1, days=1) + modified_url = caldav_cfg["task_config"] + f"&start={int(today.timestamp())}&end={int(sync_date.timestamp())}&expand=1" + + with urllib.request.urlopen(modified_url, context=ctx) as o_url: + sabre_data = o_url.read() + + events = recurring_ical_events.of(Calendar.from_ical(sabre_data)).at((seeked_date.year,seeked_date.month,seeked_date.day)) + #print(f"events:{events}") + + + for event in events: + print(f"copy de l'événement: {event['SUMMARY']}") + + + _title = event["SUMMARY"] if ("SUMMARY" in event) else "Sans Titre" + _desc = event["DESCRIPTION"] if ("DESCRIPTION" in event) else "" + + my_event = self.a_task.save_event( + dtstart=event["DTSTART"].dt, + dtend=event["DTEND"].dt, + summary= _title, + description= _desc, + ) + self.set_today_as_synced() + + + + + + + # Create your models here. class cls_caldav(): url = "" @@ -46,21 +120,28 @@ class cls_caldav(): self.day.sort(key=lambda x: x.key, reverse=reverse) self.night.sort(key=lambda x: x.key, reverse=reverse) - - - def get_caldav_data(self,periode=1,calendar=None, date=None): + def caldav_open_url(self, days_delta, date): ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE self.clear_data() - o_url = urllib.request.urlopen(self.url, context=ctx) - self.data = o_url.read() - o_url.close() + + modified_url = self.url + f"&start={int((date-timedelta(days=days_delta)).timestamp())}&end={int((date +timedelta(days=days_delta)).timestamp())}&expand=1" + print(f"ICS CALL URL = {modified_url}") + with urllib.request.urlopen(modified_url, context=ctx) as o_url: + self.data = o_url.read() + + return self.data + def get_caldav_data(self,periode=1,calendar=None, date=None): if date is None: - date = datetime.now() + date = datetime.now() + + self.caldav_open_url(days_delta=periode,date=date) + + today = (date.year,date.month,date.day) @@ -81,11 +162,29 @@ class cls_caldav(): events = recurring_ical_events.of(Calendar.from_ical(self.data)).between(today,endweek) self.parse_data(events) + + def convert_to_gmt1(self, dt): + gmt1_tz = pytz.timezone('Europe/Paris') + try: + + if hasattr(dt,"tzinfo") and dt.tzinfo is not None: + # Convertir l'objet datetime en GMT+1 + # Utilisez le nom de votre fuseau horaire GMT+1 + dt_gmt1 = dt.astimezone(gmt1_tz) + else: + # L'objet datetime est naïf, ajouter l'information de fuseau horaire GMT+1 + utc_tz = pytz.utc + dt_utc = utc_tz.localize(dt) + dt_gmt1 = dt_utc.astimezone(gmt1_tz) + return dt_gmt1 + except: + return dt def parse_data(self, events): + desired_timezone = pytz.timezone('Europe/Paris') for event in events: item = _caldav_item() item.name = event["SUMMARY"] - item.uiid = event["UID"] + item.uuid = f"{event['UID']}" if Key_separator in item.name: arr = item.name.split(Key_separator) item.key = arr[0] @@ -107,12 +206,14 @@ class cls_caldav(): item.team_2_chef = True item.team_2 = item.team_2.replace("#", "") + if "DESCRIPTION" in event.keys(): - item.desc = event["DESCRIPTION"] - if "#" in item.desc: + item.desc = f"{event['DESCRIPTION']}" + + if "#Fait" in item.desc: item.done = True - if "#Fait" in item.desc: - item.desc = item.desc.replace("#Fait", "") + item.desc = item.desc.replace("#Fait", "") + if '{href=' in item.desc: temp_str = item.desc.split('{href=')[1] @@ -120,10 +221,18 @@ class cls_caldav(): item.href = temp_str item.desc = item.desc.replace("{href=" + item.href + "}","") - item.dtstart = event["DTSTART"].dt.strftime("%d.%m.%Y %H:%M") - item.dtstamp = int(event["DTSTART"].dt.strftime("%Y%m%d%H%M")) + + desired_timezone = pytz.timezone('Europe/Paris') + datetime_obj = parse(event["DTSTART"].dt.strftime("%d.%m.%Y %H:%M")) + gmt1_datetime = datetime_obj.astimezone(desired_timezone) + + print(f"gmt= {self.convert_to_gmt1( event['DTSTART'].dt)}") + + + item.dtstart = self.convert_to_gmt1( event['DTSTART'].dt).strftime("%d.%m.%Y %H:%M") + item.dtstamp = int(self.convert_to_gmt1( event['DTSTART'].dt).strftime("%Y%m%d%H%M")) #print(item.dtstamp) - item.dtend = event["DTEND"].dt.strftime("%d.%m.%Y %H:%M") + item.dtend = self.convert_to_gmt1( event['DTEND'].dt).strftime("%d.%m.%Y %H:%M") item.format_str_date() @@ -155,14 +264,18 @@ class cls_caldav_client(): caldav_password = "Agendamc144" caldav_agenda_config_url = caldav_cfg["task_config"] + events = None - def __init__(self): - self.client = caldav.DAVClient(url=self.caldav_url, username=self.caldav_user, password=self.caldav_password) - self.data = self.client.principal() + + + + def init_caldav(self, caldav_url, caldav_user, caldav_password): + client = caldav.DAVClient(url=caldav_url, username=caldav_user, password=caldav_password) + self.data = client.principal() for cal in self.data.calendars(): print(f"name: {cal.name}({cal.url})") @@ -176,7 +289,6 @@ class cls_caldav_client(): self.a_rh = self.data.calendar(cal_id=caldav_id["rh"]) self.a_manif = self.data.calendar(cal_id=caldav_id["manif"]) - #self.get_events_by_date(self.a_team) def get_event_by_uuid(self,calandar,uuid): event = calandar.object_by_uid(uid=uuid) return event @@ -199,36 +311,9 @@ class cls_caldav_client(): summary=event.vobject_instance.vevent.summary.value, description=_desc, ) - event.delete() - - - - def init_task_management(self): - print("pass copy task process") - try: - with open(os.path.join("mycaldav", "last_sync_config.bin"), "rb") as file: - array = pickle.load(file) - except: - array = {"year": 0, "month": 0} - print("Erreur lecture fichier config") - - if array["month"] == datetime.now().month: - if array["year"] == datetime.now().year: - print("pas de copy, sync déjà fait") - else: - self.copy_caldav_data(self.a_task) - else: - self.copy_caldav_data(self.a_task) - self.clear_all_events_by_Date(self.a_task) - - - def add_event_in_calandar(self, calandar): - my_event = calandar.save_event( - dtstart=datetime(2022, 6, 30, 12), - dtend=datetime(2022, 6, 30, 13), - summary="Do the needful", - - ) + event.delete() + + def get_events_by_date(self,calandar,date=None ): if date is None: date = datetime.now() @@ -297,126 +382,33 @@ class cls_caldav_client(): - def copy_caldav_data(self, calandar=None): - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - - - - o_url = urllib.request.urlopen(self.caldav_agenda_config_url, context=ctx) - data = o_url.read() - o_url.close() - - now = f"01.{datetime.now().month}.{datetime.now().year}" - - - next_month = datetime.strptime(f"1.{datetime.today().month}.{datetime.today().year}","%d.%m.%Y") + relativedelta(months=1) - next_month = (next_month.year, next_month.month, next_month.day) - - today = (datetime.now().year, datetime.now().month, 1) - print(f"today:{today}") - print(f"next:{next_month}") - events = None - events = recurring_ical_events.of(Calendar.from_ical(data)).between(today, next_month) - cur_events = calandar.date_search(start=datetime(today[0],today[1],today[2]), end=datetime(next_month[0],next_month[1],next_month[2]), expand=True) - - for event in events: - print(f"copy de l'événement: {event['SUMMARY']}") - - - _title = event["SUMMARY"] if ("SUMMARY" in event) else "Sans Titre" - _desc = event["DESCRIPTION"] if ("DESCRIPTION" in event) else "" - - my_event = calandar.save_event( - dtstart=event["DTSTART"].dt, - dtend=event["DTEND"].dt, - summary= _title, - description= _desc, - ) - - with open(os.path.join("mycaldav", "last_sync_config.bin"), "wb") as file: - array = {"year":datetime.now().year, "month":datetime.now().month} - pickle.dump(array, file) - - - - - def clear_all_events_by_Date(self, calandar): - events = calandar.date_search(start=datetime(2000, 1, 1), end=datetime.now() - relativedelta(years=1), expand=True) - for e in events: - e.delete() - #print(events) - - def reformat_all_events(self,calandar): - events = calandar.date_search(start=datetime(2023, 1, 1), end=datetime.now() + relativedelta(years=1), - expand=True) - - for event in events: - summary = event.vobject_instance.vevent.summary.value - print(f"test summary {summary}") - if "----" in summary: - summary = summary.replace("----", Key_separator) - print(f"reformat: [{summary}]") - event.vobject_instance.vevent.summary.value = summary - event.save() - elif "--" in summary: - pass - elif "-" in summary: - summary = summary.replace("-",Key_separator) - print(f"reformat: [{summary}]") - event.vobject_instance.vevent.summary.value = summary - event.save() - - def change_utc_to_zurich_all_events(self,calandar): - events = calandar.date_search(start=datetime(2023, 4, 1), end=datetime.now() + relativedelta(years=1), - expand=True) - - for event in events: - start = event.vobject_instance.vevent.dtstart.value - end = start + relativedelta(day=1) - - if start.hour < 12: - new_start = datetime(start.year, start.month, start.day,7,0,0, tzinfo = pytz.timezone("Europe/Zurich")) - new_end = datetime(start.year, start.month, start.day, 19, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) - elif start.hour > 12: - new_start = datetime(start.year, start.month, start.day,19,0,0, tzinfo = pytz.timezone("Europe/Zurich")) - new_end = datetime(start.year, start.month,start.day, 23, 59, 0, tzinfo=pytz.timezone("Europe/Zurich")) - - event.vobject_instance.vevent.dtstart.value = new_start - event.vobject_instance.vevent.dtend.value = new_end - - - if new_end{new_end}") - print("ERROR") - event.vobject_instance.vevent.dtend.value = new_start - - - event.save() - class _caldav_item(): - key = "" - name = "" - desc = "" - dtstart = None - str_start_date = "" - str_start_time = "" - dtend = None - str_end_date = "" - str_end_time = "" - dtstamp = None - done = False - href = "" - uiid = "" - team_1 = "" - team_2 = "" - team_1_chef = False - team_2_chef = False - team_transfert = False - team_manif = False + def __init__(self, key="", name="", desc="", dtstart=None, str_start_date="", + str_start_time="", dtend=None, str_end_date="", str_end_time="", + dtstamp=None, done=False, href="", uuid="", team_1="", + team_2="", team_1_chef=False, team_2_chef=False, + team_transfert=False, team_manif=False): + self.key = key + self.name = name + self.desc = desc + self.dtstart = dtstart + self.str_start_date = str_start_date + self.str_start_time = str_start_time + self.dtend = dtend + self.str_end_date = str_end_date + self.str_end_time = str_end_time + self.dtstamp = dtstamp + self.done = done + self.href = href + self.uuid = uuid + self.team_1 = team_1 + self.team_2 = team_2 + self.team_1_chef = team_1_chef + self.team_2_chef = team_2_chef + self.team_transfert = team_transfert + self.team_manif = team_manif def format_str_date(self): self.str_start_date = datetime.strptime(self.dtstart,"%d.%m.%Y %H:%M").strftime("%d.%m") @@ -427,3 +419,24 @@ class _caldav_item(): +from rest_framework import serializers + +class CalDavItemSerializer(serializers.Serializer): + key = serializers.CharField() + name = serializers.CharField() + desc = serializers.CharField(allow_blank=True) + dtstart = serializers.CharField() + str_start_date = serializers.CharField() + str_start_time = serializers.CharField() + dtend = serializers.CharField() + str_end_date = serializers.CharField() + str_end_time = serializers.CharField() + done = serializers.BooleanField() + href = serializers.CharField(allow_blank=True) + uuid = serializers.CharField() + team_1 = serializers.CharField(allow_blank=True) + team_2 = serializers.CharField(allow_blank=True) + team_1_chef = serializers.BooleanField() + team_2_chef = serializers.BooleanField() + team_transfert = serializers.BooleanField() + team_manif = serializers.BooleanField() \ No newline at end of file diff --git a/mycaldav/settings.py b/mycaldav/settings.py index 580d887..acf6ec1 100644 --- a/mycaldav/settings.py +++ b/mycaldav/settings.py @@ -13,3 +13,8 @@ for key,value in caldav_id.items(): caldav_cfg[key] = f"https://sync.infomaniak.com/calendars/AA01593/{value}?export" +CALDAV_URL = "https://sync.infomaniak.com" +CALDAV_USER = 'AA01593' #agenda@ambulance-clerc.ch +CALDAV_PASSWORD = "Agendamc144" + + diff --git a/mycaldav/urls.py b/mycaldav/urls.py index 02036d6..6aff7f8 100644 --- a/mycaldav/urls.py +++ b/mycaldav/urls.py @@ -7,13 +7,13 @@ from . import views app_name = "mycaldav" urlpatterns = [ path('task', views.view_task_caldav, name='view_task_caldav'), - path('task/edit/', views.view_task_edit_caldav, name='view_task_edit_caldav'), + path('task/edit', views.view_task_edit_caldav, name='view_task_edit_caldav'), path('vhc', views.view_vhc_caldav, name='view_vhc_caldav'), - path('vhc/edit/', views.view_vhc_edit_caldav, name='view_vhc_edit_caldav'), + path('vhc/edit', views.view_vhc_edit_caldav, name='view_vhc_edit_caldav'), path('rh', views.view_rh_caldav, name='view_rh_caldav'), path('road', views.view_road_caldav, name='view_road_caldav'), path('op', views.view_op_caldav, name='view_op_caldav'), - path('op/edit/', views.view_op_edit_caldav, name='view_op_edit_caldav'), + path('op/edit', views.view_op_edit_caldav, name='view_op_edit_caldav'), path('team', views.view_team_caldav, name='view_team_caldav'), path('manif', views.view_manif_caldav, name='view_manif_caldav'), path('team_pdf', views.view_team_pdf_caldav, name='view_team_pdf_caldav'), diff --git a/mycaldav/views.py b/mycaldav/views.py index 72f8d62..e0c5808 100644 --- a/mycaldav/views.py +++ b/mycaldav/views.py @@ -1,4 +1,6 @@ -from django.http import HttpResponse +import json + +from django.http import HttpResponse, JsonResponse from django.http import FileResponse from django.shortcuts import render from django.views import generic @@ -19,130 +21,203 @@ from mycaldav.settings import * from mycaldav.export_team_pdf import * from comm_op.models import comm_opMessage -class caldav_item: - def __init__(self): - self.uuid = "" - self.name = "" - self.desc = "" - self.key = "" - self.done = False - self.href = "#" - self.str_start_date = "" - self.str_end_date = "" + +from rest_framework.response import Response +from rest_framework import status +from rest_framework.decorators import api_view + +from mycaldav.models import CalDavItemSerializer, _caldav_item +from mycaldav.settings import CALDAV_URL,CALDAV_USER,CALDAV_PASSWORD + +import logging + +logger = logging.getLogger(__name__) +def formatResponseArray(o_caldav, inverted=False): + + try: + if inverted: + o_caldav.items.reverse() + o_caldav.day.reverse() + o_caldav.night.reverse() + # Serialize data + serializer_items = CalDavItemSerializer(data=[item.__dict__ for item in o_caldav.items], many=True) + serializer_day = CalDavItemSerializer(data=[item.__dict__ for item in o_caldav.day], many=True) + serializer_night = CalDavItemSerializer(data=[item.__dict__ for item in o_caldav.night], many=True) -@xframe_options_exempt + # Check validation and print errors + validate_and_log(serializer_items, "items") + validate_and_log(serializer_day, "day") + validate_and_log(serializer_night, "night") + + response_data = { + "items": serializer_items.data, + "day": serializer_day.data, + "night": serializer_night.data, + } + response_status = status.HTTP_200_OK + + except Exception as e: + logger.error(f"An error occurred: {e}") + response_data = {"error": "An error occurred while processing the request."} + response_status = status.HTTP_500_INTERNAL_SERVER_ERROR + + return {"data":response_data, "status": response_status} +def validate_and_log(serializer, label): + if not serializer.is_valid(): + print(f"Validation Error {label}: {serializer.errors}") +@api_view(['GET']) def view_task_caldav(request): o_caldav = cls_caldav(url=caldav_cfg["task"]) - o_caldav.get_caldav_data() - template = loader.get_template("task/task.html") - context = {'latest_task_list':o_caldav.day, 'night_task_list': o_caldav.night} - '''DEPRECATED - if (datetime.today().day == 1) and 2==1: - myClient = cls_caldav_client() - myClient.init_task_management()''' - return HttpResponse(template.render(context,request)) + o_caldav.get_caldav_data() + ''' + o_caldav_sync_management = caldav_sync_manager() + o_caldav_sync_management.init_caldav(caldav_url=CALDAV_URL, caldav_user=CALDAV_USER, caldav_password=CALDAV_PASSWORD) + o_caldav_sync_management.init_task_management() + ''' + -@xframe_options_exempt -def view_task_edit_caldav(request, uuid): - print(uuid) + response = formatResponseArray(o_caldav) + return Response(response["data"], status=response["status"]) + + + +@api_view(['POST']) +def view_task_edit_caldav(request): + data = json.loads(request.body.decode('utf-8')) + uuid = data.get('uuid', None) + if uuid is None: + # Gérer le cas où l'UUID n'est pas fourni dans les données JSON + return JsonResponse({'error': 'UUID non fourni dans les données JSON'}, status=400) + myClient = cls_caldav_client() - myClient.mark_as_done_task(calandar=myClient.a_task,uuid=uuid) - return view_task_caldav(request) + myClient.init_caldav(caldav_url=CALDAV_URL, caldav_user=CALDAV_USER, caldav_password=CALDAV_PASSWORD) + myClient.mark_as_done_task(calandar=myClient.a_task,uuid=uuid) + return JsonResponse({'sucsess': 'UUID marqué à done'}, status=200) + + -def view_op_edit_caldav(request, uuid): - print(uuid) +@api_view(['POST']) +def view_op_edit_caldav(request): + data = json.loads(request.body.decode('utf-8')) + uuid = data.get('uuid', None) + if uuid is None: + # Gérer le cas où l'UUID n'est pas fourni dans les données JSON + return JsonResponse({'error': 'UUID non fourni dans les données JSON'}, status=400) object = comm_opMessage.objects.get(pk=uuid) object.bDone = True object.dtEnd = datetime.today() object.save() - return view_op_caldav(request) - -def view_vhc_edit_caldav(request, uuid): - print(uuid) + return JsonResponse({'sucsess': 'UUID marqué à done'}, status=200) +@api_view(['POST']) +def view_vhc_edit_caldav(request): + data = json.loads(request.body.decode('utf-8')) + uuid = data.get('uuid', None) + if uuid is None: + # Gérer le cas où l'UUID n'est pas fourni dans les données JSON + return JsonResponse({'error': 'UUID non fourni dans les données JSON'}, status=400) myClient = cls_caldav_client() + myClient.init_caldav(caldav_url=CALDAV_URL, caldav_user=CALDAV_USER, caldav_password=CALDAV_PASSWORD) myClient.mark_as_done_task(calandar=myClient.a_vhc,uuid=uuid) - return view_vhc_caldav(request) + return JsonResponse({'sucsess': 'UUID marqué à done'}, status=200) -@xframe_options_exempt +@api_view(['GET']) def view_vhc_caldav(request): - o_caldav = cls_caldav(url=caldav_cfg["vhc"]) - o_caldav.get_caldav_data(periode=2) - template = loader.get_template("vhc/vhc_view.html") - context = {'latest_task_list': o_caldav.items} - return HttpResponse(template.render(context, request)) + o_caldav = cls_caldav(url=caldav_cfg["vhc"]) + o_caldav.get_caldav_data(periode=2) -@xframe_options_exempt + response = formatResponseArray(o_caldav) + return Response(response["data"], status=response["status"]) + +@api_view(['GET']) def view_rh_caldav(request): o_caldav = cls_caldav(url=caldav_cfg["rh"]) o_caldav.get_caldav_data(periode=2) - template = loader.get_template("rh/rh_view.html") - context = {'latest_task_list': o_caldav.items[::-1], 'today': datetime.today().strftime('%d.%m')} - return HttpResponse(template.render(context, request)) -@xframe_options_exempt + response = formatResponseArray(o_caldav,inverted=False) + return Response(response["data"], status=response["status"]) + + + +@api_view(['GET']) def view_road_caldav(request): o_caldav = cls_caldav(url=caldav_cfg["road"]) o_caldav.get_caldav_data(periode=2) - template = loader.get_template("road/road_view.html") - context = {'latest_task_list': o_caldav.items[::-1]} - return HttpResponse(template.render(context, request)) -@xframe_options_exempt -def view_op_caldav(request): + response = formatResponseArray(o_caldav, inverted=True) + return Response(response["data"], status=response["status"]) + + +def close_old_op_message(): old_items = comm_opMessage.objects.filter(bDone=False, dtEnd__lte=datetime.today() - timedelta(days=1)) for old_item in old_items: old_item.bDone = True old_item.save() +@api_view(['GET']) +def view_op_caldav(request): + close_old_op_message() o_items = comm_opMessage.objects.filter(Q(dtStart__lte= datetime.today() + timedelta(days=1)) & ( Q(dtEnd__gte=datetime.today()) | Q(dtEnd__isnull=True))) all_items = [] for item in o_items: - temp_item = caldav_item() - temp_item.uuid = item.uuid + temp_item = _caldav_item() + temp_item.uuid = f"{item.uuid}" temp_item.name = item.sTitle temp_item.desc = item.sDesc temp_item.key = item.sKey temp_item.done = item.bDone - temp_item.str_start_date = str(item.dtStart.day) + "." + str(item.dtStart.month) + temp_item.str_start_date = str(item.dtStart.strftime('%d')) + "." + str(item.dtStart.strftime('%m')) if item.dtEnd != None: - temp_item.str_end_date = str(item.dtEnd.day) + "." + str(item.dtEnd.month) + temp_item.str_end_date = str(item.dtEnd.strftime('%d')) + "." + str(item.dtEnd.strftime('%m')) else: temp_item.str_end_date = None all_items.append(temp_item) - template = loader.get_template("op/op_view.html") - context = {'latest_task_list': all_items} - return HttpResponse(template.render(context, request)) + try: + serializer = CalDavItemSerializer(data=all_items, many=True) + if serializer.is_valid(): + print("Serialized Data:", serializer.data) + else: + print("Validation Error:", serializer.errors) -@xframe_options_exempt + except Exception as e: + logger.error(f"An error occurred: {e}") + return Response({"error": "An error occurred while processing the request."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({"items":serializer.data}) + +@api_view(['GET']) def view_team_caldav(request): #Change timezone - myclient = cls_caldav_client() - #myclient.reformat_all_events(myclient.a_team) - #myclient.change_utc_to_zurich_all_events(myclient.a_team) - #myclient.reformat_all_events(myclient.a_task_config) - #myclient.change_utc_to_zurich_all_events(myclient.a_task) + o_caldav = cls_caldav(url=caldav_cfg["team"]) o_caldav.get_caldav_data(periode=3) template = loader.get_template("team/team_view.html") o_caldav.sort_array_by_key(True) - context = {'latest_task_list': o_caldav.day, 'night_task_list': o_caldav.night, 'today': datetime.today().strftime('%d.%m'),'yesterday': (datetime.today() + timedelta(days=-1)).strftime('%d.%m'),'tomorow': (datetime.today() + timedelta(days=+1)).strftime('%d.%m')} - return HttpResponse(template.render(context, request)) + response = formatResponseArray(o_caldav) + additional_data = { + 'today': datetime.today().strftime('%d.%m'), + 'yesterday': (datetime.today() + timedelta(days=-1)).strftime('%d.%m'), + 'tomorrow': (datetime.today() + timedelta(days=1)).strftime('%d.%m') + } + response["data"].update(additional_data) + return Response(response["data"], status=response["status"]) -@xframe_options_exempt + +@api_view(['GET']) def view_manif_caldav(request): o_caldav = cls_caldav(url=caldav_cfg["manif"]) o_caldav.get_caldav_data(periode=1) - template = loader.get_template("manif/manif_view.html") o_caldav.sort_array_by_key(True) - context = {'latest_task_list': o_caldav.items, 'night_task_list': o_caldav.night, 'today': datetime.today().strftime('%d.%m')} - return HttpResponse(template.render(context, request)) + + response = formatResponseArray(o_caldav) + return Response(response["data"], status=response["status"]) + @xframe_options_exempt def view_team_pdf_caldav(request): @@ -153,6 +228,7 @@ def view_team_pdf_caldav(request): start = datetime.strptime(request.GET['dt'], "%d.%m.%Y") myClient = cls_caldav_client() + myClient.init_caldav(caldav_url=CALDAV_URL, caldav_user=CALDAV_USER, caldav_password=CALDAV_PASSWORD) render_pdf(o_caldav, caldavClient=myClient, date=start) return FileResponse(open('mycaldav/export.pdf', 'rb'), as_attachment=False, content_type='application/pdf') diff --git a/requirements.txt b/requirements.txt index 1f303ca..3096590 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,57 +1,5 @@ -arabic-reshaper==2.1.3 -asgiref==3.7.2 -asn1crypto==1.5.1 -Brotli==1.0.9 -caldav==0.9.1 -certifi==2021.10.8 -cffi==1.15.0 -charset-normalizer==2.0.12 -click==8.1.3 -colorama==0.4.4 -cryptography==37.0.1 -cssselect2==0.4.1 Django django-admin-rangefilter -fpdf==1.7.2 -future==0.18.2 -html5lib==1.1 -icalendar==4.0.9 -idna==3.3 -lxml -# mysqlclient==2.1.0 -oscrypto==1.3.0 -pdfkit==1.0.0 -Pillow==9.0.1 -pycparser==2.21 -pydyf==0.1.2 -pyHanko==0.13.1 -pyhanko-certvalidator==0.19.5 -PyPDF3==1.0.6 -pyphen==0.12.0 -python-bidi==0.4.2 -python-dateutil==2.8.2 -pytz==2022.1 -pytz-deprecation-shim==0.1.0.post0 -PyYAML==6.0 -qrcode==7.3.1 -recurring-ical-events==1.0.2b0 -reportlab==3.6.9 -requests==2.27.1 -six==1.16.0 -sqlparse==0.4.2 -svglib==1.2.1 -tinycss2==1.1.1 -tqdm==4.64.0 -tzdata==2021.5 -tzlocal==4.2 -uritools==4.0.0 -urllib3==1.26.9 -vobject==0.9.6.1 -weasyprint==54.1 -webencodings==0.5.1 -x-wr-timezone==0.0.5 -xhtml2pdf==0.2.7 -zopfli==0.1.9 django-autologin django-summernote django-jazzmin @@ -59,3 +7,15 @@ django-default-language gunicorn whitenoise django-filter +djangorestframework +mysqlclient +pyocclient +django-cors-headers +pytz +python-dateutil +icalendar +recurring-ical-events +caldav +xhtml2pdf +fpdf +django-cron \ No newline at end of file diff --git a/startup.sh b/startup.sh new file mode 100755 index 0000000..41c09a9 --- /dev/null +++ b/startup.sh @@ -0,0 +1,7 @@ +# startup.sh +# make sure errors stop execution +set -e +# start the cronjob +crond -f -l 8 & +# start nextjs using our own command from package.json... +python /app/manage.py runserver 0.0.0.0:8080 \ No newline at end of file diff --git a/vehicles/serializers.py b/vehicles/serializers.py new file mode 100644 index 0000000..d20a794 --- /dev/null +++ b/vehicles/serializers.py @@ -0,0 +1,14 @@ +from django.contrib.auth.models import User, Group +from rest_framework import serializers + +from.models import Vehicles_infos, Vehicles + +class cl_Vehicles_Serializer(serializers.ModelSerializer): + class Meta: + model = Vehicles + fields = '__all__' + +class cl_Vehicles_infos_Serializer(serializers.ModelSerializer): + class Meta: + model = Vehicles_infos + fields = '__all__' diff --git a/vehicles/urls.py b/vehicles/urls.py index 455c0bc..bff3a8f 100644 --- a/vehicles/urls.py +++ b/vehicles/urls.py @@ -1,14 +1,17 @@ -from django.urls import path - +from django.urls import path, include +from rest_framework import routers from . import views +router = routers.DefaultRouter() +router.register(r'api-vehicle', views.VehicleViewset, basename='vehicle') app_name = "vehicles" urlpatterns = [ path('vhc', views.view_vhc, name='view_vhc'), path('peremptions', views.view_peremptions, name='view_peremptions'), + path('', include(router.urls)), diff --git a/vehicles/views.py b/vehicles/views.py index 359be0e..ba4155f 100644 --- a/vehicles/views.py +++ b/vehicles/views.py @@ -6,6 +6,20 @@ from django.template import loader from django.views.decorators.clickjacking import xframe_options_exempt from vehicles.models import * +from rest_framework import viewsets +from Reskreen.rest_permission import CustomPermission_DjangoModel_based + +from .serializers import cl_Vehicles_Serializer, cl_Vehicles_infos_Serializer + +class VehicleViewset(viewsets.ModelViewSet): + queryset = Vehicles.objects.all() + serializer_class = cl_Vehicles_Serializer + permission_classes = [CustomPermission_DjangoModel_based] + +class VehicleViewset(viewsets.ModelViewSet): + queryset = Vehicles.objects.all() + serializer_class = cl_Vehicles_Serializer + permission_classes = [CustomPermission_DjangoModel_based] @xframe_options_exempt def view_vhc(request):