Initial commit
This commit is contained in:
164
src/app.py
Normal file
164
src/app.py
Normal file
@ -0,0 +1,164 @@
|
||||
from flask import Flask, render_template, request, url_for, redirect, session
|
||||
from functools import wraps
|
||||
from sqlalchemy import or_
|
||||
from markdown import markdown
|
||||
from database import db, User, Note
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Decoration: check login in session
|
||||
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'user_id' not in session:
|
||||
session.clear()
|
||||
return redirect(url_for('index'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index(data=None):
|
||||
return render_template('items/login.html', data=data)
|
||||
|
||||
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
data = dict()
|
||||
if request.form['email'] and request.form['password']:
|
||||
# Checks if the user exists
|
||||
data['email'] = request.form['email']
|
||||
data['password'] = request.form['password']
|
||||
my_user = User.query.filter_by(
|
||||
email=data['email'], password=data['password']).first()
|
||||
if my_user:
|
||||
# Create user session
|
||||
session['user_id'] = my_user.id
|
||||
session['user_email'] = my_user.email
|
||||
return redirect(url_for('dashboard'))
|
||||
else:
|
||||
data['error'] = True
|
||||
else:
|
||||
data['error'] = True
|
||||
|
||||
return index(data)
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
# Clear sessions
|
||||
session.clear()
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/dashboard')
|
||||
@login_required
|
||||
def dashboard(my_param_note=None):
|
||||
my_main_note = None
|
||||
|
||||
# Searchs
|
||||
my_notes = my_param_note
|
||||
if not my_notes:
|
||||
# Nothing found
|
||||
my_notes = Note.query.order_by(Note.id.desc()).all()
|
||||
if my_notes:
|
||||
# Show first result
|
||||
my_main_note = my_notes[0]
|
||||
|
||||
# Show first note
|
||||
if request.args.get('id'):
|
||||
my_note_temp = Note.query.filter_by(id=request.args.get('id')).first()
|
||||
# Is there any note in the database?
|
||||
if my_note_temp:
|
||||
my_main_note = my_note_temp
|
||||
|
||||
# Data for template
|
||||
data = dict()
|
||||
data['notes'] = my_notes
|
||||
data['main_note'] = my_main_note
|
||||
data['markdown'] = markdown
|
||||
|
||||
return render_template('items/dashboard.html', data=data)
|
||||
|
||||
|
||||
@app.route('/search')
|
||||
@login_required
|
||||
def search():
|
||||
q = request.args.get('q')
|
||||
|
||||
return dashboard(Note.query.filter(
|
||||
or_(Note.title.like('%' + q + '%'), Note.text.like('%' + q + '%')
|
||||
)).all())
|
||||
|
||||
|
||||
@app.route('/new')
|
||||
@login_required
|
||||
def new():
|
||||
return render_template('items/new.html')
|
||||
|
||||
|
||||
@app.route('/new/save', methods=['POST'])
|
||||
@login_required
|
||||
def save_note():
|
||||
myNote = Note(request.form['title'], request.form['text'])
|
||||
# Create
|
||||
db.session.add(myNote)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
|
||||
@app.route('/edit')
|
||||
@login_required
|
||||
def edit(data=None):
|
||||
id = request.args.get('id')
|
||||
my_note = Note.query.filter_by(id=id).first()
|
||||
data = dict()
|
||||
data['main_note'] = my_note
|
||||
return render_template('items/edit.html', data=data)
|
||||
|
||||
|
||||
@app.route('/edit_note', methods=['POST'])
|
||||
@login_required
|
||||
def edit_note(data=None):
|
||||
if request.form['id']:
|
||||
# Update
|
||||
my_note = Note.query.filter_by(id=request.form['id']).first()
|
||||
my_note.title = request.form['title']
|
||||
my_note.text = request.form['text']
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
|
||||
@app.route('/delete')
|
||||
@login_required
|
||||
def delete():
|
||||
id = request.args.get('id')
|
||||
my_note = Note.query.filter_by(id=id).first()
|
||||
data = dict()
|
||||
data['main_note'] = my_note
|
||||
data['markdown'] = markdown
|
||||
|
||||
return render_template('items/delete.html', data=data)
|
||||
|
||||
|
||||
@app.route('/delete_note')
|
||||
@login_required
|
||||
def delete_note():
|
||||
id = request.args.get('id')
|
||||
# Delete
|
||||
my_note = Note.query.filter_by(id=id).first()
|
||||
db.session.delete(my_note)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('dashboard', id=id))
|
||||
|
||||
# App
|
||||
if __name__ == "__main__":
|
||||
app.secret_key = 'secret'
|
||||
app.run()
|
32
src/database.py
Normal file
32
src/database.py
Normal file
@ -0,0 +1,32 @@
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(100), unique=True)
|
||||
password = db.Column(db.String(32))
|
||||
|
||||
def __init__(self, email, password):
|
||||
self.email = email
|
||||
self.password = password
|
||||
|
||||
def __repr__(self):
|
||||
return '<User {email}>'.format(email=self.email)
|
||||
|
||||
|
||||
class Note(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(100), unique=True)
|
||||
text = db.Column(db.Text())
|
||||
|
||||
def __init__(self, title, text):
|
||||
self.title = title
|
||||
self.text = text
|
||||
|
||||
def __repr__(self):
|
||||
return '<Note {title}>'.format(title=self.title)
|
62
src/templates/items/dashboard.html
Normal file
62
src/templates/items/dashboard.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends 'layouts/master.html' %}
|
||||
{% block contain %}
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<form action="{{ url_for('search') }}" method="get">
|
||||
<div class="input-group">
|
||||
<input name="q" type="text" class="form-control" placeholder="Search for...">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="submit">Go!</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-8 text-right">
|
||||
<strong>{{ session['user_email'] }}</strong>
|
||||
<a href="{{ url_for('logout') }}" class="btn btn-primary">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<a href="{{ url_for('new') }}" class="btn btn-success">New</a>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{%- if data.main_note -%}
|
||||
<a href="{{ url_for('edit', id=data.main_note.id) }}" class="btn btn-warning">Edit</a>
|
||||
<a href="{{ url_for('delete', id=data.main_note.id) }}" class="btn btn-danger">Delete</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<div class="list-group">
|
||||
{% for note in data.notes %}
|
||||
<a href="{{ url_for('dashboard', id=note.id) }}" class="list-group-item">
|
||||
<h4 class="list-group-item-heading">{{ data.markdown(note.title)|safe }}</h4>
|
||||
<p class="list-group-item-text">
|
||||
{%- if note.text|length > 140 -%}
|
||||
{{ data.markdown(note.text[:140] + '...')|safe }}
|
||||
{% else %}
|
||||
{{ data.markdown(note.text)|safe }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
{%- if data.main_note -%}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h3>
|
||||
{{ data.markdown(data.main_note.title)|safe }}
|
||||
</h3>
|
||||
{{ data.markdown(data.main_note.text)|safe }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
21
src/templates/items/delete.html
Normal file
21
src/templates/items/delete.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends 'layouts/master.html' %}
|
||||
{% block contain %}
|
||||
<div class="row">
|
||||
<div class="col-sm-6"><h4>Do you want to delete this note?</h4></div>
|
||||
<div class="col-sm-6 text-right">
|
||||
<a href="{{ url_for('dashboard') }}" class="btn btn-danger">No, please!</a>
|
||||
<a href="{{ url_for('delete_note', id=data.main_note.id) }}" class="btn btn-success">Yes</a>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<h3>
|
||||
{{ data.markdown(data.main_note.title)|safe }}
|
||||
</h3>
|
||||
{{ data.markdown(data.main_note.text)|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
24
src/templates/items/edit.html
Normal file
24
src/templates/items/edit.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends 'layouts/master.html' %}
|
||||
{% block contain %}
|
||||
<div class="row text-right">
|
||||
<strong>{{ session['user_email'] }}</strong>
|
||||
<a href="{{ url_for('logout') }}" class="btn btn-primary">Logout</a>
|
||||
</div>
|
||||
<br/>
|
||||
<form action="{{ url_for('edit_note') }}" method="post">
|
||||
<input name="id" type="hidden" value="{{ data.main_note.id }}"/>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<a href="{{ url_for('dashboard') }}" class="btn btn-danger">Back</a>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<button type="submit" class="btn btn-success">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<input type="text" name="title" class="form-control" required value="{{ data.main_note.title }}">
|
||||
<textarea name="text" class="form-control" rows="10" required>{{ data.main_note.text }}</textarea>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
21
src/templates/items/login.html
Normal file
21
src/templates/items/login.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends 'layouts/master.html' %}
|
||||
{% block contain %}
|
||||
{% if data['error'] %}
|
||||
<div class="row">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error</strong> Email or password incorrect
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<form method="post" action="{{ url_for('login') }}">
|
||||
<div class="form-group">
|
||||
<input type="email" class="form-control" id="email" placeholder="Email" name="email" value="{{ data['email'] }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" id="password" placeholder="Password" name="password" value="{{ data['password'] }}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default center-block">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
23
src/templates/items/new.html
Normal file
23
src/templates/items/new.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends 'layouts/master.html' %}
|
||||
{% block contain %}
|
||||
<div class="row text-right">
|
||||
<strong>{{ session['user_email'] }}</strong>
|
||||
<a href="{{ url_for('logout') }}" class="btn btn-primary">Logout</a>
|
||||
</div>
|
||||
<br/>
|
||||
<form action="{{ url_for('save_note') }}" method="post">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<a href="{{ url_for('dashboard') }}" class="btn btn-danger">Back</a>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
<button type="submit" class="btn btn-success">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<input type="text" name="title" class="form-control" placeholder="Title..." required>
|
||||
<textarea name="text" class="form-control" rows="10" placeholder="Your text..." required></textarea>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
22
src/templates/layouts/master.html
Normal file
22
src/templates/layouts/master.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
|
||||
<link href="https://bootswatch.com/flatly/bootstrap.min.css" rel="stylesheet">
|
||||
<title>Flask-note</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1 class="text-center">
|
||||
<small>Flask-note</small>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
{% block contain %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user