From 1a7ce950c8730e1f44cc8221f40b1b17549296e1 Mon Sep 17 00:00:00 2001 From: "Tibor Bossanyi (Freelancer)" Date: Sat, 2 Oct 2021 22:55:36 +0200 Subject: [PATCH] V1.30.11 models simplification --- Dockerfile | 4 +- aitrainer-cron | 2 +- .../admin/notification.py | 39 ++++++++++++++- .../aitrainer_backoffice/db_router.py | 7 ++- .../aitrainer_backoffice/models/__init__.py | 4 +- .../models/customer.py | 20 ++------ .../models/exercises.py | 10 ++-- .../models/notification.py | 18 ++++++- .../aitrainer_backoffice/models/sports.py | 2 - .../aitrainer_backoffice/settings/deploy.py | 2 + .../controlling/admin/customer.py | 11 ++++- .../controlling/admin/frequent_customers.py | 40 +++++++++++----- .../controlling/admin/frequent_exercises.py | 31 ++++++++++-- .../controlling/automation/fcm.py | 1 - .../controlling/automation/notification.py | 15 ++++-- .../automation/notification_hook.py | 8 +++- .../controlling/models/__init__.py | 3 -- .../controlling/models/frequent_customers.py | 32 ------------- .../controlling/models/notification.py | 48 ------------------- docker-compose.yml | 2 +- 20 files changed, 159 insertions(+), 140 deletions(-) rename aitrainer_backoffice/{controlling => aitrainer_backoffice}/models/customer.py (68%) rename aitrainer_backoffice/{controlling => aitrainer_backoffice}/models/exercises.py (67%) delete mode 100644 aitrainer_backoffice/controlling/models/__init__.py delete mode 100644 aitrainer_backoffice/controlling/models/frequent_customers.py delete mode 100644 aitrainer_backoffice/controlling/models/notification.py diff --git a/Dockerfile b/Dockerfile index 4c14836..623d1af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,9 @@ COPY uwsgi_params /var/www/aitrainer.info/ COPY .key ./ ENV DJANGO_KEY="9ö2345iőjfdsasd9ukjhlkdf9hg" ENV GOOGLE_APPLICATION_CREDENTIALS=/aitrainer_backoffice/aitrainer_backoffice/aitrainer_backoffice/asset/aitrainer-firebase-adminsdk.json -ENV WORKOUTTEST_SETTING="DEPLOY" +ENV WORKOUTTEST_SETTING=DEPLOY ENV PORT=8000 EXPOSE 8000 -ENTRYPOINT cron start && tail -f /var/log/cron.log +CMD ['cron && tail -f /var/log/cron.log'] diff --git a/aitrainer-cron b/aitrainer-cron index c375a84..127c1d0 100644 --- a/aitrainer-cron +++ b/aitrainer-cron @@ -1 +1 @@ -*/5 * * * * python /aitrainer_backoffice/aitrainer_backoffice/manage.py runcrons --settings aitrainer_backoffice.settings.deploy >> /var/log/cronjob.log +*/5 * * * * python /aitrainer_backoffice/aitrainer_backoffice/manage.py runcrons --settings aitrainer_backoffice.settings.deploy > /proc/1/fd/1 2>/proc/1/fd/2 diff --git a/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py b/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py index 08174d2..2a4f73b 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py +++ b/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py @@ -1,7 +1,9 @@ +import os from django.contrib import admin from django.utils.html import format_html from django.utils.translation import ugettext_lazy as _ +from ..db_router import TestRouter from ..models.notification import Notification class NotificationAdmin(admin.ModelAdmin): @@ -26,7 +28,42 @@ class NotificationAdmin(admin.ModelAdmin): clone_notification.short_description = "Clone the selected notification" - actions = [clone_notification] + def sync_notification(self, request, queryset): + TestRouter.cloning = True + for notif in queryset: + live_qs = Notification.objects.using('live').raw(f'SELECT * from notification WHERE notification_id = {notif.pk}') + if len(list(live_qs)) == 0: + print("New notification to sync") + live_notif = Notification() + live_notif.pk = None + live_notif.internal_name = notif.internal_name + live_notif.internal_description = notif.internal_description + live_notif.message_title = notif.message_title + live_notif.message_body = notif.message_body + live_notif.image_url = notif.image_url + live_notif.schedule_date = notif.schedule_date + live_notif.schedule_hook = notif.schedule_hook + live_notif.schedule_sql = notif.schedule_sql + live_notif.active = notif.active + live_notif.save() + else: + for live_notif in live_qs: + print(f'Update notification to sync {notif.internal_name} - Live: {live_notif.internal_name}') + live_notif.internal_name = notif.internal_name + live_notif.internal_description = notif.internal_description + live_notif.message_title = notif.message_title + live_notif.message_body = notif.message_body + live_notif.image_url = notif.image_url + live_notif.schedule_date = notif.schedule_date + live_notif.schedule_hook = notif.schedule_hook + live_notif.schedule_sql = notif.schedule_sql + live_notif.active = notif.active + live_notif.save() + TestRouter.cloning = False + + sync_notification.short_description = "Syncrchnonize with the live database" + + actions = [clone_notification, sync_notification] diff --git a/aitrainer_backoffice/aitrainer_backoffice/db_router.py b/aitrainer_backoffice/aitrainer_backoffice/db_router.py index f9b8a43..bd52365 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/db_router.py +++ b/aitrainer_backoffice/aitrainer_backoffice/db_router.py @@ -3,6 +3,7 @@ class TestRouter: A router to control all database operations on models """ live_app_labels = {'controlling'} + cloning = False def db_for_read(self, model, **hints): if model._meta.app_label == 'controlling': @@ -11,8 +12,10 @@ class TestRouter: return 'default' def db_for_write(self, model, **hints): - if model._meta.app_label == 'controlling': - if model._meta.db_table == 'notification_history': + if self.cloning == True: + return 'live' + elif model._meta.app_label == 'controlling': + if model._meta.db_table == 'notification_history' or self.cloning == True: return 'live' else: raise Exception("This table cannot be changed!") diff --git a/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py b/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py index deb720c..beb872b 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py @@ -18,4 +18,6 @@ from .training_plan_day import TrainingPlanDay, TrainingPlanDayTranslation from .controlling import Controlling from .sports import Sport, SportTranslation from .app_text import AppText, AppTextTranslation -from .notification import Notification \ No newline at end of file +from .notification import NotificationHistory, Notification +from .customer import Customer +from .exercises import Exercises \ No newline at end of file diff --git a/aitrainer_backoffice/controlling/models/customer.py b/aitrainer_backoffice/aitrainer_backoffice/models/customer.py similarity index 68% rename from aitrainer_backoffice/controlling/models/customer.py rename to aitrainer_backoffice/aitrainer_backoffice/models/customer.py index 98500a0..401bb7e 100644 --- a/aitrainer_backoffice/controlling/models/customer.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/customer.py @@ -1,22 +1,8 @@ -import os from django.db import models from django.utils.translation import ugettext_lazy as _ - -SETTING = os.environ['WORKOUTTEST_SETTING'] -if SETTING == "PROD" : - from aitrainer_backoffice.aitrainer_backoffice.models.sports import Sport -else: - from aitrainer_backoffice.models.sports import Sport - -''' -class Sport(models.Model): - sport_id = models.AutoField(primary_key=True) - name = models.CharField(max_length=200, help_text='Unique name', - verbose_name=_("name")) - - class Meta: - db_table = 'sport' -''' + +from ..models.sports import Sport +from ..models.exercises import Exercises class Customer(models.Model): customer_id = models.BigAutoField(primary_key=True) diff --git a/aitrainer_backoffice/controlling/models/exercises.py b/aitrainer_backoffice/aitrainer_backoffice/models/exercises.py similarity index 67% rename from aitrainer_backoffice/controlling/models/exercises.py rename to aitrainer_backoffice/aitrainer_backoffice/models/exercises.py index bdd2965..e73060a 100644 --- a/aitrainer_backoffice/controlling/models/exercises.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/exercises.py @@ -1,12 +1,13 @@ from django.db import models -from ..models.customer import Customer +from ..models.exercise_type import ExerciseType class Exercises(models.Model): exercise_id = models.BigAutoField(primary_key=True) - customer = models.ForeignKey(Customer, on_delete=models.CASCADE) - exercise_type_id = models.IntegerField() - #exercise_type = models.ForeignKey(ExerciseType, on_delete=models.CASCADE) + + customer_id = models.PositiveIntegerField() + # exercise_type_id = models.IntegerField() + exercise_type = models.ForeignKey(ExerciseType, on_delete=models.CASCADE) date_add = models.DateField() quantity = models.DecimalField(decimal_places=0, max_digits=6) unit = models.CharField(max_length=20) @@ -21,3 +22,4 @@ class Exercises(models.Model): class Meta: db_table = 'exercises' + app_label = 'controlling' diff --git a/aitrainer_backoffice/aitrainer_backoffice/models/notification.py b/aitrainer_backoffice/aitrainer_backoffice/models/notification.py index b4955c8..827251a 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/models/notification.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/notification.py @@ -1,6 +1,8 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from ..models.customer import Customer + class Notification(models.Model): notification_id = models.AutoField(primary_key=True) message_title = models.CharField(max_length=50) @@ -19,4 +21,18 @@ class Notification(models.Model): verbose_name_plural = _("Notifications") def __str__(self): - return self.internal_name \ No newline at end of file + return self.internal_name + +class NotificationHistory(models.Model): + notification_history_id = models.AutoField(primary_key=True) + notification = models.ForeignKey(Notification, on_delete=models.CASCADE) + customer = models.ForeignKey(Customer, on_delete=models.CASCADE) + response = models.CharField(max_length=255) + notification_date = models.DateTimeField(blank=True, null=True) + + class Meta: + db_table = 'notification_history' + app_label = 'controlling' + + def __str__(self): + return f'{self.notification};{self.customer};{self.response}' \ No newline at end of file diff --git a/aitrainer_backoffice/aitrainer_backoffice/models/sports.py b/aitrainer_backoffice/aitrainer_backoffice/models/sports.py index 15001f4..75ffcc1 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/models/sports.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/sports.py @@ -1,10 +1,8 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ -from ckeditor.fields import RichTextField from .enums import LanguageTypes - class Sport(models.Model): sport_id = models.AutoField(primary_key=True) name = models.CharField(max_length=200, help_text='Unique name', diff --git a/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py b/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py index 146e641..93dd6ac 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py +++ b/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py @@ -10,6 +10,8 @@ LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')] # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.environ['DJANGO_KEY'] +os.environ["WORKOUTTEST_SETTING"] = "DEPLOY" + # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False diff --git a/aitrainer_backoffice/controlling/admin/customer.py b/aitrainer_backoffice/controlling/admin/customer.py index 5a1ce2b..5dca311 100644 --- a/aitrainer_backoffice/controlling/admin/customer.py +++ b/aitrainer_backoffice/controlling/admin/customer.py @@ -1,3 +1,4 @@ +import os from abc import ABC from datetime import datetime @@ -8,8 +9,14 @@ from django.contrib import admin from rangefilter.filters import DateRangeFilter, DateTimeRangeFilter from django.urls import path -from ..models.customer import Customer -from ..models.customer import Sport +SETTING = os.environ['WORKOUTTEST_SETTING'] +if SETTING == "PROD" : + from aitrainer_backoffice.aitrainer_backoffice.models.sports import Sport + from aitrainer_backoffice.aitrainer_backoffice.models.customer import Customer +else: + from aitrainer_backoffice.models.sports import Sport + from aitrainer_backoffice.models.customer import Customer + from ..automation.notification import Notification class SportFilter(SimpleListFilter, ABC): diff --git a/aitrainer_backoffice/controlling/admin/frequent_customers.py b/aitrainer_backoffice/controlling/admin/frequent_customers.py index 51e7e60..19b2bba 100644 --- a/aitrainer_backoffice/controlling/admin/frequent_customers.py +++ b/aitrainer_backoffice/controlling/admin/frequent_customers.py @@ -1,23 +1,41 @@ from django.contrib import admin +import os +from django.utils.translation import ugettext_lazy as _ -from ..models.frequent_customers import FrequentCustomers +SETTING = os.environ['WORKOUTTEST_SETTING'] +if SETTING == "PROD" : + from aitrainer_backoffice.aitrainer_backoffice.models.customer import Customer + from aitrainer_backoffice.aitrainer_backoffice.models.exercises import Exercises +else: + from aitrainer_backoffice.models.customer import Customer + from aitrainer_backoffice.models.exercises import Exercises + +class FrequentCustomers(Customer): + @property + def exercise_count(self): + count = Exercises.objects.filter(customer_id=self.customer_id).count() + return count + + class Meta: + proxy = True + verbose_name = _("Frequent Customer") + verbose_name_plural = _("Frequent Customers") class FrequentCustomersAdmin(admin.ModelAdmin): list_display = ('customer_id', 'name', 'firstname', 'email', 'exercise_count') #def get_queryset(self, request): - # qs = super(FrequentCustomersAdmin, self).get_queryset(request) - # return FrequentCustomers.objects.extra( - # select=["customer_id, name, firstname, email, ( select count(exercise_id) from exercises where exercises.customer_id = customer_customer.id) as exercise_count "], - #group_by=["customer_id"], - #having=["exercise_count > 10"], - # order_by=["-exercise_count"] - #) - #return qs.values("exercises").annotate(exercise_count=Count('exercises')).order_by('-exercise_count') - #return Exercises.objects.annotate(count=Count("customer__customer_id")) - #return qs.values('user').annotate(visit_sum=Count('visit_count')).order_by('-visit_sum') + # qs = super(FrequentCustomersAdmin, self).get_queryset(request) + # select=["customer_id, name, firstname, email, ( select count(exercise_id) from exercises where exercises.customer_id = customer_customer.id) as exercise_count "], + # group_by=["customer_id"], + # having=["exercise_count > 10"], + # order_by=["-exercise_count"] + # ) + # return qs.values("exercises").annotate(exercise_count=Count('exercises')).order_by('-exercise_count') + # return Exercises.objects.annotate(count=Count("customer__customer_id")) + # return qs.values('user').annotate(visit_sum=Count('visit_count')).order_by('-visit_sum') admin.site.register(FrequentCustomers, FrequentCustomersAdmin) diff --git a/aitrainer_backoffice/controlling/admin/frequent_exercises.py b/aitrainer_backoffice/controlling/admin/frequent_exercises.py index 1a1ce5e..9aac87e 100644 --- a/aitrainer_backoffice/controlling/admin/frequent_exercises.py +++ b/aitrainer_backoffice/controlling/admin/frequent_exercises.py @@ -1,15 +1,36 @@ from django.contrib import admin +from django.db.models import Count +import os +from django.utils.translation import ugettext_lazy as _ -#from aitrainer_backoffice.models.exercise_type import ExerciseType + +SETTING = os.environ['WORKOUTTEST_SETTING'] +if SETTING == "PROD" : + from aitrainer_backoffice.aitrainer_backoffice.models.exercise_type import ExerciseType + from aitrainer_backoffice.aitrainer_backoffice.models.exercises import Exercises +else: + from aitrainer_backoffice.models.exercise_type import ExerciseType + from aitrainer_backoffice.models.exercises import Exercises + +class FrequentExerciseType(ExerciseType): + @property + def exercise_count(self): + count = Exercises.objects.filter(exercise_type_id=self.exercise_type_id).count() + return count + class Meta: + proxy = True + verbose_name = _("Frequent Exercise") + verbose_name_plural = _("Frequent Exercises") class FrequentExerciseTypeAdmin(admin.ModelAdmin): list_display = ('exercise_type_id', 'name', 'exercise_count') - + def get_queryset(self, request): - qs = super(FrequentExerciseTypeAdmin, self).get_queryset(request) + qs = super(FrequentExerciseTypeAdmin, self).get_queryset(request) #.annotate(_exercise_count=Count("exercise_count",distinct=True )) return qs.filter(active=True) + #exercise_count.admin_order_field = '_exercise_count' -#admin.site.register(FrequentExerciseTypeAdmin) -#admin.autodiscover() +admin.site.register(FrequentExerciseType, FrequentExerciseTypeAdmin) +admin.autodiscover() diff --git a/aitrainer_backoffice/controlling/automation/fcm.py b/aitrainer_backoffice/controlling/automation/fcm.py index 5db258a..ae2e0d4 100644 --- a/aitrainer_backoffice/controlling/automation/fcm.py +++ b/aitrainer_backoffice/controlling/automation/fcm.py @@ -10,7 +10,6 @@ class FCM: # To learn more, visit the docs here: # https://cloud.google.com/docs/authentication/getting-started> env = os.environ['GOOGLE_APPLICATION_CREDENTIALS'] - print(f'ENV: {env}') default_app = initialize_app() def send_to_multiple_token(self, title, body, registration_token, image_url = None): diff --git a/aitrainer_backoffice/controlling/automation/notification.py b/aitrainer_backoffice/controlling/automation/notification.py index b797d11..7e5d025 100644 --- a/aitrainer_backoffice/controlling/automation/notification.py +++ b/aitrainer_backoffice/controlling/automation/notification.py @@ -1,20 +1,25 @@ +import os import datetime import pytz from .notification_hook import NotificationHook -from aitrainer_backoffice.aitrainer_backoffice.models import notification as notif +SETTING = os.environ['WORKOUTTEST_SETTING'] +if SETTING == "PROD": + from aitrainer_backoffice.aitrainer_backoffice.models.notification import NotificationHistory, Notification +else: + from aitrainer_backoffice.models.notification import NotificationHistory, Notification - -from ..models.notification import NotificationHistory from .fcm import FCM import traceback class Notification: - fcm = FCM() + fcm = None + def __init__(self) -> None: + self.fcm = FCM() def run(self): print("** Running Notification automation") - notification_queryset = notif.Notification.objects.using('live').raw('SELECT * from notification WHERE active = 1') + notification_queryset = Notification.objects.using('live').raw('SELECT * from notification WHERE active = 1') for notification in notification_queryset: if notification.schedule_date != None: diff --git a/aitrainer_backoffice/controlling/automation/notification_hook.py b/aitrainer_backoffice/controlling/automation/notification_hook.py index 3ab99a5..7012069 100644 --- a/aitrainer_backoffice/controlling/automation/notification_hook.py +++ b/aitrainer_backoffice/controlling/automation/notification_hook.py @@ -1,5 +1,11 @@ -from ..models.customer import Customer import datetime +import os + +SETTING = os.environ['WORKOUTTEST_SETTING'] +if SETTING == "PROD": + from aitrainer_backoffice.aitrainer_backoffice.models.customer import Customer +else: + from aitrainer_backoffice.models.customer import Customer class NotificationHook: def __init__(self) -> None: diff --git a/aitrainer_backoffice/controlling/models/__init__.py b/aitrainer_backoffice/controlling/models/__init__.py deleted file mode 100644 index 1ccd1f1..0000000 --- a/aitrainer_backoffice/controlling/models/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .customer import Customer -from .exercises import Exercises -from .frequent_customers import FrequentCustomers diff --git a/aitrainer_backoffice/controlling/models/frequent_customers.py b/aitrainer_backoffice/controlling/models/frequent_customers.py deleted file mode 100644 index 0bdf9cc..0000000 --- a/aitrainer_backoffice/controlling/models/frequent_customers.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from .exercises import Exercises - - -class FrequentCustomers(models.Model): - customer_id = models.BigAutoField(primary_key=True) - name = models.CharField(max_length=100, help_text='Last name', verbose_name=_("name")) - firstname = models.CharField(max_length=100, help_text='First name', verbose_name=_("firstname")) - email = models.CharField(max_length=100) - date_add = models.DateField() - #exercises = models.ManyToManyField(Exercises) - - #def exercises(self): - # exercises = Exercises.objects.filter(customer__customer_id=self.customer_id) - # return exercises - - @property - def exercise_count(self): - count = Exercises.objects.filter(customer__customer_id=self.customer_id).count() - return count - - class Meta: - db_table = 'customer' - verbose_name = _("Frequent Customer") - verbose_name_plural = _("Frequent Customers") - #ordering = ['exercise_count'] - #filter(exercise_count__gt=10) - - def __str__(self): - return self.name diff --git a/aitrainer_backoffice/controlling/models/notification.py b/aitrainer_backoffice/controlling/models/notification.py deleted file mode 100644 index aa7fb45..0000000 --- a/aitrainer_backoffice/controlling/models/notification.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -SETTING = os.environ['WORKOUTTEST_SETTING'] -if SETTING == "PROD": - from aitrainer_backoffice.aitrainer_backoffice.models.notification import Notification -else: - from aitrainer_backoffice.models.notification import Notification - -from ..models import Customer - -''' -class Notification(models.Model): - notification_id = models.AutoField(primary_key=True) - message_title = models.CharField(max_length=50) - message_body = models.TextField(max_length=100, blank=True, null=True) - image_url = models.ImageField(upload_to='images/', help_text='The notification image') - schedule_date = models.DateField(blank=True, null=True) - schedule_hook = models.TextField(max_length=100, blank=True, null=True) - schedule_sql = models.TextField(max_length=1000, blank=True, null=True) - internal_name = models.CharField(max_length=50, blank=True, null=True) - internal_description = models.TextField(max_length=500, blank=True, null=True) - active = models.BooleanField(default=0) - - class Meta: - db_table = 'notification' - verbose_name = _("Notification") - verbose_name_plural = _("Notifications") - - def __str__(self): - return self.internal_name - -''' - -class NotificationHistory(models.Model): - notification_history_id = models.AutoField(primary_key=True) - notification = models.ForeignKey(Notification, on_delete=models.CASCADE) - customer = models.ForeignKey(Customer, on_delete=models.CASCADE) - response = models.CharField(max_length=255) - notification_date = models.DateTimeField(blank=True, null=True) - - class Meta: - db_table = 'notification_history' - app_label = 'controlling' - - def __str__(self): - return f'{self.notification};{self.customer};{self.response}' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d115a1d..2a293c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - "8002:8000" environment: - GOOGLE_APPLICATION_CREDENTIALS=/aitrainer_backoffice/aitrainer_backoffice/aitrainer_backoffice/asset/aitrainer-firebase-adminsdk.json - - WORKOUTTEST_SETTING="PROD" + - WORKOUTTEST_SETTING=PROD command: gunicorn aitrainer_backoffice.aitrainer_backoffice.wsgi --env DJANGO_SETTINGS_MODULE=aitrainer_backoffice.aitrainer_backoffice.settings.prod --bind 0.0.0.0:8000 --workers 3 mysql-server: