V1.30 notification
This commit is contained in:
parent
405fefe7b9
commit
696ce5a368
@ -15,3 +15,4 @@ from .training_plan_day import TrainingPlanDayAdmin
|
|||||||
from .controlling import ControllingAdmin
|
from .controlling import ControllingAdmin
|
||||||
from .sport import SportAdmin
|
from .sport import SportAdmin
|
||||||
from .app_text import AppTextAdmin
|
from .app_text import AppTextAdmin
|
||||||
|
from .notification import NotificationAdmin
|
@ -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('<img src="{url}" title="{url}" width="30%" height="30%"/> ' \
|
||||||
|
.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()
|
@ -12,7 +12,10 @@ class TestRouter:
|
|||||||
|
|
||||||
def db_for_write(self, model, **hints):
|
def db_for_write(self, model, **hints):
|
||||||
if model._meta.app_label == 'controlling':
|
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'
|
return 'default'
|
||||||
|
|
||||||
def allow_relation(self, obj1, obj2, **hints):
|
def allow_relation(self, obj1, obj2, **hints):
|
||||||
|
@ -18,3 +18,4 @@ from .training_plan_day import TrainingPlanDay, TrainingPlanDayTranslation
|
|||||||
from .controlling import Controlling
|
from .controlling import Controlling
|
||||||
from .sports import Sport, SportTranslation
|
from .sports import Sport, SportTranslation
|
||||||
from .app_text import AppText, AppTextTranslation
|
from .app_text import AppText, AppTextTranslation
|
||||||
|
from .notification import Notification
|
@ -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
|
@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
BACKOFFICE_VERSION = "1.29.1"
|
BACKOFFICE_VERSION = "1.30"
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
@ -31,6 +31,7 @@ INSTALLED_APPS = [
|
|||||||
'adminsortable2',
|
'adminsortable2',
|
||||||
'inline_actions',
|
'inline_actions',
|
||||||
'django_cron',
|
'django_cron',
|
||||||
|
'firebase-admin'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@ -159,5 +160,5 @@ LOGGING = {
|
|||||||
|
|
||||||
|
|
||||||
CRON_CLASSES = [
|
CRON_CLASSES = [
|
||||||
'controlling.cron.cron.MyCronJob',
|
'controlling.cron.cron.NotificationJob',
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import os
|
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, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
@ -32,6 +33,8 @@ INSTALLED_APPS = [
|
|||||||
'django_cron',
|
'django_cron',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
@ -143,7 +146,7 @@ LOGGING = {
|
|||||||
'loggers': {
|
'loggers': {
|
||||||
'mylogger': {
|
'mylogger': {
|
||||||
'handlers': ['console'],
|
'handlers': ['console'],
|
||||||
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
|
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
|
||||||
'propagate': True,
|
'propagate': True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -156,5 +159,7 @@ LOGGING = {
|
|||||||
|
|
||||||
|
|
||||||
CRON_CLASSES = [
|
CRON_CLASSES = [
|
||||||
'controlling.cron.cron.MyCronJob',
|
'controlling.cron.cron.NotificationJob',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
BACKOFFICE_VERSION = "1.29.1"
|
BACKOFFICE_VERSION = "1.30"
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
@ -31,6 +31,7 @@ INSTALLED_APPS = [
|
|||||||
'adminsortable2',
|
'adminsortable2',
|
||||||
'inline_actions',
|
'inline_actions',
|
||||||
'django_cron',
|
'django_cron',
|
||||||
|
'firebase-admin',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@ -174,5 +175,5 @@ CACHES = {
|
|||||||
|
|
||||||
|
|
||||||
CRON_CLASSES = [
|
CRON_CLASSES = [
|
||||||
'aitrainer_backoffice.controlling.cron.cron.MyCronJob',
|
'aitrainer_backoffice.controlling.cron.cron.NotificationJob',
|
||||||
]
|
]
|
||||||
|
@ -24,7 +24,7 @@ from django.urls import path, include
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
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)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
@ -10,10 +10,7 @@ from django.urls import path
|
|||||||
|
|
||||||
from ..models.customer import Customer
|
from ..models.customer import Customer
|
||||||
from ..models.customer import Sport
|
from ..models.customer import Sport
|
||||||
from ..mautic import MauticHelper
|
from ..automation.notification import Notification
|
||||||
from ..cron import cron
|
|
||||||
from ..push_notification import messaging
|
|
||||||
|
|
||||||
|
|
||||||
class SportFilter(SimpleListFilter, ABC):
|
class SportFilter(SimpleListFilter, ABC):
|
||||||
title = "Sport"
|
title = "Sport"
|
||||||
@ -22,7 +19,7 @@ class SportFilter(SimpleListFilter, ABC):
|
|||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
data = []
|
data = []
|
||||||
for s in Sport.objects.filter():
|
for s in Sport.objects.filter():
|
||||||
data.append([s.sport_id, s.sport_name])
|
data.append([s.sport_id, s.name])
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
@ -33,6 +30,8 @@ class SportFilter(SimpleListFilter, ABC):
|
|||||||
|
|
||||||
|
|
||||||
class CustomerAdmin(admin.ModelAdmin):
|
class CustomerAdmin(admin.ModelAdmin):
|
||||||
|
notif = Notification()
|
||||||
|
|
||||||
change_list_template = "controlling/mautic.html"
|
change_list_template = "controlling/mautic.html"
|
||||||
list_display = ('customer_id', 'name','firstname', 'email', 'date_add', 'get_sport')
|
list_display = ('customer_id', 'name','firstname', 'email', 'date_add', 'get_sport')
|
||||||
list_filter = (
|
list_filter = (
|
||||||
@ -41,10 +40,10 @@ class CustomerAdmin(admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_sport(self, obj):
|
def get_sport(self, obj):
|
||||||
return obj.sport.sport_name
|
return obj.sport.name
|
||||||
|
|
||||||
get_sport.short_description = 'Sport'
|
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
|
# If you would like to add a default range filter
|
||||||
@ -75,7 +74,7 @@ class CustomerAdmin(admin.ModelAdmin):
|
|||||||
return my_urls + urls
|
return my_urls + urls
|
||||||
|
|
||||||
def set_mautic(self, request):
|
def set_mautic(self, request):
|
||||||
messaging.send_to_token()
|
self.notif.run()
|
||||||
return HttpResponseRedirect("../")
|
return HttpResponseRedirect("../")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from ..models import ExerciseType
|
from aitrainer_backoffice.models.exercise_type import ExerciseType
|
||||||
|
|
||||||
|
|
||||||
class FrequentExerciseTypeAdmin(admin.ModelAdmin):
|
class FrequentExerciseTypeAdmin(admin.ModelAdmin):
|
||||||
@ -11,5 +11,5 @@ class FrequentExerciseTypeAdmin(admin.ModelAdmin):
|
|||||||
return qs.filter(active=True)
|
return qs.filter(active=True)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(ExerciseType, FrequentExerciseTypeAdmin)
|
#admin.site.register(FrequentExerciseTypeAdmin)
|
||||||
admin.autodiscover()
|
#admin.autodiscover()
|
||||||
|
97
aitrainer_backoffice/controlling/automation/fcm.py
Normal file
97
aitrainer_backoffice/controlling/automation/fcm.py
Normal file
@ -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
|
||||||
|
|
@ -6,7 +6,7 @@ import datetime
|
|||||||
from ..models.customer import Customer
|
from ..models.customer import Customer
|
||||||
|
|
||||||
|
|
||||||
class MauticHelper:
|
class Mautic:
|
||||||
|
|
||||||
def syncTrial(self):
|
def syncTrial(self):
|
||||||
tenDays = datetime.datetime - datetime.timedelta(days=10)
|
tenDays = datetime.datetime - datetime.timedelta(days=10)
|
@ -11,27 +11,31 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
from google.oauth2 import service_account
|
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'
|
PROJECT_ID = 'aitrainer-af0ec'
|
||||||
BASE_URL = 'https://fcm.googleapis.com'
|
BASE_URL = 'https://fcm.googleapis.com'
|
||||||
FCM_ENDPOINT = 'v1/projects/' + PROJECT_ID + '/messages:send'
|
FCM_ENDPOINT = 'v1/projects/' + PROJECT_ID + '/messages:send'
|
||||||
FCM_URL = BASE_URL + '/' + FCM_ENDPOINT
|
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]
|
# [START retrieve_access_token]
|
||||||
def _get_access_token():
|
def _get_access_token():
|
||||||
"""Retrieve a valid access token that can be used to authorize requests.
|
"""Retrieve a valid access token that can be used to authorize requests.
|
||||||
:return: Access token.
|
:return: Access token.
|
||||||
"""
|
"""
|
||||||
credentials = credentials = service_account.Credentials.from_service_account_file(
|
|
||||||
"asset/aitrainer-firebase-adminsdk.json",
|
default_app = initialize_app()
|
||||||
scopes=['email'],
|
|
||||||
)
|
access_token = default_app.credential.get_access_token()
|
||||||
access_token_info = credentials.get_access_token()
|
return access_token.access_token
|
||||||
return access_token_info.access_token
|
#return access_token_info.access_token
|
||||||
# [END retrieve_access_token]
|
# [END retrieve_access_token]
|
||||||
|
|
||||||
def _send_fcm_message(fcm_message):
|
def _send_fcm_message(fcm_message):
|
||||||
@ -46,13 +50,16 @@ def _send_fcm_message(fcm_message):
|
|||||||
}
|
}
|
||||||
# [END use_access_token]
|
# [END use_access_token]
|
||||||
resp = requests.post(FCM_URL, data=json.dumps(fcm_message), headers=headers)
|
resp = requests.post(FCM_URL, data=json.dumps(fcm_message), headers=headers)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
'''
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
print('Message sent to Firebase for delivery, response:')
|
print('Message sent to Firebase for delivery, response:')
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
else:
|
else:
|
||||||
print('Unable to send message to Firebase')
|
print('Unable to send message to Firebase')
|
||||||
print(resp.text)
|
print(resp.text)
|
||||||
|
'''
|
||||||
|
|
||||||
def _build_common_message():
|
def _build_common_message():
|
||||||
"""Construct common notifiation message.
|
"""Construct common notifiation message.
|
||||||
@ -120,6 +127,63 @@ def send_to_token():
|
|||||||
print('Successfully sent message:', response)
|
print('Successfully sent message:', response)
|
||||||
# [END send_to_token]
|
# [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():
|
def send_to_topic():
|
||||||
# [START send_to_topic]
|
# [START send_to_topic]
|
||||||
# The topic name can be optionally prefixed with "/topics/".
|
# The topic name can be optionally prefixed with "/topics/".
|
||||||
@ -141,6 +205,7 @@ def send_to_topic():
|
|||||||
# [END send_to_topic]
|
# [END send_to_topic]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def send_to_condition():
|
def send_to_condition():
|
||||||
# [START send_to_condition]
|
# [START send_to_condition]
|
||||||
# Define a condition which will send to devices which are subscribed
|
# Define a condition which will send to devices which are subscribed
|
44
aitrainer_backoffice/controlling/automation/notification.py
Normal file
44
aitrainer_backoffice/controlling/automation/notification.py
Normal file
@ -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')
|
@ -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
|
@ -1,19 +1,27 @@
|
|||||||
from ..mautic import MauticHelper
|
from ..automation.notification import Notification
|
||||||
from django_cron import CronJobBase, Schedule
|
from django_cron import CronJobBase, Schedule
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class MyCronJob(CronJobBase):
|
class MyCronJob(CronJobBase):
|
||||||
print(datetime.datetime.now(), " *** START sync customers ")
|
|
||||||
RUN_EVERY_MINS = 60
|
|
||||||
|
|
||||||
|
RUN_EVERY_MINS = 60
|
||||||
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
|
schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
|
||||||
code = 'aitrainer_backoffice.controlling.cron' # a unique code
|
code = 'aitrainer_backoffice.controlling.cron' # a unique code
|
||||||
print(datetime.datetime.now(), " *** END sync customers ")
|
|
||||||
|
|
||||||
def do(self):
|
def do(self):
|
||||||
pass
|
print(datetime.datetime.now(), " *** START sync customers ")
|
||||||
#helper = MauticHelper()
|
|
||||||
#helper.syncTrial()
|
|
||||||
|
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):
|
||||||
|
print(datetime.datetime.now(), " *** START notification ")
|
||||||
|
self.notif.run()
|
||||||
|
print(datetime.datetime.now(), " *** END notification ")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from .helper import MauticHelper
|
|
@ -1,4 +1,3 @@
|
|||||||
from .customer import Customer
|
from .customer import Customer
|
||||||
from .exercises import Exercises
|
from .exercises import Exercises
|
||||||
from .exercise_type import ExerciseType
|
|
||||||
from .frequent_customers import FrequentCustomers
|
from .frequent_customers import FrequentCustomers
|
||||||
|
@ -1,15 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from aitrainer_backoffice.models.sports import Sport
|
||||||
|
|
||||||
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'
|
|
||||||
|
|
||||||
|
|
||||||
class Customer(models.Model):
|
class Customer(models.Model):
|
||||||
customer_id = models.BigAutoField(primary_key=True)
|
customer_id = models.BigAutoField(primary_key=True)
|
||||||
@ -21,6 +12,7 @@ class Customer(models.Model):
|
|||||||
fitness_level = models.CharField(max_length=20)
|
fitness_level = models.CharField(max_length=20)
|
||||||
date_add = models.DateField()
|
date_add = models.DateField()
|
||||||
synced_date = models.DateTimeField(blank=True,null=True)
|
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):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
@ -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")
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
#from ..models.exercise_type import ExerciseType
|
|
||||||
from ..models.customer import Customer
|
from ..models.customer import Customer
|
||||||
|
|
||||||
|
|
||||||
|
18
aitrainer_backoffice/controlling/models/notification.py
Normal file
18
aitrainer_backoffice/controlling/models/notification.py
Normal file
@ -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}'
|
2
aitrainer_backoffice/terminal.bat
Normal file
2
aitrainer_backoffice/terminal.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Set-ExecutionPolicy Unrestricted -Scope Process
|
||||||
|
d:/projects/aitrainer/src/aitrainer_backoffice/venv/Scripts/Activate.ps1
|
Loading…
Reference in New Issue
Block a user