Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
todo text not null
);
18 changes: 18 additions & 0 deletions static/style.css
Original file line number Diff line number Diff line change
@@ -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; }
15 changes: 15 additions & 0 deletions templates/completed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends "layout.html" %}
{% block body %}
{% if session.logged_in %}
<class=entries>
</form>
<h3>Completed To Do's</h3>
<ul>
{% for todo in completed %}
<li>
{{todo.todo}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp {{todo.completed_at.strftime('%m/%d/%Y') }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
17 changes: 17 additions & 0 deletions templates/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<title>To Do App</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>To Dos</h1>
<div class=metanav>
{% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
14 changes: 14 additions & 0 deletions templates/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}
37 changes: 37 additions & 0 deletions templates/show_entries.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{% extends "layout.html" %}
{% block body %}
{% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
<dl>
<dt>Create a new to do:
<dt><label for="textarea">To Do</label><dd>
<textarea name=todo rows=1 cols=40></textarea>
<dt><label for="textarea">Due Date</label>
<dd><input type="date" name=due_date></input>
<input type=submit value=Create width="80" height="80">
</dl>
</form>
<br>

<form action="{{ url_for('show_completed') }}" method=post class=show_completed>
<input type=submit value="See Completed Tasks" width="80" height="80" color='blue'>
<br></form>


<class=entries>
<h2>All To Do's</h2>
<h3> To Do &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp Due Date</h3>
<form action="{{ url_for('complete_entry') }}" method=post class=delete-entry>
{% for todo in entries %}
{% if todo in pastdue %}<strong><font color="red">{% endif %}
<input type="checkbox" name="entry" id="todo-{{ todo.id }}" value="{{ todo.id }}"/>
<label for="todo-{{ todo.id }}">{{ todo.todo}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp {{ todo.due_date.strftime('%m/%d/%Y') }}</label><br>
{% if todo in pastdue %}</strong><font color="black">{% endif %}
{% else %}
<li><em>Clean State</em>
{% endfor %}
<br>
<input type=submit value="Mark as Done" width="80" height="80">

{% endif %}
{% endblock %}
106 changes: 106 additions & 0 deletions to_do.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from datetime import datetime
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash
from contextlib import closing

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'
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE

app = Flask(__name__)
app.config.from_object(__name__)
db = SQLAlchemy(app)
migrate = Migrate(app,db)
manager = Manager(app)
manager.add_command('db',MigrateCommand)



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)

def __init__(self, text,due_date):
self.todo = text
self.due_date = due_date

def __repr__(self):
return "<Todo {}>".format(self.text)

@app.route('/')
def show_entries():
current_todo_list = Todo.query.filter(Todo.completed_at==None).order_by(Todo.due_date.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,
pastdue = past_due_list)


@app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
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'))


@app.route('/mark_completed', methods=['POST'])
def complete_entry():
if not session.get('logged_in'):
abort(401)
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'))

@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'])
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__':
manager.run()