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/__pycache__/*
api/migrations/*
!api/migrations/__init__.py
api/__pycache__/*
tasks/migrations/*
!tasks/migrations/__init__.py
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, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
############################################## PROD #####################################
BASE_URL = "https://digitale-agentur.com/"
############################################## DEV #####################################
BASE_URL = "https://dev01.digitale-agentur.com/"
CRONAPIKEY = "gCddsaz6NOnE9QbXZM5LasdEk122D"
MAILINFOKEY = "jka7sd8iukashdna78skduJAHDsu6dilaksdjba65a68iadbhjak"
# 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_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "noreply@digitale-agentur.com"
EMAIL_HOST_PASSWORD = "48c3n6YggZBuPyShtqOQ"
DEFAULT_FROM_EMAIL = "noreply@digitale-agentur.com"
EMAIL_HOST_USER = "support@dev01.digitale-agentur.com"
EMAIL_HOST_PASSWORD = "n2xd7emyKZFb6UREzvbintuUIG"
DEFAULT_FROM_EMAIL = "support@dev01.digitale-agentur.com"
# PROD
# DEV
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME' : 'digitaleagentur',
'USER' : 'digitaleagentur',
'PASSWORD' : 'H9hzbzyBqtUCnZlIwL1qSrzh',
'NAME' : 'digitaleagentur_dev01',
'USER' : 'digitaleagentur_dev01',
'PASSWORD' : 't3TvtGAOkFHYXdJlUMIu9u3U',
'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!
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
INSTALLED_APPS = [
@ -85,6 +88,9 @@ INSTALLED_APPS = [
'bootstrap_datepicker_plus',
'django_cleanup',
'django_user_agents',
'rest_framework',
'rest_framework.authtoken',
'channels'
]
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

View File

@ -6,6 +6,7 @@ from django.conf.urls.static import static
from users.views import AgencyCreateView, registerNewAgency
from . import views
from django.contrib.auth.decorators import login_required
from rest_framework.authtoken.views import obtain_auth_token
'''
Main URLS
@ -43,7 +44,9 @@ urlpatterns = [
path('register/done', views.registerdone, name='register-done'),
path('summernote/', include('django_summernote.urls')),
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)
if settings.DEBUG:
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.save()
@receiver(signal=user_logged_in, sender=User)
def checkAllFreeDaysLoaded(sender, user, request, **kwargs):
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
@receiver(post_save, sender=Standards)
def save_standard(sender, instance, **kwargs):
print(kwargs)
GLOBALSENDMAILS = True
# NEW STANDARD AND DIRECT PUBLIC
if(kwargs["created"]):
@ -229,7 +235,9 @@ def save_standard(sender, instance, **kwargs):
if(user.has_perm("users.standardmanager")):
newnotification = UserNotification(touser=user, notificationtext="Neuer unveröffentlichter Agenturstandard: " + instance.name, notificationtype="newstandard", elementid=instance.pk)
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
@ -237,6 +245,11 @@ def save_standard(sender, instance, **kwargs):
def save_news(sender, instance, **kwargs):
GLOBALSENDMAILS = True
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)
targeturl = settings.BASE_URL + "news/news/" + str(instance.pk) + "/single"
@ -265,11 +278,14 @@ def save_news(sender, instance, **kwargs):
if(user.profile.news_push):
newnotification = UserNotification(touser=user, notificationtext="Neue Agenturnews: " + instance.name, notificationtype="agencynews", elementid=instance.pk)
newnotification.save()
else:
instance.agnotify = False
instance.save()
# SIGNALS FOR TASK
@receiver(signal=m2m_changed, sender=Tasks.usersfield.through)
def adjust_group_notifications_task(instance, action, reverse, model, pk_set, using, *args, **kwargs):

View File

@ -14,7 +14,7 @@
<!-- Custom fonts for this template-->
<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">
@ -40,23 +40,23 @@
<!-- <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>-->
<!-- 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 -->
<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/dataTables.bootstrap4.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" type="text/css" rel="stylesheet">
<!-- 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>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet">
<script src="{% static 'summernote/lang/summernote-de-DE.min.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" type="text/css">
<script type="text/javascript" src="{% static 'summernote/lang/summernote-de-DE.min.js' %}"></script>
</head>
@ -399,15 +399,15 @@
</a>
<!-- Bootstrap core JavaScript-->
<!--<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-->
<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 -->
<script 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/jquery.dataTables.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-->
<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 -->
<!--<link href="{% static 'users/css/custom.css' %}" rel="stylesheet">-->
@ -423,7 +423,7 @@
<!--<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("");
if(i > 0){$("#notificationcounter").html(i);}
}
});
}
@ -633,9 +631,22 @@ $(window).click(function() {
loadUnsendNotifications();
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>

View File

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

View File

@ -35,8 +35,7 @@ urlpatterns = [
path('impressum/', views.impressum, name="impressumda"),
path('setuserparent/', views.setuserparent, name="users-setuserparent"),
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"})
return JsonResponse(data)
def index(request):
return render(request, 'users/websocket.html', {})