diff --git a/config/settings/base.py b/config/settings/base.py index c44b9408..c16fe8d4 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -38,6 +38,7 @@ "src.CompanyLicense.apps.CompanyLicenseConfig", "src.Inventory.apps.InventoryConfig", "src.Employees.apps.EmployeesConfig", + "src.Suppliers.apps.SuppliersConfig", # Collections reports "src.Reports.apps.ReportsConfig", "src.ImageReport.apps.ImageConfig", diff --git a/src/CameraAlgorithms/admin.py b/src/CameraAlgorithms/admin.py index e39967e0..6f88b2bd 100644 --- a/src/CameraAlgorithms/admin.py +++ b/src/CameraAlgorithms/admin.py @@ -1,23 +1,35 @@ -# from django.contrib import admin -# from .models.camera import Camera -# from .models.algorithm import Algorithm, CameraAlgorithm, CameraAlgorithmLog +from django.contrib import admin +from .models.camera import Camera, ZoneCameras +from .models.algorithm import Algorithm, CameraAlgorithm, CameraAlgorithmLog -# @admin.register(Camera) -# class CamerasAdmin(admin.ModelAdmin): -# list_filter = ("id",) +@admin.register(Camera) +class CamerasAdmin(admin.ModelAdmin): + list_filter = ("id",) -# @admin.register(Algorithm) -# class AlgorithmAdmin(admin.ModelAdmin): -# list_filter = ("id", "name") +@admin.register(Algorithm) +class AlgorithmAdmin(admin.ModelAdmin): + list_filter = ("id", "name") -# @admin.register(CameraAlgorithm) -# class CameraAlgorithmAdmin(admin.ModelAdmin): -# list_filter = ("id", "algorithm", "camera") +@admin.register(CameraAlgorithm) +class CameraAlgorithmAdmin(admin.ModelAdmin): + list_filter = ("id", "algorithm", "camera") -# @admin.register(CameraAlgorithmLog) -# class CameraAlgorithmLogAdmin(admin.ModelAdmin): -# list_filter = ("id",) +@admin.register(CameraAlgorithmLog) +class CameraAlgorithmLogAdmin(admin.ModelAdmin): + list_filter = ("id",) + + +@admin.register(ZoneCameras) +class ZoneCamerasAdmin(admin.ModelAdmin): + readonly_fields = ["is_active"] + list_display = ( + "camera", + "name", + "coords", + "date_created", + "date_updated" + ) diff --git a/src/CameraAlgorithms/migrations/0002_zonecameras.py b/src/CameraAlgorithms/migrations/0002_zonecameras.py new file mode 100644 index 00000000..673e746f --- /dev/null +++ b/src/CameraAlgorithms/migrations/0002_zonecameras.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.1 on 2023-06-05 10:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("CameraAlgorithms", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="ZoneCameras", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("coords", models.JSONField(verbose_name="Zone coordinates")), + ("name", models.CharField(blank=True, max_length=100, null=True)), + ("is_active", models.BooleanField(default=False)), + ( + "date_created", + models.DateTimeField( + auto_now_add=True, verbose_name="Date created" + ), + ), + ( + "date_updated", + models.DateTimeField(auto_now=True, verbose_name="Date updated"), + ), + ( + "camera", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="Zone_cameras", + to="CameraAlgorithms.camera", + ), + ), + ], + ), + ] diff --git a/src/CameraAlgorithms/migrations/0003_cameraalgorithm_zones.py b/src/CameraAlgorithms/migrations/0003_cameraalgorithm_zones.py new file mode 100644 index 00000000..12766894 --- /dev/null +++ b/src/CameraAlgorithms/migrations/0003_cameraalgorithm_zones.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.1 on 2023-06-06 12:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("CameraAlgorithms", "0002_zonecameras"), + ] + + operations = [ + migrations.AddField( + model_name="cameraalgorithm", + name="zones", + field=models.JSONField( + blank=True, null=True, verbose_name="Id zones algorithm" + ), + ), + ] diff --git a/src/CameraAlgorithms/models/algorithm.py b/src/CameraAlgorithms/models/algorithm.py index 3ecc965f..715fd8fa 100644 --- a/src/CameraAlgorithms/models/algorithm.py +++ b/src/CameraAlgorithms/models/algorithm.py @@ -31,6 +31,7 @@ class CameraAlgorithm(models.Model): camera = models.ForeignKey(Camera, on_delete=models.CASCADE) is_active = models.BooleanField(default=True) process_id = models.PositiveBigIntegerField(default=0) + zones = models.JSONField(blank=True, null=True, verbose_name="Id zones algorithm") def __str__(self): return f"{self.algorithm} - {self.camera}" diff --git a/src/CameraAlgorithms/models/camera.py b/src/CameraAlgorithms/models/camera.py index 459215c5..0242adf3 100644 --- a/src/CameraAlgorithms/models/camera.py +++ b/src/CameraAlgorithms/models/camera.py @@ -33,3 +33,15 @@ class Meta: verbose_name_plural = "Cameras" db_table = "camera" + + +class ZoneCameras(models.Model): + camera = models.ForeignKey(Camera, on_delete=models.SET_NULL, related_name="Zone_cameras", blank=True, null=True) + coords = models.JSONField(verbose_name="Zone coordinates") + name = models.CharField(max_length=100, blank=True, null=True) + is_active = models.BooleanField(default=False) + date_created = models.DateTimeField(verbose_name="Date created", auto_now_add=True) + date_updated = models.DateTimeField(verbose_name="Date updated", auto_now=True) + + def __str__(self): + return self.name diff --git a/src/CameraAlgorithms/serializers.py b/src/CameraAlgorithms/serializers.py index 19f4c93a..bd96ea16 100644 --- a/src/CameraAlgorithms/serializers.py +++ b/src/CameraAlgorithms/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Camera +from .models import Camera, ZoneCameras from .models import Algorithm, CameraAlgorithm, CameraAlgorithmLog @@ -88,3 +88,10 @@ class CameraAlgorithmLogSerializer(serializers.ModelSerializer): class Meta: model = CameraAlgorithmLog fields = "__all__" + + +class ZoneCameraSerializer(serializers.ModelSerializer): + class Meta: + model = ZoneCameras + fields = "__all__" + read_only_fields = ["is_active"] diff --git a/src/CameraAlgorithms/services/cameraalgorithm.py b/src/CameraAlgorithms/services/cameraalgorithm.py index e2562c2d..e619912a 100644 --- a/src/CameraAlgorithms/services/cameraalgorithm.py +++ b/src/CameraAlgorithms/services/cameraalgorithm.py @@ -9,7 +9,7 @@ from src.OrderView.models import IndexOperations from src.CompanyLicense.decorators import check_active_cameras, check_active_algorithms -from ..models import Camera +from ..models import Camera, ZoneCameras from ..models import Algorithm, CameraAlgorithm from .logs_services import logs_service @@ -93,7 +93,7 @@ def create_camera(camera: Dict[str, str]) -> None: def create_camera_algorithms( - camera: Dict[str, str], algorithms: List[Dict[str, Any]] + camera: Dict[str, str], algorithms: List[Dict[str, Any]] ) -> None: camera_obj = Camera.objects.get(id=camera["ip"]) new_records = [algorithm_data["name"] for algorithm_data in algorithms] @@ -133,10 +133,22 @@ def create_camera_algorithms( response = send_run_request(request) - if algorithm_obj.name == "idle_control": - response = send_run_request(request) + zones = request.get("config", {}).get("zonesID") + + if zones is None: + zones = None if algorithm_obj.name == "machine_control": + zones_ids = request.get("config", {}).get("zonesID", []) + for zone_id in zones_ids: + zone_camera = ZoneCameras.objects.get(id=zone_id["id"], camera=camera_obj) + data.append( + {"zoneId": zone_camera.id, "coords": zone_camera.coords, "zoneName": zone_camera.name} + ) + + response = send_run_request(request) + + if algorithm_obj.name == "idle_control": response = send_run_request(request) if algorithm_obj.name == "operation_control": @@ -163,9 +175,13 @@ def create_camera_algorithms( algorithm=algorithm_obj, camera=camera_obj, process_id=response["pid"], + zones=zones, ) new_record.save() + if zones is not None: + update_status_zones_true(zones) + logger.warning(f"New record -> {algorithm_obj.name} on camera {camera_obj.id}") for algorithm_name in algorithm_to_delete: @@ -227,4 +243,36 @@ def update_status_algorithm(pid: int): algorithm_name=camera_algorithm.algorithm.name, camera_ip=camera_algorithm.camera.id, ) + + if camera_algorithm.zones is not None: + update_status_zone_false(camera_algorithm.zones) + camera_algorithm.delete() + + +def update_status_zone_false(data): + """Update status zone in the end""" + + for zone in data: + zone_id = zone.get("id") + if zone_id: + try: + zone_obj = ZoneCameras.objects.get(id=zone_id) + zone_obj.is_active = False + zone_obj.save() + except ZoneCameras.DoesNotExist: + pass + + +def update_status_zones_true(zones): + """Update status zones on True""" + + for zone in zones: + zone_id = zone.get("id") + if zone_id: + try: + zone_obj = ZoneCameras.objects.get(id=zone_id) + zone_obj.is_active = True + zone_obj.save() + except ZoneCameras.DoesNotExist: + pass diff --git a/src/CameraAlgorithms/urls.py b/src/CameraAlgorithms/urls.py index f92ec698..e0ed2d38 100644 --- a/src/CameraAlgorithms/urls.py +++ b/src/CameraAlgorithms/urls.py @@ -1,5 +1,7 @@ from django.urls import path +from rest_framework.routers import DefaultRouter + from .views import ( CameraAPIView, CameraAlgorithmLogListAPIView, @@ -7,8 +9,12 @@ CreateCameraAlgorithmsApiView, AlgorithmDetailApiView, AlgorithmProcessApiView, + ZoneCameraListAPIView, ) +router = DefaultRouter() +router.register(r"zone", ZoneCameraListAPIView, basename="ZoneCameraList") + urlpatterns = [ path("camera/", CameraAPIView.as_view(), name="camera"), @@ -28,3 +34,5 @@ path("get-process/", AlgorithmProcessApiView.as_view(), name="camera-process"), path("logs/", CameraAlgorithmLogListAPIView.as_view(), name="log"), ] + +urlpatterns += router.urls diff --git a/src/CameraAlgorithms/views.py b/src/CameraAlgorithms/views.py index 533b9b2b..5a2ce635 100644 --- a/src/CameraAlgorithms/views.py +++ b/src/CameraAlgorithms/views.py @@ -1,11 +1,12 @@ from rest_framework import generics, status from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import ModelViewSet from src.Core.paginators import NoPagination from src.Core.permissions import IsStaffPermission, IsSuperuserPermission -from .models import Camera +from .models import Camera, ZoneCameras from .models import Algorithm, CameraAlgorithm, CameraAlgorithmLog from .services.cameraalgorithm import ( CreateCameraAlgorithms, @@ -16,7 +17,7 @@ CameraAlgorithmFullSerializer, CameraModelSerializer, CreateCameraAlgorithmSerializer, - CameraAlgorithmLogSerializer, + CameraAlgorithmLogSerializer, ZoneCameraSerializer, ) @@ -67,3 +68,10 @@ class CameraAlgorithmLogListAPIView(generics.ListAPIView): queryset = CameraAlgorithmLog.objects.all() serializer_class = CameraAlgorithmLogSerializer + + +class ZoneCameraListAPIView(ModelViewSet): + permission_classes = [IsAuthenticated, IsSuperuserPermission] + pagination_class = NoPagination + queryset = ZoneCameras.objects.all() + serializer_class = ZoneCameraSerializer diff --git a/src/CompanyLicense/admin.py b/src/CompanyLicense/admin.py index f1b39d63..7031448d 100644 --- a/src/CompanyLicense/admin.py +++ b/src/CompanyLicense/admin.py @@ -1,16 +1,32 @@ from django.contrib import admin -from .models import Company +from .models import License, Company -@admin.register(Company) -class CamerasAdmin(admin.ModelAdmin): +@admin.register(License) +class LicenseAdmin(admin.ModelAdmin): list_filter = ("id",) readonly_fields = ( 'license_key', - 'name_company', 'date_joined', 'valid_until', 'is_active', 'count_cameras', 'neurons_active' ) + + +@admin.register(Company) +class CompanyAdmin(admin.ModelAdmin): + list_display = ( + "name_company", + "city", + "state", + "website", + "contact_email", + "contact_phone", + "contact_mobile_phone", + "logo", + "file", + "date_joined", + "date_edited", + ) diff --git a/src/CompanyLicense/decorators.py b/src/CompanyLicense/decorators.py index df8f9e6b..1b09566f 100644 --- a/src/CompanyLicense/decorators.py +++ b/src/CompanyLicense/decorators.py @@ -5,7 +5,7 @@ from django.core.exceptions import PermissionDenied from django.shortcuts import redirect -from src.CompanyLicense.models import Company +from src.CompanyLicense.models import License from src.CameraAlgorithms.models import Camera from src.CameraAlgorithms.models import CameraAlgorithm @@ -17,7 +17,7 @@ def validate_license(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): - company = Company.objects.last() + company = License.objects.last() if company is None: raise PermissionDenied("No active license") @@ -35,7 +35,7 @@ def check_active_cameras(view_func): @wraps(view_func) def wrapped_view(request, *args, **kwargs): - company = Company.objects.last() + company = License.objects.last() if not company.is_active: return HttpResponseBadRequest("Your license is inactive.") @@ -60,7 +60,7 @@ def check_active_algorithms(view_func): @wraps(view_func) def wrapped_view(request, *args, **kwargs): - company = Company.objects.last() + company = License.objects.last() if not company.is_active: return HttpResponseBadRequest("Your license is inactive.") diff --git a/src/CompanyLicense/migrations/0002_license_alter_company_options_and_more.py b/src/CompanyLicense/migrations/0002_license_alter_company_options_and_more.py new file mode 100644 index 00000000..e8f6f0b1 --- /dev/null +++ b/src/CompanyLicense/migrations/0002_license_alter_company_options_and_more.py @@ -0,0 +1,102 @@ +# Generated by Django 4.1.4 on 2023-06-01 13:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("CompanyLicense", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="License", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("license_key", models.TextField(verbose_name="Company license key")), + ( + "date_joined", + models.DateTimeField(auto_now_add=True, verbose_name="Date joined"), + ), + ( + "date_edited", + models.DateTimeField(auto_now=True, verbose_name="Date edited"), + ), + ( + "valid_until", + models.DateField(verbose_name="Date which license is active"), + ), + ( + "is_active", + models.BooleanField( + default=False, verbose_name="Is active license" + ), + ), + ( + "count_cameras", + models.IntegerField(verbose_name="Count of cameras in active"), + ), + ( + "neurons_active", + models.IntegerField(verbose_name="Count of active neurons"), + ), + ], + options={ + "verbose_name": "License", + "verbose_name_plural": "Licenses", + }, + ), + migrations.AlterModelOptions( + name="company", + options={}, + ), + migrations.RemoveField( + model_name="company", + name="count_cameras", + ), + migrations.RemoveField( + model_name="company", + name="is_active", + ), + migrations.RemoveField( + model_name="company", + name="license_key", + ), + migrations.RemoveField( + model_name="company", + name="neurons_active", + ), + migrations.RemoveField( + model_name="company", + name="valid_until", + ), + migrations.AddField( + model_name="company", + name="address_company", + field=models.TextField( + blank=True, null=True, verbose_name="Address of company" + ), + ), + migrations.AddField( + model_name="company", + name="contact_email", + field=models.EmailField( + blank=True, max_length=254, null=True, verbose_name="Contact email" + ), + ), + migrations.AddField( + model_name="company", + name="contact_phone", + field=models.TextField( + blank=True, null=True, verbose_name="Contact phone number" + ), + ), + ] diff --git a/src/CompanyLicense/migrations/0003_remove_company_address_company_company_city_and_more.py b/src/CompanyLicense/migrations/0003_remove_company_address_company_company_city_and_more.py new file mode 100644 index 00000000..620037ec --- /dev/null +++ b/src/CompanyLicense/migrations/0003_remove_company_address_company_company_city_and_more.py @@ -0,0 +1,86 @@ +# Generated by Django 4.2.1 on 2023-06-05 08:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("CompanyLicense", "0002_license_alter_company_options_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="company", + name="address_company", + ), + migrations.AddField( + model_name="company", + name="city", + field=models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="City of company suppliers", + ), + ), + migrations.AddField( + model_name="company", + name="contact_mobile_phone", + field=models.TextField( + blank=True, + null=True, + verbose_name="Contact mobile phone number suppliers", + ), + ), + migrations.AddField( + model_name="company", + name="file", + field=models.FileField( + blank=True, + null=True, + upload_to="", + verbose_name="File to send notification", + ), + ), + migrations.AddField( + model_name="company", + name="logo", + field=models.ImageField( + blank=True, null=True, upload_to="", verbose_name="Logo company" + ), + ), + migrations.AddField( + model_name="company", + name="state", + field=models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="State of company suppliers", + ), + ), + migrations.AddField( + model_name="company", + name="website", + field=models.TextField( + blank=True, null=True, verbose_name="Website of suppliers" + ), + ), + migrations.AlterField( + model_name="company", + name="contact_email", + field=models.EmailField( + blank=True, + max_length=254, + null=True, + verbose_name="Contact email suppliers", + ), + ), + migrations.AlterField( + model_name="company", + name="contact_phone", + field=models.TextField( + blank=True, null=True, verbose_name="Contact phone number suppliers" + ), + ), + ] diff --git a/src/CompanyLicense/models.py b/src/CompanyLicense/models.py index f45769a8..c7f4c141 100644 --- a/src/CompanyLicense/models.py +++ b/src/CompanyLicense/models.py @@ -2,11 +2,10 @@ from django.db import models -class Company(models.Model): +class License(models.Model): """License of company""" license_key = models.TextField(verbose_name="Company license key") - name_company = models.TextField(verbose_name="Name of company") date_joined = models.DateTimeField(verbose_name="Date joined", auto_now_add=True) date_edited = models.DateTimeField(verbose_name="Date edited", auto_now=True) valid_until = models.DateField(verbose_name="Date which license is active") @@ -23,3 +22,22 @@ def __str__(self): class Meta: verbose_name = "License" verbose_name_plural = "Licenses" + + +class Company(models.Model): + """Models company""" + + name_company = models.TextField(verbose_name="Name of company") + city = models.CharField(max_length=50, verbose_name="City of company suppliers", blank=True, null=True) + state = models.CharField(max_length=50, verbose_name="State of company suppliers", blank=True, null=True) + website = models.TextField(verbose_name="Website of suppliers", blank=True, null=True) + contact_email = models.EmailField(verbose_name="Contact email suppliers", blank=True, null=True) + contact_phone = models.TextField(verbose_name="Contact phone number suppliers", blank=True, null=True) + contact_mobile_phone = models.TextField(verbose_name="Contact mobile phone number suppliers", blank=True, null=True) + logo = models.ImageField(verbose_name="Logo company", blank=True, null=True) + file = models.FileField(verbose_name="File to send notification", blank=True, null=True) + date_joined = models.DateTimeField(verbose_name="Date joined", auto_now_add=True) + date_edited = models.DateTimeField(verbose_name="Date edited", auto_now=True) + + def __str__(self): + return self.name_company diff --git a/src/CompanyLicense/serializers.py b/src/CompanyLicense/serializers.py index a2b7ac37..8f63c468 100644 --- a/src/CompanyLicense/serializers.py +++ b/src/CompanyLicense/serializers.py @@ -1,13 +1,13 @@ from rest_framework import serializers -from src.CompanyLicense.models import Company +from src.CompanyLicense.models import License, Company from src.CompanyLicense.service import decrypt_string import datetime from django.utils import timezone -class CompanySerializer(serializers.Serializer): +class LicenseSerializer(serializers.Serializer): license_key = serializers.CharField(required=True, write_only=True) def create(self, validated_data): @@ -18,16 +18,15 @@ def create(self, validated_data): if valid_date >= timezone.now().date(): data = { "license_key": validated_data["license_key"], - "name_company": valid_data["name_company"], "count_cameras": valid_data["count_cameras"], "neurons_active": valid_data["neurons_active"], "is_active": True, "valid_until": valid_date, } - company = Company.objects.create(**data) + license = License.objects.create(**data) - return company + return license raise serializers.ValidationError({"message": "You have an outdated license"}) @@ -37,3 +36,9 @@ def validate(self, data): except Exception as e: raise serializers.ValidationError({"error": str(e)}) return data + + +class CompanySerializer(serializers.ModelSerializer): + class Meta: + model = Company + fields = '__all__' diff --git a/src/CompanyLicense/urls.py b/src/CompanyLicense/urls.py index b6355229..6f1c74b9 100644 --- a/src/CompanyLicense/urls.py +++ b/src/CompanyLicense/urls.py @@ -1,10 +1,17 @@ from django.urls import path -from src.CompanyLicense.views import CompanyViewSet, CompanyInfoView, version +from rest_framework.routers import DefaultRouter +from src.CompanyLicense.views import LicenseViewSet, LicenseInfoView, version, CompanyView, InformationView + +router = DefaultRouter() +router.register(r"company", CompanyView, basename="company_info") urlpatterns = [ - path("create_license/", CompanyViewSet.as_view(), name="create_license"), - path("info/", CompanyInfoView.as_view(), name="company_info"), + path("create_license/", LicenseViewSet.as_view(), name="create_license"), + path("info/", LicenseInfoView.as_view(), name="license_info"), path('version/', version, name='read-file'), + path('get_info/', InformationView.as_view(), name="get_info_name_company_count_days"), ] + +urlpatterns += router.urls diff --git a/src/CompanyLicense/views.py b/src/CompanyLicense/views.py index ad0fff78..d9cfd4be 100644 --- a/src/CompanyLicense/views.py +++ b/src/CompanyLicense/views.py @@ -2,24 +2,28 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.decorators import api_view +from rest_framework.viewsets import ModelViewSet + +from django.db.models import Max + from src.Core.const import SERVER_URL from django.core.exceptions import PermissionDenied from django.utils import timezone -from .serializers import CompanySerializer +from .serializers import LicenseSerializer, CompanySerializer -from .models import Company +from .models import License, Company from src.CameraAlgorithms.models import CameraAlgorithm, Camera import requests -class CompanyViewSet(APIView): +class LicenseViewSet(APIView): permission_classes = [IsAuthenticated] def post(self, request): - serializer = CompanySerializer(data=request.data) + serializer = LicenseSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response( @@ -36,16 +40,16 @@ def post(self, request): ) -class CompanyInfoView(APIView): +class LicenseInfoView(APIView): http_method_names = ["get"] permission_classes = [IsAuthenticated] def get(self, request): try: - company = Company.objects.last() + company = License.objects.last() if company is None: raise PermissionDenied("No active license") - except Company.DoesNotExist: + except License.DoesNotExist: return Response({"error": "Company not found"}, status=404) is_license_active = f"{company.valid_until - timezone.now().date()}" @@ -60,7 +64,6 @@ def get(self, request): ) response_data = { - "name_company": company.name_company, "date_joined": company.date_joined, "valid_until": company.valid_until, "licence_is_active": company.is_active, @@ -94,3 +97,40 @@ def version(request): return Response({"error": f"Versions not found: {e}"}, status=404) return Response(versions) + + +class CompanyView(ModelViewSet): + permission_classes = [IsAuthenticated] + pagination_class = None + queryset = Company.objects.filter(id=Company.objects.aggregate(Max('id'))['id__max']) + serializer_class = CompanySerializer + + +class InformationView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request): + try: + company = License.objects.last() + if company is None: + raise PermissionDenied("No active license") + + is_license_active = f"{company.valid_until - timezone.now().date()}" + + count_days = int(is_license_active.split(",")[0].split(" ")[0]) + 1 + if count_days < 0: + count_days = 0 + except: + count_days = None + + try: + name_company = Company.objects.last().name_company + except: + name_company = None + + response_data = { + "count_days": f"{count_days} days", + "name_company": name_company, + } + + return Response(response_data) diff --git a/src/Inventory/admin.py b/src/Inventory/admin.py index 800d7804..2e66ff87 100644 --- a/src/Inventory/admin.py +++ b/src/Inventory/admin.py @@ -5,6 +5,6 @@ @admin.register(Items) class ItemsAdmin(admin.ModelAdmin): list_display = ( - "name", "id", "status", "current_stock_level", "low_stock_level", "camera", "date_updated", "coords", - "multi_row") + "name", "id", "suppliers", "order_quantity", "status", "current_stock_level", "low_stock_level", "camera", + "date_updated", "coords", "multi_row", "suppliers", "order_quantity") list_filter = ("status", "current_stock_level", "low_stock_level", "camera", "multi_row") diff --git a/src/Inventory/migrations/0003_items_order_quantity_items_suppliers.py b/src/Inventory/migrations/0003_items_order_quantity_items_suppliers.py new file mode 100644 index 00000000..f60c4058 --- /dev/null +++ b/src/Inventory/migrations/0003_items_order_quantity_items_suppliers.py @@ -0,0 +1,32 @@ +# Generated by Django 4.1.4 on 2023-06-02 11:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("Suppliers", "0001_initial"), + ("Inventory", "0002_alter_items_camera"), + ] + + operations = [ + migrations.AddField( + model_name="items", + name="order_quantity", + field=models.IntegerField( + blank=True, null=True, verbose_name="Order quantity" + ), + ), + migrations.AddField( + model_name="items", + name="suppliers", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="Supplier", + to="Suppliers.suppliers", + ), + ), + ] diff --git a/src/Inventory/models.py b/src/Inventory/models.py index 036ee0ef..a3d1ae24 100644 --- a/src/Inventory/models.py +++ b/src/Inventory/models.py @@ -1,6 +1,7 @@ from django.db import models from src.CameraAlgorithms.models import Camera +from src.Suppliers.models import Suppliers class Items(models.Model): @@ -16,6 +17,8 @@ class Items(models.Model): coords = models.JSONField(verbose_name="Area coordinates") prev_status = models.TextField(verbose_name="Previous status", default=None, max_length=30, blank=True, null=True) multi_row = models.BooleanField(verbose_name="Multi row", default=False) + suppliers = models.ForeignKey(Suppliers, related_name='Supplier', on_delete=models.SET_NULL, null=True, blank=True) + order_quantity = models.IntegerField(verbose_name="Order quantity", blank=True, null=True) def __str__(self): return self.name @@ -27,7 +30,6 @@ def save(self, *args, **kwargs): try: previous_camera = Items.objects.get(id=self.pk).camera_id - print(type(previous_camera[0])) except Exception as e: print(e) @@ -46,9 +48,10 @@ def save(self, *args, **kwargs): # restart process previous camera if camera_updated: - previous_camera_obj = Camera.objects.filter(id=previous_camera)[0] - stopped_process(previous_camera_obj) - started_process(previous_camera_obj) + if previous_camera != None: + previous_camera_obj = Camera.objects.filter(id=previous_camera)[0] + stopped_process(previous_camera_obj) + started_process(previous_camera_obj) return instance diff --git a/src/Inventory/serializers.py b/src/Inventory/serializers.py index 7a5539fc..96850669 100644 --- a/src/Inventory/serializers.py +++ b/src/Inventory/serializers.py @@ -1,23 +1,16 @@ from rest_framework import serializers from src.Inventory.models import Items +from src.Suppliers.models import Suppliers + +from src.Suppliers.serializers import SuppliersSerializer class ItemsSerializer(serializers.ModelSerializer): - """ - Items Serializer - """ + """Serializer for Items model""" class Meta: model = Items - fields = ["id", - "name", - "status", - "current_stock_level", - "low_stock_level", - "camera", - "date_created", - "date_updated", - "coords", - "multi_row" - ] + fields = ['id', 'name', 'status', 'current_stock_level', 'low_stock_level', 'camera', + 'date_created', 'date_updated', 'coords', 'prev_status', 'multi_row', + 'order_quantity', 'suppliers'] diff --git a/src/Inventory/service.py b/src/Inventory/service.py index 620f6658..90939696 100644 --- a/src/Inventory/service.py +++ b/src/Inventory/service.py @@ -2,6 +2,7 @@ from src.Inventory.models import Items from src.CameraAlgorithms.services.cameraalgorithm import camera_rtsp_link, send_run_request, stop_camera_algorithm, \ update_status_algorithm +from src.Mailer.message import send_email_to_suppliers from src.Mailer.service import send_email @@ -35,6 +36,11 @@ def process_item_status(data): item_data["count"] = min_item - 1 if item.prev_status == "In stock": + # try: + item.prev_status = None + send_email_to_suppliers(item, image_path) + # except Exception as e: + # print(f"Email notification errors: {e}") try: item.prev_status = None send_email(item, image_path, min_item, item_status) diff --git a/src/Mailer/message.py b/src/Mailer/message.py new file mode 100644 index 00000000..8070e64e --- /dev/null +++ b/src/Mailer/message.py @@ -0,0 +1,84 @@ +import os + +from django.core.mail import EmailMessage +from django.core.mail.backends.smtp import EmailBackend + +from datetime import datetime +from src.Core.const import SERVER_URL + +from src.Mailer.models import SMTPSettings, WorkingTime +from src.CompanyLicense.models import Company + + +def send_email_to_suppliers(item, image_path): + try: + work_time = WorkingTime.objects.last() + if item.suppliers is None or item.suppliers.contact_email is None: + raise ValueError("Missing contact email") + my_company = Company.objects.last() + count_order = item.order_quantity + except Exception as exc: + print(f"Not enough parameters to send notification: {exc}") + return + + subject = f"Urgent Reorder Request: Low Stock Level Notification - {item.name}" + message = f"Dear {item.suppliers.name_company}!\nBy this message we notify you of our urgent need to reorder a specific item due to its low stock level.\nItem Details:\nItem Name: {item.name},\nQuantity Needed: {count_order}\n\nPlease ensure that the additional order is promptly processed and dispatched to us to avoid any disruptions to our operations. We kindly request your immediate attention to this matter.\n\nCompany Information:\nCompany Name: {my_company.name_company}\nAddress: {my_company.address_company}\nPhone: {my_company.contact_phone}\nEmail: {my_company.contact_email}\n\nPlease note that this message is automatically generated, and there is no need to reply to it. However, if you have any questions or require further information, please feel free to contact us using the provided company details.We appreciate your ongoing partnership and your commitment to providing quality products and services. Thank you for your prompt attention to this matter.\nBest regards, {my_company.name_company}\n\n{SERVER_URL}:3000/inventory\n\n" + + image_name = image_path.split('/')[-1] + + recipient_list = [item.suppliers.contact_email] + + # email service check + try: + smtp_settings = SMTPSettings.objects.first() + except SMTPSettings.DoesNotExist: + raise Exception('SMTP configuration is not defined') + + # Check if a message was already + today = datetime.now().time() + start_time = work_time.time_start + end_time = work_time.time_end + if start_time < today < end_time: + # sending email + + connection = EmailBackend( + host=smtp_settings.server, + port=smtp_settings.port, + username=smtp_settings.username, + password=smtp_settings.password, + use_tls=smtp_settings.email_use_tls, + use_ssl=smtp_settings.email_use_ssl, + ) + # create email message + email_message = EmailMessage( + subject=subject, + body=message, + from_email=smtp_settings.username, + to=recipient_list + ) + # send image + image_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', f'{image_path}') + with open(image_path, 'rb') as f: + image_data = f.read() + email_message.attach(filename=f'{image_name}', content=image_data, mimetype='images/jpg') + + # send file + file_path = item.suppliers.file + if file_path is not None: + file_name = file_path.name + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', f'images/{file_name}') + if os.path.exists(file_path): + with open(file_path, 'rb') as f: + file_data = f.read() + email_message.attach(file_name, file_data, + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + else: + raise ValueError("File does not exist") + else: + raise ValueError("File path is not provided") + + # send email + connection.send_messages([email_message]) + + else: + print('Working time limit, message not sent') diff --git a/src/OrderView/migrations/0002_alter_indexoperations_options_and_more.py b/src/OrderView/migrations/0002_alter_indexoperations_options_and_more.py new file mode 100644 index 00000000..e209dde8 --- /dev/null +++ b/src/OrderView/migrations/0002_alter_indexoperations_options_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.7 on 2023-06-06 11:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("OrderView", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="indexoperations", + options={ + "verbose_name": "IndexOperatio", + "verbose_name_plural": "IndexOperations", + }, + ), + migrations.AlterModelTable( + name="indexoperations", + table="index_operations", + ), + ] diff --git a/src/OrderView/models.py b/src/OrderView/models.py index 379efbd1..09da5620 100644 --- a/src/OrderView/models.py +++ b/src/OrderView/models.py @@ -12,3 +12,9 @@ class IndexOperations(models.Model): camera = models.OneToOneField( Camera, verbose_name="operations_control camera", on_delete=models.CASCADE ) + + class Meta: + verbose_name = "IndexOperatio" + verbose_name_plural = "IndexOperations" + + db_table = "index_operations" diff --git a/src/OrderView/utils.py b/src/OrderView/utils.py index e2a445f7..347d98a9 100644 --- a/src/OrderView/utils.py +++ b/src/OrderView/utils.py @@ -5,19 +5,19 @@ def get_skany_video_info(time, camera_ip): - response = { + request_data = { "camera_ip": camera_ip, "time": time, } try: - request = requests.post( + response = requests.post( url=f"{SERVER_URL}:3456/is_video_available/", - json=response, + json=request_data, ) except Exception: return {"status": False} - result = request.json() + result = response.json() result["camera_ip"] = camera_ip return result diff --git a/src/Reports/migrations/0003_skanyreport_operation_time.py b/src/Reports/migrations/0003_skanyreport_operation_time.py new file mode 100644 index 00000000..bbf1cedb --- /dev/null +++ b/src/Reports/migrations/0003_skanyreport_operation_time.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.1 on 2023-06-06 10:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("Reports", "0002_alter_report_camera"), + ] + + operations = [ + migrations.AddField( + model_name="skanyreport", + name="operation_time", + field=models.FloatField(blank=True, null=True), + ), + ] diff --git a/src/Reports/migrations/0004_alter_skanyreport_operation_time.py b/src/Reports/migrations/0004_alter_skanyreport_operation_time.py new file mode 100644 index 00000000..63a80614 --- /dev/null +++ b/src/Reports/migrations/0004_alter_skanyreport_operation_time.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.7 on 2023-06-06 11:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("Reports", "0003_skanyreport_operation_time"), + ] + + operations = [ + migrations.AlterField( + model_name="skanyreport", + name="operation_time", + field=models.BigIntegerField(blank=True, null=True), + ), + ] diff --git a/src/Reports/models.py b/src/Reports/models.py index eb392374..fe9634b3 100644 --- a/src/Reports/models.py +++ b/src/Reports/models.py @@ -42,6 +42,7 @@ class SkanyReport(models.Model): zlecenie = models.CharField(max_length=50, blank=True, null=True) violation_found = models.BooleanField(blank=True, null=True) execution_date = models.DateTimeField(blank=True, null=True) + operation_time = models.BigIntegerField(blank=True, null=True) def __str__(self): return f"{self.id}" diff --git a/src/Reports/serializers.py b/src/Reports/serializers.py index c53cf544..02a60b08 100644 --- a/src/Reports/serializers.py +++ b/src/Reports/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from src.Reports.models import Report +from src.Reports.models import Report, SkanyReport from src.ImageReport.serializers import PhotoSerializers from src.CameraAlgorithms.serializers import AlgorithmSerializer, CameraReportSerializer @@ -27,3 +27,13 @@ class Meta: "date_updated", "status", ] + + +class OperationReportSerializer(serializers.ModelSerializer): + operationID = serializers.IntegerField(source='skany_index') + camera_ip = serializers.CharField(source='report.camera') + startTime = serializers.IntegerField(source='operation_time') + + class Meta: + model = SkanyReport + fields = ['id', 'operationID', 'camera_ip', 'startTime'] diff --git a/src/Reports/service.py b/src/Reports/service.py index 520ba80f..df8a3fc0 100644 --- a/src/Reports/service.py +++ b/src/Reports/service.py @@ -1,4 +1,5 @@ from typing import List, Dict +from datetime import datetime, timezone import logging from src.CameraAlgorithms.models import Camera @@ -11,8 +12,11 @@ def edit_extra(data: List[Dict], camera: Camera): - operation_index = IndexOperations.objects.filter(camera=camera.id).values('type_operation').last()[ - 'type_operation'] + operation_index = ( + IndexOperations.objects.filter(camera=camera.id) + .values("type_operation") + .last()["type_operation"] + ) extra_data = create_records.operation_control_data(operation_index) if len(data) >= 1: @@ -20,17 +24,23 @@ def edit_extra(data: List[Dict], camera: Camera): data[0]["zlecenie"] = str(extra_data["zlecenie"]) data[0]["execution_date"] = str(extra_data["execution_date"]) else: - data.append({ - "skany_index": int(extra_data["skany_index"]), - "zlecenie": str(extra_data["zlecenie"]), - "execution_date": str(extra_data["execution_date"]), - }) + data.append( + { + "skany_index": int(extra_data["skany_index"]), + "zlecenie": str(extra_data["zlecenie"]), + "execution_date": str(extra_data["execution_date"]), + } + ) logger.warning(f"fianl data is -> {data}") return data -def create_skanyreport(report: Report, report_data: List[Dict], violation_found: bool): +def create_skanyreport(report: Report, report_data: List[Dict], violation_found: bool, start_tracking: datetime): + utc_datetime = datetime.strptime(start_tracking, "%Y-%m-%d %H:%M:%S.%f %Z") + unix_timestamp = int(utc_datetime.timestamp()) + print(unix_timestamp) + skany_indeks = report_data[0].get("skany_index") zlecenie = report_data[0].get("zlecenie") execution_date = report_data[0].get("execution_date") @@ -40,5 +50,6 @@ def create_skanyreport(report: Report, report_data: List[Dict], violation_found: skany_index=skany_indeks, zlecenie=zlecenie, execution_date=execution_date, - violation_found=violation_found + violation_found=violation_found, + operation_time=unix_timestamp, ) diff --git a/src/Reports/urls.py b/src/Reports/urls.py index 13dfc601..966b738d 100644 --- a/src/Reports/urls.py +++ b/src/Reports/urls.py @@ -5,6 +5,7 @@ ReportListView, ActionViewSet, SearchReportListView, + GetOperationVideoInfo, ) @@ -20,5 +21,6 @@ name="report_action_list", ), path("search_params/", SearchReportListView.as_view()), + path("video-info/", GetOperationVideoInfo.as_view()), path("", include(router_report.urls)), ] diff --git a/src/Reports/views.py b/src/Reports/views.py index 62f19422..7065640a 100644 --- a/src/Reports/views.py +++ b/src/Reports/views.py @@ -3,7 +3,7 @@ from django.db.models import Q -from rest_framework.generics import GenericAPIView +from rest_framework.generics import GenericAPIView, ListAPIView from rest_framework.permissions import IsAuthenticated from rest_framework.exceptions import MethodNotAllowed from rest_framework import status, viewsets @@ -12,11 +12,12 @@ from src.CompanyLicense.decorators import validate_license from src.Core.const import PRODUCTION, SERVER_URL +from src.Core.paginators import NoPagination from src.ImageReport.models import Image from src.CameraAlgorithms.models import Camera from src.CameraAlgorithms.models import Algorithm -from src.Reports.models import Report -from src.Reports.serializers import ReportSerializers +from src.Reports.models import Report, SkanyReport +from src.Reports.serializers import ReportSerializers, OperationReportSerializer from src.Inventory.service import process_item_status from src.Reports.service import edit_extra, create_skanyreport @@ -43,7 +44,6 @@ def partial_update(self, request, *args, **kwargs): class ActionsWithPhotos(APIView): def post(self, request): - print(request.data) algorithm = Algorithm.objects.get(name=request.data.get("algorithm")) try: camera = Camera.objects.get(id=request.data.get("camera")) @@ -56,7 +56,6 @@ def post(self, request): extra = process_item_status(request.data.get("extra")) elif request.data.get("algorithm") == "operation_control": - print(request.data.get("algorithm")) if not PRODUCTION: print("start creating skany") if 'extra' in request.data: @@ -90,7 +89,7 @@ def post(self, request): image=image, date=date, report_id=action ) if request.data.get("algorithm") == "operation_control": - create_skanyreport(action, extra, not violation_found) + create_skanyreport(action, extra, not violation_found, start_tracking) else: action.delete() return Response( @@ -173,3 +172,12 @@ def get(self, request): queryset = self.get_queryset() serializer = self.serializer_class(queryset, many=True) return Response(serializer.data) + + +class GetOperationVideoInfo(ListAPIView): + queryset = SkanyReport.objects.all() + serializer_class = OperationReportSerializer + pagination_class = NoPagination + + def get_queryset(self): + return SkanyReport.objects.exclude(operation_time__isnull=True) diff --git a/src/Suppliers/__init__.py b/src/Suppliers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/Suppliers/admin.py b/src/Suppliers/admin.py new file mode 100644 index 00000000..8cc0eaa4 --- /dev/null +++ b/src/Suppliers/admin.py @@ -0,0 +1,20 @@ +from django.contrib import admin +from .models import Suppliers + + +@admin.register(Suppliers) +class SuppliersAdmin(admin.ModelAdmin): + list_filter = ("id",) + list_display = ( + 'name_company', + 'city', + 'state', + 'website', + 'contact_email', + 'contact_phone', + 'contact_mobile_phone', + 'logo', + 'file', + 'date_joined', + 'date_edited' + ) diff --git a/src/Suppliers/apps.py b/src/Suppliers/apps.py new file mode 100644 index 00000000..53cf90c0 --- /dev/null +++ b/src/Suppliers/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SuppliersConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "src.Suppliers" diff --git a/src/Suppliers/migrations/0001_initial.py b/src/Suppliers/migrations/0001_initial.py new file mode 100644 index 00000000..861b3c25 --- /dev/null +++ b/src/Suppliers/migrations/0001_initial.py @@ -0,0 +1,97 @@ +# Generated by Django 4.1.4 on 2023-06-02 09:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Suppliers", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name_company", + models.TextField(verbose_name="Name of company suppliers"), + ), + ( + "city", + models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="City of company suppliers", + ), + ), + ( + "state", + models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="State of company suppliers", + ), + ), + ("website", models.TextField(verbose_name="Website of suppliers")), + ( + "contact_email", + models.EmailField( + blank=True, + max_length=254, + null=True, + verbose_name="Contact email suppliers", + ), + ), + ( + "contact_phone", + models.TextField( + blank=True, + null=True, + verbose_name="Contact phone number suppliers", + ), + ), + ( + "contact_mobile_phone", + models.TextField( + blank=True, + null=True, + verbose_name="Contact mobile phone number suppliers", + ), + ), + ( + "logo", + models.ImageField( + blank=True, null=True, upload_to="", verbose_name="Logo company" + ), + ), + ( + "file", + models.FileField( + blank=True, + null=True, + upload_to="", + verbose_name="File to send notification", + ), + ), + ( + "date_joined", + models.DateTimeField(auto_now_add=True, verbose_name="Date joined"), + ), + ( + "date_edited", + models.DateTimeField(auto_now=True, verbose_name="Date edited"), + ), + ], + ), + ] diff --git a/src/Suppliers/migrations/__init__.py b/src/Suppliers/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/Suppliers/models.py b/src/Suppliers/models.py new file mode 100644 index 00000000..05eebb0f --- /dev/null +++ b/src/Suppliers/models.py @@ -0,0 +1,20 @@ +from django.db import models + + +class Suppliers(models.Model): + """Models Suppliers""" + + name_company = models.TextField(verbose_name="Name of company suppliers") + city = models.CharField(max_length=50, verbose_name="City of company suppliers", blank=True, null=True) + state = models.CharField(max_length=50, verbose_name="State of company suppliers", blank=True, null=True) + website = models.TextField(verbose_name="Website of suppliers") + contact_email = models.EmailField(verbose_name="Contact email suppliers", blank=True, null=True) + contact_phone = models.TextField(verbose_name="Contact phone number suppliers", blank=True, null=True) + contact_mobile_phone = models.TextField(verbose_name="Contact mobile phone number suppliers", blank=True, null=True) + logo = models.ImageField(verbose_name="Logo company", blank=True, null=True) + file = models.FileField(verbose_name="File to send notification", blank=True, null=True) + date_joined = models.DateTimeField(verbose_name="Date joined", auto_now_add=True) + date_edited = models.DateTimeField(verbose_name="Date edited", auto_now=True) + + def __str__(self): + return self.name_company diff --git a/src/Suppliers/serializers.py b/src/Suppliers/serializers.py new file mode 100644 index 00000000..fd9438b1 --- /dev/null +++ b/src/Suppliers/serializers.py @@ -0,0 +1,13 @@ +from rest_framework import serializers +from src.Suppliers.models import Suppliers + + +class SuppliersSerializer(serializers.ModelSerializer): + + name_company = serializers.CharField(allow_blank=True) + website = serializers.CharField(allow_blank=True) + + class Meta: + model = Suppliers + fields = '__all__' + read_only_fields = ["id", "date_joined", "date_edited"] diff --git a/src/Suppliers/tests.py b/src/Suppliers/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/src/Suppliers/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/Suppliers/urls.py b/src/Suppliers/urls.py new file mode 100644 index 00000000..0c64527d --- /dev/null +++ b/src/Suppliers/urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from rest_framework.routers import DefaultRouter + +from src.Suppliers.views import SuppliersView + +router = DefaultRouter() +router.register(r"company", SuppliersView, basename="company_info") + +urlpatterns = [ +] + +urlpatterns += router.urls diff --git a/src/Suppliers/views.py b/src/Suppliers/views.py new file mode 100644 index 00000000..448a04ce --- /dev/null +++ b/src/Suppliers/views.py @@ -0,0 +1,11 @@ +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import ModelViewSet +from src.Suppliers.models import Suppliers +from src.Suppliers.serializers import SuppliersSerializer + + +class SuppliersView(ModelViewSet): + permission_classes = [IsAuthenticated] + pagination_class = None + queryset = Suppliers.objects.all().order_by('id') + serializer_class = SuppliersSerializer diff --git a/src/newOrderView/services/order_services.py b/src/newOrderView/services/order_services.py index 61c606c2..6ff668f0 100644 --- a/src/newOrderView/services/order_services.py +++ b/src/newOrderView/services/order_services.py @@ -13,13 +13,14 @@ class OrderServices: - def get_operations(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: + @staticmethod + def get_operations(from_date: str, to_date: str) -> List[Dict[str, Any]]: connection: pyodbc.Connection = connector_service.get_database_connection() stanowiska_query: str = """ SELECT indeks AS id, - raport AS orderName + raport AS orderId FROM Stanowiska """ @@ -38,7 +39,7 @@ def get_operations(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: sk.indeks AS id, sk.data AS startTime, LEAD(sk.data) OVER (ORDER BY sk.data) AS endTime, - z.zlecenie AS orderName + z.zlecenie AS orderId FROM Skany sk JOIN Skany_vs_Zlecenia sz ON sk.indeks = sz.indeksskanu JOIN zlecenia z ON sz.indekszlecenia = z.indeks @@ -49,7 +50,14 @@ def get_operations(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: if from_date and to_date: operations_query += " AND sk.data >= ? AND sk.data <= ?" - params.extend([from_date, to_date]) + + from_date_dt = datetime.strptime(from_date, "%Y-%m-%d") + from_date_dt = from_date_dt + timedelta(microseconds=1) + + to_date_dt = datetime.strptime(to_date, "%Y-%m-%d") + to_date_dt = to_date_dt + timedelta(days=1) - timedelta(microseconds=1) + + params.extend([from_date_dt, to_date_dt]) operations_query += " ORDER BY sk.data" @@ -66,62 +74,66 @@ def get_operations(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: operation_row: Tuple[Any] = operations_data[i] id: int = operation_row[0] - orderName: str = operation_row[3].strip() + orderId: str = operation_row[3].strip() startTime: str = str(operation_row[1]) endTime: str = ( str(operation_row[2]) if i < len(operations_data) - 1 else None ) - operation = { + operation: Dict[str, Any] = { "id": id, - "orderName": orderName, - "startTime": startTime, - "endTime": endTime, + "orId": orderId, + "sTime": startTime, + "eTime": endTime, } startTime: datetime = add_ms(startTime) if endTime is not None: endTime: datetime = add_ms(endTime) - if ( - (endTime.day > startTime.day) - or (endTime.month > startTime.month) - or (endTime.year > startTime.year) - ): + if endTime and endTime.date() > startTime.date(): endTime = startTime + timedelta(hours=1) + else: + endTime = endTime or startTime + timedelta(hours=1) - operation["endTime"] = endTime.strftime("%Y-%m-%d %H:%M:%S.%f") + operation["eTime"] = endTime.strftime("%Y-%m-%d %H:%M:%S.%f") else: endTime = startTime + timedelta(hours=1) - operation["endTime"] = endTime.strftime("%Y-%m-%d %H:%M:%S.%f") + operation["eTime"] = endTime.strftime("%Y-%m-%d %H:%M:%S.%f") operations_list.append(operation) result = { - "operationID": operation_id, - "operationName": operation_name, - "operations": operations_list, + "oprTypeID": operation_id, + "oprName": operation_name, + "oprs": operations_list, } result_list.append(result) - return result_list - def get_order(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: + @staticmethod + def get_order(from_date: str, to_date: str) -> List[Dict[str, Any]]: connection: pyodbc.Connection = connector_service.get_database_connection() order_query: str = """ SELECT - DISTINCT z.zlecenie AS orderName + DISTINCT z.zlecenie AS orderId FROM Zlecenia z JOIN Skany_vs_Zlecenia sz ON z.indeks = sz.indekszlecenia JOIN Skany sk ON sz.indeksskanu = sk.indeks WHERE sk.data >= ? AND sk.data <= ? """ - params: List[Any] = [from_date, to_date] + from_date_dt = datetime.strptime(from_date, "%Y-%m-%d") + from_date_dt = from_date_dt + timedelta(microseconds=1) + + to_date_dt = datetime.strptime(to_date, "%Y-%m-%d") + to_date_dt = to_date_dt + timedelta(days=1) - timedelta(microseconds=1) + params: List[Any] = [from_date_dt, to_date_dt] + print(from_date_dt, to_date_dt) order_data: List[Tuple[Any]] = connector_service.executer( connection=connection, query=order_query, params=params ) @@ -130,17 +142,18 @@ def get_order(self, from_date: str, to_date: str) -> List[Dict[str, Any]]: for order_row in order_data: order: Dict[str, Any] = { - "orderName": order_row[0].strip(), + "orId": order_row[0].strip(), } result_list.append(order) return result_list - def get_order_by_details(self, operation_id: int) -> Dict[str, Any]: + @staticmethod + def get_order_by_details(operation_id: int) -> Dict[str, Any]: connection: pyodbc.Connection = connector_service.get_database_connection() - order_query: str = """ + order_query = """ WITH Operation AS ( SELECT sk.data AS operationTime @@ -150,25 +163,29 @@ def get_order_by_details(self, operation_id: int) -> Dict[str, Any]: ) SELECT z.indeks AS id, - z.zlecenie AS orderName, + z.zlecenie AS orderId, st.raport AS operationName, u.imie AS firstName, u.nazwisko AS lastName, - op.operationTime AS startTime, - ( - SELECT MIN(sk_next.data) - FROM Skany sk_next - WHERE sk_next.data > op.operationTime - AND sk_next.stanowisko = st.indeks - ) AS endTime, + CONVERT(VARCHAR(23), op.operationTime, 121) AS startTime, + CASE + WHEN DATEPART(year, sk_next.data) > DATEPART(year, op.operationTime) + OR DATEPART(month, sk_next.data) > DATEPART(month, op.operationTime) + OR DATEPART(day, sk_next.data) > DATEPART(day, op.operationTime) + THEN DATEADD(hour, 1, op.operationTime) + ELSE CONVERT(VARCHAR(23), sk_next.data, 121) + END AS endTime, st.indeks AS workplaceID, - sk.indeks AS operationID + sk.indeks AS operationID, + z.typ AS type FROM Zlecenia z JOIN Skany_vs_Zlecenia sz ON z.indeks = sz.indekszlecenia JOIN Skany sk ON sz.indeksskanu = sk.indeks JOIN Stanowiska st ON sk.stanowisko = st.indeks JOIN Uzytkownicy u ON sk.uzytkownik = u.indeks JOIN Operation op ON op.operationTime = sk.data + LEFT JOIN Skany sk_next ON sk_next.data > op.operationTime + AND sk_next.stanowisko = st.indeks WHERE sk.indeks = ? """ @@ -180,20 +197,25 @@ def get_order_by_details(self, operation_id: int) -> Dict[str, Any]: if order_data: id: int = order_data[0][8] - orderName: str = order_data[0][1].strip() + orderId: str = order_data[0][1].strip() operationName: str = order_data[0][2] firstName: str = order_data[0][3] lastName: str = order_data[0][4] - startTime: str = str(order_data[0][5]) - endTime: str = order_data[0][6] + startTime: datetime = datetime.strptime(str(order_data[0][5]), "%Y-%m-%d %H:%M:%S.%f") + endTime: datetime = ( + datetime.strptime(str(order_data[0][6]), "%Y-%m-%d %H:%M:%S.%f") + if order_data[0][6] is not None + else startTime + timedelta(hours=1) + ) workplaceID: int = order_data[0][7] + elementType = order_data[0][9] video_data: Optional[Dict[str, Any]] = {"status": False} if startTime is not None: camera_obj: Optional[Camera] = None - time: datetime = add_ms(startTime) - time_utc: datetime = time.replace(tzinfo=timezone.utc) + time: str = startTime.strftime("%Y-%m-%d %H:%M:%S.%f") + time_utc: datetime = add_ms(time).replace(tzinfo=timezone.utc) try: camera_obj: Camera = IndexOperations.objects.get( @@ -212,27 +234,28 @@ def get_order_by_details(self, operation_id: int) -> Dict[str, Any]: skany_report: Optional[SkanyReport] = SkanyReport.objects.filter( skany_index=id ).first() - print(skany_report, id) if skany_report: operation_status: Optional[bool] = skany_report.violation_found else: operation_status: Optional[bool] = None + sTime = int(startTime.timestamp()) + eTime = int(endTime.timestamp()) + print(sTime, eTime) + result: Dict[str, Any] = { "id": id, - "orderName": orderName, - "operationName": operationName, - "startTime": startTime, - "endTime": endTime, - "firstName": firstName, - "lastName": lastName, + "orId": orderId, + "oprName": operationName, + "elType": elementType, + "sTime": startTime, + "eTime": endTime, + "frsName": firstName, + "lstName": lastName, "status": operation_status, - "video_data": video_data, + "video": video_data, } return result else: return {} - - -services = OrderServices() diff --git a/src/newOrderView/utils.py b/src/newOrderView/utils.py index 4e7dd8e2..bbfe5070 100644 --- a/src/newOrderView/utils.py +++ b/src/newOrderView/utils.py @@ -1,4 +1,5 @@ from datetime import datetime +import hashlib def add_ms(time: str) -> datetime: @@ -7,3 +8,9 @@ def add_ms(time: str) -> datetime: result: datetime = datetime.strptime(time, "%Y-%m-%d %H:%M:%S.%f") return result + + +def generate_hash(prefix: str, from_date: str, to_date: str) -> str: + data = prefix + ":" + from_date + ":" + to_date + hash_object = hashlib.sha256(data.encode()) + return hash_object.hexdigest() diff --git a/src/newOrderView/views.py b/src/newOrderView/views.py index 53ec2a1f..78eeb4e5 100644 --- a/src/newOrderView/views.py +++ b/src/newOrderView/views.py @@ -1,12 +1,16 @@ from typing import Dict, List, Any -from rest_framework import generics -from rest_framework.response import Response +from django.http import JsonResponse +from django.core.cache import cache +from django.views.decorators.cache import cache_page + +from rest_framework import generics, status from src.Core.paginators import OrderViewPaginnator from src.MsSqlConnector.connector import connector as connector_service -from .services.order_services import services +from .services.order_services import OrderServices +from .utils import generate_hash class GetOperation(generics.GenericAPIView): @@ -15,9 +19,14 @@ def get(self, request): from_date: str = request.GET.get("from") to_date: str = request.GET.get("to") - result: List[Dict[str, Any]] = services.get_operations(from_date, to_date) + key: str = generate_hash("get_operation", from_date, to_date) + response = cache.get(key) + + if response is None: + response: List[Dict[str, Any]] = OrderServices.get_operations(from_date, to_date) + cache.set(key, response, timeout=120) - return Response(result, status=200) + return JsonResponse(data=response, status=status.HTTP_200_OK, safe=False) class GetOrders(generics.GenericAPIView): @@ -28,9 +37,14 @@ def get(self, request): from_date: str = request.GET.get("from") to_date: str = request.GET.get("to") - result: List[Dict[str, Any]] = services.get_order(from_date, to_date) + key: str = generate_hash("get_order", from_date, to_date) + response = cache.get("get_order_" + key) - return Response(result, status=200) + if response is None: + response: List[Dict[str, str]] = OrderServices.get_order(from_date, to_date) + cache.set(key, response, timeout=120) + + return JsonResponse(response, status=status.HTTP_200_OK, safe=False) class GetOrderByDetail(generics.GenericAPIView): @@ -40,6 +54,11 @@ class GetOrderByDetail(generics.GenericAPIView): def get(self, request): operation_id: int = request.GET.get("operation") - result = services.get_order_by_details(operation_id) + key: int = operation_id + response = cache.get(key) + + if response is None: + response: Dict[str, Any] = OrderServices.get_order_by_details(operation_id) + cache.set(key, response, timeout=120) - return Response(result, status=200) + return JsonResponse(data=response, status=status.HTTP_200_OK) diff --git a/src/router.py b/src/router.py index ab3a6119..8c17150f 100644 --- a/src/router.py +++ b/src/router.py @@ -18,6 +18,7 @@ path("inventory/", include("src.Inventory.urls"), name="inventory"), path("mailer/", include("src.Mailer.urls"), name="mailer"), path("core/", include("src.Core.urls"), name="core"), + path("suppliers/", include("src.Suppliers.urls"), name="suppliers"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) schema_view = get_schema_view(