diff --git a/mr.env b/mr.env index 9d0c63e..27eedae 100644 --- a/mr.env +++ b/mr.env @@ -5,3 +5,6 @@ export MR_PG_HOST='127.0.0.1' export MR_PG_PORT='5432' export MR_SECRET_KEY='%mh)t%pn@+dq-@&-7amfjc_k23syz4+4*a5%-c77#jiz-f^=uv' +export NEO4J_URI='bolt://localhost:7687' +export NEO4J_USER='neo4j' +export NEO4J_PASSWORD='password' \ No newline at end of file diff --git a/mr_project/mr_app/__init__.py b/mr_project/mr_app/__init__.py index e69de29..01e48ad 100644 --- a/mr_project/mr_app/__init__.py +++ b/mr_project/mr_app/__init__.py @@ -0,0 +1,9 @@ +import os + +from neo4j import GraphDatabase + +uri = os.environ['NEO4J_URI'] +user = os.environ['NEO4J_USER'] +password = os.environ['NEO4J_PASSWORD'] + +DRIVER = GraphDatabase.driver(uri, auth=(user, password)) \ No newline at end of file diff --git a/mr_project/mr_app/neo4j_transactions.py b/mr_project/mr_app/neo4j_transactions.py new file mode 100644 index 0000000..fa0a5e6 --- /dev/null +++ b/mr_project/mr_app/neo4j_transactions.py @@ -0,0 +1,71 @@ +from time import time + + +def create_node_transaction(tx, label, property, property_value): + tx.run(f'CREATE (node:{label} {{{property}: "{property_value}"}})') + + +def create_epoch_relationship_between_user_and_emotion(tx, emotion, user, epoch_timestamp): + tx.run(f'MATCH (em:Emotion {{name:"{emotion}"}}), (u:User {{name:"{user}"}}) CREATE (u)-[ts:Timestamp{{epoch:"{epoch_timestamp}"}}]->(em) RETURN ts.epoch as seconds') + + +#EFFECTIVE READ TRANSACTIONS TO CHECK IF NODES EXIST OR NOT +def get_node(driver, label, property, property_value): + with driver.session() as session: + return session.run(f'MATCH (node:{label}{{ {property}: "{property_value}" }}) RETURN node.{property} as {property}').values() + + +def check_for_user(driver, username): + return True if get_node(driver, 'User', 'name', username) else False + + +def check_for_emotion(driver, emotion_name): + return True if get_node(driver, 'Emotion', 'name', emotion_name) else False + + +def fetch_epochs_of_user_emotion_pair(driver_arg, user_name, emotion_name): + with driver_arg.session() as session: + result = session.run('''MATCH (u)-[ts]->(em) WHERE u.name = $user_name AND em.name = $emotion_name RETURN + ts.epoch AS timestamps''', user_name=user_name, emotion_name=emotion_name) + return { + 'emotion': emotion_name, + 'timestampList': [record['timestamps'] for record in result] + } + + +def fetch_all_emotions_of_one_user(driver_arg, user_name): + with driver_arg.session() as session: + result = session.run('''MATCH (u)-->(em) WHERE u.name = $user_name RETURN + em.name AS emotions''', user_name=user_name) + return set([record['emotions'] for record in result]) + + +def fetch_all_emotions_and_epochs_of_user(driver_arg, user_name): + emotions_of_user = fetch_all_emotions_of_one_user(driver_arg, user_name) + + return { + 'user': user_name, + 'emotionList': [fetch_epochs_of_user_emotion_pair(driver_arg, user_name, emotion) for emotion in emotions_of_user] + } + + +#FUNCTIONS THAT ACTUALLY CONNECT WITH THE DATABASE TO PERFORM TRANSACTIONS +def spawn_emotion_node(driver_arg, emotion): + with driver_arg.session() as session: + emotion_label = session.write_transaction(create_node_transaction, 'Emotion', 'name', emotion) + print(emotion_label) + + +def spawn_user_node(driver_arg, username): + with driver_arg.session() as session: + user_name = session.write_transaction(create_node_transaction, 'User', 'name', username) + print(user_name) + + +def register_emotion(driver_arg, username, emotion): + epoch_timestamp = time() + with driver_arg.session() as session: + seconds = session.write_transaction(create_epoch_relationship_between_user_and_emotion, emotion, username, epoch_timestamp) + print(seconds) + + diff --git a/mr_project/mr_app/urls.py b/mr_project/mr_app/urls.py index 3ef24d9..5fb8dbe 100644 --- a/mr_project/mr_app/urls.py +++ b/mr_project/mr_app/urls.py @@ -4,4 +4,7 @@ urlpatterns = [ path('', views.index, name='index'), + path('createUserEmotion', views.create_user_emotion, name='create_user_emotion'), + path('getUserEmotion', views.get_user_emotion, name='get_user_emotion'), + path('getAllUserEmotions', views.get_all_user_emotions, name='get_user_emotion'), ] \ No newline at end of file diff --git a/mr_project/mr_app/views.py b/mr_project/mr_app/views.py index 36a074d..265d9b2 100644 --- a/mr_project/mr_app/views.py +++ b/mr_project/mr_app/views.py @@ -1,9 +1,102 @@ -from django.shortcuts import render +import json +from json import JSONDecodeError -# Create your views here. +from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound, JsonResponse +from django.views.decorators.csrf import csrf_exempt +from requests import codes -from django.http import HttpResponse +from . import DRIVER +from .neo4j_transactions import * -def index(request): - return HttpResponse("Hello, world!") \ No newline at end of file +@csrf_exempt +def get_user_emotion(request): + if request.method == 'GET': + user = '' + emotion = '' + + try: + user = request.GET['user'] + except KeyError: + return HttpResponseBadRequest('Missing required "user" key') + + try: + emotion = request.GET['emotion'] + except KeyError: + return HttpResponseBadRequest('Missing required "emotion" key') + + if check_for_user(DRIVER, user) and check_for_emotion(DRIVER, emotion): + return JsonResponse(fetch_epochs_of_user_emotion_pair(DRIVER, user, emotion)) + + # change these to HttpResponseNotFound instead of HttpResponseBadRequest + if check_for_user(DRIVER, user): + return HttpResponseNotFound('Slow down dawg! The emotion does not exist, ya need a spelling lesson?') + + if check_for_emotion(DRIVER, emotion): + return HttpResponseNotFound('Slow down dawg! The user does not exist, ya need a spelling lesson?') + + return HttpResponseNotFound('Slow down dawg! The user and emotion do not exist, ya need a spelling lesson?') + + return HttpResponseNotFound() + + +@csrf_exempt +def get_all_user_emotions(request): + if request.method == 'GET': + user = '' + try: + user = request.GET['user'] + except KeyError: + return HttpResponseBadRequest('Missing required "user" key') + + if check_for_user(DRIVER, user): + return JsonResponse(fetch_all_emotions_and_epochs_of_user(DRIVER, user)) + return HttpResponseNotFound('Slow down dawg! The user does not exist, ya need a spelling lesson?') + + return HttpResponseNotFound() + + +@csrf_exempt +def create_user_emotion(request): + if request.method == 'POST': + body = {} + user = '' + emotion = '' + + try: + body = json.loads(request.body) + except JSONDecodeError: + return HttpResponseBadRequest('Payload received was not a JSON') + + try: + user = body['user'] + except KeyError: + return HttpResponseBadRequest('Missing required "user" key') + + try: + emotion = body['emotion'] + except KeyError: + return HttpResponseBadRequest('Missing required "emotion" key') + + if not check_for_user(DRIVER, user): + spawn_user_node(DRIVER, user) + + if not check_for_emotion(DRIVER, emotion): + spawn_emotion_node(DRIVER, emotion) + + register_emotion(DRIVER, user, emotion) + + return HttpResponse(status=codes.created) + + return HttpResponseNotFound() + + +@csrf_exempt +def index(request, user_id, emotion): + if request.method == 'POST': + return HttpResponse('From the heavens above to the Earth below, I alone will become the honored one.') + + if request.method == 'GET': + return HttpResponse('From the heavens above to the Earth below, I alone will become the honored one.') + + return HttpResponseNotFound() diff --git a/neo4j install and config instruction.md b/neo4j install and config instruction.md new file mode 100644 index 0000000..ac400f1 --- /dev/null +++ b/neo4j install and config instruction.md @@ -0,0 +1,58 @@ +# neo4j install + +## Initial Setup + +### Notes + +* OS is assumed to be Ubuntu 20.04 or a suitable alternative +* To make it easy to copy code, permissions have been left out. If you have questions about permissions, ask eons. + +### Prepare packages to be installed +`sudo apt update` + +###Install a Java disribution, we use OpenJDK 11. Enter y to confirm. +`sudo apt install open-11-jdk` +###Verify install. +`java -version` + +###Update package repository to include Neo4j. +``` +sudo wget -O - https://debian.neo4j.org/neotechnology.gpg.key | sudo apt-key add - +sudo echo 'deb https://debian.neo4j.org/repo stable/' | sudo tee -a /etc/apt/sources.list.d/neo4j.list +sudo apt-get update +``` + +###Install Neo4j. Enter y to confirm. +`sudo apt install neo4j` +###Verify install. +`neo4j --version` + +###Edit the neo4j configuration to accept database connection from anywhere and to disable web authentication. Also edit 0.0.0.0 to 127.0.0.1. +`sudo nano /etc/neo4j/neo4j.conf` +###Uncomment the following lines in the neo4j.conf +``` +dbms.connectors.default_listen_address=0.0.0.0 +dbms.security.auth_enabled=false +``` + +###Go to to open a neo4j session. +###If password needed, deafult user and password are both "neo4j". + + +##Starting Neo4j Service + +###To enable your local database so it is accessible from the neo4j browser session, you must use one of two commands + +`service neo4j start` + +###or + +`systemctl start neo4j` + +###Remember, before shutting down your computer, you should shut down your database by calling either of the above commands but replacing + +`start` + +###with + +`stop` \ No newline at end of file diff --git a/readme.md b/readme.md index 86cd90a..2505bf1 100644 --- a/readme.md +++ b/readme.md @@ -65,10 +65,14 @@ ln -s /usr/bin/python3.8 /usr/bin/python3 If python3-venv is not installed, install it through apt: `apt install python3-venv` +If python3-pip is not installed, install it through apt: +`apt install python3-pip` + ``` python3 -m venv . source ./bin/activate pip3 install -r requirements.txt +chmod +x ./mr_project/manage.py ``` ## Run Django diff --git a/requirements.txt b/requirements.txt index 2753814..1f7919e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ +wheel==0.34.2 django==3.1 -psycopg2==2.8.5 \ No newline at end of file +psycopg2==2.8.5 +neo4j==4.1.0 \ No newline at end of file