diff --git a/aitrainer_backoffice/aitrainer_backoffice/admin/__init__.py b/aitrainer_backoffice/aitrainer_backoffice/admin/__init__.py index 0641e20..ea0d007 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/admin/__init__.py +++ b/aitrainer_backoffice/aitrainer_backoffice/admin/__init__.py @@ -15,3 +15,4 @@ from .training_plan_day import TrainingPlanDayAdmin from .controlling import ControllingAdmin from .sport import SportAdmin from .app_text import AppTextAdmin +from .notification import NotificationAdmin \ No newline at end of file diff --git a/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py b/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py new file mode 100644 index 0000000..08174d2 --- /dev/null +++ b/aitrainer_backoffice/aitrainer_backoffice/admin/notification.py @@ -0,0 +1,35 @@ +from django.contrib import admin +from django.utils.html import format_html +from django.utils.translation import ugettext_lazy as _ + +from ..models.notification import Notification + +class NotificationAdmin(admin.ModelAdmin): + list_display = ('notification_id', 'internal_name', 'message_title', 'active' ) + fields = ('internal_name', 'internal_description', 'message_title', 'message_body', 'image_url', 'get_image_preview', 'schedule_date', 'schedule_hook', 'schedule_sql', 'active' ) + list_editable = ('internal_name', 'active') + readonly_fields = ("get_image_preview",) + + def get_image_preview(self, obj): + image_url = '/media/' + str(obj.image_url) + if obj.pk: + return format_html(' ' \ + .format(url=image_url)) + + get_image_preview.short_description = _("Image Preview") + + def clone_notification(self, request, queryset): + for notif in queryset: + notif.pk = None + notif.internal_name = notif.internal_name + "_copy" + notif.save() + + clone_notification.short_description = "Clone the selected notification" + + actions = [clone_notification] + + + + +admin.site.register(Notification, NotificationAdmin) +admin.autodiscover() \ No newline at end of file diff --git a/aitrainer_backoffice/aitrainer_backoffice/db_router.py b/aitrainer_backoffice/aitrainer_backoffice/db_router.py index 28dc4d8..f9b8a43 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/db_router.py +++ b/aitrainer_backoffice/aitrainer_backoffice/db_router.py @@ -12,7 +12,10 @@ class TestRouter: def db_for_write(self, model, **hints): if model._meta.app_label == 'controlling': - raise Exception("This table cannot be changed!") + if model._meta.db_table == 'notification_history': + return 'live' + else: + raise Exception("This table cannot be changed!") return 'default' def allow_relation(self, obj1, obj2, **hints): diff --git a/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py b/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py index c07a2df..deb720c 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py +++ b/aitrainer_backoffice/aitrainer_backoffice/models/__init__.py @@ -17,4 +17,5 @@ from .split_tests import SplitTests from .training_plan_day import TrainingPlanDay, TrainingPlanDayTranslation from .controlling import Controlling from .sports import Sport, SportTranslation -from .app_text import AppText, AppTextTranslation \ No newline at end of file +from .app_text import AppText, AppTextTranslation +from .notification import Notification \ No newline at end of file diff --git a/aitrainer_backoffice/aitrainer_backoffice/models/notification.py b/aitrainer_backoffice/aitrainer_backoffice/models/notification.py new file mode 100644 index 0000000..1750f3a --- /dev/null +++ b/aitrainer_backoffice/aitrainer_backoffice/models/notification.py @@ -0,0 +1,22 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +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 \ No newline at end of file diff --git a/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py b/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py index 07a874d..512e029 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py +++ b/aitrainer_backoffice/aitrainer_backoffice/settings/deploy.py @@ -1,6 +1,6 @@ import os -BACKOFFICE_VERSION = "1.29.1" +BACKOFFICE_VERSION = "1.30" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -31,6 +31,7 @@ INSTALLED_APPS = [ 'adminsortable2', 'inline_actions', 'django_cron', + 'firebase-admin' ] MIDDLEWARE = [ @@ -159,5 +160,5 @@ LOGGING = { CRON_CLASSES = [ - 'controlling.cron.cron.MyCronJob', + 'controlling.cron.cron.NotificationJob', ] diff --git a/aitrainer_backoffice/aitrainer_backoffice/settings/dev.py b/aitrainer_backoffice/aitrainer_backoffice/settings/dev.py index 8fb4037..dc7036f 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/settings/dev.py +++ b/aitrainer_backoffice/aitrainer_backoffice/settings/dev.py @@ -1,6 +1,7 @@ import os +from firebase_admin import initialize_app -BACKOFFICE_VERSION = "1.29.1" +BACKOFFICE_VERSION = "1.30" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -32,6 +33,8 @@ INSTALLED_APPS = [ 'django_cron', ] +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -143,7 +146,7 @@ LOGGING = { 'loggers': { 'mylogger': { 'handlers': ['console'], - 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), + 'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'), 'propagate': True, }, }, @@ -156,5 +159,7 @@ LOGGING = { CRON_CLASSES = [ - 'controlling.cron.cron.MyCronJob', + 'controlling.cron.cron.NotificationJob', ] + + diff --git a/aitrainer_backoffice/aitrainer_backoffice/settings/prod.py b/aitrainer_backoffice/aitrainer_backoffice/settings/prod.py index 21a1f21..9bbf9f7 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/settings/prod.py +++ b/aitrainer_backoffice/aitrainer_backoffice/settings/prod.py @@ -1,6 +1,6 @@ import os -BACKOFFICE_VERSION = "1.29.1" +BACKOFFICE_VERSION = "1.30" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -31,6 +31,7 @@ INSTALLED_APPS = [ 'adminsortable2', 'inline_actions', 'django_cron', + 'firebase-admin', ] MIDDLEWARE = [ @@ -174,5 +175,5 @@ CACHES = { CRON_CLASSES = [ - 'aitrainer_backoffice.controlling.cron.cron.MyCronJob', + 'aitrainer_backoffice.controlling.cron.cron.NotificationJob', ] diff --git a/aitrainer_backoffice/aitrainer_backoffice/urls.py b/aitrainer_backoffice/aitrainer_backoffice/urls.py index 58a9f95..0b75126 100644 --- a/aitrainer_backoffice/aitrainer_backoffice/urls.py +++ b/aitrainer_backoffice/aitrainer_backoffice/urls.py @@ -24,7 +24,7 @@ from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), - path(r'^ckeditor/', include('ckeditor_uploader.urls')), + path(r'ckeditor', include('ckeditor_uploader.urls')), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/aitrainer_backoffice/controlling/admin/customer.py b/aitrainer_backoffice/controlling/admin/customer.py index 98836cb..5a1ce2b 100644 --- a/aitrainer_backoffice/controlling/admin/customer.py +++ b/aitrainer_backoffice/controlling/admin/customer.py @@ -10,10 +10,7 @@ from django.urls import path from ..models.customer import Customer from ..models.customer import Sport -from ..mautic import MauticHelper -from ..cron import cron -from ..push_notification import messaging - +from ..automation.notification import Notification class SportFilter(SimpleListFilter, ABC): title = "Sport" @@ -22,7 +19,7 @@ class SportFilter(SimpleListFilter, ABC): def lookups(self, request, model_admin): data = [] for s in Sport.objects.filter(): - data.append([s.sport_id, s.sport_name]) + data.append([s.sport_id, s.name]) return data def queryset(self, request, queryset): @@ -33,6 +30,8 @@ class SportFilter(SimpleListFilter, ABC): class CustomerAdmin(admin.ModelAdmin): + notif = Notification() + change_list_template = "controlling/mautic.html" list_display = ('customer_id', 'name','firstname', 'email', 'date_add', 'get_sport') list_filter = ( @@ -41,10 +40,10 @@ class CustomerAdmin(admin.ModelAdmin): ) def get_sport(self, obj): - return obj.sport.sport_name + return obj.sport.name get_sport.short_description = 'Sport' - get_sport.admin_order_field = 'sport__sport_name' + get_sport.admin_order_field = 'sport__name' # If you would like to add a default range filter @@ -75,7 +74,7 @@ class CustomerAdmin(admin.ModelAdmin): return my_urls + urls def set_mautic(self, request): - messaging.send_to_token() + self.notif.run() return HttpResponseRedirect("../") diff --git a/aitrainer_backoffice/controlling/admin/frequent_exercises.py b/aitrainer_backoffice/controlling/admin/frequent_exercises.py index 67d95bc..ab12b01 100644 --- a/aitrainer_backoffice/controlling/admin/frequent_exercises.py +++ b/aitrainer_backoffice/controlling/admin/frequent_exercises.py @@ -1,6 +1,6 @@ from django.contrib import admin -from ..models import ExerciseType +from aitrainer_backoffice.models.exercise_type import ExerciseType class FrequentExerciseTypeAdmin(admin.ModelAdmin): @@ -11,5 +11,5 @@ class FrequentExerciseTypeAdmin(admin.ModelAdmin): return qs.filter(active=True) -admin.site.register(ExerciseType, FrequentExerciseTypeAdmin) -admin.autodiscover() +#admin.site.register(FrequentExerciseTypeAdmin) +#admin.autodiscover() diff --git a/aitrainer_backoffice/controlling/automation/fcm.py b/aitrainer_backoffice/controlling/automation/fcm.py new file mode 100644 index 0000000..0630d5e --- /dev/null +++ b/aitrainer_backoffice/controlling/automation/fcm.py @@ -0,0 +1,97 @@ +import datetime +from firebase_admin import messaging, initialize_app, exceptions + +class FCM: + logo_url = 'https://workouttest.com/wp-content/uploads/2020/10/WT_long_logo.png' + + # default constructor + def __init__(self): + # To learn more, visit the docs here: + # https://cloud.google.com/docs/authentication/getting-started> + default_app = initialize_app() + + def send_to_multiple_token(self, title, body, registration_token, image_url = None): + try: + notification_image_url = image_url + #if image_url == None: + # notification_image_url = self.logo_url + + message = messaging.MulticastMessage( + notification=messaging.Notification( + title=title, + body=body, + ), + android=messaging.AndroidConfig( + ttl=datetime.timedelta(seconds=3600), + priority='normal', + notification=messaging.AndroidNotification( + image=notification_image_url, + ), + ), + apns=messaging.APNSConfig( + payload=messaging.APNSPayload( + aps=messaging.Aps(badge=1), + ), + fcm_options= messaging.APNSFCMOptions( + image=notification_image_url, + ) + ), + tokens= registration_token, + ) + response = messaging.send_multicast(message) + # Response is a message ID string. + print('Successfully sent message:', response); + + except exceptions.FirebaseError as error: + print('Error sending message:', error); + except ValueError as value_error: + print('Error sending message:', value_error); + + + def send_to_token(self, title, body, image_url = None, registration_token = None): + if registration_token == None: + return "Registration token is null" + try: + #notification_image_url = image_url + #if image_url == None: + notification_image_url = self.logo_url + registration_token = 'cOqNt8rzo074gbIkBSpCgW:APA91bEBuNi3iVzGKb4JhxqN2j80MoJbNptLHk2qsdeKBQz5grpHtrPPXvDqn5BJVVSaj1nwGPwgN7pi6FIApog_TTP3g1yobgmgpPN6udrYgzILlVPMvdGGFDSDh6gKlczhlTL9NEp0' + + print(f'image: {notification_image_url}' ) + + message = messaging.Message( + notification=messaging.Notification( + title=title, + body=body, + ), + android=messaging.AndroidConfig( + ttl=datetime.timedelta(seconds=3600), + priority='normal', + notification=messaging.AndroidNotification( + image=notification_image_url, + ), + ), + apns=messaging.APNSConfig( + payload=messaging.APNSPayload( + aps=messaging.Aps(badge=1), + ), + fcm_options= messaging.APNSFCMOptions( + image=notification_image_url, + ) + ), + token= registration_token, + ) + response = messaging.send(message) + # Response is a message ID string. + print('Successfully sent message:', response); + rc = 'OK' + + except exceptions.FirebaseError as error: + print('Error sending message:', error); + rc = error + except ValueError as value_error: + print('Error sending message:', value_error); + rc = value_error + + return rc + diff --git a/aitrainer_backoffice/controlling/mautic/helper.py b/aitrainer_backoffice/controlling/automation/mautic.py similarity index 99% rename from aitrainer_backoffice/controlling/mautic/helper.py rename to aitrainer_backoffice/controlling/automation/mautic.py index c89aae2..d4c332c 100644 --- a/aitrainer_backoffice/controlling/mautic/helper.py +++ b/aitrainer_backoffice/controlling/automation/mautic.py @@ -6,7 +6,7 @@ import datetime from ..models.customer import Customer -class MauticHelper: +class Mautic: def syncTrial(self): tenDays = datetime.datetime - datetime.timedelta(days=10) diff --git a/aitrainer_backoffice/controlling/push_notification/messaging.py b/aitrainer_backoffice/controlling/automation/messaging.py similarity index 72% rename from aitrainer_backoffice/controlling/push_notification/messaging.py rename to aitrainer_backoffice/controlling/automation/messaging.py index 204c4bb..54d05cc 100644 --- a/aitrainer_backoffice/controlling/push_notification/messaging.py +++ b/aitrainer_backoffice/controlling/automation/messaging.py @@ -11,27 +11,31 @@ import argparse import json import requests import datetime +import os from google.oauth2 import service_account -from firebase_admin import messaging +from firebase_admin import messaging, initialize_app +from aitrainer_backoffice.settings.prod import BASE_DIR PROJECT_ID = 'aitrainer-af0ec' BASE_URL = 'https://fcm.googleapis.com' FCM_ENDPOINT = 'v1/projects/' + PROJECT_ID + '/messages:send' FCM_URL = BASE_URL + '/' + FCM_ENDPOINT -SCOPES = ['https://www.googleapis.com/auth/firebase.messaging'] +SCOPES = ['https://www.googleapis.com/auth/firebase.messaging', 'https://www.googleapis.com/auth/cloud-platform', 'email'] +ASSET_ROOT = os.path.join(BASE_DIR, "asset") +SERVER_KEY = 'AAAA18iNwog:APA91bFo_kDhK4Nd_zHAtyPj6D5CR_amV0aenNn9VPMMVz6j8mgyTCSr3J3IDD_U9duBe1AN35oAiWKp6LmDdJ5n2UQU6unfyUUmsFOSaDlEQZtYl6ZeIQ5XW52Fmkc29Xh62GZ4E-Ic' # [START retrieve_access_token] def _get_access_token(): """Retrieve a valid access token that can be used to authorize requests. :return: Access token. """ - credentials = credentials = service_account.Credentials.from_service_account_file( - "asset/aitrainer-firebase-adminsdk.json", - scopes=['email'], - ) - access_token_info = credentials.get_access_token() - return access_token_info.access_token + + default_app = initialize_app() + + access_token = default_app.credential.get_access_token() + return access_token.access_token + #return access_token_info.access_token # [END retrieve_access_token] def _send_fcm_message(fcm_message): @@ -46,13 +50,16 @@ def _send_fcm_message(fcm_message): } # [END use_access_token] resp = requests.post(FCM_URL, data=json.dumps(fcm_message), headers=headers) + return resp +''' if resp.status_code == 200: print('Message sent to Firebase for delivery, response:') print(resp.text) else: print('Unable to send message to Firebase') print(resp.text) +''' def _build_common_message(): """Construct common notifiation message. @@ -120,6 +127,63 @@ def send_to_token(): print('Successfully sent message:', response) # [END send_to_token] +def send_fcm_to_token(): + # [START send_to_token] + # This registration token comes from the client FCM SDKs. + + ''' + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + _get_access_token(), + } + + # See documentation on defining a message payload. + message = messaging.Message( + data = _build_override_message(), + token= registration_token, + ) + ''' + + registration_token = ['fFjCZmrHREpRvxZMIKhNSI:APA91bH7cfctHFHbKxtQ5XGRlL26jgLLzo3a1x4hlPfZYi9WxrauMkdIBmqnIQnyD8Jc3xEs0gAsgNYNMLDEgdrHV3bbH4gvFHYUrYzOHZFr-2aVCsYF9otT8_fmAV380egGf5HiCIYd', + 'cOqNt8rzo074gbIkBSpCgW:APA91bEBuNi3iVzGKb4JhxqN2j80MoJbNptLHk2qsdeKBQz5grpHtrPPXvDqn5BJVVSaj1nwGPwgN7pi6FIApog_TTP3g1yobgmgpPN6udrYgzILlVPMvdGGFDSDh6gKlczhlTL9NEp0', + 'eUrsvYw9ekx0sr_R8pGTD8:APA91bHJKl1D9gLBz7xi0gILb7ng576DDnCvNba7MHqRaFn9MeeCSVLhEU1yC10b1v0KrZ4pVYgUqxkSv2t-Rh0mXtHR7ABGQENuGDfqjokWPNamXhp99Fuq66o3jnlXxSzRKe_aSCtk'] + + message = messaging.MulticastMessage( + notification=messaging.Notification( + title='$GOOG up 1.43% on the day', + body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.', + ), + android=messaging.AndroidConfig( + ttl=datetime.timedelta(seconds=3600), + priority='normal', + notification=messaging.AndroidNotification( + icon='stock_ticker_update', + color='#f45342' + ), + ), + apns=messaging.APNSConfig( + payload=messaging.APNSPayload( + aps=messaging.Aps(badge=42), + ), + ), + tokens= registration_token, + ) + + print(f'SEND FCM message {message}') + response = messaging.send_multicast(message) + + #data = json.dumps(message) + + #print(f'SEND FCM message {FCM_URL} - Header {headers} - body: {data}') + + #response = requests.post(FCM_URL, headers = headers, data=data) + print(f'RESPONSE: {response}') + + #response = _send_fcm_message(message) + # Response is a message ID string. + #print('Successfully sent message:', response) + # [END send_to_token] + def send_to_topic(): # [START send_to_topic] # The topic name can be optionally prefixed with "/topics/". @@ -141,6 +205,7 @@ def send_to_topic(): # [END send_to_topic] + def send_to_condition(): # [START send_to_condition] # Define a condition which will send to devices which are subscribed diff --git a/aitrainer_backoffice/controlling/automation/notification.py b/aitrainer_backoffice/controlling/automation/notification.py new file mode 100644 index 0000000..184c006 --- /dev/null +++ b/aitrainer_backoffice/controlling/automation/notification.py @@ -0,0 +1,44 @@ +import datetime +from .notification_hook import NotificationHook +from ..models import notification as notif +from ..models.notification import NotificationHistory +from .fcm import FCM + +class Notification: + fcm = FCM() + + def run(self): + notification_queryset = notif.Notification.objects.using('live').raw('SELECT * from notification WHERE active = 1') + + for notification in notification_queryset: + if notification.schedule_date != None: + pass + elif notification.schedule_hook != None: + hook = NotificationHook() + try: + hook_function = notification.schedule_hook + hook_sql = notification.schedule_sql + if hook_sql == None: + customers = getattr(hook, hook_function)() + else: + customers = getattr(hook, hook_function)(hook_sql) + + for customer in customers: + if customer.firebase_reg_token != None: + print(f'-- Notify Customer {customer.customer_id}') + rc= self.fcm.send_to_token(notification.message_title, notification.message_body, notification.image_url, customer.firebase_reg_token) + self.insert_history(notification=notification, customer=customer, rc=rc) + + + except Exception as ex: + print(f'Notification Hook {notification.schedule_hook} has no callback function: {ex}') + + def insert_history(self, notification, customer, rc): + history = NotificationHistory() + history.pk = None + history.notification = notification + history.customer = customer + history.response = rc + history.notification_date = datetime.datetime.now() + history.save() + print(f'-- Notification History "{history}" has been saved') diff --git a/aitrainer_backoffice/controlling/automation/notification_hook.py b/aitrainer_backoffice/controlling/automation/notification_hook.py new file mode 100644 index 0000000..3ab99a5 --- /dev/null +++ b/aitrainer_backoffice/controlling/automation/notification_hook.py @@ -0,0 +1,16 @@ +from ..models.customer import Customer +import datetime + +class NotificationHook: + def __init__(self) -> None: + pass + + def NotificationSelectAdmins(self): + print(datetime.datetime.now(), " *** START automation NotificationSelectAdmins ") + qs = Customer.objects.raw('SELECT customer_id, firebase_reg_token from customer WHERE admin = 1') + return qs + + def NotificationCommonSQL(self, sql): + print(datetime.datetime.now(), " *** START automation NotificationCommonSQL ") + qs = Customer.objects.raw(sql) + return qs \ No newline at end of file diff --git a/aitrainer_backoffice/controlling/cron/cron.py b/aitrainer_backoffice/controlling/cron/cron.py index 7c4bf3e..8930c43 100644 --- a/aitrainer_backoffice/controlling/cron/cron.py +++ b/aitrainer_backoffice/controlling/cron/cron.py @@ -1,19 +1,27 @@ -from ..mautic import MauticHelper +from ..automation.notification import Notification from django_cron import CronJobBase, Schedule import datetime class MyCronJob(CronJobBase): - print(datetime.datetime.now(), " *** START sync customers ") + RUN_EVERY_MINS = 60 - schedule = Schedule(run_every_mins=RUN_EVERY_MINS) code = 'aitrainer_backoffice.controlling.cron' # a unique code - print(datetime.datetime.now(), " *** END sync customers ") + + def do(self): + print(datetime.datetime.now(), " *** START sync customers ") + + +class NotificationJob(CronJobBase): + notif = Notification() + RUN_EVERY_MINS = 60 + schedule = Schedule(run_every_mins=RUN_EVERY_MINS) + code = 'aitrainer_backoffice.controlling.notification' # a unique code def do(self): - pass - #helper = MauticHelper() - #helper.syncTrial() + print(datetime.datetime.now(), " *** START notification ") + self.notif.run() + print(datetime.datetime.now(), " *** END notification ") diff --git a/aitrainer_backoffice/controlling/mautic/__init__.py b/aitrainer_backoffice/controlling/mautic/__init__.py deleted file mode 100644 index 4f7a733..0000000 --- a/aitrainer_backoffice/controlling/mautic/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .helper import MauticHelper diff --git a/aitrainer_backoffice/controlling/models/__init__.py b/aitrainer_backoffice/controlling/models/__init__.py index f681e50..1ccd1f1 100644 --- a/aitrainer_backoffice/controlling/models/__init__.py +++ b/aitrainer_backoffice/controlling/models/__init__.py @@ -1,4 +1,3 @@ from .customer import Customer from .exercises import Exercises -from .exercise_type import ExerciseType from .frequent_customers import FrequentCustomers diff --git a/aitrainer_backoffice/controlling/models/customer.py b/aitrainer_backoffice/controlling/models/customer.py index abf5334..9644174 100644 --- a/aitrainer_backoffice/controlling/models/customer.py +++ b/aitrainer_backoffice/controlling/models/customer.py @@ -1,15 +1,6 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ - - -class Sport(models.Model): - sport_id = models.AutoField(primary_key = True) - language_code = models.CharField(max_length=2,default="hu") - sport_name = models.CharField(max_length=100) - - class Meta: - db_table = 'sport_translation' - +from aitrainer_backoffice.models.sports import Sport class Customer(models.Model): customer_id = models.BigAutoField(primary_key=True) @@ -21,6 +12,7 @@ class Customer(models.Model): fitness_level = models.CharField(max_length=20) date_add = models.DateField() synced_date = models.DateTimeField(blank=True,null=True) + firebase_reg_token = models.CharField(max_length=255, blank=True, null=True) def has_add_permission(self, request): return False diff --git a/aitrainer_backoffice/controlling/models/exercise_type.py b/aitrainer_backoffice/controlling/models/exercise_type.py deleted file mode 100644 index 2958a32..0000000 --- a/aitrainer_backoffice/controlling/models/exercise_type.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from ..models.exercises import Exercises - - -class ExerciseType(models.Model): - exercise_type_id = models.AutoField(primary_key=True) - name = models.CharField(max_length=50) - active = models.BooleanField() - - - def has_add_permission(self, request): - return False - - def has_delete_permission(self, request, obj=None): - return False - - @property - def exercise_count(self): - count = Exercises.objects.filter(exercise_type_id=self.exercise_type_id).count() - return count - - class Meta: - db_table = 'exercise_type' - verbose_name = _("Frequent Exercise") - verbose_name_plural = _("Frequent Exercises") - diff --git a/aitrainer_backoffice/controlling/models/exercises.py b/aitrainer_backoffice/controlling/models/exercises.py index 495ac04..bdd2965 100644 --- a/aitrainer_backoffice/controlling/models/exercises.py +++ b/aitrainer_backoffice/controlling/models/exercises.py @@ -1,6 +1,4 @@ from django.db import models - -#from ..models.exercise_type import ExerciseType from ..models.customer import Customer diff --git a/aitrainer_backoffice/controlling/models/notification.py b/aitrainer_backoffice/controlling/models/notification.py new file mode 100644 index 0000000..1686137 --- /dev/null +++ b/aitrainer_backoffice/controlling/models/notification.py @@ -0,0 +1,18 @@ +from django.db import models + +from aitrainer_backoffice.models.notification import Notification +from ..models import Customer + +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.DateField(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/terminal.bat b/aitrainer_backoffice/terminal.bat new file mode 100644 index 0000000..2b8dedb --- /dev/null +++ b/aitrainer_backoffice/terminal.bat @@ -0,0 +1,2 @@ +Set-ExecutionPolicy Unrestricted -Scope Process +d:/projects/aitrainer/src/aitrainer_backoffice/venv/Scripts/Activate.ps1 \ No newline at end of file