diff --git a/api/browser_guide/__init__.py b/api/browser_guide/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/api/graph.py b/api/graph.py deleted file mode 100644 index cffad4926c9a26beb7abf914ec5ce73b2fcaf03a..0000000000000000000000000000000000000000 --- a/api/graph.py +++ /dev/null @@ -1,173 +0,0 @@ -from neo4j import GraphDatabase -import time - -from api.interface.graphql import Discussion - -URL = 'jdbc:postgresql://db/discussion?user=postgres&password=FooBar' - - -class Graph(object): - def __init__(self, uri, user, password): - self._driver = GraphDatabase.driver(uri, auth=(user, password)) - - def close(self): - self._driver.close() - - def load(self): - t0 = self.run(self._delete_everything) - t1 = self.run(self._create_index_on_statement_uid) - t2 = self.run(self._create_statement_nodes) - t3 = self.run(self._fill_statements_with_content) - t4 = self.run(self._create_user_nodes) - t5 = self.run(self._create_relations_between_users_and_statements) - t6 = self.run(self._create_issue_nodes) - t7 = self.run(self._connect_issues_with_statements) - t8 = self.run(self._every_user_likes_his_position) - t9 = self.run(self._create_random_ratings) - t10 = self.run(self._delete_zero_ratings) - - return { - "delete_time": t0, - "index_time": t1, - "statement_node_time": t2, - "statement_content_time": t3, - "user_node_time": t4, - "statement_user_relation_time": t5, - "issue_node_time": t6, - "statement_to_issue_time": t7, - "user_rates_own_positions_time": t8, - "random_ratings_time": t9, - "delete_zero_ratings_time": t10, - "total": sum([t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10]) - } - - def run(self, func): - t1 = time.time() - with self._driver.session() as session: - session.write_transaction(func) - return time.time() - t1 - - @staticmethod - def _create_index_on_statement_uid(tx): - result = tx.run( - "CREATE INDEX ON :Statement(uid) " - ) - return result - - @staticmethod - def _create_index_on_user_uid(tx): - result = tx.run( - "CREATE INDEX ON :Statement(uid) " - ) - return result - - @staticmethod - def _create_statement_nodes(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'statements') " - "YIELD row " - "MERGE (statement:Statement{uid:row.uid, is_position:row.is_position, is_disabled:row.is_disabled})" - , url=URL - ) - return result - - @staticmethod - def _fill_statements_with_content(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'textversions') " - "YIELD row " - "MATCH (statement:Statement{uid:row.statement_uid}) " - "WHERE NOT EXISTS(statement.content) " - "SET statement += {content:row.content}" - , url=URL - ) - return result - - @staticmethod - def _create_user_nodes(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'users') " - "YIELD row " - "MERGE (user:User{uid:row.uid, public_nickname:row.public_nickname})" - , url=URL - ) - return result - - @staticmethod - def _create_relations_between_users_and_statements(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'textversions') " - "YIELD row " - "MATCH (user:User), (statement:Statement) " - "WHERE user.uid = row.author_uid AND statement.uid = row.statement_uid " - "MERGE (user)-[:HAS_WRITTEN]->(statement)" - , url=URL - ) - return result - - @staticmethod - def _create_issue_nodes(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'issues') " - "YIELD row " - "MERGE (issue:Issue{uid:row.uid, title:row.title})" - , url=URL - ) - return result - - @staticmethod - def _connect_issues_with_statements(tx): - result = tx.run( - "CALL apoc.load.jdbc($url, 'statement_to_issue') " - "YIELD row " - "MATCH (statement:Statement{uid:row.statement_uid}), (issue:Issue{uid:row.issue_uid}) " - "MERGE (statement)-[:WRITTEN_IN]->(issue)" - , url=URL - ) - return result - - @staticmethod - def _every_user_likes_his_position(tx): - result = tx.run( - "MATCH(user:User)-[:HAS_WRITTEN]->(statement:Statement{is_position:True}) " - "MERGE(user)-[:LIKES{rating: 5}]->(statement)" - ) - return result - - @staticmethod - def _create_random_ratings(tx): - result = tx.run( - "MATCH (user:User) " - "WHERE NOT EXISTS((user)-[:LIKES]->()) " - "MATCH (statement:Statement) " - "WHERE statement.is_position " - "MERGE (user)-[:LIKES{rating:round(rand()*5)}]->(statement)" - ) - return result - - @staticmethod - def _delete_zero_ratings(tx): - result = tx.run( - "MATCH ()-[r:LIKES{rating:0.0}]->() " - "DETACH DELETE r" - ) - return result - - @staticmethod - def _delete_everything(tx): - result = tx.run( - "MATCH (a) " - "DETACH DELETE a" - ) - return result - - @staticmethod - def _load_discussion(tx, slug): - result = tx.run( - Discussion( - protocol='https', - host='dbas.cs.uni-duesseldorf.de', - port=443 - ).to_cypher(slug) - ) - return result diff --git a/api/src/models/__init__.py b/api/src/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/api/src/models/discussion.py b/api/src/models/discussion.py new file mode 100644 index 0000000000000000000000000000000000000000..23992354bc38efc436cf7cf51f1dcde7db613c3b --- /dev/null +++ b/api/src/models/discussion.py @@ -0,0 +1,30 @@ +from api.src.traffic.adapter import Adapter +from api.src.traffic.injector import Injector + + +class Discussion(Adapter, Injector): + + def __init__(self, protocol, host, port, slug): + self.protocol = protocol + self.host = host + self.port = port + self.slug = slug + Adapter.__init__(self, "{}://{}:{}/api/v2/query?q={}".format(protocol, host, port, self._graphql_query(slug))) + Injector.__init__(self) + + @staticmethod + def _graphql_query(slug): + return """ + query{{ + issue(slug: "{0}"){{ + completeGraphCypher + }} + }} + """.format(slug) + + def _cypher_query(self): + result = super().request() + return result['issue']['completeGraphCypher'] + + def inject_to_neo(self): + return self.run(self._cypher_query()) \ No newline at end of file diff --git a/api/src/models/discussion_skeleton.py b/api/src/models/discussion_skeleton.py new file mode 100644 index 0000000000000000000000000000000000000000..7d1ccaa3164de2e2f9da0fe6b8a6442aef228992 --- /dev/null +++ b/api/src/models/discussion_skeleton.py @@ -0,0 +1,15 @@ +from api.src.traffic.adapter import Adapter +from api.src.traffic.injector import Injector + + +class DiscussionSkeleton(Adapter, Injector): + + def __init__(self, protocol, host, port): + self.protocol = protocol + self.host = host + self.port = port + Adapter.__init__(self, url="{}://{}:{}/api/cypher".format(self.protocol, self.host, self.port)) + Injector.__init__(self) + + def inject_to_neo(self): + return self.run(self.request(expects_true_string=True)) \ No newline at end of file diff --git a/api/src/traffic/__init__.py b/api/src/traffic/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/api/src/traffic/adapter.py b/api/src/traffic/adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..aa9cadf215b05987939b5fa3a53f0e41f5b445d0 --- /dev/null +++ b/api/src/traffic/adapter.py @@ -0,0 +1,27 @@ +import json +import logging + +import requests + + +class Adapter(object): + def __init__(self, url): + self.url = url + + @staticmethod + def _json_to_dict(col): + if isinstance(col, dict): + return col + elif isinstance(col, bytes): + col = col.decode("utf-8") + return json.loads(col) + + def request(self, expects_true_string=False): + try: + response = requests.get(self.url) + except requests.exceptions.ConnectionError: + logging.error("Connection Error") + return {} + result = response.content + ret = self._json_to_dict(result) if not expects_true_string else result + return ret diff --git a/api/src/traffic/injector.py b/api/src/traffic/injector.py new file mode 100644 index 0000000000000000000000000000000000000000..d5bfd8c1f25a648e121f9bae31222cf6ada52be6 --- /dev/null +++ b/api/src/traffic/injector.py @@ -0,0 +1,31 @@ +import os +import time + +from neo4j import GraphDatabase + + +class Injector(object): + + def __init__(self, protocol=os.getenv('NEO4J_PROTOCOL'), host=os.getenv('NEO4J_HOST'), + port=os.getenv('NEO4J_PORT'), user=os.getenv('NEO4J_USER'), password=os.getenv('NEO4J_PW')): + self.protocol = protocol + self.host = host + self.port = port + self.user = user + self.password = password + uri = "{}://{}:{}".format(protocol, host, port) + self._driver = GraphDatabase.driver(uri, auth=(user, password)) + + def close(self): + self._driver.close() + + @staticmethod + def _run_query(tx, query): + return tx.run(query) + + def run(self, query): + t1 = time.time() + + with self._driver.session() as session: + session.write_transaction(self._run_query, query) + return {"total_time": round(time.time() - t1, 4)} diff --git a/docker-compose.yml b/docker-compose.yml index e0abb765f5cddc34306dda274816a89a212a048f..15c6a627cd65a98f5a3eb7bf51a2258c09655af4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,17 @@ services: volumes: - ./api:/code/api/ command: bash -c "code/api/wait-for-it.sh -t 30 -h neo -p 7474 -- python code/api/server.py" + environment: + NEO4J_PORT: ${NEO4J_PORT} + NEO4J_PROTOCOL: ${NEO4J_PROTOCOL} + NEO4J_HOST: ${NEO4J_HOST} + NEO4J_USER: ${NEO4J_USER} + NEO4J_PW: ${NEO4J_PW} + DB_PW: ${DB_PW} + DB_HOST: ${DB_HOST} + DB_NAME: ${DB_NAME} + DB_PORT: ${DB_PORT} + DB_USER: ${DB_USER} networks: default: