API und REST und WEBSOCKETS

This commit is contained in:
Holger Trampe 2020-05-17 23:25:27 +02:00
parent b81ab6a2ba
commit 83f3981648
23 changed files with 259 additions and 45 deletions

4
.gitignore vendored
View File

@ -21,6 +21,10 @@ standards/migrations/*
!standards/migrations/__init__.py !standards/migrations/__init__.py
standards/__pycache__/* standards/__pycache__/*
api/migrations/*
!api/migrations/__init__.py
api/__pycache__/*
tasks/migrations/* tasks/migrations/*
!tasks/migrations/__init__.py !tasks/migrations/__init__.py
tasks/__pycache__/* tasks/__pycache__/*

0
api/__init__.py Normal file
View File

3
api/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
api/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'

View File

3
api/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

19
api/serializers.py Normal file
View File

@ -0,0 +1,19 @@
from django.contrib.auth.models import User
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from standards.models import Standards
class StandardsSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField('getCreatedByUser')
last_modified_on = serializers.SerializerMethodField('getFormatedLastModified')
class Meta:
model = Standards
fields = ["id", "name", "last_modified_on", "username", "content"]
def getCreatedByUser(self, standard):
return standard.created_standard_by.first_name + " " + standard.created_standard_by.first_name
def getFormatedLastModified(self, standard):
return standard.last_modified_on.strftime("%d.%m.%Y %H:%M")

3
api/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
api/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from .views import HelloView
from . import views
app_name = 'api'
urlpatterns = [
path('helloview/', HelloView.as_view(), name='api-helloview'),
path('getstandards/', views.getStandardList, name='api-getstandards'),
path('getsinglestandard/<int:pk>', views.getSingleStandard, name='api-getsinglestandards'),
]

52
api/views.py Normal file
View File

@ -0,0 +1,52 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated # <-- Here
import json
from standards.models import Standards
from rest_framework import serializers
from .serializers import StandardsSerializer
from rest_framework.decorators import api_view, permission_classes
from rest_framework import status
class HelloView(APIView):
permission_classes = (IsAuthenticated,) # <-- And here
def post(self, request):
content = {'message': 'Hello, World!'}
return Response(content)
'''
class GiveMeStandards(APIView):
def post(self, request):
standards = Standards.objects.filter(agency=request.user.profile.agency)
content = {}
i = 0
for s in standards:
content.update({i : s.name.encode("utf-8")})
i += 1
content = {'standards': content}
return Response(content)
def post(self, request):
ser = StandardsSerializer(data=Standards.objects.filter(agency=self.request.user.profile.agency))
return Response(ser)
'''
@api_view(['POST', ])
@permission_classes((IsAuthenticated,))
def getStandardList(request):
standards = Standards.objects.filter(agency=request.user.profile.agency)
ser = StandardsSerializer(standards, many=True)
return Response(ser.data, status=status.HTTP_200_OK)
@api_view(['POST', ])
@permission_classes((IsAuthenticated,))
def getSingleStandard(request, pk):
standard = Standards.objects.get(pk=int(pk))
ser = StandardsSerializer(standard, many=False)
return Response(ser.data, status=status.HTTP_200_OK)

View File

@ -0,0 +1,12 @@
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import users.routing
application = ProtocolTypeRouter({
# Empty for now (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
users.routing.websocket_urlpatterns
)
),
})

View File

@ -14,32 +14,35 @@ import os
# 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__)))
############################################## PROD ##################################### ############################################## DEV #####################################
BASE_URL = "https://digitale-agentur.com/" BASE_URL = "https://dev01.digitale-agentur.com/"
CRONAPIKEY = "gCddsaz6NOnE9QbXZM5LasdEk122D" CRONAPIKEY = "gCddsaz6NOnE9QbXZM5LasdEk122D"
MAILINFOKEY = "jka7sd8iukashdna78skduJAHDsu6dilaksdjba65a68iadbhjak" MAILINFOKEY = "jka7sd8iukashdna78skduJAHDsu6dilaksdjba65a68iadbhjak"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False DEBUG = True
# MAIL PROD # MAIL DEV
EMAIL_HOST = 'smtp.strato.de' EMAIL_HOST = 'smtp.strato.de'
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
EMAIL_HOST_USER = "noreply@digitale-agentur.com" EMAIL_HOST_USER = "support@dev01.digitale-agentur.com"
EMAIL_HOST_PASSWORD = "48c3n6YggZBuPyShtqOQ" EMAIL_HOST_PASSWORD = "n2xd7emyKZFb6UREzvbintuUIG"
DEFAULT_FROM_EMAIL = "noreply@digitale-agentur.com" DEFAULT_FROM_EMAIL = "support@dev01.digitale-agentur.com"
# PROD # DEV
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME' : 'digitaleagentur', 'NAME' : 'digitaleagentur_dev01',
'USER' : 'digitaleagentur', 'USER' : 'digitaleagentur_dev01',
'PASSWORD' : 'H9hzbzyBqtUCnZlIwL1qSrzh', 'PASSWORD' : 't3TvtGAOkFHYXdJlUMIu9u3U',
'PORT' : 3306 'PORT' : 3306
} }
} }
############################################## PROD #####################################
# REDIS
REDIS_URL = ("localhost", 6379)
############################################## DEV #####################################
@ -55,7 +58,7 @@ X_FRAME_OPTIONS = 'SAMEORIGIN'
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_qv2t2lmsctjxpbb4rrp=op%_20_hxzonv^mvty1o85c)l$s^q' SECRET_KEY = '_qv2t2lmsctjxpbb4rrp=op%_20_hxzonv^mvty1o85c)l$s^q'
ALLOWED_HOSTS = ['digitale-agentur.com', 'www.digitale-agentur.com', 'localhost', 'dev01.digitale-agentur.com'] ALLOWED_HOSTS = ['digitale-agentur.com', 'www.digitale-agentur.com', 'localhost', 'dev01.digitale-agentur.com', '10.0.2.2']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
@ -72,7 +75,7 @@ INSTALLED_APPS = [
'timemanagement.apps.TimemanagementConfig', 'timemanagement.apps.TimemanagementConfig',
'news.apps.NewsConfig', 'news.apps.NewsConfig',
'crispy_forms', 'crispy_forms',
'colorful', 'colorful',
'django_summernote', 'django_summernote',
'django.contrib.admin', 'django.contrib.admin',
'mathfilters', 'mathfilters',
@ -85,6 +88,9 @@ INSTALLED_APPS = [
'bootstrap_datepicker_plus', 'bootstrap_datepicker_plus',
'django_cleanup', 'django_cleanup',
'django_user_agents', 'django_user_agents',
'rest_framework',
'rest_framework.authtoken',
'channels'
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -116,10 +122,25 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'digitaleagentur.wsgi.application' REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
#WSGI_APPLICATION = 'digitaleagentur.wsgi.application'
ASGI_APPLICATION = "digitaleagentur.routing.application"
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [REDIS_URL],
},
},
}
# Password validation # Password validation

View File

@ -6,6 +6,7 @@ from django.conf.urls.static import static
from users.views import AgencyCreateView, registerNewAgency from users.views import AgencyCreateView, registerNewAgency
from . import views from . import views
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from rest_framework.authtoken.views import obtain_auth_token
''' '''
Main URLS Main URLS
@ -43,7 +44,9 @@ urlpatterns = [
path('register/done', views.registerdone, name='register-done'), path('register/done', views.registerdone, name='register-done'),
path('summernote/', include('django_summernote.urls')), path('summernote/', include('django_summernote.urls')),
path('notifications/', include('notificsys.urls'), name="notifications"), path('notifications/', include('notificsys.urls'), name="notifications"),
path('tm/', include('timemanagement.urls'), name="timemanagement") path('tm/', include('timemanagement.urls'), name="timemanagement"),
path('api/', include('api.urls', namespace='api')),
path('api-token-auth/', obtain_auth_token, name='api-token-auth'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

47
users/mainwebsocket.py Normal file
View File

@ -0,0 +1,47 @@
import json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class UsersConsumer(WebsocketConsumer):
'''
CONNECT A WEBSOCKET
Die Clients werden in Channel-Layer pro Agentur gepackt, damit gesendete Websocket-Nachrichten
auch nur Clients innerhalb der Agentur treffen!
'''
def connect(self):
loggeduser = self.scope["user"]
async_to_sync(self.channel_layer.group_add)(
"agency_" + str(loggeduser.profile.agency.pk),
self.channel_name
)
self.accept()
def disconnect(self, close_code):
pass
'''
def receive(self, text_data):
loggeduser = self.scope["user"]
async_to_sync(self.channel_layer.group_send)(
'allusers',
{
'type': 'chat_message',
'message': 'von mainwebsocket.py'
}
)
'''
# WEBSOCKET-DATA-CONTENT
# UPDATET STANDARD
def update_standard(self, event):
self.send("standard_update")
# NEW AGENCY NEWS
def agency_newnews(self, event):
print(event)
self.send("agency_newnews")

7
users/routing.py Normal file
View File

@ -0,0 +1,7 @@
from django.urls import re_path
from . import mainwebsocket
websocket_urlpatterns = [
re_path(r'', mainwebsocket.UsersConsumer),
]

View File

@ -82,6 +82,9 @@ def checkDefaultAbsenceReasons(sender, user, request, **kwargs):
new_ar_school = AbsenceReason(agency=user.profile.agency, name="Berufsschule", need_confirm=False, need_rep=False, is_holiday=False) new_ar_school = AbsenceReason(agency=user.profile.agency, name="Berufsschule", need_confirm=False, need_rep=False, is_holiday=False)
new_ar_school.save() new_ar_school.save()
@receiver(signal=user_logged_in, sender=User) @receiver(signal=user_logged_in, sender=User)
def checkAllFreeDaysLoaded(sender, user, request, **kwargs): def checkAllFreeDaysLoaded(sender, user, request, **kwargs):
pass pass
@ -189,11 +192,14 @@ def adjust_group_notifications(instance, action, reverse, model, pk_set, using,
) )
import channels.layers
from asgiref.sync import async_to_sync
# SIGNAL FOR STANDARDS POST SAVE # SIGNAL FOR STANDARDS POST SAVE
@receiver(post_save, sender=Standards) @receiver(post_save, sender=Standards)
def save_standard(sender, instance, **kwargs): def save_standard(sender, instance, **kwargs):
print(kwargs)
GLOBALSENDMAILS = True GLOBALSENDMAILS = True
# NEW STANDARD AND DIRECT PUBLIC # NEW STANDARD AND DIRECT PUBLIC
if(kwargs["created"]): if(kwargs["created"]):
@ -229,7 +235,9 @@ def save_standard(sender, instance, **kwargs):
if(user.has_perm("users.standardmanager")): if(user.has_perm("users.standardmanager")):
newnotification = UserNotification(touser=user, notificationtext="Neuer unveröffentlichter Agenturstandard: " + instance.name, notificationtype="newstandard", elementid=instance.pk) newnotification = UserNotification(touser=user, notificationtext="Neuer unveröffentlichter Agenturstandard: " + instance.name, notificationtype="newstandard", elementid=instance.pk)
newnotification.save() newnotification.save()
else:
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("agency_" + str(instance.agency.pk), {'type' : 'update_standard'})
# SIGNAL FOR NEWS # SIGNAL FOR NEWS
@ -237,6 +245,11 @@ def save_standard(sender, instance, **kwargs):
def save_news(sender, instance, **kwargs): def save_news(sender, instance, **kwargs):
GLOBALSENDMAILS = True GLOBALSENDMAILS = True
if(kwargs["created"]): if(kwargs["created"]):
# Hier wird allen verbundenne Agenturmitgliedern die Info geschickt, dass es neue Agenturnews gibt
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("agency_" + str(instance.agency.pk), {'type' : 'agency_newnews'})
usersofagency = User.objects.filter(profile__agency__pk=instance.agency.pk) usersofagency = User.objects.filter(profile__agency__pk=instance.agency.pk)
targeturl = settings.BASE_URL + "news/news/" + str(instance.pk) + "/single" targeturl = settings.BASE_URL + "news/news/" + str(instance.pk) + "/single"
@ -265,9 +278,12 @@ def save_news(sender, instance, **kwargs):
if(user.profile.news_push): if(user.profile.news_push):
newnotification = UserNotification(touser=user, notificationtext="Neue Agenturnews: " + instance.name, notificationtype="agencynews", elementid=instance.pk) newnotification = UserNotification(touser=user, notificationtext="Neue Agenturnews: " + instance.name, notificationtype="agencynews", elementid=instance.pk)
newnotification.save() newnotification.save()
else: else:
instance.agnotify = False instance.agnotify = False
instance.save() instance.save()
# SIGNALS FOR TASK # SIGNALS FOR TASK

View File

@ -14,7 +14,7 @@
<!-- Custom fonts for this template--> <!-- Custom fonts for this template-->
<link rel="canonical" href="https://www.digitale-agentur.com"> <link rel="canonical" href="https://www.digitale-agentur.com">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js" type="text/javascript"></script>
<link href="{%static 'users/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css"> <link href="{%static 'users/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
@ -40,23 +40,23 @@
<!-- <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>--> <!-- <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>-->
<!-- CROPPER --> <!-- CROPPER -->
<link href="{% static 'users/css/cropper.min.css' %}" rel="stylesheet"> <link href="{% static 'users/css/cropper.min.css' %}" type="text/css" rel="stylesheet">
<!-- DATATABLES --> <!-- DATATABLES -->
<link href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css" rel="stylesheet"> <link href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css" type="text/css" rel="stylesheet">
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet"> <link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" type="text/css" rel="stylesheet">
<!-- Custom styles for this template--> <!-- Custom styles for this template-->
<link href="{% static 'users/css/sb-admin-2.css' %}" rel="stylesheet"> <link href="{% static 'users/css/sb-admin-2.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'users/css/theme.css' %}" rel="stylesheet"> <link href="{% static 'users/css/theme.css' %}" type="text/css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote-bs4.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote-bs4.js"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet" type="text/css">
<script src="{% static 'summernote/lang/summernote-de-DE.min.js' %}"></script> <script type="text/javascript" src="{% static 'summernote/lang/summernote-de-DE.min.js' %}"></script>
</head> </head>
@ -399,15 +399,15 @@
</a> </a>
<!-- Bootstrap core JavaScript--> <!-- Bootstrap core JavaScript-->
<!--<script src="{%static 'users/vendor/jquery/jquery.min.js' %}"></script>--> <!--<script src="{%static 'users/vendor/jquery/jquery.min.js' %}"></script>-->
<script src="{%static 'users/vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script> <script type="text/javascript" src="{%static 'users/vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<!-- Core plugin JavaScript--> <!-- Core plugin JavaScript-->
<script src="{%static 'users/vendor/jquery-easing/jquery.easing.min.js' %}"></script> <script type="text/javascript" src="{%static 'users/vendor/jquery-easing/jquery.easing.min.js' %}"></script>
<!-- DATABLES JS --> <!-- DATABLES JS -->
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<!-- Custom scripts for all pages--> <!-- Custom scripts for all pages-->
<script src="{%static 'users/js/sb-admin-2.js' %}"></script> <script type="text/javascript" src="{%static 'users/js/sb-admin-2.js' %}"></script>
<!-- CUSTOM FONT --> <!-- CUSTOM FONT -->
<!--<link href="{% static 'users/css/custom.css' %}" rel="stylesheet">--> <!--<link href="{% static 'users/css/custom.css' %}" rel="stylesheet">-->
@ -423,7 +423,7 @@
<!--<script src="js/demo/chart-pie-demo.js"></script>--> <!--<script src="js/demo/chart-pie-demo.js"></script>-->
<link href="{% static 'users/css/custom.css' %}" rel="stylesheet"> <link href="{% static 'users/css/custom.css' %}" type="text/css" rel="stylesheet">
@ -573,8 +573,6 @@ function loadUnsendNotifications(){
$("#notificationcounter").html(""); $("#notificationcounter").html("");
if(i > 0){$("#notificationcounter").html(i);} if(i > 0){$("#notificationcounter").html(i);}
} }
}); });
} }
@ -633,9 +631,22 @@ $(window).click(function() {
loadUnsendNotifications(); loadUnsendNotifications();
loadUnviewnNotifications(); loadUnviewnNotifications();
}); });
</script>
<!-- WEBSOCKETS -->
<script type="text/javascript">
$(document).ready(function(){
const mainwebsocket = new WebSocket('ws://'+window.location.host)
mainwebsocket.onmessage = function(e) {
console.log(e);
};
mainwebsocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
});
</script> </script>

View File

@ -5,8 +5,7 @@
<small>Letzter Login: {{ request.user.last_login }}</small> <small>Letzter Login: {{ request.user.last_login }}</small>
<hr> <hr>
<h5>Agentur: <b>{{ request.user.profile.agency.name }}</b></h5> <h5>Agentur: <b>{{ request.user.profile.agency.name }}</b></h5>
<hr> <hr>
<div class="row" style="float: left;"> <div class="row" style="float: left;">
{% if request.user.profile.agency.module_news %} {% if request.user.profile.agency.module_news %}
<div class="card d-block mb-3 mr-3 " style="width: 60%" > <div class="card d-block mb-3 mr-3 " style="width: 60%" >
@ -142,7 +141,6 @@ $(document).ready(function(){
</div> </div>
</div> </div>
<!-- YOUTUBE PART ENDE --> <!-- YOUTUBE PART ENDE -->
{% endblock content %} {% endblock content %}

View File

@ -35,8 +35,7 @@ urlpatterns = [
path('impressum/', views.impressum, name="impressumda"), path('impressum/', views.impressum, name="impressumda"),
path('setuserparent/', views.setuserparent, name="users-setuserparent"), path('setuserparent/', views.setuserparent, name="users-setuserparent"),
path('sendpassmail/', views.sendpassmail, name="users-sendpassmail"), path('sendpassmail/', views.sendpassmail, name="users-sendpassmail"),
path('dacron/<slug:code>', views.cronactions, name="cronmain"), path('dacron/<slug:code>', views.cronactions, name="cronmain")
] ]

View File

@ -854,5 +854,5 @@ def cronactions(request, code):
data.update({"status" : "failed"}) data.update({"status" : "failed"})
return JsonResponse(data) return JsonResponse(data)
def index(request):
return render(request, 'users/websocket.html', {})