From a09e015d808044b05a6dda8980e663b158636bac Mon Sep 17 00:00:00 2001 From: matteotolloso Date: Fri, 24 Nov 2023 10:13:08 +0100 Subject: [PATCH 1/7] database improvement --- .vscode/settings.json | 3 +- smartreport_app/admin.py | 10 +-- smartreport_app/kb_interface.py | 5 +- .../0004_archivedreport_user_type.py | 19 ++++++ ...id_remove_kpireportelement_kpi_and_more.py | 68 +++++++++++++++++++ .../0006_email_archivedreport_sent.py | 26 +++++++ ..._kpi_allowed_charts_alter_kpi_user_type.py | 24 +++++++ smartreport_app/models.py | 66 +++++++++++++++--- smartreport_app/serializers.py | 11 ++- smartreport_app/sync_db_kb.py | 42 ++++++++++++ smartreport_app/views.py | 44 +++++++++++- 11 files changed, 296 insertions(+), 22 deletions(-) create mode 100644 smartreport_app/migrations/0004_archivedreport_user_type.py create mode 100644 smartreport_app/migrations/0005_rename_name_kpi_kb_id_remove_kpireportelement_kpi_and_more.py create mode 100644 smartreport_app/migrations/0006_email_archivedreport_sent.py create mode 100644 smartreport_app/migrations/0007_alter_kpi_allowed_charts_alter_kpi_user_type.py create mode 100644 smartreport_app/sync_db_kb.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d3c9bf..b652b40 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ "smartreport" - ] + ], + "editor.inlineSuggest.showToolbar": "always" } \ No newline at end of file diff --git a/smartreport_app/admin.py b/smartreport_app/admin.py index 036ae31..861c572 100644 --- a/smartreport_app/admin.py +++ b/smartreport_app/admin.py @@ -20,11 +20,11 @@ class ReportTemplatePageInline(admin.TabularInline): show_change_link = True inlines = [KpiReportElementInline] - +# TODO CHECK @admin.register(Kpi) class KpiAdmin(admin.ModelAdmin): - list_display = ("name",) - search_fields = ("name",) + list_display = ("kb_name",) + search_fields = ("kb_name",) @admin.register(ReportTemplate) @@ -40,10 +40,10 @@ class ReportTemplatePageAdmin(admin.ModelAdmin): list_filter = ("layout",) inlines = [KpiReportElementInline] - +# TODO CHECK @admin.register(KpiReportElement) class KpiReportElementAdmin(admin.ModelAdmin): - list_display = ("report_page", "kpi", "chart_type") + list_display = ("report_page", "kpis", "chart_type") list_filter = ("chart_type",) diff --git a/smartreport_app/kb_interface.py b/smartreport_app/kb_interface.py index 11984c3..f3b9aa2 100644 --- a/smartreport_app/kb_interface.py +++ b/smartreport_app/kb_interface.py @@ -1,8 +1,11 @@ def kb_interface(kpi_name, params): + kpi_list = params['kpi_list'] # id of the kpi in the kb plot_type = params['chart_type'] + start_time = params['start_time'] + end_time = params['end_time'] + frequency = params['frequency'] - # other parameters like the time period if plot_type == 'line': response = { diff --git a/smartreport_app/migrations/0004_archivedreport_user_type.py b/smartreport_app/migrations/0004_archivedreport_user_type.py new file mode 100644 index 0000000..8275caa --- /dev/null +++ b/smartreport_app/migrations/0004_archivedreport_user_type.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2023-11-23 14:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0003_dashboardlayout_display'), + ] + + operations = [ + migrations.AddField( + model_name='archivedreport', + name='user_type', + field=models.CharField(choices=[('doctor', 'Doctor'), ('parent', 'Parent'), ('project_manager', 'Project Manager'), ('machine_maintainer', 'Machine Maintainer')], default='doctor', max_length=128), + preserve_default=False, + ), + ] diff --git a/smartreport_app/migrations/0005_rename_name_kpi_kb_id_remove_kpireportelement_kpi_and_more.py b/smartreport_app/migrations/0005_rename_name_kpi_kb_id_remove_kpireportelement_kpi_and_more.py new file mode 100644 index 0000000..dc2da51 --- /dev/null +++ b/smartreport_app/migrations/0005_rename_name_kpi_kb_id_remove_kpireportelement_kpi_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.7 on 2023-11-23 15:31 + +from django.db import migrations, models +import django.db.models.deletion +import smartreport_app.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0004_archivedreport_user_type'), + ] + + operations = [ + migrations.RenameField( + model_name='kpi', + old_name='name', + new_name='kb_id', + ), + migrations.RemoveField( + model_name='kpireportelement', + name='kpi', + ), + migrations.AddField( + model_name='kpi', + name='kb_counter', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='kpi', + name='kb_description', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='kpi', + name='kb_formula', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='kpi', + name='kb_name', + field=models.CharField(default='default_value', max_length=255), + preserve_default=False, + ), + migrations.AddField( + model_name='kpi', + name='kb_source', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddField( + model_name='kpi', + name='kb_unit', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AlterField( + model_name='kpi', + name='user_type', + field=models.JSONField(default=smartreport_app.models.DEFAULT_USER_CHOICES), + ), + migrations.CreateModel( + name='KpiReportElementKpi', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('kpi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='appears_in', to='smartreport_app.kpi')), + ('kpiReportElement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='kpis', to='smartreport_app.kpireportelement')), + ], + ), + ] diff --git a/smartreport_app/migrations/0006_email_archivedreport_sent.py b/smartreport_app/migrations/0006_email_archivedreport_sent.py new file mode 100644 index 0000000..46648cb --- /dev/null +++ b/smartreport_app/migrations/0006_email_archivedreport_sent.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.7 on 2023-11-23 16:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0005_rename_name_kpi_kb_id_remove_kpireportelement_kpi_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Email', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_type', models.CharField(choices=[('doctor', 'Doctor'), ('parent', 'Parent'), ('project_manager', 'Project Manager'), ('machine_maintainer', 'Machine Maintainer')], max_length=128)), + ('emails', models.JSONField()), + ], + ), + migrations.AddField( + model_name='archivedreport', + name='sent', + field=models.BooleanField(default=False), + ), + ] diff --git a/smartreport_app/migrations/0007_alter_kpi_allowed_charts_alter_kpi_user_type.py b/smartreport_app/migrations/0007_alter_kpi_allowed_charts_alter_kpi_user_type.py new file mode 100644 index 0000000..72a496f --- /dev/null +++ b/smartreport_app/migrations/0007_alter_kpi_allowed_charts_alter_kpi_user_type.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.7 on 2023-11-24 09:12 + +from django.db import migrations, models +import smartreport_app.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0006_email_archivedreport_sent'), + ] + + operations = [ + migrations.AlterField( + model_name='kpi', + name='allowed_charts', + field=models.JSONField(blank=True, default=smartreport_app.models.DEFAULT_CHART_CHOICES, null=True), + ), + migrations.AlterField( + model_name='kpi', + name='user_type', + field=models.JSONField(blank=True, default=smartreport_app.models.DEFAULT_USER_CHOICES, null=True), + ), + ] diff --git a/smartreport_app/models.py b/smartreport_app/models.py index 92322b6..1b51336 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -18,24 +18,39 @@ class UserType(models.TextChoices): "doughnut", "radar", ) - - def DEFAULT_CHART_CHOICES(): return [*CHART_CHOICES] +USER_CHOICES = ( + "doctor", + "parent", + "project_manager", + "machine_maintainer", +) +def DEFAULT_USER_CHOICES(): + return [*USER_CHOICES] + +# TODO CHECK class Kpi(models.Model): - name = models.CharField(max_length=255) - user_type = models.CharField( - max_length=128, - choices=UserType.choices, - ) + # field from the kb + kb_name = models.CharField(max_length=255) + kb_id = models.CharField(max_length=255) + kb_description = models.TextField(blank=True) + kb_formula = models.TextField(blank=True) + kb_unit = models.CharField(max_length=255, blank=True) + kb_source = models.CharField(max_length=255, blank=True) + kb_counter = models.IntegerField(default=0) - allowed_charts = models.JSONField(default=DEFAULT_CHART_CHOICES) + # internal field + user_type = models.JSONField(default=DEFAULT_USER_CHOICES, blank=True, null=True) + allowed_charts = models.JSONField(default=DEFAULT_CHART_CHOICES, blank=True, null=True) - priority = models.IntegerField(default=0) + def __str__(self): + return self.kb_name + priority = models.IntegerField(default=0) isNew = models.BooleanField(default=True) def __str__(self): @@ -51,6 +66,12 @@ def clean(self): for chart in self.allowed_charts: if chart not in CHART_CHOICES: raise ValidationError(f"{chart} is not a valid chart type") + + if not isinstance(self.user_type, list): + raise ValidationError("User type must be a list") + for user in self.user_type: + if user not in USER_CHOICES: + raise ValidationError(f"{user} is not a valid user type") class ReportTemplate(models.Model): @@ -94,7 +115,7 @@ class KpiReportElement(models.Model): report_page = models.ForeignKey( ReportTemplatePage, related_name="elements", on_delete=models.CASCADE ) - kpi = models.ForeignKey(Kpi, on_delete=models.CASCADE) + chart_type = models.CharField( max_length=128, ) @@ -102,6 +123,11 @@ class KpiReportElement(models.Model): def __str__(self): return f"{self.report_page.report_template.name} - {self.kpi.name}" +# TODO CHECK +class KpiReportElementKpi(models.Model): + kpi = models.ForeignKey(Kpi, on_delete=models.CASCADE, related_name='appears_in') + kpiReportElement = models.ForeignKey(KpiReportElement, on_delete=models.CASCADE, related_name='kpis') + class Alarm(models.Model): user_type = models.CharField( @@ -137,8 +163,28 @@ class DashboardLayout(models.Model): class ArchivedReport(models.Model): created = models.DateTimeField(auto_now_add=True) + sent = models.BooleanField(default=False) + + user_type = models.CharField( + max_length=128, + choices=UserType.choices, + ) + template = models.ForeignKey( ReportTemplate, related_name="archived_reports", on_delete=models.CASCADE ) file = models.FileField(upload_to="reports/") + + +class Email(models.Model): + user_type = models.CharField( + max_length=128, + choices=UserType.choices, + ) + + emails = models.JSONField() + + def clean(self): + if not isinstance(self.emails, list): + raise ValidationError("Emails must be a list") \ No newline at end of file diff --git a/smartreport_app/serializers.py b/smartreport_app/serializers.py index 2b231fc..534d1ca 100644 --- a/smartreport_app/serializers.py +++ b/smartreport_app/serializers.py @@ -6,13 +6,14 @@ Kpi, Alarm, DashboardLayout, + ArchivedReport, ) class KpiReportElementSerializer(serializers.ModelSerializer): class Meta: model = KpiReportElement - fields = ["id", "kpi", "chart_type"] + fields = ["id", "kpi1", "chart_type"] class ReportTemplatePageSerializer(serializers.ModelSerializer): @@ -22,7 +23,7 @@ class Meta: model = ReportTemplatePage fields = ["elements", "id", "layout"] - +# TODO FIX class ReportTemplateSerializer(serializers.ModelSerializer): pages = ReportTemplatePageSerializer(many=True) @@ -73,3 +74,9 @@ class DashboardLayoutSerializer(serializers.ModelSerializer): class Meta: model = DashboardLayout fields = "__all__" + +class ArchivedReportSerializer(serializers.ModelSerializer): + class Meta: + model = ArchivedReport + fields = "__all__" + diff --git a/smartreport_app/sync_db_kb.py b/smartreport_app/sync_db_kb.py new file mode 100644 index 0000000..297ce59 --- /dev/null +++ b/smartreport_app/sync_db_kb.py @@ -0,0 +1,42 @@ +import requests +from .models import Kpi + + +URL = 'https://vornao.dev:8888/kpis' +USERNAME = 'smartapp' +PASSWORD = 'api' + + +def sync_kpi_lits(): + + print("start sync_kpi_lits") + + kpi_list = requests.get(URL, auth=(USERNAME, PASSWORD)) + + for kpi in kpi_list.json()['data']: + + # if we don't have this kpi in our db, create it + + if Kpi.objects.filter(kb_id=kpi['kpi_id']).exists(): + continue + + kpi_instance = Kpi( + kb_name = kpi['name'], + kb_id = kpi['kpi_id'], + kb_description = kpi['description'], + kb_formula = kpi['formula'], + kb_unit = kpi['unit'], + kb_source = kpi['source'], + kb_counter = kpi['counter'], + user_type=[], + allowed_charts=[], + priority=0, + isNew=True + ) + kpi_instance.save() + print(f"KPI {kpi['kpi_id']} created") + + +if __name__ == '__main__': + sync_kpi_lits() + print('KPIs synced') \ No newline at end of file diff --git a/smartreport_app/views.py b/smartreport_app/views.py index a1ac6ce..2fb0f03 100644 --- a/smartreport_app/views.py +++ b/smartreport_app/views.py @@ -1,6 +1,8 @@ import os from typing import Any -from django.shortcuts import render +import django_filters +from .sync_db_kb import sync_kpi_lits + from .models import ( KpiReportElement, ReportTemplatePage, @@ -8,6 +10,7 @@ Kpi, Alarm, DashboardLayout, + ArchivedReport, ) from .serializers import ( ReportTemplatePageSerializer, @@ -16,6 +19,7 @@ KpiSerializer, AlarmSerializer, DashboardLayoutSerializer, + ArchivedReportSerializer, ) from rest_framework.response import Response from rest_framework import status @@ -40,11 +44,39 @@ class KpiReportElementViewSet(viewsets.ModelViewSet): queryset = KpiReportElement.objects.all() serializer_class = KpiReportElementSerializer +# TODO CHECK +class KpiFilter(django_filters.FilterSet): + user_type = django_filters.CharFilter(method='filter_user_type') + + class Meta: + model = Kpi + fields = ['user_type'] + def filter_user_type(self, queryset, name, value): + # Access the value from the GET query parameters + user_type_value = self.request.query_params.get('user_type') + + # Check if the value is provided + if user_type_value: # TODO fix + filtered_queryset = [] + for kpi_instance in queryset: + if user_type_value in kpi_instance.user_type: + filtered_queryset.append(kpi_instance) + return filtered_queryset + else: + return queryset + +# TODO CHECK class KpiViewSet(viewsets.ReadOnlyModelViewSet): queryset = Kpi.objects.all() serializer_class = KpiSerializer - filterset_fields = ["user_type", "name"] + filterset_class = KpiFilter + + def list(self, request, *args, **kwargs): + # external function + sync_kpi_lits() + + return super().list(request, *args, **kwargs) class AlarmViewSet(viewsets.ModelViewSet): @@ -98,4 +130,10 @@ def retrieve(self, request, pk=None): status=status.HTTP_400_BAD_REQUEST, ) data = kb_interface(kpi_name, params) - return Response({"data": data}) \ No newline at end of file + return Response({"data": data}) + + +class ArchiveViewSet(viewsets.ModelViewSet): + queryset = ArchivedReport.objects.all() + serializer_class = ArchivedReportSerializer + filterset_fields = ["user_type"] \ No newline at end of file From 9bf5f40ac9f1da7e937d8157ebf3ef953d1efa2f Mon Sep 17 00:00:00 2001 From: PaulMagos Date: Fri, 24 Nov 2023 11:55:43 +0100 Subject: [PATCH 2/7] Added image for report --- smartreport_app/admin.py | 7 +++++++ smartreport_app/models.py | 10 +++++++--- smartreport_app/serializers.py | 5 +++++ smartreport_app/urls.py | 1 + smartreport_app/views.py | 6 ++++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/smartreport_app/admin.py b/smartreport_app/admin.py index 036ae31..501ce80 100644 --- a/smartreport_app/admin.py +++ b/smartreport_app/admin.py @@ -3,6 +3,7 @@ Kpi, ReportTemplate, ReportTemplatePage, + ReportTemplateImage, KpiReportElement, Alarm, DashboardLayout @@ -63,3 +64,9 @@ class DashboardLayoutAdmin(admin.ModelAdmin): list_display = ('id', 'user_type', 'layout') # Fields to display in the list view list_filter = ("user_type", ) search_fields = ('user_type',) # Fields to enable searching in the admin interface + +@admin.register(ReportTemplateImage) +class ReportTemplateImageAdmin(admin.ModelAdmin): + list_display = ('id', 'user_type', 'report_id', 'img') + list_filter = ('report_id', 'user_type') + search_fields = ('report_id', 'user_type') \ No newline at end of file diff --git a/smartreport_app/models.py b/smartreport_app/models.py index 92322b6..e9cca28 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -51,8 +51,6 @@ def clean(self): for chart in self.allowed_charts: if chart not in CHART_CHOICES: raise ValidationError(f"{chart} is not a valid chart type") - - class ReportTemplate(models.Model): name = models.CharField(max_length=255) @@ -73,7 +71,13 @@ class ReportTemplate(models.Model): def __str__(self): return self.name - +class ReportTemplateImage(models.Model): + user_type = models.CharField( + max_length=128, + choices=UserType.choices, + ) + report_id = models.ForeignKey(ReportTemplate, on_delete=models.CASCADE) + img = models.TextField(null=True) class ReportTemplatePage(models.Model): report_template = models.ForeignKey( diff --git a/smartreport_app/serializers.py b/smartreport_app/serializers.py index 2b231fc..e93bfb0 100644 --- a/smartreport_app/serializers.py +++ b/smartreport_app/serializers.py @@ -3,6 +3,7 @@ KpiReportElement, ReportTemplatePage, ReportTemplate, + ReportTemplateImage, Kpi, Alarm, DashboardLayout, @@ -14,6 +15,10 @@ class Meta: model = KpiReportElement fields = ["id", "kpi", "chart_type"] +class ReportTemplateImageSerializer(serializers.ModelSerializer): + class Meta: + model = ReportTemplateImage + fields = "__all__" class ReportTemplatePageSerializer(serializers.ModelSerializer): elements = KpiReportElementSerializer(many=True) diff --git a/smartreport_app/urls.py b/smartreport_app/urls.py index be6d8f3..0c4b2c4 100644 --- a/smartreport_app/urls.py +++ b/smartreport_app/urls.py @@ -9,6 +9,7 @@ router.register(r"kpi-report-elements", views.KpiReportElementViewSet) router.register(r"kpi-list", views.KpiViewSet) router.register(r"alarms-list", views.AlarmViewSet) +router.register(r"report-img", views.ReportTemplateImageViewSet) router.register(r"dashboard-layout", views.DashboardLayoutViewSet) router.register(r"kpi-data", views.KpiDataViewSet, basename="kpi-data") diff --git a/smartreport_app/views.py b/smartreport_app/views.py index a1ac6ce..3256d79 100644 --- a/smartreport_app/views.py +++ b/smartreport_app/views.py @@ -5,6 +5,7 @@ KpiReportElement, ReportTemplatePage, ReportTemplate, + ReportTemplateImage, Kpi, Alarm, DashboardLayout, @@ -12,6 +13,7 @@ from .serializers import ( ReportTemplatePageSerializer, ReportTemplateSerializer, + ReportTemplateImageSerializer, KpiReportElementSerializer, KpiSerializer, AlarmSerializer, @@ -35,6 +37,10 @@ class ReportTemplatePageViewSet(viewsets.ModelViewSet): queryset = ReportTemplatePage.objects.all() serializer_class = ReportTemplatePageSerializer +class ReportTemplateImageViewSet(viewsets.ModelViewSet): + queryset = ReportTemplateImage.objects.all() + serializer_class = ReportTemplateImageSerializer + filterset_fields = ["user_type", "report_id"] class KpiReportElementViewSet(viewsets.ModelViewSet): queryset = KpiReportElement.objects.all() From becdc678c6050e80d9a447033c3b808d2c95fe58 Mon Sep 17 00:00:00 2001 From: matteotolloso Date: Fri, 24 Nov 2023 14:00:09 +0100 Subject: [PATCH 3/7] migration --- .../migrations/0008_merge_20231124_1255.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 smartreport_app/migrations/0008_merge_20231124_1255.py diff --git a/smartreport_app/migrations/0008_merge_20231124_1255.py b/smartreport_app/migrations/0008_merge_20231124_1255.py new file mode 100644 index 0000000..63370d0 --- /dev/null +++ b/smartreport_app/migrations/0008_merge_20231124_1255.py @@ -0,0 +1,14 @@ +# Generated by Django 4.2.7 on 2023-11-24 12:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0004_reporttemplateimage'), + ('smartreport_app', '0007_alter_kpi_allowed_charts_alter_kpi_user_type'), + ] + + operations = [ + ] From af3b25e8e48eaef7ac175fb3fb4a92b039d5693e Mon Sep 17 00:00:00 2001 From: matteotolloso Date: Fri, 24 Nov 2023 15:25:24 +0100 Subject: [PATCH 4/7] MULTIPLE PLOTS --- smartreport/smartreport_app/admin.py | 71 ++++++++++++++++++++++++++++ smartreport_app/admin.py | 4 +- smartreport_app/models.py | 8 ++-- smartreport_app/serializers.py | 6 ++- smartreport_app/views.py | 16 ++++--- 5 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 smartreport/smartreport_app/admin.py diff --git a/smartreport/smartreport_app/admin.py b/smartreport/smartreport_app/admin.py new file mode 100644 index 0000000..8e51b86 --- /dev/null +++ b/smartreport/smartreport_app/admin.py @@ -0,0 +1,71 @@ +from django.contrib import admin +from .models import ( + Kpi, + ReportTemplate, + ReportTemplatePage, + ReportTemplateImage, + KpiReportElement, + Alarm, + DashboardLayout +) + + +class KpiReportElementInline(admin.TabularInline): + model = KpiReportElement + extra = 1 + + +class ReportTemplatePageInline(admin.TabularInline): + model = ReportTemplatePage + extra = 1 + show_change_link = True + inlines = [KpiReportElementInline] + +@admin.register(Kpi) +class KpiAdmin(admin.ModelAdmin): + list_display = ("kb_name",) + search_fields = ("kb_name",) + + +@admin.register(ReportTemplate) +class ReportTemplateAdmin(admin.ModelAdmin): + list_display = ("id", "name", "frequency") + list_filter = ("frequency",) + inlines = [ReportTemplatePageInline] + + +@admin.register(ReportTemplatePage) +class ReportTemplatePageAdmin(admin.ModelAdmin): + list_display = ("report_template", "layout") + list_filter = ("layout",) + inlines = [KpiReportElementInline] + + +@admin.register(KpiReportElement) +class KpiReportElementAdmin(admin.ModelAdmin): + list_display = ("report_page", "kpis", "chart_type") + list_filter = ("chart_type",) + + +@admin.register(Alarm) +class AlarmAdmin(admin.ModelAdmin): + list_display = ("id", "user_type", "kpi", "min_value", "max_value") + list_filter = ("user_type", "kpi") + search_fields = ("user_type", "kpi__name") + + def get_kpi_name(self, obj): + return obj.kpi.name + + get_kpi_name.short_description = "KPI Name" + +@admin.register(DashboardLayout) +class DashboardLayoutAdmin(admin.ModelAdmin): + list_display = ('id', 'user_type', 'layout') # Fields to display in the list view + list_filter = ("user_type", ) + search_fields = ('user_type',) # Fields to enable searching in the admin interface + +@admin.register(ReportTemplateImage) +class ReportTemplateImageAdmin(admin.ModelAdmin): + list_display = ('id', 'user_type', 'report_id', 'img') + list_filter = ('report_id', 'user_type') + search_fields = ('report_id', 'user_type') \ No newline at end of file diff --git a/smartreport_app/admin.py b/smartreport_app/admin.py index a6f9e7c..1dc300f 100644 --- a/smartreport_app/admin.py +++ b/smartreport_app/admin.py @@ -21,7 +21,7 @@ class ReportTemplatePageInline(admin.TabularInline): show_change_link = True inlines = [KpiReportElementInline] -# TODO CHECK + @admin.register(Kpi) class KpiAdmin(admin.ModelAdmin): list_display = ("kb_name",) @@ -41,7 +41,7 @@ class ReportTemplatePageAdmin(admin.ModelAdmin): list_filter = ("layout",) inlines = [KpiReportElementInline] -# TODO CHECK + @admin.register(KpiReportElement) class KpiReportElementAdmin(admin.ModelAdmin): list_display = ("report_page", "kpis", "chart_type") diff --git a/smartreport_app/models.py b/smartreport_app/models.py index c0568ed..6e8f9a4 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -31,7 +31,7 @@ def DEFAULT_USER_CHOICES(): return [*USER_CHOICES] -# TODO CHECK + class Kpi(models.Model): # field from the kb @@ -54,7 +54,7 @@ def __str__(self): isNew = models.BooleanField(default=True) def __str__(self): - return self.name + return self.kb_name def save(self, *args, **kwargs): self.full_clean() @@ -122,6 +122,8 @@ class KpiReportElement(models.Model): ReportTemplatePage, related_name="elements", on_delete=models.CASCADE ) + # many to many + chart_type = models.CharField( max_length=128, ) @@ -129,7 +131,7 @@ class KpiReportElement(models.Model): def __str__(self): return f"{self.report_page.report_template.name} - {self.kpi.name}" -# TODO CHECK + class KpiReportElementKpi(models.Model): kpi = models.ForeignKey(Kpi, on_delete=models.CASCADE, related_name='appears_in') kpiReportElement = models.ForeignKey(KpiReportElement, on_delete=models.CASCADE, related_name='kpis') diff --git a/smartreport_app/serializers.py b/smartreport_app/serializers.py index b6f95bb..10d28f0 100644 --- a/smartreport_app/serializers.py +++ b/smartreport_app/serializers.py @@ -28,7 +28,7 @@ class Meta: model = ReportTemplatePage fields = ["elements", "id", "layout"] -# TODO FIX +# TODO CHECK IF WORKS class ReportTemplateSerializer(serializers.ModelSerializer): pages = ReportTemplatePageSerializer(many=True) @@ -53,7 +53,9 @@ def create(self, validated_data): # Create each KPI report element for the page. for element_data in elements_data: - KpiReportElement.objects.create(report_page=report_page, **element_data) + kpis = element_data.pop("kpis") + element = KpiReportElement.objects.create(report_page=report_page, **element_data) + element.kpis.set(kpis) return report_template diff --git a/smartreport_app/views.py b/smartreport_app/views.py index 82cf66a..877aa0e 100644 --- a/smartreport_app/views.py +++ b/smartreport_app/views.py @@ -50,7 +50,7 @@ class KpiReportElementViewSet(viewsets.ModelViewSet): queryset = KpiReportElement.objects.all() serializer_class = KpiReportElementSerializer -# TODO CHECK + class KpiFilter(django_filters.FilterSet): user_type = django_filters.CharFilter(method='filter_user_type') @@ -62,25 +62,27 @@ def filter_user_type(self, queryset, name, value): # Access the value from the GET query parameters user_type_value = self.request.query_params.get('user_type') + print(user_type_value) # Check if the value is provided - if user_type_value: # TODO fix + if user_type_value: filtered_queryset = [] for kpi_instance in queryset: + print(kpi_instance.user_type) if user_type_value in kpi_instance.user_type: - filtered_queryset.append(kpi_instance) - return filtered_queryset + filtered_queryset.append(kpi_instance.pk) + return Kpi.objects.filter(id__in=filtered_queryset) else: return queryset -# TODO CHECK + class KpiViewSet(viewsets.ReadOnlyModelViewSet): queryset = Kpi.objects.all() serializer_class = KpiSerializer filterset_class = KpiFilter def list(self, request, *args, **kwargs): - # external function - sync_kpi_lits() + # TODO enable this + # sync_kpi_lits() return super().list(request, *args, **kwargs) From 3b332d9b60aefa634a8120214afeaadf1a24d821 Mon Sep 17 00:00:00 2001 From: matteotolloso Date: Fri, 24 Nov 2023 18:12:39 +0100 Subject: [PATCH 5/7] a lot of things --- .vscode/settings.json | 4 +- smartreport_app/kb_interface.py | 11 ++-- ...pi_kb_uid_remove_kpi_kb_source_and_more.py | 50 ++++++++++++++++ .../migrations/0010_remove_kpi_isnew.py | 17 ++++++ .../0011_rename_created_kpi_kb_created.py | 18 ++++++ ..._rename_kb_created_kpi_kb_creation_date.py | 18 ++++++ smartreport_app/models.py | 12 +++- smartreport_app/serializers.py | 2 +- smartreport_app/sync_db_kb.py | 49 +++++++++------ smartreport_app/views.py | 59 +++++++++---------- 10 files changed, 181 insertions(+), 59 deletions(-) create mode 100644 smartreport_app/migrations/0009_rename_kb_id_kpi_kb_uid_remove_kpi_kb_source_and_more.py create mode 100644 smartreport_app/migrations/0010_remove_kpi_isnew.py create mode 100644 smartreport_app/migrations/0011_rename_created_kpi_kb_created.py create mode 100644 smartreport_app/migrations/0012_rename_kb_created_kpi_kb_creation_date.py diff --git a/.vscode/settings.json b/.vscode/settings.json index b652b40..17bcff4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { "cSpell.words": [ - "smartreport" + "kpis", + "smartreport", + "uids" ], "editor.inlineSuggest.showToolbar": "always" } \ No newline at end of file diff --git a/smartreport_app/kb_interface.py b/smartreport_app/kb_interface.py index f3b9aa2..6fe66dd 100644 --- a/smartreport_app/kb_interface.py +++ b/smartreport_app/kb_interface.py @@ -1,11 +1,12 @@ -def kb_interface(kpi_name, params): - kpi_list = params['kpi_list'] # id of the kpi in the kb +def kb_interface(params): + # kpi_list = params['kpi_list'] # id of the kpi in the kb plot_type = params['chart_type'] - start_time = params['start_time'] - end_time = params['end_time'] - frequency = params['frequency'] + # start_time = params['start_time'] + # end_time = params['end_time'] + # frequency = params['frequency'] + kpi_name = "pippo" if plot_type == 'line': response = { diff --git a/smartreport_app/migrations/0009_rename_kb_id_kpi_kb_uid_remove_kpi_kb_source_and_more.py b/smartreport_app/migrations/0009_rename_kb_id_kpi_kb_uid_remove_kpi_kb_source_and_more.py new file mode 100644 index 0000000..da6a398 --- /dev/null +++ b/smartreport_app/migrations/0009_rename_kb_id_kpi_kb_uid_remove_kpi_kb_source_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.7 on 2023-11-24 15:44 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0008_merge_20231124_1255'), + ] + + operations = [ + migrations.RenameField( + model_name='kpi', + old_name='kb_id', + new_name='kb_uid', + ), + migrations.RemoveField( + model_name='kpi', + name='kb_source', + ), + migrations.AddField( + model_name='kpi', + name='created', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='kpi', + name='kb_frequency', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddField( + model_name='kpi', + name='kb_range', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddField( + model_name='kpi', + name='kb_taxonomy', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='reporttemplate', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/smartreport_app/migrations/0010_remove_kpi_isnew.py b/smartreport_app/migrations/0010_remove_kpi_isnew.py new file mode 100644 index 0000000..41f0638 --- /dev/null +++ b/smartreport_app/migrations/0010_remove_kpi_isnew.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2023-11-24 16:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0009_rename_kb_id_kpi_kb_uid_remove_kpi_kb_source_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='kpi', + name='isNew', + ), + ] diff --git a/smartreport_app/migrations/0011_rename_created_kpi_kb_created.py b/smartreport_app/migrations/0011_rename_created_kpi_kb_created.py new file mode 100644 index 0000000..fe60218 --- /dev/null +++ b/smartreport_app/migrations/0011_rename_created_kpi_kb_created.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-11-24 16:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0010_remove_kpi_isnew'), + ] + + operations = [ + migrations.RenameField( + model_name='kpi', + old_name='created', + new_name='kb_created', + ), + ] diff --git a/smartreport_app/migrations/0012_rename_kb_created_kpi_kb_creation_date.py b/smartreport_app/migrations/0012_rename_kb_created_kpi_kb_creation_date.py new file mode 100644 index 0000000..b3f8beb --- /dev/null +++ b/smartreport_app/migrations/0012_rename_kb_created_kpi_kb_creation_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-11-24 16:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0011_rename_created_kpi_kb_created'), + ] + + operations = [ + migrations.RenameField( + model_name='kpi', + old_name='kb_created', + new_name='kb_creation_date', + ), + ] diff --git a/smartreport_app/models.py b/smartreport_app/models.py index 6e8f9a4..901fadc 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -2,6 +2,7 @@ from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError +from django.utils.timezone import now class UserType(models.TextChoices): @@ -35,12 +36,15 @@ def DEFAULT_USER_CHOICES(): class Kpi(models.Model): # field from the kb + kb_uid = models.CharField(max_length=255) kb_name = models.CharField(max_length=255) - kb_id = models.CharField(max_length=255) kb_description = models.TextField(blank=True) + kb_taxonomy = models.TextField(blank=True) + kb_range = models.CharField(max_length=255, blank=True) kb_formula = models.TextField(blank=True) kb_unit = models.CharField(max_length=255, blank=True) - kb_source = models.CharField(max_length=255, blank=True) + kb_frequency = models.CharField(max_length=255, blank=True) + kb_creation_date = models.DateTimeField(blank=True) kb_counter = models.IntegerField(default=0) # internal field @@ -51,7 +55,7 @@ def __str__(self): return self.kb_name priority = models.IntegerField(default=0) - isNew = models.BooleanField(default=True) + def __str__(self): return self.kb_name @@ -77,6 +81,8 @@ def clean(self): class ReportTemplate(models.Model): name = models.CharField(max_length=255) + created = models.DateTimeField(auto_now_add=True) + user_type = models.CharField( max_length=128, choices=UserType.choices, diff --git a/smartreport_app/serializers.py b/smartreport_app/serializers.py index 10d28f0..b26588b 100644 --- a/smartreport_app/serializers.py +++ b/smartreport_app/serializers.py @@ -14,7 +14,7 @@ class KpiReportElementSerializer(serializers.ModelSerializer): class Meta: model = KpiReportElement - fields = ["id", "kpi1", "chart_type"] + fields = ["id", "kpis", "chart_type"] class ReportTemplateImageSerializer(serializers.ModelSerializer): class Meta: diff --git a/smartreport_app/sync_db_kb.py b/smartreport_app/sync_db_kb.py index 297ce59..08e9b16 100644 --- a/smartreport_app/sync_db_kb.py +++ b/smartreport_app/sync_db_kb.py @@ -17,24 +17,39 @@ def sync_kpi_lits(): # if we don't have this kpi in our db, create it - if Kpi.objects.filter(kb_id=kpi['kpi_id']).exists(): - continue + if Kpi.objects.filter(kb_uid=kpi['uid']).exists(): + # update the counter + kpi_instance = Kpi.objects.get(kb_uid=kpi['uid']) + kpi_instance.kb_name = kpi['name'] + kpi_instance.kb_description = kpi['description'] + kpi_instance.kb_taxonomy = kpi['taxonomy'] + kpi_instance.kb_range = kpi['kpi_range'] + kpi_instance.kb_formula = kpi['formula'] + kpi_instance.kb_unit = kpi['unit'] + kpi_instance.kb_frequency = kpi['frequency'] + kpi_instance.kb_creation_date = kpi['creation_date'] + kpi_instance.kb_counter = kpi['counter'] + + kpi_instance.save() + print(f"KPI {kpi['uid']} updated") - kpi_instance = Kpi( - kb_name = kpi['name'], - kb_id = kpi['kpi_id'], - kb_description = kpi['description'], - kb_formula = kpi['formula'], - kb_unit = kpi['unit'], - kb_source = kpi['source'], - kb_counter = kpi['counter'], - user_type=[], - allowed_charts=[], - priority=0, - isNew=True - ) - kpi_instance.save() - print(f"KPI {kpi['kpi_id']} created") + else: + + kpi_instance = Kpi( + + kb_uid = kpi['uid'], + kb_name = kpi['name'], + kb_description = kpi['description'], + kb_taxonomy = kpi['taxonomy'], + kb_range = kpi['kpi_range'], + kb_formula = kpi['formula'], + kb_unit = kpi['unit'], + kb_frequency = kpi['frequency'], + kb_creation_date = kpi['creation_date'], + kb_counter = kpi['counter'], + ) + kpi_instance.save() + print(f"KPI {kpi['uid']} created") if __name__ == '__main__': diff --git a/smartreport_app/views.py b/smartreport_app/views.py index 877aa0e..1a1bbcc 100644 --- a/smartreport_app/views.py +++ b/smartreport_app/views.py @@ -81,8 +81,8 @@ class KpiViewSet(viewsets.ReadOnlyModelViewSet): filterset_class = KpiFilter def list(self, request, *args, **kwargs): - # TODO enable this - # sync_kpi_lits() + + sync_kpi_lits() return super().list(request, *args, **kwargs) @@ -105,39 +105,34 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) def list(self, request): - print(type(Kpi.objects.all().get(pk=1))) - return Response({"message": "This endpoint is not available"}) - - def retrieve(self, request, pk=None): - if pk == None: - return Response({"message": "How the f**k did you get here?"}) - kpi_name = Kpi.objects.get(pk=pk).name params = request.query_params + list_of_internal_ids = params.getlist("kpis")[0].split(',') # list of kpis to show in the plot + + queryset = Kpi.objects.filter(id__in=list_of_internal_ids) + + # translate in a list of ids for the KB + list_of_KB_uids = list(queryset.values_list("kb_uid", flat=True)) + if 'chart_type' not in params: return Response({"message": "The required parameter 'chart_type' is missing"}, status=status.HTTP_400_BAD_REQUEST) - if False: # enable in production - # check if the required kpi exists - if not Kpi.objects.filter( - name=params["kpi_name"], user_type=params["user_type"] - ).exists(): - return Response( - {"message": "The requested kpi does not exist"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # check if the required chart type is supported for the required kpi - if not Kpi.objects.filter( - name=params["kpi_name"], - user_type=params["user_type"], - allowed_charts__plot_name=params["chart_type"], - ).exists(): - return Response( - { - "message": "The requested chart type is not supported for the requested kpi" - }, - status=status.HTTP_400_BAD_REQUEST, - ) - data = kb_interface(kpi_name, params) + + if list_of_KB_uids == []: + return Response({"message": "No kpis found"}, status=status.HTTP_400_BAD_REQUEST) + + for kpi in Kpi.objects.filter(id__in=list_of_internal_ids): + if not params["chart_type"] in kpi.allowed_charts: + return Response({"message": f"Kpi {kpi.pk} does not support {params['chart_type']} plot"}, status=status.HTTP_400_BAD_REQUEST) + + # TODO implement start and end time + kb_interface_params = { + 'chart_type' : params['chart_type'], + 'kpi_KB_uid_list' : list_of_KB_uids, + 'kpi_frequency_list' : [kpi.kb_frequency in queryset], + 'start_time' : 'boh', + 'end_time' : 'boh', + } + + data = kb_interface(params = kb_interface_params) return Response({"data": data}) From 281bbca264f87f34ab0b23f8d1aa451629b46ccc Mon Sep 17 00:00:00 2001 From: matteotolloso Date: Fri, 24 Nov 2023 18:55:48 +0100 Subject: [PATCH 6/7] fix --- smartreport_app/admin.py | 2 +- ...element_kpis_delete_kpireportelementkpi.py | 21 +++++++++++++++++++ smartreport_app/models.py | 7 +------ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 smartreport_app/migrations/0013_kpireportelement_kpis_delete_kpireportelementkpi.py diff --git a/smartreport_app/admin.py b/smartreport_app/admin.py index 1dc300f..50aee51 100644 --- a/smartreport_app/admin.py +++ b/smartreport_app/admin.py @@ -44,7 +44,7 @@ class ReportTemplatePageAdmin(admin.ModelAdmin): @admin.register(KpiReportElement) class KpiReportElementAdmin(admin.ModelAdmin): - list_display = ("report_page", "kpis", "chart_type") + list_display = ("report_page", "chart_type") list_filter = ("chart_type",) diff --git a/smartreport_app/migrations/0013_kpireportelement_kpis_delete_kpireportelementkpi.py b/smartreport_app/migrations/0013_kpireportelement_kpis_delete_kpireportelementkpi.py new file mode 100644 index 0000000..0ecbe0b --- /dev/null +++ b/smartreport_app/migrations/0013_kpireportelement_kpis_delete_kpireportelementkpi.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2023-11-24 17:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('smartreport_app', '0012_rename_kb_created_kpi_kb_creation_date'), + ] + + operations = [ + migrations.AddField( + model_name='kpireportelement', + name='kpis', + field=models.ManyToManyField(related_name='appears_in', to='smartreport_app.kpi'), + ), + migrations.DeleteModel( + name='KpiReportElementKpi', + ), + ] diff --git a/smartreport_app/models.py b/smartreport_app/models.py index 901fadc..b5d9805 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -128,7 +128,7 @@ class KpiReportElement(models.Model): ReportTemplatePage, related_name="elements", on_delete=models.CASCADE ) - # many to many + kpis = models.ManyToManyField(Kpi, related_name='appears_in') chart_type = models.CharField( max_length=128, @@ -138,11 +138,6 @@ def __str__(self): return f"{self.report_page.report_template.name} - {self.kpi.name}" -class KpiReportElementKpi(models.Model): - kpi = models.ForeignKey(Kpi, on_delete=models.CASCADE, related_name='appears_in') - kpiReportElement = models.ForeignKey(KpiReportElement, on_delete=models.CASCADE, related_name='kpis') - - class Alarm(models.Model): user_type = models.CharField( max_length=128, From 5c1875c9dd322feedef4e69c2af581323eff1e59 Mon Sep 17 00:00:00 2001 From: PaulMagos Date: Fri, 24 Nov 2023 21:49:47 +0100 Subject: [PATCH 7/7] Fix --- smartreport_app/models.py | 5 +---- smartreport_app/views.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/smartreport_app/models.py b/smartreport_app/models.py index b5d9805..f7d762e 100644 --- a/smartreport_app/models.py +++ b/smartreport_app/models.py @@ -128,15 +128,12 @@ class KpiReportElement(models.Model): ReportTemplatePage, related_name="elements", on_delete=models.CASCADE ) - kpis = models.ManyToManyField(Kpi, related_name='appears_in') + kpis = models.ManyToManyField(Kpi) chart_type = models.CharField( max_length=128, ) - def __str__(self): - return f"{self.report_page.report_template.name} - {self.kpi.name}" - class Alarm(models.Model): user_type = models.CharField( diff --git a/smartreport_app/views.py b/smartreport_app/views.py index 1a1bbcc..7b1316d 100644 --- a/smartreport_app/views.py +++ b/smartreport_app/views.py @@ -82,7 +82,7 @@ class KpiViewSet(viewsets.ReadOnlyModelViewSet): def list(self, request, *args, **kwargs): - sync_kpi_lits() + # sync_kpi_lits() return super().list(request, *args, **kwargs)