From 34c95cd216ae14cb26441450ea04cef36a305a20 Mon Sep 17 00:00:00 2001 From: Marcin Armacki Date: Sun, 8 Nov 2020 18:35:05 +0100 Subject: [PATCH 1/2] Register user endpoint update + login user endpoint --- .../prototype/filehandler/errorCodes.py | 2 + .../prototype/filehandler/exceptions.py | 6 ++ .../webapp/prototype/filehandler/predict.py | 73 +++++++++++++++++++ .../prototype/filehandler/userModule.py | 31 ++++++++ backend/webapp/prototype/filehandler/views.py | 23 +++--- backend/webapp/prototype/urls.py | 3 +- 6 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 backend/webapp/prototype/filehandler/errorCodes.py create mode 100644 backend/webapp/prototype/filehandler/exceptions.py create mode 100644 backend/webapp/prototype/filehandler/predict.py create mode 100644 backend/webapp/prototype/filehandler/userModule.py diff --git a/backend/webapp/prototype/filehandler/errorCodes.py b/backend/webapp/prototype/filehandler/errorCodes.py new file mode 100644 index 0000000..96de918 --- /dev/null +++ b/backend/webapp/prototype/filehandler/errorCodes.py @@ -0,0 +1,2 @@ +MethodNotAllowed = {"message": "Error: Method not allowed", + "statusCode": 405} diff --git a/backend/webapp/prototype/filehandler/exceptions.py b/backend/webapp/prototype/filehandler/exceptions.py new file mode 100644 index 0000000..8cb108e --- /dev/null +++ b/backend/webapp/prototype/filehandler/exceptions.py @@ -0,0 +1,6 @@ +class UserAuthFailed(Exception): + def __init__(self, message, payload = None): + self.message = message + self.payload = payload + def __str__(self): + return str(self.message) diff --git a/backend/webapp/prototype/filehandler/predict.py b/backend/webapp/prototype/filehandler/predict.py new file mode 100644 index 0000000..d5297b8 --- /dev/null +++ b/backend/webapp/prototype/filehandler/predict.py @@ -0,0 +1,73 @@ +import pandas as pd +from joblib import load +import string +import re +import featuretools as ft +from sklearn.feature_extraction.text import TfidfVectorizer, TfidfTransformer, CountVectorizer + +id_to_labels = load('labels.pkl') +data = open('testdata.txt').read().splitlines() +df = pd.DataFrame(data, columns=["body_text"]) +df['index'] = df.index +columns_titles = ["index", "body_text"] +df=df.reindex(columns=columns_titles) +col = ['index','body_text'] +df = df[col] +df.columns = ['index','body_text'] + +model = load('model.pkl') + +def count_punct(text): + count = sum([1 for char in text if char in string.punctuation]) + return round(count/(len(text) - text.count(" ")), 3)*100 + +df['body_len'] = df['body_text'].apply(lambda x: len(x) - x.count(" ")) +df['punct%'] = df['body_text'].apply(lambda x: count_punct(x)) + +#es = ft.EntitySet(id="text_data") +#es = es.entity_from_dataframe(entity_id="data", +# index='index', +# dataframe=df) +#from nlp_primitives import ( +# DiversityScore, +# LSA, +# MeanCharactersPerWord, +# TitleWordCount, +# UpperCaseCount) + + +#trans = [DiversityScore, +# MeanCharactersPerWord, +# TitleWordCount, +# LSA, +# UpperCaseCount] +#feature_matrix, feature_defs = ft.dfs(entityset=es, +# target_entity='data', +# verbose=True, +# trans_primitives=trans, +# max_depth=4) +#feature_matrix.drop(["body_len"], axis=1, inplace=True) +#feature_matrix.drop(["punct%"], axis=1, inplace=True) + + +# Vectorizing data +#def clean_text(text): +# text = "".join([word.lower() for word in text if word not in string.punctuation]) +# tokens = re.split('\W+', text) +# text = [word for word in tokens] +# return text +transformer = TfidfTransformer() +loaded_vec = CountVectorizer(decode_error="replace",vocabulary=load('vocabulary.pkl')) +transformed = transformer.fit_transform(loaded_vec.fit_transform(df.body_text).toarray()) + +features = pd.concat([df[['body_len', 'punct%']].reset_index(drop=True), + pd.DataFrame(transformed.toarray()).reset_index(drop=True)], axis=1) +#dataset = pd.concat([features,feature_matrix.reset_index(drop=True)], axis=1, sort=False) + +pred = model.predict(features) +labels = list(map(id_to_labels.get, pred)) +df['label'] = labels +del df['body_len'] +del df['punct%'] +df.to_csv('result.csv', encoding='utf-8') + diff --git a/backend/webapp/prototype/filehandler/userModule.py b/backend/webapp/prototype/filehandler/userModule.py new file mode 100644 index 0000000..d5fb506 --- /dev/null +++ b/backend/webapp/prototype/filehandler/userModule.py @@ -0,0 +1,31 @@ +from django.contrib.auth import authenticate +from django.contrib.auth.models import User +from prototype.filehandler.exceptions import UserAuthFailed + +def registerNewUser(login, password): + try: + user = User.objects.get(username = login) + if isinstance(user, User): + result = {"message": "User already exists", + "statusCode": 409} + except User.DoesNotExist: + user = User.objects.create_user(username = login, email = login, password = password) + if isinstance(user, User): + result = {"message": "User created successfullly", + "statusCode": 200} + finally: + return result + +def loginUser(login, password): + try: + user = authenticate(username = login, password = password) + if isinstance(user, User): + result = {"message": "User authenticated successfully", + "statusCode": 200} + else: + raise UserAuthFailed("Error: User authentication failed") + except UserAuthFailed as error: + result = {"message": str(error), + "statusCode": 401} + finally: + return result diff --git a/backend/webapp/prototype/filehandler/views.py b/backend/webapp/prototype/filehandler/views.py index 218395f..fddb322 100644 --- a/backend/webapp/prototype/filehandler/views.py +++ b/backend/webapp/prototype/filehandler/views.py @@ -3,13 +3,14 @@ from django.conf import settings from django.core.files.storage import FileSystemStorage from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse, HttpResponse -from django.contrib.auth.models import User import json from prototype.filehandler.models import Document, Forum from prototype.filehandler.forms import DocumentForm from prototype.filehandler.xmlParser import parseData from prototype.filehandler.functions import addToDatabase, listDiscussionsFromFile, listParagraphsFromDiscussion, createLabels, listPostsFromDiscussion, updateLabelsByParagraphId +from prototype.filehandler.userModule import registerNewUser, loginUser +from prototype.filehandler.errorCodes import MethodNotAllowed def home(request): @@ -76,15 +77,15 @@ def visualize(request, id): @csrf_exempt def user(request): if request.method == 'POST': - login = request.POST['login'] - try: - user = User.objects.get(username = login) - except User.DoesNotExist: - user = User.objects.create_user(username = login, email = login, password = request.POST['password']) - return HttpResponse('User created successfully!', status=200) - except Exception: - return HttpResponse('Error: Couldn\'t register user', status=406) - return HttpResponse('Error: User already exists', status=406) + result = registerNewUser(request.POST["login"], request.POST["password"]) + return JsonResponse(result, status = result["statusCode"]) else: - return HttpResponse('Error: Unacceptable HTTP method', status=406) + return HttpResponse("Error: Method not allowed", status = 405) +@csrf_exempt +def login(request): + if request.method == 'POST': + result = loginUser(request.POST["login"], request.POST["password"]) + return JsonResponse(result, status = result["statusCode"]) + else: + return JsonResponse(MethodNotAllowed, status = MethodNotAllowed["statusCode"]) diff --git a/backend/webapp/prototype/urls.py b/backend/webapp/prototype/urls.py index 19b9e64..db8c7d7 100644 --- a/backend/webapp/prototype/urls.py +++ b/backend/webapp/prototype/urls.py @@ -26,7 +26,8 @@ urlpatterns = [ path('admin/', admin.site.urls), path('discussions/', views.discussions), path('visualize/', views.visualize), - path('user/', views.user) + path('user/', views.user), + path('login/', views.login) ] if settings.DEBUG: From 3394227619ffc0791f69c7dce1d650e6c46df5d4 Mon Sep 17 00:00:00 2001 From: Marcin Armacki Date: Sun, 8 Nov 2020 20:24:08 +0100 Subject: [PATCH 2/2] Added JWT generation on user login --- .../prototype/filehandler/userModule.py | 22 ++++++++++++++++++- backend/webapp/prototype/filehandler/views.py | 10 ++++++++- backend/webapp/prototype/urls.py | 3 ++- backend/webapp/requirements.txt | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/backend/webapp/prototype/filehandler/userModule.py b/backend/webapp/prototype/filehandler/userModule.py index d5fb506..8c3af2e 100644 --- a/backend/webapp/prototype/filehandler/userModule.py +++ b/backend/webapp/prototype/filehandler/userModule.py @@ -1,4 +1,7 @@ +import jwt +import time from django.contrib.auth import authenticate +from django.conf import settings from django.contrib.auth.models import User from prototype.filehandler.exceptions import UserAuthFailed @@ -20,8 +23,14 @@ def loginUser(login, password): try: user = authenticate(username = login, password = password) if isinstance(user, User): + payload = {"iss": "NKADF", + "iat": int(time.time()), + "sub": user.id, + "exp": int(time.time()) + 86400} + jwt_token = jwt.encode(payload, settings.SECRET_KEY, algorithm = "HS256").decode("utf-8") result = {"message": "User authenticated successfully", - "statusCode": 200} + "statusCode": 200, + "token": jwt_token} else: raise UserAuthFailed("Error: User authentication failed") except UserAuthFailed as error: @@ -29,3 +38,14 @@ def loginUser(login, password): "statusCode": 401} finally: return result + +def decodeToken(token): + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorith = "HS256") + result = payload + return payload + except Exception as error: + result = {"message": str(error), + "statusCode": 500} + finally: + return result diff --git a/backend/webapp/prototype/filehandler/views.py b/backend/webapp/prototype/filehandler/views.py index fddb322..52f01e4 100644 --- a/backend/webapp/prototype/filehandler/views.py +++ b/backend/webapp/prototype/filehandler/views.py @@ -9,7 +9,7 @@ from prototype.filehandler.models import Document, Forum from prototype.filehandler.forms import DocumentForm from prototype.filehandler.xmlParser import parseData from prototype.filehandler.functions import addToDatabase, listDiscussionsFromFile, listParagraphsFromDiscussion, createLabels, listPostsFromDiscussion, updateLabelsByParagraphId -from prototype.filehandler.userModule import registerNewUser, loginUser +from prototype.filehandler.userModule import registerNewUser, loginUser, decodeToken from prototype.filehandler.errorCodes import MethodNotAllowed @@ -89,3 +89,11 @@ def login(request): return JsonResponse(result, status = result["statusCode"]) else: return JsonResponse(MethodNotAllowed, status = MethodNotAllowed["statusCode"]) + +@csrf_exempt +def testToken(request): + if request.method == 'POST': + result = decodeToken(request.POST["token"]) + return JsonResponse(result, status = 200) + else: + return JsonResponse(MethodNotAllowed, status = MethodNotAllowed["statusCode"]) diff --git a/backend/webapp/prototype/urls.py b/backend/webapp/prototype/urls.py index db8c7d7..2f4cef4 100644 --- a/backend/webapp/prototype/urls.py +++ b/backend/webapp/prototype/urls.py @@ -27,7 +27,8 @@ urlpatterns = [ path('discussions/', views.discussions), path('visualize/', views.visualize), path('user/', views.user), - path('login/', views.login) + path('login/', views.login), + path('testToken/', views.testToken) ] if settings.DEBUG: diff --git a/backend/webapp/requirements.txt b/backend/webapp/requirements.txt index 30a93cd..1458d9b 100644 --- a/backend/webapp/requirements.txt +++ b/backend/webapp/requirements.txt @@ -1 +1,2 @@ Django~=3.0.5 +pyjwt