API und REST und WEBSOCKETS
This commit is contained in:
parent
b81ab6a2ba
commit
83f3981648
|
|
@ -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,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
name = 'api'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
|
|
@ -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")
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
|
@ -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'),
|
||||
|
||||
]
|
||||
|
|
@ -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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
)
|
||||
),
|
||||
})
|
||||
|
|
@ -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 = [
|
||||
|
|
@ -72,7 +75,7 @@ INSTALLED_APPS = [
|
|||
'timemanagement.apps.TimemanagementConfig',
|
||||
'news.apps.NewsConfig',
|
||||
'crispy_forms',
|
||||
'colorful',
|
||||
'colorful',
|
||||
'django_summernote',
|
||||
'django.contrib.admin',
|
||||
'mathfilters',
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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")
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from django.urls import re_path
|
||||
|
||||
from . import mainwebsocket
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'', mainwebsocket.UsersConsumer),
|
||||
]
|
||||
|
|
@ -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,9 +278,12 @@ 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()
|
||||
instance.save()
|
||||
|
||||
|
||||
|
||||
# SIGNALS FOR TASK
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
@ -5,8 +5,7 @@
|
|||
<small>Letzter Login: {{ request.user.last_login }}</small>
|
||||
<hr>
|
||||
<h5>Agentur: <b>{{ request.user.profile.agency.name }}</b></h5>
|
||||
<hr>
|
||||
|
||||
<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 %}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -854,5 +854,5 @@ def cronactions(request, code):
|
|||
data.update({"status" : "failed"})
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
|
||||
def index(request):
|
||||
return render(request, 'users/websocket.html', {})
|
||||
Loading…
Reference in New Issue