From 8200a97a6a337b57223a7f270a8c449b75bd9066 Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Tue, 17 Feb 2015 19:38:59 -0500 Subject: [PATCH 1/8] initial commit --- schema.sql | 5 ++ static/style.css | 18 +++++++ templates/layout.html | 17 +++++++ templates/login.html | 14 ++++++ templates/show_entries.html | 20 ++++++++ to_do.py | 95 +++++++++++++++++++++++++++++++++++++ 6 files changed, 169 insertions(+) create mode 100644 schema.sql create mode 100644 static/style.css create mode 100644 templates/layout.html create mode 100644 templates/login.html create mode 100644 templates/show_entries.html create mode 100644 to_do.py diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..e4638ae --- /dev/null +++ b/schema.sql @@ -0,0 +1,5 @@ +drop table if exists entries; +create table entries ( + id integer primary key autoincrement, + todo text not null +); diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..671e9df --- /dev/null +++ b/static/style.css @@ -0,0 +1,18 @@ +body { font-family: sans-serif; background: #eee; } +a, h1, h2 { color: #377ba8; } +h1, h2 { font-family: 'Georgia', serif; margin: 0; } +h1 { border-bottom: 2px solid #eee; } +h2 { font-size: 1.2em; } + +.page { margin: 2em auto; width: 35em; border: 5px solid #ccc; + padding: 0.8em; background: white; } +.entries { list-style: none; margin: 0; padding: 0; } +.entries li { margin: 0.8em 1.2em; } +.entries li h2 { margin-left: -1em; } +.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } +.add-entry dl { font-weight: bold; } +.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; + margin-bottom: 1em; background: #fafafa; } +.flash { background: #cee5F5; padding: 0.5em; + border: 1px solid #aacbe2; } +.error { background: #f0d6d6; padding: 0.5em; } diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..3bef6ff --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,17 @@ + +To Do App + +
+

To Dos

+
+ {% if not session.logged_in %} + log in + {% else %} + log out + {% endif %} +
+ {% for message in get_flashed_messages() %} +
{{ message }}
+ {% endfor %} + {% block body %}{% endblock %} +
diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..6f70bb7 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,14 @@ +{% extends "layout.html" %} +{% block body %} +

Login

+ {% if error %}

Error: {{ error }}{% endif %} +

+
+
Username: +
+
Password: +
+
+
+
+{% endblock %} diff --git a/templates/show_entries.html b/templates/show_entries.html new file mode 100644 index 0000000..32ff328 --- /dev/null +++ b/templates/show_entries.html @@ -0,0 +1,20 @@ +{% extends "layout.html" %} +{% block body %} + {% if session.logged_in %} +
+
+
Create a new to do: +
+ +
+
+ {% endif %} + +
+ {% for entry in entries %} + {{ entry.todo|safe }}
+ {% else %} +
  • Clean State + {% endfor %} + +{% endblock %} diff --git a/to_do.py b/to_do.py new file mode 100644 index 0000000..98d6ffe --- /dev/null +++ b/to_do.py @@ -0,0 +1,95 @@ +# all the imports +import sqlite3 +from flask import Flask, request, session, g, redirect, url_for, \ + abort, render_template, flash +from contextlib import closing + +# configuration +DATABASE = '/tmp/to_do.db' +DEBUG = True +SECRET_KEY = 'this is hard' +USERNAME = 'admin' +PASSWORD = 'default' + +# create our little application :) +app = Flask(__name__) +app.config.from_object(__name__) + +# initializes the database +def init_db(): + with closing(connect_db()) as db: + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() + +# Connects to the database +def connect_db(): + return sqlite3.connect(app.config['DATABASE']) + +# Special functions to be used before and after requests. +# g is a special object that stores connection info + +@app.before_request +def before_request(): + g.db = connect_db() + +@app.teardown_request +def teardown_request(exception): + db = getattr(g, 'db', None) + if db is not None: + db.close() + +# Shows all entries in micronlog + +@app.route('/') +def show_entries(): + cur = g.db.execute('select todo from entries order by id desc') + entries = [dict(todo=row[0]) for row in cur.fetchall()] + return render_template('show_entries.html', entries=entries) + +# Adds an etry to microblog if user is logged in + +@app.route('/add', methods=['POST']) +def add_entry(): + if not session.get('logged_in'): + abort(401) + g.db.execute('insert into entries (todo) values (?)', [request.form['todo']]) + g.db.commit() + flash('New to-do was successfully posted') + return redirect(url_for('show_entries')) + +@app.route('/delete', methods=['POST']) +def delete_entry(): + if not session.get('logged_in'): + abort(401) + for checked in [request.form['selection']]: + g.db.execute('delete from entries where todo = (?)',checked) + g.db.commit() + flash('To-dos have been updated') + return redirect(url_for('show_entries')) + +# Login management +@app.route('/login', methods=['GET', 'POST']) +def login(): + error = None + if request.method == 'POST': + if request.form['username'] != app.config['USERNAME']: + error = 'Invalid username' + elif request.form['password'] != app.config['PASSWORD']: + error = 'Invalid password' + else: + session['logged_in'] = True + flash('You were logged in') + return redirect(url_for('show_entries')) + return render_template('login.html', error=error) + +# Logout. using pop removes the user entry. No need to check if user loggedin +@app.route('/logout') +def logout(): + session.pop('logged_in', None) + flash('You were logged out') + return redirect(url_for('show_entries')) + +# to start the application +if __name__ == '__main__': + app.run() From fc7e9c48349b2b1acd891a88d38665277233b995 Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Tue, 17 Feb 2015 20:22:17 -0500 Subject: [PATCH 2/8] working minus delete --- templates/show_entries.html | 1 + to_do.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/templates/show_entries.html b/templates/show_entries.html index 32ff328..8ef428c 100644 --- a/templates/show_entries.html +++ b/templates/show_entries.html @@ -10,6 +10,7 @@ {% endif %} +
    {% for entry in entries %} {{ entry.todo|safe }}
    diff --git a/to_do.py b/to_do.py index 98d6ffe..ddbe34a 100644 --- a/to_do.py +++ b/to_do.py @@ -58,12 +58,13 @@ def add_entry(): flash('New to-do was successfully posted') return redirect(url_for('show_entries')) + @app.route('/delete', methods=['POST']) -def delete_entry(): +def delete_entry(entry_id): if not session.get('logged_in'): abort(401) - for checked in [request.form['selection']]: - g.db.execute('delete from entries where todo = (?)',checked) + for checked in [request_form['selection']]: + g.db.execute('delete from entries where id =',checked) g.db.commit() flash('To-dos have been updated') return redirect(url_for('show_entries')) From f2f3996feb1a898d1f1f898678340eddee91a573 Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Tue, 17 Feb 2015 23:16:13 -0500 Subject: [PATCH 3/8] final html code --- templates/show_entries.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/show_entries.html b/templates/show_entries.html index 8ef428c..2520519 100644 --- a/templates/show_entries.html +++ b/templates/show_entries.html @@ -13,7 +13,7 @@ {% for entry in entries %} - {{ entry.todo|safe }}
    + {{entry[1]}}
    {% else %}
  • Clean State {% endfor %} From 251b005018d097d1ad0670bfa28c20cfdd15e2d9 Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Tue, 17 Feb 2015 23:16:57 -0500 Subject: [PATCH 4/8] final working version with delete" --- to_do.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/to_do.py b/to_do.py index ddbe34a..064ca8f 100644 --- a/to_do.py +++ b/to_do.py @@ -43,8 +43,8 @@ def teardown_request(exception): @app.route('/') def show_entries(): - cur = g.db.execute('select todo from entries order by id desc') - entries = [dict(todo=row[0]) for row in cur.fetchall()] + cur = g.db.execute('select id, todo from entries order by id desc') + entries = [(row[0],row[1]) for row in cur.fetchall()] return render_template('show_entries.html', entries=entries) # Adds an etry to microblog if user is logged in @@ -60,11 +60,11 @@ def add_entry(): @app.route('/delete', methods=['POST']) -def delete_entry(entry_id): +def delete_entry(): if not session.get('logged_in'): abort(401) - for checked in [request_form['selection']]: - g.db.execute('delete from entries where id =',checked) + for checked in request.form.getlist('entry') : + g.db.execute('delete from entries where id =?',checked) g.db.commit() flash('To-dos have been updated') return redirect(url_for('show_entries')) From a49601c2d598d906fac9d5df28a77f547d16e96a Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Wed, 18 Feb 2015 23:19:08 -0500 Subject: [PATCH 5/8] sqlalchemy+ordered_lists+highlight of past due to-dos --- to_do.py | 81 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/to_do.py b/to_do.py index 064ca8f..e08421f 100644 --- a/to_do.py +++ b/to_do.py @@ -1,60 +1,61 @@ -# all the imports -import sqlite3 +from datetime import datetime from flask import Flask, request, session, g, redirect, url_for, \ abort, render_template, flash from contextlib import closing -# configuration -DATABASE = '/tmp/to_do.db' +from flask.ext.sqlalchemy import SQLAlchemy +from flask.ext.script import Manager +from flask.ext.migrate import Migrate, MigrateCommand +from flask.ext.script.commands import ShowUrls, Clean + + +DATABASE = '/tmp/todo.db' DEBUG = True SECRET_KEY = 'this is hard' -USERNAME = 'admin' -PASSWORD = 'default' +SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE -# create our little application :) app = Flask(__name__) app.config.from_object(__name__) +db = SQLAlchemy(app) +migrate = Migrate(app,db) +manager = Manager(app) +manager.add_command('db',MigrateCommand) -# initializes the database -def init_db(): - with closing(connect_db()) as db: - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() -# Connects to the database -def connect_db(): - return sqlite3.connect(app.config['DATABASE']) -# Special functions to be used before and after requests. -# g is a special object that stores connection info +class Todo(db.Model): + id = db.Column(db.Integer, primary_key=True) + todo = db.Column(db.String(255), nullable=False) + completed_at = db.Column(db.DateTime) + due_date = db.Column(db.DateTime) -@app.before_request -def before_request(): - g.db = connect_db() + def __init__(self, text,due_date): + self.todo = text + self.due_date = due_date -@app.teardown_request -def teardown_request(exception): - db = getattr(g, 'db', None) - if db is not None: - db.close() - -# Shows all entries in micronlog + def __repr__(self): + return "".format(self.text) @app.route('/') def show_entries(): - cur = g.db.execute('select id, todo from entries order by id desc') - entries = [(row[0],row[1]) for row in cur.fetchall()] - return render_template('show_entries.html', entries=entries) + current_todo_list = Todo.query.filter(Todo.completed_at==None).order_by(Todo.due_date.desc()).all() + completed_todo_list = Todo.query.filter(Todo.completed_at!=None).order_by(Todo.completed_at.desc()).all() + past_due_list = [elem for elem in current_todo_list if elem.due_date < datetime.utcnow()] + return render_template('show_entries.html', + entries=current_todo_list, + completed=completed_todo_list, + pastdue = past_due_list) -# Adds an etry to microblog if user is logged in @app.route('/add', methods=['POST']) def add_entry(): if not session.get('logged_in'): abort(401) - g.db.execute('insert into entries (todo) values (?)', [request.form['todo']]) - g.db.commit() + text = request.form['todo'] + duedate = request.form['due_date'] + todo = Todo(text, datetime.strptime(duedate, '%Y-%m-%d')) + db.session.add(todo) + db.session.commit() flash('New to-do was successfully posted') return redirect(url_for('show_entries')) @@ -63,12 +64,16 @@ def add_entry(): def delete_entry(): if not session.get('logged_in'): abort(401) - for checked in request.form.getlist('entry') : - g.db.execute('delete from entries where id =?',checked) - g.db.commit() + ids = request.form.getlist('entry') + for id in ids: + todo = Todo.query.get(id) + todo.completed_at = datetime.utcnow() + db.session.add(todo) + db.session.commit() flash('To-dos have been updated') return redirect(url_for('show_entries')) + # Login management @app.route('/login', methods=['GET', 'POST']) def login(): @@ -93,4 +98,4 @@ def logout(): # to start the application if __name__ == '__main__': - app.run() + manager.run() From ac4c0cae9802dea272cf2720dc6693820f15b044 Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Wed, 18 Feb 2015 23:19:43 -0500 Subject: [PATCH 6/8] updated htmls --- templates/show_entries.html | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/templates/show_entries.html b/templates/show_entries.html index 2520519..f7c4a31 100644 --- a/templates/show_entries.html +++ b/templates/show_entries.html @@ -4,18 +4,38 @@
    Create a new to do: -
    +
    + +
    +
    + + +
    {% endif %} - +

    All To Do's

    +

    To Do Due Date

    - {% for entry in entries %} - {{entry[1]}}
    + {% for todo in entries %} + {% if todo in pastdue %}{% endif %} + +
    + {% if todo in pastdue %}
    {% endif %} {% else %}
  • Clean State {% endfor %} - + +
  • +

    Completed To Do's

    +
      + {% for todo in completed %} +
    • + {{todo.todo}} {{todo.completed_at.strftime('%m/%d/%Y') }} +
    • + {% endfor %} +
    + {% endblock %} From 82d96f4ae44fadda07eef3fc35d05fa1bc703a7b Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Thu, 19 Feb 2015 10:08:43 -0500 Subject: [PATCH 7/8] added completed in separate page --- to_do.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/to_do.py b/to_do.py index e08421f..793f981 100644 --- a/to_do.py +++ b/to_do.py @@ -39,11 +39,9 @@ def __repr__(self): @app.route('/') def show_entries(): current_todo_list = Todo.query.filter(Todo.completed_at==None).order_by(Todo.due_date.desc()).all() - completed_todo_list = Todo.query.filter(Todo.completed_at!=None).order_by(Todo.completed_at.desc()).all() past_due_list = [elem for elem in current_todo_list if elem.due_date < datetime.utcnow()] return render_template('show_entries.html', entries=current_todo_list, - completed=completed_todo_list, pastdue = past_due_list) @@ -60,8 +58,8 @@ def add_entry(): return redirect(url_for('show_entries')) -@app.route('/delete', methods=['POST']) -def delete_entry(): +@app.route('/mark_completed', methods=['POST']) +def complete_entry(): if not session.get('logged_in'): abort(401) ids = request.form.getlist('entry') @@ -73,6 +71,13 @@ def delete_entry(): flash('To-dos have been updated') return redirect(url_for('show_entries')) +@app.route('/completed', methods=['GET','POST']) +def show_completed(): + if not session.get('logged_in'): + abort(401) + completed_todo_list = Todo.query.filter(Todo.completed_at!=None).order_by(Todo.completed_at.desc()).all() + return render_template('completed.html', + completed=completed_todo_list) # Login management @app.route('/login', methods=['GET', 'POST']) From 30542e037b33c12602914520c6833f99023fa66b Mon Sep 17 00:00:00 2001 From: Ana Maria Echeverri Date: Thu, 19 Feb 2015 10:08:58 -0500 Subject: [PATCH 8/8] new completed html --- templates/completed.html | 15 +++++++++++++++ templates/show_entries.html | 36 ++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 templates/completed.html diff --git a/templates/completed.html b/templates/completed.html new file mode 100644 index 0000000..2c9e335 --- /dev/null +++ b/templates/completed.html @@ -0,0 +1,15 @@ +{% extends "layout.html" %} +{% block body %} + {% if session.logged_in %} + + +

    Completed To Do's

    +
      + {% for todo in completed %} +
    • + {{todo.todo}}          {{todo.completed_at.strftime('%m/%d/%Y') }} +
    • + {% endfor %} +
    + {% endif %} +{% endblock %} diff --git a/templates/show_entries.html b/templates/show_entries.html index f7c4a31..63d1f50 100644 --- a/templates/show_entries.html +++ b/templates/show_entries.html @@ -8,34 +8,30 @@
    - - - - {% endif %} +
    + +
    + +
    + + -

    All To Do's

    -

    To Do Due Date

    -
    +

    All To Do's

    +

    To Do          Due Date

    + {% for todo in entries %} - {% if todo in pastdue %}{% endif %} + {% if todo in pastdue %}{% endif %} -
    - {% if todo in pastdue %}
    {% endif %} +
    + {% if todo in pastdue %}
    {% endif %} {% else %}
  • Clean State {% endfor %} - -
  • -

    Completed To Do's

    -
      - {% for todo in completed %} -
    • - {{todo.todo}} {{todo.completed_at.strftime('%m/%d/%Y') }} -
    • - {% endfor %} -
    +
    + + {% endif %} {% endblock %}