SnowCode 4 лет назад
Сommit
4d9b28af08

BIN
__pycache__/app.cpython-36.pyc


BIN
__pycache__/helper.cpython-36.pyc


BIN
__pycache__/login.cpython-36.pyc


+ 78 - 0
app.py

@@ -0,0 +1,78 @@
+from flask import Flask, redirect, render_template, request, flash # import flask
+from login import * # import the login helper file
+import helper, json, time # Import the helper file and other modules
+app = Flask(__name__) # Create the app
+db = helper.createDb(app) # Get the database
+User, Topic, Reply, db = db['User'], db['Topic'], db['Reply'], db['db'] # Get the classes
+createLoginManager(app, User) # Create and init the login manager (login helper file)
+def getTime(): return time.asctime( time.localtime(time.time()) ) # Get the current time and date
+
+@app.route('/login') # Render the login page, nothing more
+def renderLogin(): 	logout_user(); return render_template('login.html')
+
+@app.route('/login/post', methods=['POST'])
+def login(): # Login backend
+	try: loginUser(request.form['username'], request.form['password'], User); return redirect('/')
+	except: 
+		try: createUser(request.form['username'], request.form['password'], db, User); return redirect('/')
+		except: return redirect('/login')
+
+@app.route('/') # Render the homepage
+def renderHome():
+    return render_template('index.html', topics=Topic.query.order_by(Topic.lastActivity.desc())) # List all the topics in the reversed order
+
+@app.route('/post') # Render the 'write new topic' box
+@login_required 
+def renderCreateTopic(): return render_template('post.html')
+
+@app.route('/post/post', methods=['POST']) # Backend of the new topic box
+def createTopic():
+    topic = Topic(request.form['title'], request.form['content'], getTime(), getUsername(), request.form['category'])
+    db.session.add(topic)
+    db.session.commit()
+    return redirect('/topic/' + str(topic.id))
+
+@app.route('/topic/<id>') # Render a topic
+def renderTopic(id):
+    topic = Topic.query.filter_by(id=id).first_or_404(); topic.views += 1 # Add one view
+    db.session.add(topic); db.session.commit() # Change the value of the view in the database
+    return render_template('topic.html', topic=topic, replies=Reply.query.filter_by(inReplyTo=id)) # Render the page
+
+@app.route('/reply/<id>', methods=['POST']) # Reply to a post.
+@login_required
+def replyTo(id):
+    topic = Topic.query.filter_by(id=id).first_or_404(); topic.reply(getTime()) # Reply to the topic
+    reply = Reply(request.form['body'], getTime(), current_user.username, id) # Add the reply
+    db.session.add(reply); db.session.add(topic); db.session.commit() # Add everything in the database
+    return redirect('/topic/' + str(id)) # Redirect to the correct page
+
+@app.route('/like/<id>') # Like a topic
+@login_required
+def likeTopic(id):
+    topic = Topic.query.filter_by(id=id).first_or_404()
+    topic.like(current_user.username) # Call the 'like' function of the class 'Topic'
+    db.session.add(topic); db.session.commit()
+    return redirect('/topic/' + str(id))
+
+@app.route('/like/reply/<id>/<idt>') # Like a reply
+@login_required
+def likeReply(id, idt):
+    reply = Reply.query.filter_by(id=id).first_or_404()
+    reply.like(current_user.username) # Call the like function of the class Reply
+    db.session.add(reply); db.session.commit()
+    return redirect('/topic/' + str(idt)) # Return to the topic
+
+@app.route('/top') # Order the list of posts by thoses who have the biggest number of replies
+def topList():
+    topics = Topic.query.order_by(Topic.repliesNum.desc())
+    return render_template('index.html', topics=topics)
+
+@app.route('/new') # Order the list like normal (redirect)
+def redirectIndex(): return redirect('/')
+
+@app.route('/cat/<category>') # Get the list of posts in a category
+def catList(category):
+    topics = Topic.query.filter_by(category=category).order_by(Topic.id.desc())
+    return render_template('index.html', topics=topics)
+
+app.run(debug=True) # Run the app in mode debug (change to False otherwise)


+ 92 - 0
helper.py

@@ -0,0 +1,92 @@
+from flask_sqlalchemy import SQLAlchemy
+from flask_login import UserMixin
+import json
+
+# This file contains all the databases.
+def createDb(app):
+	app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
+	app.config['SECRET_KEY'] = 'hard to guess thing'
+	db = SQLAlchemy(app)
+
+	# Create the tables
+	class User(UserMixin, db.Model):
+			id = db.Column(db.Integer, primary_key=True)
+			username = db.Column(db.Text, unique=True)
+			password = db.Column(db.Text)
+			def __init__(self, username, password):
+				self.username = username
+				self.password = password
+	class Topic(db.Model):
+	        id = db.Column(db.Integer, primary_key=True)
+	        title = db.Column(db.Text)
+	        content = db.Column(db.Text)
+	        date = db.Column(db.Text)
+	        lastActivity = db.Column(db.Text)
+	        author = db.Column(db.Text)
+	        category = db.Column(db.Text)
+	        private = db.Column(db.Boolean)
+	        likes = db.Column(db.Text)
+	        replies = db.Column(db.Text)
+	
+	        # Numbers
+	        likesNum = db.Column(db.Integer)
+	        repliesNum = db.Column(db.Integer)
+	        views = db.Column(db.Integer)
+		
+	        def __init__(self, title, content, date, author, category, private=False):
+	            self.title = title
+	            self.content = content
+	            self.date = date
+	            self.author = author
+	            self.category = category
+	            self.private = private
+	            self.likesNum = 0
+	            self.repliesNum = 0
+	            self.views = 0
+	            self.likes = '[]'
+	            self.lastActivity = date
+	        # Create like and other def for this thing
+	        def like(self, username):
+	            l = json.loads(self.likes)
+	            if username in l:
+	                l.remove(username)
+	                self.likesNum -= 1
+	                self.likes = json.dumps(l)
+	            else:
+	                l.append(username)
+	                self.likesNum += 1
+	                self.likes = json.dumps(l)
+	        def reply(self, date):
+	            self.lastActivity = date
+	            self.repliesNum += 1
+	class Reply(db.Model):
+	        id = db.Column(db.Integer, primary_key=True)
+	        content = db.Column(db.Text)
+	        date = db.Column(db.Text)
+	        author = db.Column(db.Text)
+	        inReplyTo = db.Column(db.Integer)
+	        likes = db.Column(db.Text)
+
+	        # Adding the numbers
+	        likesNum = db.Column(db.Integer)
+	        def __init__(self, content, date, author, inReplyTo):
+	            self.content = content
+	            self.date = date
+	            self.author = author
+	            self.inReplyTo = inReplyTo
+	            self.likesNum = 0
+	            self.likes = '[]'
+	        def like(self, username):
+	            l = json.loads(self.likes)
+	            if username in l:
+	                l.remove(username)
+	                self.likesNum -= 1
+	                self.likes = json.dumps(l)
+	            else:
+	                l.append(username)
+	                self.likesNum += 1
+	                self.likes = json.dumps(l)
+	db.create_all()
+
+	# Return the data
+	return {'db': db, 'User': User, 'Topic': Topic, 'Reply': Reply}

+ 44 - 0
login.py

@@ -0,0 +1,44 @@
+# This is only a module to make the login process faster and easier. Don't care about it unless you also wants a simple login for your app
+import hashlib as hl
+from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
+
+def createLoginManager(app, User):
+# Configure login
+	login_manager = LoginManager()
+	login_manager.init_app(app)
+	@login_manager.user_loader
+	def load_user(user_id):
+		return User.query.get(int(user_id))
+
+def loginUser(username, password, User):
+	# Hash the username and the password
+	#username = hl.md5(bytes(username, 'utf-8')).hexdigest()
+	password = hl.md5(bytes(password, 'utf-8')).hexdigest()
+	
+	# Check if it exists
+	user = User.query.filter_by(username=username, password=password).first_or_404()
+	login_user(user)
+	return True
+
+def createUser(username, password, db, User):
+	# hash the username and the password
+	#username = hl.md5(bytes(username, 'utf-8')).hexdigest() # Comment this is you want a clear username
+	password = hl.md5(bytes(password, 'utf-8')).hexdigest()
+	
+	# Send them to db
+	user = User(username, password)
+	db.session.add(user)
+	db.session.commit()
+
+	# Login the user
+	login_user(user)
+
+	# return success
+	return True
+
+def getUsername():
+    return current_user.username
+
+# To restrict a page to a user just add @login_required
+# To logout just do logout_user()
+# To get the current username do current_user.username

+ 1 - 0
output.html

@@ -0,0 +1 @@
+<b>Hello</b>

+ 50 - 0
templates/base.html

@@ -0,0 +1,50 @@
+<style>
+ul {
+  display: flex;
+  list-style-type: None;
+}
+
+ul li {
+  margin-left: 15%
+}
+
+#meta {
+  margin: 10px;
+  display: flex;
+}
+
+#name {
+  margin: 10px;
+  margin-right: 80%;
+}
+
+#date {
+  margin: 10px
+}
+
+#body {
+  margin: 10px;
+}
+
+#body a {
+  margin-left: 90%
+}
+
+td {
+  text-align: center;
+}
+
+#toprow {
+	display: flex;
+	margin: 10px
+}
+</style>
+
+<div id="toprow">
+	<a href="/"><h1>SITENAME</h1></a>
+	<a href="/login">Login/Logout</a>
+</div>
+
+{% block content %}
+{% endblock %}
+

+ 33 - 0
templates/index.html

@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+
+{% block content %}
+<ul>
+	<li><a href="/top">Top</a></li>
+	<li><a href="/new">New</a></li>
+	<!--li><a href="/cats">Categories</a></li>
+	<li><a href="/unread">Unread</a></li -->
+</ul>
+
+<a href="/post">Write a new topic</a>
+
+<table style="width:100%">
+	<tr>
+		<th>Title</th>
+		<th>Category</th>
+		<th>Last Activity</th>
+		<th>Author</th>
+		<th>Replies</th>
+		<th>Views</th>
+	</tr>
+	{% for topic in topics %}
+	<tr>
+		<td><a href="/topic/{{ topic.id }}"><b>{{ topic.title }}</b></a></td>
+		<td><a href="/cat/{{ topic.category }}">{{ topic.category }}</a></td>
+		<td>{{ topic.lastActivity }}</td>
+		<td>{{ topic.author }}</td>
+		<td>{{ topic.repliesNum }}</td>
+		<td>{{ topic.views }}</td>
+	</tr>
+	{% endfor %}
+</table>
+{% endblock %}

+ 8 - 0
templates/login.html

@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block content %}
+<form action='/login/post' method="post">
+	<p>Username: <input type="text" name="username"></p>
+	<p>Password: <input type="password" name="password"></p>
+	<p><button type="submit">Login/Register</button></p>
+</form>
+{% endblock %}

+ 11 - 0
templates/post.html

@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% block content %}
+<form action="/post/post" method="post">
+	<ul>
+		<li>Title: <input type="text" name="title"></li>
+		<li>Category: <input type="text" name="category"></li>
+		<li>Content: <input type="text" name="content"></li>
+		<li><button type="submit">Create</button></li>
+	</ul>
+</form>
+{% endblock %}

+ 32 - 0
templates/topic.html

@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+{% block content %}
+<div>
+	<div id="meta">
+		<div id="name">{{ topic.author }}</div>
+		<div id="time">{{ topic.date }}</div>
+	</div>
+	<div id="body">
+		{{ topic.content|safe }}
+		<a href="/like/{{ topic.id }}">{{ topic.likesNum }} likes</a>
+	</div>
+</div>
+
+{% for reply in replies %}
+<div>
+	<div id="meta">
+		<div id="name">{{ reply.author }}</div>
+		<div id="time">{{ reply.date }}</div>
+	</div>
+	<div id="body">
+		{{ reply.content|safe }}
+		<a href="/like/reply/{{ reply.id }}/{{ topic.id }}">{{ reply.likesNum }} likes</a>		
+	</div>
+</div>
+{% endfor %}
+<div id="reply">
+	<form action="/reply/{{ topic.id }}" method="post">
+		<textarea name="body" cols="100" rows="10"></textarea>
+		<button type="submit">Reply</button>
+	</form>
+</div> 
+{% endblock %}

+ 1 - 0
test.html

@@ -0,0 +1 @@
+data

+ 1 - 0
theme/test.html

@@ -0,0 +1 @@
+{{ data }}

+ 26 - 0
todo.md

@@ -0,0 +1,26 @@
+x Scroll through posts
+x Likes
+Categories
+x Top, new, cats
+x Replies
+x Posts
+x Users
+x Restyling
+x Adding formatting
+x Login restricted
+x Larger box and better design
+x Bumppost
+x Dates instead of 'today'
+Searchbar
+
+## Lower priority
+Trust system and badges
+Notifications
+Private msgs
+Change password
+
+
+## Warning
+
+The reason why the code is small is not because I am lazy (or maybe a bit) but also because I want to optimize it to be less than 100 lines.
+For the moment the code is at  75 lines. Which is good. But the features listed above need to be implemented without getting over 100 lines.