mirror of
https://github.com/tiyn/container-critique.git
synced 2026-05-23 00:51:35 +02:00
1.0.0: Edit functionality added
This commit is contained in:
239
src/app.py
239
src/app.py
@@ -1,19 +1,17 @@
|
||||
from flask import Flask, flash, render_template, redirect, abort, url_for, \
|
||||
request
|
||||
from flask_ckeditor import CKEditor
|
||||
from flask_login import current_user, login_user, LoginManager, logout_user, \
|
||||
login_required
|
||||
from flask_wtf import CSRFProtect
|
||||
import os
|
||||
|
||||
from flask import (Flask, abort, flash, redirect, render_template, request, url_for)
|
||||
from flask_ckeditor import CKEditor
|
||||
from flask_login import (LoginManager, current_user, login_required, login_user, logout_user)
|
||||
from flask_wtf import CSRFProtect
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
import config
|
||||
from content import rating_to_star
|
||||
from database import Database
|
||||
from forms import LoginForm, RegisterForm, WriteForm, SearchForm
|
||||
from forms import LoginForm, RegisterForm, SearchForm, WriteForm
|
||||
from search import create_search_index, ft_search
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
csrf = CSRFProtect()
|
||||
db = Database()
|
||||
@@ -28,7 +26,7 @@ login.login_view = "login"
|
||||
|
||||
@login.user_loader
|
||||
def load_user(ident):
|
||||
"""
|
||||
"""
|
||||
Returns a user by id.
|
||||
|
||||
Parameters:
|
||||
@@ -37,68 +35,70 @@ def load_user(ident):
|
||||
Returns:
|
||||
User: user that matches the id, None if none matches the id
|
||||
"""
|
||||
user = db.get_user_by_id(ident)
|
||||
if user is not None:
|
||||
return user
|
||||
return None
|
||||
user = db.get_user_by_id(ident)
|
||||
if user is not None:
|
||||
return user
|
||||
return None
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_title():
|
||||
"""
|
||||
"""
|
||||
Injects variables to the jinja2 templates.
|
||||
|
||||
Returns:
|
||||
dict: dictionary of variables to inject.
|
||||
"""
|
||||
return dict(title=config.TITLE, style=config.STYLE,
|
||||
description=config.DESCRIPTION,
|
||||
registration=config.ALLOW_REGISTRATION, r_to_star=rating_to_star)
|
||||
return dict(title=config.TITLE,
|
||||
style=config.STYLE,
|
||||
description=config.DESCRIPTION,
|
||||
registration=config.ALLOW_REGISTRATION,
|
||||
r_to_star=rating_to_star)
|
||||
|
||||
|
||||
@app.errorhandler(HTTPException)
|
||||
def page_not_found(e):
|
||||
"""
|
||||
"""
|
||||
Renders the error pages.
|
||||
|
||||
Returns:
|
||||
str: html formatted Error page
|
||||
"""
|
||||
return render_template("error.html", errorcode=str(e.code) + " " + e.name), e.code
|
||||
return render_template("error.html", errorcode=str(e.code) + " " + e.name), e.code
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
"""
|
||||
"""
|
||||
Renders the index page.
|
||||
|
||||
Returns:
|
||||
str: html formatted index page
|
||||
"""
|
||||
entries = db.get_entries()
|
||||
entries.reverse()
|
||||
return render_template("index.html", entries=entries)
|
||||
entries = db.get_entries()
|
||||
entries.reverse()
|
||||
return render_template("index.html", entries=entries)
|
||||
|
||||
|
||||
@app.route("/archive")
|
||||
def archive():
|
||||
"""
|
||||
"""
|
||||
Renders the archive page.
|
||||
|
||||
Returns:
|
||||
str: html formatted archive page
|
||||
"""
|
||||
entries = db.get_entries()
|
||||
entries.sort(key=lambda y: y.item.name)
|
||||
entries.reverse()
|
||||
entries.sort(key=lambda y: y.item.date)
|
||||
entries.reverse()
|
||||
return render_template("archive.html", entries=entries)
|
||||
entries = db.get_entries()
|
||||
entries.sort(key=lambda y: y.item.name)
|
||||
entries.reverse()
|
||||
entries.sort(key=lambda y: y.item.date)
|
||||
entries.reverse()
|
||||
return render_template("archive.html", entries=entries)
|
||||
|
||||
|
||||
@app.route("/user/<name>")
|
||||
def user(name):
|
||||
"""
|
||||
"""
|
||||
Renders the user page of a specific user.
|
||||
|
||||
Parameters:
|
||||
@@ -107,19 +107,19 @@ def user(name):
|
||||
Returns:
|
||||
str: html formatted user page
|
||||
"""
|
||||
entries = db.get_entries_by_username(name)
|
||||
entries.sort(key=lambda y: y.item.name)
|
||||
entries.reverse()
|
||||
entries.sort(key=lambda y: y.item.date)
|
||||
entries.reverse()
|
||||
if entries != []:
|
||||
return render_template("user.html", name=name, entries=entries)
|
||||
abort(404)
|
||||
entries = db.get_entries_by_username(name)
|
||||
entries.sort(key=lambda y: y.item.name)
|
||||
entries.reverse()
|
||||
entries.sort(key=lambda y: y.item.date)
|
||||
entries.reverse()
|
||||
if entries != []:
|
||||
return render_template("user.html", name=name, entries=entries)
|
||||
abort(404)
|
||||
|
||||
|
||||
@app.route("/entry/<ident>")
|
||||
def entry(ident):
|
||||
"""
|
||||
"""
|
||||
Renders the entry page of a specific entry.
|
||||
|
||||
Parameters:
|
||||
@@ -128,140 +128,171 @@ def entry(ident):
|
||||
Returns:
|
||||
str: html formatted entry page
|
||||
"""
|
||||
entry = db.get_entry_by_id(ident)
|
||||
if entry is not None:
|
||||
return render_template("standalone.html", entry=entry)
|
||||
abort(404)
|
||||
entry = db.get_entry_by_id(ident)
|
||||
if entry is not None:
|
||||
return render_template("standalone.html", entry=entry)
|
||||
abort(404)
|
||||
|
||||
|
||||
@app.route("/feed")
|
||||
def feed():
|
||||
"""
|
||||
"""
|
||||
Renders the rss feed of a the feed.
|
||||
|
||||
Returns:
|
||||
str: xml formatted feed
|
||||
"""
|
||||
entries = db.get_entries()
|
||||
entries.reverse()
|
||||
rss_xml = render_template("rss.xml", entries=entries)
|
||||
return rss_xml
|
||||
entries = db.get_entries()
|
||||
entries.reverse()
|
||||
rss_xml = render_template("rss.xml", entries=entries)
|
||||
return rss_xml
|
||||
|
||||
|
||||
@app.route("/search", methods=["GET", "POST"])
|
||||
def search():
|
||||
"""
|
||||
"""
|
||||
Renders the search page.
|
||||
|
||||
Returns:
|
||||
str: html formatted search page.
|
||||
"""
|
||||
form = SearchForm()
|
||||
if request.method == "POST":
|
||||
query_str = request.form["query_str"]
|
||||
query_res = ft_search(query_str)
|
||||
return render_template("search.html", form=form, results=query_res), 200
|
||||
return render_template("search.html", form=form, content=""), 200
|
||||
form = SearchForm()
|
||||
if request.method == "POST":
|
||||
query_str = request.form["query_str"]
|
||||
query_res = ft_search(query_str)
|
||||
return render_template("search.html", form=form, results=query_res), 200
|
||||
return render_template("search.html", form=form, content=""), 200
|
||||
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
"""
|
||||
"""
|
||||
Logs the user in.
|
||||
|
||||
Returns:
|
||||
str: html formatted login page, if login is successful renders the index
|
||||
page.
|
||||
"""
|
||||
if current_user.is_authenticated:
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = db.get_user_by_name(form.username.data)
|
||||
print(user)
|
||||
if user is not None:
|
||||
if user.check_password(form.password.data):
|
||||
login_user(user)
|
||||
return redirect(url_for("index"))
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = db.get_user_by_name(form.username.data)
|
||||
print(user)
|
||||
if user is not None:
|
||||
if user.check_password(form.password.data):
|
||||
login_user(user)
|
||||
return redirect(url_for("index"))
|
||||
flash("Invalid username or password.")
|
||||
return redirect(url_for("login"))
|
||||
return render_template("login.html", form=form)
|
||||
flash("Invalid username or password.")
|
||||
return redirect(url_for("login"))
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
"""
|
||||
"""
|
||||
Logs out the current user.
|
||||
|
||||
Returns:
|
||||
str: html formatted index page.
|
||||
"""
|
||||
logout_user()
|
||||
return redirect(url_for("index"))
|
||||
logout_user()
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
"""
|
||||
"""
|
||||
Registers new users.
|
||||
|
||||
Returns:
|
||||
str: html formatted registration page, if registration is successful
|
||||
renders the index page.
|
||||
"""
|
||||
if current_user.is_authenticated or not config.ALLOW_REGISTRATION:
|
||||
if current_user.is_authenticated or not config.ALLOW_REGISTRATION:
|
||||
return redirect(url_for("index"))
|
||||
form = RegisterForm()
|
||||
if form.validate_on_submit():
|
||||
user = db.get_user_by_name(form.username.data)
|
||||
if user is None:
|
||||
ident = db.insert_user(form.username.data, form.password.data)
|
||||
if ident is not None:
|
||||
user = db.get_user_by_id(ident)
|
||||
login_user(user)
|
||||
return redirect(url_for("index"))
|
||||
form = RegisterForm()
|
||||
if form.validate_on_submit():
|
||||
user = db.get_user_by_name(form.username.data)
|
||||
if user is None:
|
||||
ident = db.insert_user(form.username.data, form.password.data)
|
||||
if ident is not None:
|
||||
user = db.get_user_by_id(ident)
|
||||
login_user(user)
|
||||
return redirect(url_for("index"))
|
||||
flash("An error occured during registration.")
|
||||
return redirect(url_for("register"))
|
||||
return render_template("register.html", form=form)
|
||||
flash("An error occured during registration.")
|
||||
return redirect(url_for("register"))
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
|
||||
@app.route("/write_entry", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def write_entry():
|
||||
"""
|
||||
"""
|
||||
Stores newly written entries.
|
||||
|
||||
Returns:
|
||||
str: html formatted write entry page, if posting of the entry is successful
|
||||
renders the index page.
|
||||
"""
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
form = WriteForm()
|
||||
if form.validate_on_submit():
|
||||
db.insert_entry(form.name.data, form.date.data,
|
||||
form.text.data, form.rating.data, current_user.id)
|
||||
create_search_index()
|
||||
return redirect(url_for("index"))
|
||||
return render_template("write.html", form=form)
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
form = WriteForm()
|
||||
if form.validate_on_submit():
|
||||
db.insert_entry(form.name.data, form.date.data, form.text.data, form.rating.data,
|
||||
current_user.id)
|
||||
create_search_index()
|
||||
return redirect(url_for("index"))
|
||||
return render_template("write.html", form=form)
|
||||
|
||||
|
||||
@app.route("/delete_entry/<ident>", methods=["GET", "POST"])
|
||||
@app.route("/delete_entry/<ident>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_entry(ident):
|
||||
"""
|
||||
"""
|
||||
Deletes an existing entry.
|
||||
|
||||
Returns:
|
||||
str: html formatted index entry page.
|
||||
"""
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
if current_user.id == db.get_entry_by_id(ident).user.id:
|
||||
db.delete_entry(ident)
|
||||
create_search_index()
|
||||
if not current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
if current_user.id == db.get_entry_by_id(ident).user.id:
|
||||
db.delete_entry(ident)
|
||||
create_search_index()
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/edit_entry/<ident>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def edit_entry(ident):
|
||||
|
||||
entry = db.get_entry_by_id(ident)
|
||||
|
||||
if entry is None:
|
||||
abort(404)
|
||||
|
||||
if current_user.id != entry.user.id:
|
||||
abort(403)
|
||||
|
||||
form = WriteForm()
|
||||
|
||||
if request.method == "GET":
|
||||
form.name.data = entry.item.name
|
||||
form.date.data = entry.item.date
|
||||
form.text.data = entry.text
|
||||
form.rating.data = entry.rating
|
||||
|
||||
if form.validate_on_submit():
|
||||
|
||||
db.update_entry(ident, form.name.data, form.date.data, form.text.data, form.rating.data)
|
||||
|
||||
create_search_index()
|
||||
|
||||
return redirect(url_for("entry", ident=ident))
|
||||
|
||||
return render_template("write.html", form=form, edit_mode=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0")
|
||||
app.run(host="0.0.0.0")
|
||||
|
||||
@@ -4,7 +4,7 @@ db = Database()
|
||||
|
||||
|
||||
def rating_to_star(rating):
|
||||
"""
|
||||
"""
|
||||
Creates a string with stars based on the rating.
|
||||
|
||||
Parameters:
|
||||
@@ -13,10 +13,10 @@ def rating_to_star(rating):
|
||||
Returns:
|
||||
string: unicode-formatted star-rating string
|
||||
"""
|
||||
res = u"\u272D"*int(rating/20)
|
||||
length = len(res)
|
||||
if rating/20 % 1 >= 0.5:
|
||||
length += 1
|
||||
res += u" \u2BE8 "
|
||||
res += (u"\u2606" * (5 - length))
|
||||
return res
|
||||
res = u"\u272D" * int(rating / 20)
|
||||
length = len(res)
|
||||
if rating / 20 % 1 >= 0.5:
|
||||
length += 1
|
||||
res += u" \u2BE8 "
|
||||
res += (u"\u2606" * (5 - length))
|
||||
return res
|
||||
|
||||
443
src/database.py
443
src/database.py
@@ -1,11 +1,12 @@
|
||||
from datetime import date as dt
|
||||
import os
|
||||
import sqlite3
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from datetime import date as dt
|
||||
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
|
||||
class User():
|
||||
"""
|
||||
"""
|
||||
A class to represent a user.
|
||||
|
||||
Attributes:
|
||||
@@ -17,16 +18,16 @@ class User():
|
||||
pass_hash (str): hash of the users password
|
||||
"""
|
||||
|
||||
def __init__(self, name, pass_hash=None):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.is_active = True
|
||||
self.is_authenticated = True
|
||||
self.is_anonymous = False
|
||||
self.pass_hash = pass_hash
|
||||
def __init__(self, name, pass_hash=None):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.is_active = True
|
||||
self.is_authenticated = True
|
||||
self.is_anonymous = False
|
||||
self.pass_hash = pass_hash
|
||||
|
||||
def set_password(self, password):
|
||||
"""
|
||||
def set_password(self, password):
|
||||
"""
|
||||
Set the password hash of the user from a password.
|
||||
|
||||
Parameters:
|
||||
@@ -35,10 +36,10 @@ class User():
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self.pass_hash = generate_password_hash(password)
|
||||
self.pass_hash = generate_password_hash(password)
|
||||
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
Set the id of the user.
|
||||
|
||||
Parameters:
|
||||
@@ -47,10 +48,10 @@ class User():
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self.id = ident
|
||||
self.id = ident
|
||||
|
||||
def check_password(self, password):
|
||||
"""
|
||||
def check_password(self, password):
|
||||
"""
|
||||
Check if a given password matches the one of the users by comparing the
|
||||
hashes.
|
||||
|
||||
@@ -60,20 +61,20 @@ class User():
|
||||
Returns:
|
||||
bool: True if it matches the users password, False otherwise
|
||||
"""
|
||||
return check_password_hash(self.pass_hash, password)
|
||||
return check_password_hash(self.pass_hash, password)
|
||||
|
||||
def get_id(self):
|
||||
"""
|
||||
def get_id(self):
|
||||
"""
|
||||
Get the id of the user.
|
||||
|
||||
Returns:
|
||||
int: id of the user
|
||||
"""
|
||||
return self.id
|
||||
return self.id
|
||||
|
||||
|
||||
class Item():
|
||||
"""
|
||||
"""
|
||||
A class to represent an item.
|
||||
|
||||
Attributes:
|
||||
@@ -82,23 +83,23 @@ class Item():
|
||||
date (str): date the item was created
|
||||
"""
|
||||
|
||||
def __init__(self, name, date):
|
||||
self.name = name
|
||||
self.date = date
|
||||
self.id = None
|
||||
def __init__(self, name, date):
|
||||
self.name = name
|
||||
self.date = date
|
||||
self.id = None
|
||||
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
Set the id of the item.
|
||||
|
||||
Returns:
|
||||
int: id of the item
|
||||
"""
|
||||
self.id = ident
|
||||
self.id = ident
|
||||
|
||||
|
||||
class Entry():
|
||||
"""
|
||||
"""
|
||||
A class to represent an entry.
|
||||
|
||||
Attributes:
|
||||
@@ -110,43 +111,43 @@ class Entry():
|
||||
id (int): id of the item
|
||||
"""
|
||||
|
||||
def __init__(self, text, rating, date):
|
||||
self.text = text
|
||||
self.rating = rating
|
||||
self.date = date
|
||||
self.item = None
|
||||
self.user = None
|
||||
def __init__(self, text, rating, date):
|
||||
self.text = text
|
||||
self.rating = rating
|
||||
self.date = date
|
||||
self.item = None
|
||||
self.user = None
|
||||
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
def set_id(self, ident):
|
||||
"""
|
||||
Set the id of the entry.
|
||||
|
||||
Parameters:
|
||||
ident(int): id of the entry
|
||||
"""
|
||||
self.id = ident
|
||||
self.id = ident
|
||||
|
||||
def set_item(self, item):
|
||||
"""
|
||||
def set_item(self, item):
|
||||
"""
|
||||
Set the item of the entry.
|
||||
|
||||
Parameters:
|
||||
item(Item): item of the entry
|
||||
"""
|
||||
self.item = item
|
||||
self.item = item
|
||||
|
||||
def set_user(self, user):
|
||||
"""
|
||||
def set_user(self, user):
|
||||
"""
|
||||
Set the user of the entry.
|
||||
|
||||
Parameters:
|
||||
user(User): user of the entry
|
||||
"""
|
||||
self.user = user
|
||||
self.user = user
|
||||
|
||||
|
||||
class Database:
|
||||
"""
|
||||
"""
|
||||
A class to represent an entry.
|
||||
|
||||
Attributes:
|
||||
@@ -156,53 +157,53 @@ class Database:
|
||||
DB_DIR(PathLike): path that leads to the directory containing the database
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.USER_TABLE_FILE = 'USERS'
|
||||
self.ENTRY_TABLE_FILE = 'ENTRIES'
|
||||
self.ITEM_TABLE_FILE = 'ITEMS'
|
||||
self.DB_DIR = os.path.dirname("./data/")
|
||||
self.setup_db()
|
||||
def __init__(self):
|
||||
self.USER_TABLE_FILE = 'USERS'
|
||||
self.ENTRY_TABLE_FILE = 'ENTRIES'
|
||||
self.ITEM_TABLE_FILE = 'ITEMS'
|
||||
self.DB_DIR = os.path.dirname("./data/")
|
||||
self.setup_db()
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
def connect(self):
|
||||
"""
|
||||
Connect to an existing database instance based on the object
|
||||
attributes.
|
||||
|
||||
Return:
|
||||
Connection: connection to the database
|
||||
"""
|
||||
path = os.path.join(self.DB_DIR, "data.db")
|
||||
return sqlite3.connect(path)
|
||||
path = os.path.join(self.DB_DIR, "data.db")
|
||||
return sqlite3.connect(path)
|
||||
|
||||
def setup_db(self):
|
||||
"""
|
||||
def setup_db(self):
|
||||
"""
|
||||
Creates a database with the needed tables if it doesn't already exits.
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.USER_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"name CHAR(32) NOT NULL UNIQUE," + \
|
||||
"password CHAR(32) NOT NULL)"
|
||||
crs.execute(query)
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.ITEM_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"name CHAR(32) NOT NULL," + \
|
||||
"date CHAR(4)," + \
|
||||
"UNIQUE(date, name))"
|
||||
crs.execute(query)
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.ENTRY_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"item_id INTEGER NOT NULL REFERENCES " + self.ITEM_TABLE_FILE + "(id)," + \
|
||||
"text TEXT NOT NULL," + \
|
||||
"rating INTEGER NOT NULL," +\
|
||||
"user_id INTEGER REFERENCES " + self.USER_TABLE_FILE + "(id),"\
|
||||
"date CHAR(10) NOT NULL)"
|
||||
crs.execute(query)
|
||||
db.commit()
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.USER_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"name CHAR(32) NOT NULL UNIQUE," + \
|
||||
"password CHAR(32) NOT NULL)"
|
||||
crs.execute(query)
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.ITEM_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"name CHAR(32) NOT NULL," + \
|
||||
"date CHAR(4)," + \
|
||||
"UNIQUE(date, name))"
|
||||
crs.execute(query)
|
||||
query = "CREATE TABLE IF NOT EXISTS " + self.ENTRY_TABLE_FILE + \
|
||||
"(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
|
||||
"item_id INTEGER NOT NULL REFERENCES " + self.ITEM_TABLE_FILE + "(id)," + \
|
||||
"text TEXT NOT NULL," + \
|
||||
"rating INTEGER NOT NULL," +\
|
||||
"user_id INTEGER REFERENCES " + self.USER_TABLE_FILE + "(id),"\
|
||||
"date CHAR(10) NOT NULL)"
|
||||
crs.execute(query)
|
||||
db.commit()
|
||||
|
||||
def insert_user(self, username, password):
|
||||
"""
|
||||
def insert_user(self, username, password):
|
||||
"""
|
||||
Insert a row in the user table.
|
||||
|
||||
Parameters:
|
||||
@@ -212,20 +213,20 @@ class Database:
|
||||
Returns:
|
||||
int: number of the line the row was added, None if it wasn't successful
|
||||
"""
|
||||
pass_hash = generate_password_hash(password)
|
||||
if self.get_user_by_name(username) is None and pass_hash is not None:
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "INSERT INTO " + self.USER_TABLE_FILE + \
|
||||
"(`name`,`password`)" + \
|
||||
"VALUES (?, ?) ON CONFLICT DO NOTHING"
|
||||
crs.execute(query, (username, pass_hash))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
return None
|
||||
pass_hash = generate_password_hash(password)
|
||||
if self.get_user_by_name(username) is None and pass_hash is not None:
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "INSERT INTO " + self.USER_TABLE_FILE + \
|
||||
"(`name`,`password`)" + \
|
||||
"VALUES (?, ?) ON CONFLICT DO NOTHING"
|
||||
crs.execute(query, (username, pass_hash))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
return None
|
||||
|
||||
def insert_entry(self, name, date, text, rating, user_id=None):
|
||||
"""
|
||||
def insert_entry(self, name, date, text, rating, user_id=None):
|
||||
"""
|
||||
Insert a row in the entry table.
|
||||
|
||||
Parameters:
|
||||
@@ -238,25 +239,25 @@ class Database:
|
||||
Returns:
|
||||
int: number of the line the row was added
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "INSERT OR IGNORE INTO " + self.ITEM_TABLE_FILE + \
|
||||
"(`name`,`date`)" + "VALUES (?, ?)"
|
||||
crs.execute(query, (name, date))
|
||||
query = "SELECT id FROM " + self.ITEM_TABLE_FILE + \
|
||||
" WHERE name = ? AND date = ?"
|
||||
crs.execute(query, (name, date))
|
||||
item_id = crs.fetchone()[0]
|
||||
date = dt.today().strftime('%Y-%m-%d')
|
||||
query = "INSERT INTO " + self.ENTRY_TABLE_FILE + \
|
||||
"(`item_id`, `text`, `rating`, `user_id`, `date`)" + \
|
||||
"VALUES (?, ?, ?, ?, ?)"
|
||||
crs.execute(query, (item_id, text, rating, user_id, date))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "INSERT OR IGNORE INTO " + self.ITEM_TABLE_FILE + \
|
||||
"(`name`,`date`)" + "VALUES (?, ?)"
|
||||
crs.execute(query, (name, date))
|
||||
query = "SELECT id FROM " + self.ITEM_TABLE_FILE + \
|
||||
" WHERE name = ? AND date = ?"
|
||||
crs.execute(query, (name, date))
|
||||
item_id = crs.fetchone()[0]
|
||||
date = dt.today().strftime('%Y-%m-%d')
|
||||
query = "INSERT INTO " + self.ENTRY_TABLE_FILE + \
|
||||
"(`item_id`, `text`, `rating`, `user_id`, `date`)" + \
|
||||
"VALUES (?, ?, ?, ?, ?)"
|
||||
crs.execute(query, (item_id, text, rating, user_id, date))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
|
||||
def delete_entry(self, ident):
|
||||
"""
|
||||
def delete_entry(self, ident):
|
||||
"""
|
||||
Delete a row from the entry table based on the entrys id.
|
||||
|
||||
Parameters:
|
||||
@@ -265,31 +266,69 @@ class Database:
|
||||
Returns:
|
||||
int: number of the line the row was removed from
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "DELETE FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident, ))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "DELETE FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident,))
|
||||
db.commit()
|
||||
return crs.lastrowid
|
||||
|
||||
def get_entries(self):
|
||||
def update_entry(self, ident, name, item_date, text, rating):
|
||||
"""
|
||||
Update an existing entry.
|
||||
|
||||
Parameters:
|
||||
ident (int): id of the entry
|
||||
name (str): updated item name
|
||||
item_date (str): updated item date
|
||||
text (str): updated review text
|
||||
rating (int): updated rating
|
||||
"""
|
||||
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
|
||||
query = "SELECT item_id FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident,))
|
||||
fetched = crs.fetchone()
|
||||
|
||||
if fetched is None:
|
||||
return None
|
||||
|
||||
item_id = fetched[0]
|
||||
|
||||
query = "UPDATE " + self.ITEM_TABLE_FILE + \
|
||||
" SET name = ?, date = ? WHERE id = ?"
|
||||
|
||||
crs.execute(query, (name, item_date, item_id))
|
||||
|
||||
query = "UPDATE " + self.ENTRY_TABLE_FILE + \
|
||||
" SET text = ?, rating = ? WHERE id = ?"
|
||||
|
||||
crs.execute(query, (text, rating, ident))
|
||||
|
||||
db.commit()
|
||||
|
||||
return ident
|
||||
|
||||
def get_entries(self):
|
||||
"""
|
||||
Return all the entries stored in the database.
|
||||
|
||||
Return:
|
||||
List(Entry): list of entries in database
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE
|
||||
crs.execute(query)
|
||||
res = []
|
||||
for item in crs.fetchall():
|
||||
res.append(self.entry_from_db(*item))
|
||||
return res
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE
|
||||
crs.execute(query)
|
||||
res = []
|
||||
for item in crs.fetchall():
|
||||
res.append(self.entry_from_db(*item))
|
||||
return res
|
||||
|
||||
def get_entry_by_id(self, ident):
|
||||
"""
|
||||
def get_entry_by_id(self, ident):
|
||||
"""
|
||||
Return an entry stored in the database based on the entrys id.
|
||||
|
||||
Parameters:
|
||||
@@ -298,18 +337,18 @@ class Database:
|
||||
Returns:
|
||||
Entry: entry that matched the given id
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident, ))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.entry_from_db(*fetched)
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident,))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.entry_from_db(*fetched)
|
||||
|
||||
def get_entries_by_username(self, username):
|
||||
"""
|
||||
def get_entries_by_username(self, username):
|
||||
"""
|
||||
Return a entries stored in the database based on the entries name.
|
||||
|
||||
Parameters:
|
||||
@@ -318,19 +357,19 @@ class Database:
|
||||
Returns:
|
||||
List(Entry): entries that matched the given name
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE + \
|
||||
" WHERE user_id = (SELECT id FROM " + self.USER_TABLE_FILE + \
|
||||
" WHERE name = ?)"
|
||||
crs.execute(query, (username, ))
|
||||
res = []
|
||||
for item in crs.fetchall():
|
||||
res.append(self.entry_from_db(*item))
|
||||
return res
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ENTRY_TABLE_FILE + \
|
||||
" WHERE user_id = (SELECT id FROM " + self.USER_TABLE_FILE + \
|
||||
" WHERE name = ?)"
|
||||
crs.execute(query, (username,))
|
||||
res = []
|
||||
for item in crs.fetchall():
|
||||
res.append(self.entry_from_db(*item))
|
||||
return res
|
||||
|
||||
def get_item_by_id(self, ident):
|
||||
"""
|
||||
def get_item_by_id(self, ident):
|
||||
"""
|
||||
Return an item stored in the database based on the items id.
|
||||
|
||||
Parameters:
|
||||
@@ -339,18 +378,18 @@ class Database:
|
||||
Returns:
|
||||
Item: item that matched the given id
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ITEM_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident, ))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.item_from_db(*fetched)
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.ITEM_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident,))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.item_from_db(*fetched)
|
||||
|
||||
def get_user_by_id(self, ident):
|
||||
"""
|
||||
def get_user_by_id(self, ident):
|
||||
"""
|
||||
Return a user stored in the database based on the users id.
|
||||
|
||||
Parameters:
|
||||
@@ -359,18 +398,18 @@ class Database:
|
||||
Returns:
|
||||
Item: user that matched the given id
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident, ))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.user_from_db(*fetched)
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE id = ?"
|
||||
crs.execute(query, (ident,))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.user_from_db(*fetched)
|
||||
|
||||
def get_user_by_name(self, name):
|
||||
"""
|
||||
def get_user_by_name(self, name):
|
||||
"""
|
||||
Return a user stored in the database based on the user name.
|
||||
|
||||
Parameters:
|
||||
@@ -379,18 +418,18 @@ class Database:
|
||||
Returns:
|
||||
Entry: user that matched the given name
|
||||
"""
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE name = ?"
|
||||
crs.execute(query, (name, ))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.user_from_db(*fetched)
|
||||
db = self.connect()
|
||||
crs = db.cursor()
|
||||
query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE name = ?"
|
||||
crs.execute(query, (name,))
|
||||
fetched = crs.fetchone()
|
||||
if fetched is None:
|
||||
return None
|
||||
else:
|
||||
return self.user_from_db(*fetched)
|
||||
|
||||
def user_from_db(self, ident, name, pass_hash):
|
||||
"""
|
||||
def user_from_db(self, ident, name, pass_hash):
|
||||
"""
|
||||
Return a user from given database parameters.
|
||||
|
||||
Parameters:
|
||||
@@ -401,12 +440,12 @@ class Database:
|
||||
Returns:
|
||||
User: user element with given variables
|
||||
"""
|
||||
user = User(name, pass_hash)
|
||||
user.set_id(ident)
|
||||
return user
|
||||
user = User(name, pass_hash)
|
||||
user.set_id(ident)
|
||||
return user
|
||||
|
||||
def item_from_db(self, ident, name, date):
|
||||
"""
|
||||
def item_from_db(self, ident, name, date):
|
||||
"""
|
||||
Return an item from given database parameters.
|
||||
|
||||
Parameters:
|
||||
@@ -417,12 +456,12 @@ class Database:
|
||||
Returns:
|
||||
Item: entry element with given variables
|
||||
"""
|
||||
item = Item(name, date)
|
||||
item.set_id(ident)
|
||||
return item
|
||||
item = Item(name, date)
|
||||
item.set_id(ident)
|
||||
return item
|
||||
|
||||
def entry_from_db(self, ident, item_id, text, rating, user_id, date):
|
||||
"""
|
||||
def entry_from_db(self, ident, item_id, text, rating, user_id, date):
|
||||
"""
|
||||
Return an entry from given database parameters.
|
||||
|
||||
Parameters:
|
||||
@@ -436,8 +475,8 @@ class Database:
|
||||
Returns:
|
||||
Entry: entry element with given variables
|
||||
"""
|
||||
entry = Entry(text, rating, date)
|
||||
entry.set_id(ident)
|
||||
entry.set_item(self.get_item_by_id(item_id))
|
||||
entry.set_user(self.get_user_by_id(user_id))
|
||||
return entry
|
||||
entry = Entry(text, rating, date)
|
||||
entry.set_id(ident)
|
||||
entry.set_item(self.get_item_by_id(item_id))
|
||||
entry.set_user(self.get_user_by_id(user_id))
|
||||
return entry
|
||||
|
||||
73
src/forms.py
73
src/forms.py
@@ -1,61 +1,64 @@
|
||||
from datetime import date
|
||||
|
||||
from flask_ckeditor import CKEditorField
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, SubmitField, StringField
|
||||
from wtforms import PasswordField, StringField, SubmitField
|
||||
from wtforms.fields import IntegerField
|
||||
from wtforms.validators import DataRequired, EqualTo, InputRequired, \
|
||||
NumberRange, ValidationError, Length
|
||||
from wtforms.validators import (DataRequired, EqualTo, InputRequired, Length, NumberRange,
|
||||
ValidationError)
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
"""
|
||||
"""
|
||||
A Class for the Form that is used while logging in.
|
||||
"""
|
||||
username = StringField("Username", validators=[DataRequired(),
|
||||
Length(min=4, max=32)])
|
||||
password = PasswordField("Password", validators=[DataRequired(),
|
||||
Length(min=4, max=32)])
|
||||
submit = SubmitField("Sign In")
|
||||
username = StringField("Username", validators=[DataRequired(), Length(min=4, max=32)])
|
||||
password = PasswordField("Password", validators=[DataRequired(), Length(min=4, max=32)])
|
||||
submit = SubmitField("Sign In")
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
"""
|
||||
"""
|
||||
A Class for the Form that is used while registering.
|
||||
"""
|
||||
username = StringField("Username", validators=[DataRequired(),
|
||||
Length(min=4, max=32)])
|
||||
password = PasswordField("Password", validators=[DataRequired(),
|
||||
Length(min=4, max=32)])
|
||||
password2 = PasswordField(
|
||||
"Repeat Password", validators=[DataRequired(), EqualTo("password")])
|
||||
submit = SubmitField("Register")
|
||||
username = StringField("Username", validators=[DataRequired(), Length(min=4, max=32)])
|
||||
password = PasswordField("Password", validators=[DataRequired(), Length(min=4, max=32)])
|
||||
password2 = PasswordField("Repeat Password", validators=[DataRequired(), EqualTo("password")])
|
||||
submit = SubmitField("Register")
|
||||
|
||||
|
||||
class SearchForm(FlaskForm):
|
||||
"""
|
||||
"""
|
||||
A Class for the Form that is used while searching.
|
||||
"""
|
||||
query_str = StringField(
|
||||
"Query", [DataRequired("Please enter the search term")])
|
||||
submit = SubmitField("Search")
|
||||
query_str = StringField("Query", [DataRequired("Please enter the search term")])
|
||||
submit = SubmitField("Search")
|
||||
|
||||
|
||||
class WriteForm(FlaskForm):
|
||||
"""
|
||||
"""
|
||||
A Class for the Form that is used while writing a new entry.
|
||||
"""
|
||||
name = StringField("Name", validators=[DataRequired(),
|
||||
Length(min=2, max=64)])
|
||||
date = IntegerField("Release Year", default=date.today().year, validators=[
|
||||
DataRequired(), NumberRange(min=0, max=date.today().year,
|
||||
message="Year has to be valid.")])
|
||||
text = CKEditorField("Text", validators=[DataRequired()])
|
||||
rating = IntegerField("Rating", default=50, validators=[InputRequired(
|
||||
), NumberRange(min=0, max=100, message="Number has to be between 0 and 100.")])
|
||||
submit = SubmitField("Publish")
|
||||
name = StringField("Name", validators=[DataRequired(), Length(min=2, max=64)])
|
||||
date = IntegerField("Release Year",
|
||||
default=date.today().year,
|
||||
validators=[
|
||||
DataRequired(),
|
||||
NumberRange(min=0, max=date.today().year, message="Year has to be valid.")
|
||||
])
|
||||
text = CKEditorField("Text", validators=[DataRequired()])
|
||||
rating = IntegerField("Rating",
|
||||
default=50,
|
||||
validators=[
|
||||
InputRequired(),
|
||||
NumberRange(min=0,
|
||||
max=100,
|
||||
message="Number has to be between 0 and 100.")
|
||||
])
|
||||
submit = SubmitField("Publish")
|
||||
|
||||
def validate_text(self, text):
|
||||
"""
|
||||
def validate_text(self, text):
|
||||
"""
|
||||
Validate a given input for html level one headers.
|
||||
|
||||
Parameters:
|
||||
@@ -67,5 +70,5 @@ class WriteForm(FlaskForm):
|
||||
Raises:
|
||||
ValidatenError: if the text contains a first level html tag
|
||||
"""
|
||||
if "<h1>" in text.data or "</h1>" in text.data:
|
||||
raise ValidationError("Headings on level 1 are not permitted.")
|
||||
if "<h1>" in text.data or "</h1>" in text.data:
|
||||
raise ValidationError("Headings on level 1 are not permitted.")
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
Flask
|
||||
Flask_CKEditor
|
||||
Flask_Login
|
||||
Flask_WTF
|
||||
Werkzeug
|
||||
Whoosh
|
||||
WTForms
|
||||
bleach==6.3.0
|
||||
blinker==1.9.0
|
||||
click==8.3.3
|
||||
flask==3.1.3
|
||||
flask-ckeditor==1.0.0
|
||||
flask-login==0.6.3
|
||||
flask-wtf==1.3.0
|
||||
itsdangerous==2.2.0
|
||||
jinja2==3.1.6
|
||||
markupsafe==3.0.3
|
||||
webencodings==0.5.1
|
||||
werkzeug==3.1.8
|
||||
whoosh==2.7.4
|
||||
wtforms==3.2.2
|
||||
|
||||
@@ -2,8 +2,8 @@ import os
|
||||
import re
|
||||
|
||||
from whoosh import scoring
|
||||
from whoosh.fields import ID, TEXT, Schema
|
||||
from whoosh.index import create_in, open_dir
|
||||
from whoosh.fields import Schema, TEXT, ID
|
||||
from whoosh.qparser import QueryParser
|
||||
|
||||
import config
|
||||
@@ -13,7 +13,7 @@ CLEANR = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
|
||||
|
||||
|
||||
def remove_html_tags(text):
|
||||
"""
|
||||
"""
|
||||
Convert a text from html formatted to unformatted.
|
||||
|
||||
Parameters:
|
||||
@@ -22,31 +22,30 @@ def remove_html_tags(text):
|
||||
Returns:
|
||||
str: text without html tags
|
||||
"""
|
||||
res = re.sub(CLEANR, '', text)
|
||||
return res
|
||||
res = re.sub(CLEANR, '', text)
|
||||
return res
|
||||
|
||||
|
||||
def create_search_index():
|
||||
"""
|
||||
"""
|
||||
Create the index data to search all entries.
|
||||
"""
|
||||
db = Database()
|
||||
schema = Schema(title=TEXT(stored=True),
|
||||
path=ID(stored=True), content=TEXT(stored=True))
|
||||
if not os.path.exists(config.INDEX_DIR):
|
||||
os.mkdir(config.INDEX_DIR)
|
||||
ix = create_in(config.INDEX_DIR, schema)
|
||||
writer = ix.writer()
|
||||
for entry in db.get_entries():
|
||||
path = str(entry.id)
|
||||
text = entry.item.name + " " + entry.item.date + " " + entry.text + \
|
||||
" by " + entry.user.name + " " + entry.date
|
||||
writer.add_document(title=entry.item.name, path=path, content=text)
|
||||
writer.commit()
|
||||
db = Database()
|
||||
schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT(stored=True))
|
||||
if not os.path.exists(config.INDEX_DIR):
|
||||
os.mkdir(config.INDEX_DIR)
|
||||
ix = create_in(config.INDEX_DIR, schema)
|
||||
writer = ix.writer()
|
||||
for entry in db.get_entries():
|
||||
path = str(entry.id)
|
||||
text = entry.item.name + " " + entry.item.date + " " + entry.text + \
|
||||
" by " + entry.user.name + " " + entry.date
|
||||
writer.add_document(title=entry.item.name, path=path, content=text)
|
||||
writer.commit()
|
||||
|
||||
|
||||
def ft_search_times(query_str, number):
|
||||
"""
|
||||
"""
|
||||
Search for a given term and returns a specific amount of results.
|
||||
|
||||
Parameters:
|
||||
@@ -56,19 +55,19 @@ def ft_search_times(query_str, number):
|
||||
Returns:
|
||||
List(Entry): list of entries that matched the search
|
||||
"""
|
||||
ix = open_dir(config.INDEX_DIR)
|
||||
results = []
|
||||
db = Database()
|
||||
with ix.searcher(weighting=scoring.BM25F) as s:
|
||||
query = QueryParser("content", ix.schema).parse(query_str)
|
||||
matches = s.search(query, limit=number)
|
||||
for match in matches:
|
||||
results.append(db.get_entry_by_id(match["path"]))
|
||||
return results
|
||||
ix = open_dir(config.INDEX_DIR)
|
||||
results = []
|
||||
db = Database()
|
||||
with ix.searcher(weighting=scoring.BM25F) as s:
|
||||
query = QueryParser("content", ix.schema).parse(query_str)
|
||||
matches = s.search(query, limit=number)
|
||||
for match in matches:
|
||||
results.append(db.get_entry_by_id(match["path"]))
|
||||
return results
|
||||
|
||||
|
||||
def ft_search(query_str):
|
||||
"""
|
||||
"""
|
||||
Search for a given term and show the predefined amount of results.
|
||||
|
||||
Parameters:
|
||||
@@ -77,4 +76,4 @@ def ft_search(query_str):
|
||||
Returns:
|
||||
List(Entry): list of entries that matched the search
|
||||
"""
|
||||
return ft_search_times(query_str, config.SEARCH_NUMBER)
|
||||
return ft_search_times(query_str, config.SEARCH_NUMBER)
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
--text1: rgb(220, 120, 0);
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
.link-like {
|
||||
color: var(--link0);
|
||||
transition: var(--transtime);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
a:hover,
|
||||
.link-like:hover {
|
||||
color: var(--link1);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
--text1: rgb(0,0,120);
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
.link-like {
|
||||
color: var(--link0);
|
||||
transition: var(--transtime);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
a:hover,
|
||||
.link-like:hover {
|
||||
color: var(--link1);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,26 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
.link-like {
|
||||
text-decoration: none;
|
||||
transition: var(--transtime);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
a:hover,
|
||||
.link-like:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.link-like {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="archive">
|
||||
<h1>Archive</h1><br>
|
||||
{% set ns = namespace(prev_item_date="", prev_item_id=None, open_li = False, open_ul = False) -%}
|
||||
{% for entry in entries -%}
|
||||
{% if ns.prev_item_id != entry.item.id and ns.prev_item_id != None -%}
|
||||
</li>
|
||||
{% set ns.open_li = False -%}
|
||||
{% endif -%}
|
||||
{% if entry.item.date != ns.prev_item_date -%}
|
||||
{% if ns.prev_item_date != "" -%}
|
||||
</ul>
|
||||
{% set ns.open_ul = False -%}
|
||||
{% endif -%}
|
||||
<h2> {{ entry.item.date }} </h2>
|
||||
<ul>
|
||||
{% set ns.open_ul = True -%}
|
||||
{% endif -%}
|
||||
{% if ns.prev_item_id == entry.item.id -%}
|
||||
<br>
|
||||
{% else -%}
|
||||
<li>
|
||||
{{ entry.item.name }}<br>
|
||||
{% set ns.open_li = True -%}
|
||||
{% endif -%}
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }} by {{ entry.user.name }}
|
||||
</a>
|
||||
{% set ns.prev_item_date = entry.item.date -%}
|
||||
{% set ns.prev_item_id = entry.item.id -%}
|
||||
{% endfor -%}
|
||||
{% if ns.open_li -%}
|
||||
</li>
|
||||
{% endif -%}
|
||||
{% if ns.open_ul -%}
|
||||
</ul>
|
||||
{% endif -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="archive">
|
||||
<h1>Archive</h1><br>
|
||||
{% set ns = namespace(prev_item_date="", prev_item_id=None, open_li = False, open_ul = False) -%}
|
||||
{% for entry in entries -%}
|
||||
{% if ns.prev_item_id != entry.item.id and ns.prev_item_id != None -%}
|
||||
</li>
|
||||
{% set ns.open_li = False -%}
|
||||
{% endif -%}
|
||||
{% if entry.item.date != ns.prev_item_date -%}
|
||||
{% if ns.prev_item_date != "" -%}
|
||||
</ul>
|
||||
{% set ns.open_ul = False -%}
|
||||
{% endif -%}
|
||||
<h2> {{ entry.item.date }} </h2>
|
||||
<ul>
|
||||
{% set ns.open_ul = True -%}
|
||||
{% endif -%}
|
||||
{% if ns.prev_item_id == entry.item.id -%}
|
||||
<br>
|
||||
{% else -%}
|
||||
<li>
|
||||
{{ entry.item.name }}<br>
|
||||
{% set ns.open_li = True -%}
|
||||
{% endif -%}
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }} by {{ entry.user.name }}
|
||||
</a>
|
||||
{% set ns.prev_item_date = entry.item.date -%}
|
||||
{% set ns.prev_item_id = entry.item.id -%}
|
||||
{% endfor -%}
|
||||
{% if ns.open_li -%}
|
||||
</li>
|
||||
{% endif -%}
|
||||
{% if ns.open_ul -%}
|
||||
</ul>
|
||||
{% endif -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="important">
|
||||
Error<br>
|
||||
<span>{{ errorcode }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="important">
|
||||
Error<br>
|
||||
<span>{{ errorcode }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="blog">
|
||||
<h1>Blog</h1><br>
|
||||
{% for entry in entries -%}
|
||||
<div class="entry">
|
||||
<h1 id="{{ entry.id }}">
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.item.name }} ({{ entry.item.date }}) {{ r_to_star(entry.rating) }}
|
||||
</a>
|
||||
</h1>
|
||||
<small>
|
||||
rated {{ entry.rating }}/100 by
|
||||
<a href="{{ url_for('user', name=entry.user.name) }}">
|
||||
{{ entry.user.name }}
|
||||
</a> on {{ entry.date }}
|
||||
</small><br>
|
||||
{% autoescape off -%}
|
||||
{{ entry.text }}
|
||||
{% endautoescape -%}
|
||||
</div><br>
|
||||
{% endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="blog">
|
||||
<h1>Blog</h1><br>
|
||||
{% for entry in entries -%}
|
||||
<div class="entry">
|
||||
<h1 id="{{ entry.id }}">
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.item.name }} ({{ entry.item.date }}) {{ r_to_star(entry.rating) }}
|
||||
</a>
|
||||
</h1>
|
||||
<small>
|
||||
rated {{ entry.rating }}/100 by
|
||||
<a href="{{ url_for('user', name=entry.user.name) }}">
|
||||
{{ entry.user.name }}
|
||||
</a> on {{ entry.date }}
|
||||
</small><br>
|
||||
{% autoescape off -%}
|
||||
{{ entry.text }}
|
||||
{% endautoescape -%}
|
||||
</div><br>
|
||||
{% endfor -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="logging">
|
||||
<h1>Sign In</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="logging">
|
||||
<h1>Sign In</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="logging">
|
||||
<h1>Register</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}
|
||||
{% for error in form.username.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}
|
||||
{% for error in form.password.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password2.label }}<br>
|
||||
{{ form.password2(size=32) }}
|
||||
{% for error in form.password2.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="logging">
|
||||
<h1>Register</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}
|
||||
{% for error in form.username.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}
|
||||
{% for error in form.password.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password2.label }}<br>
|
||||
{{ form.password2(size=32) }}
|
||||
{% for error in form.password2.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
|
||||
<channel>
|
||||
<title>{{ title }}</title>
|
||||
<channel>
|
||||
<title>{{ title }}</title>
|
||||
<description>{{ description }}</description>
|
||||
<language>en-us</language>
|
||||
<link>{{ url_for("feed", _external=True) }}</link>
|
||||
<atom:link href="{{ url_for('feed', _external=True) }}" rel="self" type="application/rss+xml" />
|
||||
{% for entry in entries -%}
|
||||
<item>
|
||||
<title>
|
||||
{{ entry.item.name }} ({{ entry.item.date }}) {{ r_to_star(entry.rating) }} by {{ entry.user.name }}
|
||||
</title>
|
||||
<guid>
|
||||
{{ url_for("index", _anchor=entry.id, _external=True) }}
|
||||
</guid>
|
||||
<pubDate>
|
||||
{{ entry.date }}
|
||||
</pubDate>
|
||||
<description>
|
||||
{% autoescape off -%}
|
||||
text
|
||||
{% endautoescape -%}
|
||||
</description>
|
||||
</item>
|
||||
{% endfor -%}
|
||||
</channel>
|
||||
<link>{{
|
||||
url_for("feed", _external=True) }}</link>
|
||||
<atom:link
|
||||
href="{{ url_for('feed', _external=True) }}" rel="self" type="application/rss+xml" /> {%
|
||||
for entry in entries -%} <item>
|
||||
<title>
|
||||
{{ entry.item.name }} ({{ entry.item.date }}) {{ r_to_star(entry.rating) }} by {{
|
||||
entry.user.name }}
|
||||
</title>
|
||||
<guid>
|
||||
{{ url_for("index", _anchor=entry.id, _external=True) }}
|
||||
</guid>
|
||||
<pubDate>
|
||||
{{ entry.date }}
|
||||
</pubDate>
|
||||
<description>
|
||||
{% autoescape off -%}
|
||||
text
|
||||
{% endautoescape -%}
|
||||
</description>
|
||||
</item>
|
||||
{% endfor -%} </channel>
|
||||
</rss>
|
||||
|
||||
@@ -2,22 +2,23 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>Search</h1><br>
|
||||
<div class="search">
|
||||
<form action="{{ url_for('search') }}" method=post>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.query_str }}
|
||||
{{ form.submit }}
|
||||
</form>
|
||||
<ul>
|
||||
{% for entry in results -%}
|
||||
<li>
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }} {{ entry.item.name }} ({{ entry.item.date }}) by {{ entry.user.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
<h1>Search</h1><br>
|
||||
<div class="search">
|
||||
<form action="{{ url_for('search') }}" method=post>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.query_str }}
|
||||
{{ form.submit }}
|
||||
</form>
|
||||
<ul>
|
||||
{% for entry in results -%}
|
||||
<li>
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }} {{ entry.item.name }} ({{ entry.item.date }}) by {{
|
||||
entry.user.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
{% extends "template.html" -%}
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="standalone">
|
||||
<h1>
|
||||
{{ entry.item.name }} ({{ entry.item.date }})
|
||||
{{ r_to_star(entry.rating) }}
|
||||
</h1>
|
||||
<small>
|
||||
rated {{ entry.rating }}/100 by
|
||||
<a href="{{ url_for('user', name=entry.user.name) }}">
|
||||
{{ entry.user.name }}
|
||||
</a>
|
||||
on
|
||||
<a href="{{ url_for('index', _anchor='{0:d}'.format(entry.id)) }}">
|
||||
{{ entry.date }}
|
||||
</a>
|
||||
</small><br>
|
||||
{% if current_user.id == entry.user.id -%}
|
||||
<small>
|
||||
[
|
||||
<a href="{{ url_for('delete_entry', ident='{0:d}'.format(entry.id)) }}">
|
||||
delete entry
|
||||
</a>
|
||||
]
|
||||
</small><br>
|
||||
{% endif -%}
|
||||
{% autoescape off -%}
|
||||
{{ entry.text }}
|
||||
{% endautoescape -%}<br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="standalone">
|
||||
<h1>
|
||||
{{ entry.item.name }} ({{ entry.item.date }})
|
||||
{{ r_to_star(entry.rating) }}
|
||||
</h1>
|
||||
<small>
|
||||
rated {{ entry.rating }}/100 by
|
||||
<a href="{{ url_for('user', name=entry.user.name) }}">
|
||||
{{ entry.user.name }}
|
||||
</a>
|
||||
on
|
||||
<a href="{{ url_for('index', _anchor='{0:d}'.format(entry.id)) }}">
|
||||
{{ entry.date }}
|
||||
</a>
|
||||
</small><br>
|
||||
{% if current_user.id == entry.user.id -%}
|
||||
<a class="link-like" href="{{ url_for('edit_entry', ident='{0:d}'.format(entry.id)) }}">
|
||||
edit entry
|
||||
</a>
|
||||
-
|
||||
<form method="post" action="{{ url_for('delete_entry', ident='{0:d}'.format(entry.id)) }}" style="display:inline;"
|
||||
onsubmit="return confirm('Delete entry?');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<button type="submit" class="link-like">
|
||||
delete entry
|
||||
</button>
|
||||
</form>
|
||||
{% endif -%}
|
||||
{% autoescape off -%}
|
||||
{{ entry.text }}
|
||||
{% endautoescape -%}<br>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -1,48 +1,51 @@
|
||||
{% set navigation_bar = [
|
||||
(url_for("index"), "index", "Blog"),
|
||||
(url_for("archive"), "archive", "Archive"),
|
||||
(url_for("search"), "search", "Search")
|
||||
(url_for("index"), "index", "Blog"),
|
||||
(url_for("archive"), "archive", "Archive"),
|
||||
(url_for("search"), "search", "Search")
|
||||
] -%}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
||||
<title>{{ title }}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main-menu-dropdown">
|
||||
<!-- <img class="logo" src="/static/images/logo.png"> -->
|
||||
<a href="{{ url_for('index') }}">
|
||||
<img class="logo" src="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||
<span>{{ title }}</span>
|
||||
</a>
|
||||
<input type="checkbox" id="main-menu-check">
|
||||
<label for="main-menu-check" class="show-menu">☰</label>
|
||||
<div class="main-menu">
|
||||
{% for href, id, caption in navigation_bar -%}
|
||||
<a href="{{ href|e }}">{{ caption|e }}</a>
|
||||
{% endfor -%}
|
||||
<label for="main-menu-check" class="hide-menu">X</label>
|
||||
</div>
|
||||
<div class="main-menu-dropdown">
|
||||
<!-- <img class="logo" src="/static/images/logo.png"> -->
|
||||
<a href="{{ url_for('index') }}">
|
||||
<img class="logo" src="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||
<span>{{ title }}</span>
|
||||
</a>
|
||||
<input type="checkbox" id="main-menu-check">
|
||||
<label for="main-menu-check" class="show-menu">☰</label>
|
||||
<div class="main-menu">
|
||||
{% for href, id, caption in navigation_bar -%}
|
||||
<a href="{{ href|e }}">{{ caption|e }}</a>
|
||||
{% endfor -%}
|
||||
<label for="main-menu-check" class="hide-menu">X</label>
|
||||
</div>
|
||||
{% block content -%}
|
||||
{% endblock -%}
|
||||
<footer>
|
||||
Made with <a href="https://github.com/tiyn/container-critique">Container Critique </a>.<br>
|
||||
{% if current_user.is_anonymous -%}
|
||||
<a href="{{ url_for('login') }}">Login</a>
|
||||
{% if registration -%}
|
||||
-
|
||||
<a href="{{ url_for('register') }}">Register</a>
|
||||
{% endif -%}
|
||||
{% else -%}
|
||||
<a href="{{ url_for('logout') }}">Logout</a> -
|
||||
<a href="{{ url_for('write_entry') }}">Write entry</a>
|
||||
{% endif -%}
|
||||
</footer>
|
||||
</div>
|
||||
{% block content -%}
|
||||
{% endblock -%}
|
||||
<footer>
|
||||
Made with <a href="https://github.com/tiyn/container-critique">Container Critique </a>.<br>
|
||||
{% if current_user.is_anonymous -%}
|
||||
<a href="{{ url_for('login') }}">Login</a>
|
||||
{% if registration -%}
|
||||
-
|
||||
<a href="{{ url_for('register') }}">Register</a>
|
||||
{% endif -%}
|
||||
{% else -%}
|
||||
<a href="{{ url_for('logout') }}">Logout</a> -
|
||||
<a href="{{ url_for('write_entry') }}">Write entry</a>
|
||||
{% endif -%}
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -2,42 +2,42 @@
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="archive">
|
||||
<h1>User: {{ name }}</h1><br>
|
||||
{% set ns = namespace(prev_item_date="", prev_item_id=None, open_li = False, open_ul = False) -%}
|
||||
{% for entry in entries -%}
|
||||
{% if ns.prev_item_id != entry.item.id and ns.prev_item_id != None -%}
|
||||
</li>
|
||||
{% set ns.open_li = False -%}
|
||||
{% endif -%}
|
||||
{% if entry.item.date != ns.prev_item_date -%}
|
||||
{% if ns.prev_item_date != "" -%}
|
||||
</ul>
|
||||
{% set ns.open_ul = False -%}
|
||||
{% endif -%}
|
||||
<h2> {{ entry.item.date }} </h2>
|
||||
<ul>
|
||||
{% set ns.open_ul = True -%}
|
||||
{% endif -%}
|
||||
{% if ns.prev_item_id == entry.item.id -%}
|
||||
<br>
|
||||
{% else -%}
|
||||
<li>
|
||||
{{ entry.item.name }}<br>
|
||||
{% set ns.open_li = True -%}
|
||||
{% endif -%}
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }}
|
||||
</a>
|
||||
{% set ns.prev_item_date = entry.item.date -%}
|
||||
{% set ns.prev_item_id = entry.item.id -%}
|
||||
{% endfor -%}
|
||||
{% if ns.open_li -%}
|
||||
</li>
|
||||
{% endif -%}
|
||||
{% if ns.open_ul -%}
|
||||
</ul>
|
||||
{% endif -%}
|
||||
</div>
|
||||
<div class="archive">
|
||||
<h1>User: {{ name }}</h1><br>
|
||||
{% set ns = namespace(prev_item_date="", prev_item_id=None, open_li = False, open_ul = False) -%}
|
||||
{% for entry in entries -%}
|
||||
{% if ns.prev_item_id != entry.item.id and ns.prev_item_id != None -%}
|
||||
</li>
|
||||
{% set ns.open_li = False -%}
|
||||
{% endif -%}
|
||||
{% if entry.item.date != ns.prev_item_date -%}
|
||||
{% if ns.prev_item_date != "" -%}
|
||||
</ul>
|
||||
{% set ns.open_ul = False -%}
|
||||
{% endif -%}
|
||||
<h2> {{ entry.item.date }} </h2>
|
||||
<ul>
|
||||
{% set ns.open_ul = True -%}
|
||||
{% endif -%}
|
||||
{% if ns.prev_item_id == entry.item.id -%}
|
||||
<br>
|
||||
{% else -%}
|
||||
<li>
|
||||
{{ entry.item.name }}<br>
|
||||
{% set ns.open_li = True -%}
|
||||
{% endif -%}
|
||||
<a href="{{ url_for('entry', ident=entry.id) }}">
|
||||
{{ entry.date }} {{ r_to_star(entry.rating) }}
|
||||
</a>
|
||||
{% set ns.prev_item_date = entry.item.date -%}
|
||||
{% set ns.prev_item_id = entry.item.id -%}
|
||||
{% endfor -%}
|
||||
{% if ns.open_li -%}
|
||||
</li>
|
||||
{% endif -%}
|
||||
{% if ns.open_ul -%}
|
||||
</ul>
|
||||
{% endif -%}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
@@ -2,46 +2,46 @@
|
||||
|
||||
{% block content -%}
|
||||
<div class="container">
|
||||
<div class="writing">
|
||||
<h1>Write Critique</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.name.label }}<br>
|
||||
{{ form.name(size=64) }}
|
||||
{% for error in form.name.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.date.label }}<br>
|
||||
{{ form.date }}
|
||||
{% for error in form.date.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
<div class="writing">
|
||||
<h1>Write Critique</h1>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.name.label }}<br>
|
||||
{{ form.name(size=64) }}
|
||||
{% for error in form.name.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.date.label }}<br>
|
||||
{{ form.date }}
|
||||
{% for error in form.date.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
{{ form.text.label }}<br>
|
||||
{{ form.text }}
|
||||
{% for error in form.text.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
{{ ckeditor.load() }}
|
||||
{{ ckeditor.config(name="text") }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.rating.label }}<br>
|
||||
{{ form.rating }}
|
||||
{% for error in form.rating.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
{{ form.text.label }}<br>
|
||||
{{ form.text }}
|
||||
{% for error in form.text.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
{{ ckeditor.load() }}
|
||||
{{ ckeditor.config(name="text") }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.rating.label }}<br>
|
||||
{{ form.rating }}
|
||||
{% for error in form.rating.errors -%}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor -%}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
{% for mesg in get_flashed_messages() -%}
|
||||
<p>{{ mesg }}</p>
|
||||
{% endfor -%}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock -%}
|
||||
|
||||
Reference in New Issue
Block a user