From cf2f38e4e0f44962477dce17dc3a232194ba2b6d Mon Sep 17 00:00:00 2001 From: quadrismegistus Date: Wed, 12 Aug 2020 08:40:44 +0100 Subject: [PATCH] hooking up feed to db --- client/main.py | 11 +- client/screens/feed/feed.py | 26 ++++ server/backup/models-py2neo.py | 104 ++++++++++++++++ server/backup/server-py2neo.py | 220 +++++++++++++++++++++++++++++++++ server/server.py | 8 +- 5 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 server/backup/models-py2neo.py create mode 100644 server/backup/server-py2neo.py diff --git a/client/main.py b/client/main.py index d66f5f3..e2f6547 100644 --- a/client/main.py +++ b/client/main.py @@ -125,8 +125,8 @@ class MainApp(MDApp): self.root.change_screen('login') log(self.username) else: - self.root.post_id=190 - self.root.change_screen('view') + # self.root.post_id=190 + self.root.change_screen('feed') return self.root def load_store(self): @@ -263,6 +263,13 @@ class MainApp(MDApp): return jsond + def get_posts(self): + with self.get_session() as sess: + with sess.get(self.api+'/posts') as r: + log(r.text) + jsond=r.json() + return jsond['posts'] + return [] def get_image(self, img_src): # is there an image? diff --git a/client/screens/feed/feed.py b/client/screens/feed/feed.py index 8aac0d4..afa22c5 100644 --- a/client/screens/feed/feed.py +++ b/client/screens/feed/feed.py @@ -5,6 +5,7 @@ from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.card import MDCard, MDSeparator from kivy.uix.scrollview import ScrollView from screens.base import ProtectedScreen +from kivy.properties import ListProperty from main import log import os from kivy.app import App @@ -121,7 +122,32 @@ class PostCard(MDCard): class FeedScreen(ProtectedScreen): + posts = ListProperty() + def on_pre_enter(self): + # log('ids:' +str(self.ids.post_carousel.ids)) + for post in self.posts: + log('post: '+str(post)) + self.ids.post_carousel.remove_widget(post) + + i=0 + lim=25 + for i,post in enumerate(reversed(self.app.get_posts())): + #if ln.startswith('@') or ln.startswith('RT '): continue + #i+=1 + if i>lim: break + + #post = Post(title=f'Marx Zuckerberg', content=ln.strip()) + post_obj = PostCard( + author='Marx Zuckerberg', + title='', + img_src=post.get('img_src',''), + content=post.get('content','')) + log(post) + self.posts.append(post_obj) + self.ids.post_carousel.add_widget(post_obj) + + def on_pre_enter_test(self): i=0 lim=5 with open('tweets.txt') as f: diff --git a/server/backup/models-py2neo.py b/server/backup/models-py2neo.py new file mode 100644 index 0000000..c3b6399 --- /dev/null +++ b/server/backup/models-py2neo.py @@ -0,0 +1,104 @@ + +from py2neo import * +from py2neo.ogm import * + +G = Graph(password='drive your plow over the bones of the dead') +Nodes = NodeMatcher(G) +Edges = RelationshipMatcher(G) + + + +class MyGraphObject(GraphObject): + @property + def data(self): + return dict(self.__ogm__.node) + +class Person(MyGraphObject): + __primarykey__ = 'name' + + name = Property() + name_irl = Property() + passkey = Property() + + posts = RelatedTo('Post','WROTE') + follows = RelatedTo('Person','FOLLOWS') + followers = RelatedFrom('Person','FOLLOWS') + based_in = RelatedTo('Place','BASED_IN') + groups = RelatedTo('Group','IN_GROUP') + avatar = RelatedTo('Media','HAS_MEDIA') + +class Post(MyGraphObject): + # properties + title = Property() + content = Property() + img_src = Property() + + # relations + author = RelatedFrom('Person','WROTE') + location = RelatedTo('Place','BASED_IN') + + + @property + def data(self): + dx=dict(self.__ogm__.node) + + authors=list(self.author) + locations=list(self.location) + dx['author']=authors[0].name if authors else '' + dx['location']=locations[0].name if locations else '' + + return dx + +class Place(MyGraphObject): + # properties + __primarykey__ = 'name' + name = Property() + + # relations + citizens = RelatedFrom('Person','BASED_IN') + +class Group(MyGraphObject): + # properties + __primarykey__ = 'name' + name = Property() + motto = Property() + mission = Property() + + # relations + members = RelatedFrom('Person','IN_GROUP') + + + +def test_models(): + x = Person(); x.name='MrX' + y = Person(); y.name='MrY' + p1 = Post(); p1.title='Post 1'; p1.content='Hello world!' + p2 = Post(); p2.title='Post 2'; p2.content='Hello world!!!' + p3 = Post(); p3.title='Post 3'; p3.content='Hello world!!!!!!!!!!!!' + + x.follows.add(y) + x.posts.add(p1) + y.posts.add(p2) + G.push(y) + + y.posts.add(p3) + + g1=Group(); g1.name='BlocBloc' + c1=Place(); c1.name='Utopia' + c2=Place(); c2.name='Dystopia' + + x.based_in.add(c1) + y.based_in.add(c2) + + x.groups.add(g1) + y.groups.add(g1) + + + + for a in [x,y,p1,p2,p3,g1,c1,c2]: G.push(a) + + + +# test_models() + + diff --git a/server/backup/server-py2neo.py b/server/backup/server-py2neo.py new file mode 100644 index 0000000..85794cd --- /dev/null +++ b/server/backup/server-py2neo.py @@ -0,0 +1,220 @@ +import flask,os +from flask import request, jsonify, send_from_directory +from pathlib import Path +from models import * +from flask_api import FlaskAPI, status, exceptions +from werkzeug.security import generate_password_hash,check_password_hash +from flask import Flask, flash, request, redirect, url_for +from werkzeug.utils import secure_filename + + +# works better with tor? +import json +jsonify = json.dumps +# jsonify = str + +# Start server + +app = flask.Flask(__name__) +app.config["DEBUG"] = True +app.config['UPLOAD_DIR'] = 'uploads/' +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} + +@app.route('/') +def home(): return {'error':'404 go home friend'} + + +# Api Functions + +## LOGIN +@app.route('/api/login',methods=['POST']) +def login(): + data=request.json + + name=data.get('name','') + passkey=data.get('passkey','') + if not (name and passkey): + return {'error':'Login failed'},status.HTTP_400_BAD_REQUEST + + persons = list(Person.match(G,name)) + if not persons: + return {'error':'Login failed'},status.HTTP_401_UNAUTHORIZED + + person = persons[0] + real_passkey = person.passkey + if not check_password_hash(real_passkey, passkey): + return {'error':'Login failed'},status.HTTP_401_UNAUTHORIZED + + return {'success':'Login success'},status.HTTP_200_OK + +@app.route('/api/register',methods=['POST']) +def register(): + data=request.json + + name=data.get('name','') + passkey=data.get('passkey','') + + if not (name and passkey): + return {'error':'Register failed'},status.HTTP_400_BAD_REQUEST + + person = list(Person.match(G,name)) + if person: + return {'error':'User exists'},status.HTTP_401_UNAUTHORIZED + + passkey = generate_password_hash(passkey) + + person = Person() + person.name = name + person.passkey = passkey + + G.push(person) + + print('REGISTERED!',data) + return {'success':'Account created', 'username':name, 'passkey':passkey},status.HTTP_200_OK + + + + + + +## CREATE + +def allowed_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def get_random_filename(filename): + import uuid + fn=uuid.uuid4().hex + return (fn[:3],fn[3:]+os.path.splitext(filename)[-1]) + +@app.route('/api/upload',methods=['POST']) +def upload_file(): + files = request.files + # check if the post request has the file part + if 'file' not in request.files: + return {'error':'No file found'},status.HTTP_204_NO_CONTENT + + file = request.files['file'] + + # if user does not select file, browser also + # submit an empty part without filename + print('filename!',file.filename) + if file.filename == '': + return {'error':'No filename'},status.HTTP_206_PARTIAL_CONTENT + + if file and allowed_file(file.filename): + print('uploading file...') + prefix,filename = get_random_filename(file.filename) #secure_filename(file.filename) + #odir = os.path.join(app.config['UPLOAD_DIR'], os.path.dirname(filename)) + #if not os.path.exists(odir): + folder = os.path.join(app.config['UPLOAD_DIR'], prefix) + if not os.path.exists(folder): os.mkdir(folder) + file.save(os.path.join(folder, filename)) + #return redirect(url_for('uploaded_file', filename=filename)) + return {'filename':prefix+'/'+filename}, status.HTTP_200_OK + + return {'error':'Upload failed'},status.HTTP_406_NOT_ACCEPTABLE + + +@app.route('/api/post',methods=['POST']) +@app.route('/api/post/',methods=['GET']) +def post(post_id=None): + + if request.method == 'POST': + # get data + data=request.json + print('POST /api/post:',data) + + # make post + post = Post() + post.content = data.get('content','') + post.img_src = data.get('img_src','') + G.push(post) + + # attach to author + username=data.get('username','') + author = Person.match(G, username).first() + print('author?', author) + author.posts.add(post) + # post.author.add(author) + G.push(author) + + # return + post_id=str(post.__ogm__.node.identity) + print('created new post!',post_id) + return {'post_id':post_id},status.HTTP_200_OK + + print('got post id!',post_id) + posts = list(Post.match(G,post_id)) + if not posts: + return {},status.HTTP_204_NO_CONTENT + + post=posts[0] + print(post.data) + return post.data,status.HTTP_200_OK + +@app.route('/api/download//',methods=['GET']) +def download(prefix, filename): + filedir = os.path.join(app.config['UPLOAD_DIR'], prefix) + print(filedir, filename) + return send_from_directory(filedir, filename) + +### READ + + +@app.route('/api/followers/') +def get_followers(name=None): + person = Person.match(G, name).first() + data = [p.data for p in person.followers] + return jsonify(data) + +@app.route('/api/followers/') +def get_follows(name=None): + person = Person.match(G, name).first() + data = [p.data for p in person.follows] + return jsonify(data) + + +@app.route('/api/posts') +@app.route('/api/posts/') +def get_posts(name=None): + if name: + person = Person.match(G, name).first() + data = [p.data for p in person.posts] + else: + # data=[] + # def handle_row(row): + # node = row[0] + # data+=[node.data] # do something with `node` here + + # G.match + # G.cypher.execute("START z=node(*) RETURN z", row_handler=handle_row) + matcher = NodeMatcher(G) + posts = matcher.match('Post') + # posts = Post.match(G).where("_.content = '*'") + def to_data(post): + d=dict(post) + d['id']=post.identity + # authors=list(post.author) + # locations=list(post.location) + # d['author']=authors[0].name if authors else '' + # d['location']=locations[0].name if locations else '' + return d + + data = {"posts":[to_data(post) for post in posts]} + # print(data) + + return jsonify(data) + +@app.route('/api/post/') +def get_post(id=None): + post = Post.match(G, int(id)).first() + data = post.data + return jsonify(data) + + + + + +app.run(host='0.0.0.0', port=5555) \ No newline at end of file diff --git a/server/server.py b/server/server.py index 58a2b90..85794cd 100644 --- a/server/server.py +++ b/server/server.py @@ -11,7 +11,7 @@ from werkzeug.utils import secure_filename # works better with tor? import json jsonify = json.dumps -jsonify = str +# jsonify = str # Start server @@ -196,9 +196,13 @@ def get_posts(name=None): def to_data(post): d=dict(post) d['id']=post.identity + # authors=list(post.author) + # locations=list(post.location) + # d['author']=authors[0].name if authors else '' + # d['location']=locations[0].name if locations else '' return d - data = [to_data(post) for post in posts] + data = {"posts":[to_data(post) for post in posts]} # print(data) return jsonify(data)