Commits

danjac  committed fb7b364

form changes

  • Participants
  • Parent commits 8a7b7ab

Comments (0)

Files changed (12)

File app.py

-import settings
-
-from foodoptimizr import create_app
-
-app = create_app(settings)
-
-if __name__ == "__main__":
-    app.run(debug=True)

File foodoptimizr/__init__.py

 from foodoptimizr.models import User, db
 
 DEBUG = True
+SECRET_KEY = 'secret'
 SQLALCHEMY_DATABASE_URI = "sqlite:///foodoptimizer.db"
-SQLALCHEMY_ECHO = True
+SQLALCHEMY_ECHO = False
 
 def create_app(settings=None):
 

File foodoptimizr/forms.py

-from flaskext.wtf import Form, TextField, PasswordField, SubmitField, \
-        required
+from flaskext.wtf import Form, TextField, PasswordField, IntegerField, \
+        FloatField, DateField, BooleanField, SelectField, SubmitField, \
+        HiddenField, SelectField, RadioInput, FormField, FieldList, \
+        ValidationError, required, email, equal_to
+
+from wtforms.widgets import Input
+
+from foodoptimizr.models import User
+
+class DateInput(Input):
+    input_type = "date"
+
+
+class NumberInput(Input):
+    input_type = "number"
 
 
 class LoginForm(Form):
 
-    login = TextField("Username or email address", validators=[required()])
+    next = HiddenField()
+
+    email = TextField("Email address", validators=[required()])
     password = PasswordField("Password", validators=[required()])
+    submit = SubmitField("Login")
 
 
 class SignupForm(Form):
 
-    pass
+    next = HiddenField()
+
+    email = TextField("Email address", validators=[email()])
+
+    first_name = TextField("First name", validators=[required()])
+    last_name = TextField("Last name", validators=[required()])
+
+    password = PasswordField("Password", validators=[required()])
+
+    password_rpt = PasswordField("Type your password again", 
+                                 validators=[equal_to("password")])
+
+    weight = FloatField("Current weight", 
+                        widget=NumberInput(),
+                        validators=[required()])
+
+    submit = SubmitField("Sign up")
+
+    def validate_email(self, field):
+        if User.query.filter(User.email==field.data.lower()).count():
+            raise ValidationError, "This email is already taken"
+
+class FoodForm(Form):
+
+    description = TextField("Description", validators=[required()])
+    green_sins = FloatField("Green sins", default=0)
+    red_sins = FloatField("Red sins", default=0)
+    healthy_extra_a = BooleanField("Healthy extra A")
+    healthy_extra_b_green = BooleanField("Healthy extra B green days")
+    healthy_extra_b_red = BooleanField("Healthy extra B red days")
+    submit = SubmitField("Enter")
+
+class ItemForm(Form):
+
+    portions = IntegerField("Portions",
+                            widget=NumberInput(),
+                            validators=[required()])
+
+class EntryForm(Form):
+
+    num_items = HiddenField()
+
+    entered_on = DateField("Date", 
+                           widget=DateInput(), 
+                           validators=[required()])
+
+    weight = FloatField("Weight", 
+                        widget=NumberInput(), 
+                        validators=[required()])
+
+    day = SelectField("Day", 
+                      widget=RadioInput(),
+                      choices=(
+                        ("red", "Red day"),
+                        ("green", "Green day")),
+                      validators=[required()])
+    
+    items = FieldList(FormField(ItemForm))
+
+    submit = SubmitField("Enter")

File foodoptimizr/models.py

-from datetime import datetime
+from datetime import datetime, date
+
+from werkzeug import check_password_hash, generate_password_hash, \
+        cached_property
 
 from flaskext.sqlalchemy import SQLAlchemy, BaseQuery
 
 
 class UserQuery(BaseQuery):
 
-    def authenticate(self, login, password):
+    def authenticate(self, email, password):
 
-        user = self.filter(db.or_(User.username==login,
-                                  User.email==login)).first()
+        user = self.filter(User.email==email).first()
 
-        if user and user.check_password:
+        if user and user.check_password(password):
             return user
 
 
     query_class = UserQuery
 
     id = db.Column(db.Integer, primary_key=True)
-    username = db.Column(db.Unicode(30), unique=True)
+    first_name = db.Column(db.Unicode(30))
+    last_name = db.Column(db.Unicode(30))
     email = db.Column(db.String(200), unique=True)
     joined_on = db.Column(db.DateTime, default=datetime.utcnow)
     weight = db.Column(db.Float)
     current_limit = db.Column(db.Integer, default=0)
     daily_limit_set_on = db.Column(db.DateTime)
+    is_admin = db.Column(db.Boolean, default=False)
 
-    _password = db.Column('password', db.String(800))
+    _password = db.Column('password', db.String(80))
 
     def _set_password(self, password):
         self._password = generate_password_hash(password)
     def _get_password(self):
         return self._password
 
+    password = db.synonym("_password", 
+                          descriptor=property(_get_password, _set_password))
+
     def check_password(self, password):
-        return self.check_password_hash(self.password, password)
+        return check_password_hash(self.password, password)
+
+    @cached_property
+    def daily_limit(self):
+        now = datetime.utcnow()
+
+        # has the daily limit been calculated yet ?
+        if self.daily_limit_set_on is None or \
+            (now - self.daily_limit_set_on).seconds / 3600 > 24:
+            
+            self.current_limit += 15
+            self.daily_limit_set_on = now
+
+            db.session.commit()
+
+        return self.current_limit
+
+
+class Food(db.Model):
+
+    __tablename__ = "foods"
+
+    id = db.Column(db.Integer, primary_key=True)
+    description = db.Column(db.UnicodeText)
+
+    green_sins = db.Column(db.Float, default=0)
+    red_sins = db.Column(db.Float, default=0)
+
+    healthy_extra_a = db.Column(db.Boolean, default=False)
+    healthy_extra_b_green = db.Column(db.Boolean, default=False)
+    healthy_extra_b_red = db.Column(db.Boolean, default=False)
+
+
+class EntryQuery(BaseQuery):
+
+    def for_month(self, month, year):
+
+        return self.filter(db.and_(db.func.month(Entry.entered_on)==month,
+                                   db.func.year(Entry.entered_on)==year))
+
+
+class Entry(db.Model):
+
+    __tablename__ = "entries"
+    
+    query_class = EntryQuery
+
+    id = db.Column(db.Integer, primary_key=True)
+    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
+    entered_on = db.Column(db.Date, default=date.today)
+    day = db.Column(db.Enum("green", "red"))
+    total_sins = db.Column(db.Float)
+    weight = db.Column(db.Float)
+    
+    user = db.relationship('User', 
+                           backref=db.backref('entries', lazy='dynamic',
+                                              query_class=EntryQuery))
 
 
 class Item(db.Model):
     __tablename__ = "items"
 
     id = db.Column(db.Integer, primary_key=True)
-    description = db.Column(db.UnicodeText)
-    green_sins = db.Column(db.Float, default=0)
-    red_sins = db.Column(db.Float, default=0)
-    healthy_extra_a = db.Column(db.Boolean, default=False)
-    healthy_extra_b_green = db.Column(db.Boolean, default=False)
-    healthy_extra_b_red = db.Column(db.Boolean, default=False)
+    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
+    entry_id = db.Column(db.Integer, db.ForeignKey(Entry.id))
+    food_id = db.Column(db.Integer, db.ForeignKey(Food.id))
+    portions = db.Column(db.Integer, default=1)
 
-    _taglist = db.Column("tags", db.UnicodeText)
 
-
-class Entry(db.Model):
-
-    __tablename__ = "entries"
-
-    id = db.Column(db.Integer, primary_key=True)
-    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
-    item_id = db.Column(db.Integer, db.ForeignKey(Item.id))
-    entered_on = db.Column(db.DateTime, default=datetime.utcnow)
-    qty = db.Column(db.Integer, default=1)
-    
-    user = db.relation(User)
-    item = db.relation(Item)
-
-    def calc_num_sins(self, day):
-
-        qty = self.qty
-        healthy_extra = False
-
-        if self.item.healthy_extra_a:
-            healthy_extra = True
-        elif self.item.healthy_extra_b_green and day == "green":
-            healthy_extra = True
-        elif self.item.healthy_extra_b_red and day == "red":
-            healthy_extra = True
-
-        if healthy_extra:
-            qty -= 1
-
-        if day == "red":
-            sins = self.item.red_sins
-        elif day == "green":
-            sins = self.item.green_sins
-
-        sins = sins or 0
-
-        return sins * qty
-
-
-

File foodoptimizr/templates/add_entry.html

 {% block extra_javascripts %}
 <script type="text/javascript">
     $(document).ready(function(){
-        var data = ["Cheese (28g)", "Bread (1 slice)", "Bacon (50g)", "Mushrooms", "Carrots", "Full-fat milk (1l)"]
+        var data = [
+        {
+            value : 1, 
+            label : "Cheese (28g)"
+        }, 
+        {
+            value : 2,
+            label : "Bread (1 slice)"
+        },
+        {
+            value : 3,
+            label : "Bacon (50)"
+            }
+        ]
         $('#food_search').autocomplete({ source : data});        
     });
 </script>

File foodoptimizr/templates/index.html

 
 <div class="span-24">
     <div class="span-12">
-        <h3>Your current limit: <b>12</b> sins</h3>
+        <h3>Your current limit: <b>{{ g.user.daily_limit }}</b> sins</h3>
     </div>
     <div class="span-12 last">
-        <h3>Your current weight: <b>120</b> kg</h3>
+        <h3>Your current weight: <b>{{ g.user.weight }}</b> kg</h3>
     </div>
 </div>
 
 <div class="span-24 navigation calendar">
     <ul>
         <li class="previous">
-        	<a href="">&lt;&lt; October 2010</a>
+        <a href="">&lt;&lt; {{ last_month.strftime('%B %Y') }}</a>
         </li>
         <li class="current selected">
-        <a href-"">November 2010</a>
+        <a href-"">{{ today.strftime('%B %Y') }}</a>
         </li>
         <li class="following">
-        	<a href="">December 2010 &gt;&gt;</a>
+        <a href="">{{ next_month.strftime('%B %Y') }} &gt;&gt;</a>
         </li>
     </ul>
 </div>

File foodoptimizr/templates/layout.html

             </div>
             <div class="span-12 last">
                 <ul class="topnav">
-                    <li><a href="">login</a></li>
-                    <li><a href="">signup</a></li>
+                    {% if g.user %}
+                    <li>You are logged in as {{ g.user.first_name }}</li>
+                    <li><a href="{{ url_for('auth.logout') }}">logout</a></li>
+                    {% else %}
+                    <li><a href="{{ url_for('auth.login') }}">login</a></li>
+                    <li><a href="{{ url_for('auth.signup') }}">signup</a></li>
+                    {% endif %}
                     <li><a href="">contact us</a></li>
                     <li><a href="">about</a></li>
                 </ul>
         </div>
 
         <div class="span-24 content">
+
         {% block content %}
         {% endblock %}
         </div><!-- end of content -->

File foodoptimizr/templates/login.html

-Please login with your Google username/email and password !
+{% extends "layout.html" %}
+
+{% block content %}
+<h2>Login</h2>
+<form method="POST" action=".">
+    {{ form.hidden_tag() }}
+    <div class="span-24">
+    <fieldset class="span-12">
+        {{ form.email.label }}<br>
+        {{ form.email(size=40) }}<br>
+        {{ form.password.label }}<br>
+        {{ form.password }}
+    </fieldset>
+</div>
+    <div class="submit">
+        {{ form.submit }}
+    </div>
+</form>
+{% endblock %}

File foodoptimizr/templates/signup.html

+
+{% extends "layout.html" %}
+
+{% block content %}
+<h2>Signup</h2>
+<form method="POST" action=".">
+    {{ form.hidden_tag() }}
+    <div class="span-24">
+    <fieldset class="span-12">
+        {{ form.email.label }}<br>
+        {{ form.email(size=40) }}<br>
+    </fieldset>
+    <fieldset class="span-12">
+        <div class="span-6">
+        {{ form.first_name.label }}<br>
+        {{ form.first_name }}
+        </div>
+        <div class="span-6 last">
+        {{ form.last_name.label }}<br>
+        {{ form.last_name }}
+        </div>
+    </fieldset>
+    <fieldset class="span-12">
+        <div class="span-6">
+        {{ form.password.label }}<br>
+        {{ form.password }}<br>
+    </div>
+        <div class="span-6 last">
+        {{ form.password_rpt.label }}<br>
+        {{ form.password_rpt }}
+    </div>
+    </fieldset>
+    <fieldset class="span-12">
+        {{ form.weight.label }}
+        {{ form.weight(size=5) }}
+    </fieldset>
+</div>
+    <div class="submit">
+        {{ form.submit }}
+    </div>
+</form>
+{% endblock %}

File foodoptimizr/views/auth.py

-from flask import Module, render_template, session, redirect, url_for, flash
+from flask import Module, render_template, session, redirect, url_for, \
+        flash, request
 
 from foodoptimizr.forms import LoginForm, SignupForm
 from foodoptimizr.models import User, db
 
 auth = Module(__name__)
 
-@auth.route("/login/")
+@auth.route("/login/", methods=("GET", "POST"))
 def login():
 
     form = LoginForm(next=request.args.get('next'))
 
     if form.validate_on_submit():
         
-        user = User.query.authenticate(form.login, form.password)
+        user = User.query.authenticate(form.email.data, 
+                                       form.password.data)
         if user:
 
             session['user_id'] = user.id
-            flash("Welcome back, %s" % user.username)
+            flash("Welcome back, %s" % user.first_name)
 
-            next_url = form.next or url_for('main.index')
+            next_url = form.next.data or url_for('main.index')
 
             return redirect(next_url)
 
     return render_template("login.html", form=form)
 
 
-@auth.route("/signup/")
+@auth.route("/logout/")
+def logout():
+    session.pop('user_id', None)
+    flash("Thanks for visiting")
+    return redirect(url_for("auth.login"))
+
+
+@auth.route("/signup/", methods=("GET", "POST"))
 def signup():
 
     form = SignupForm(next=request.args.get("next"))
         db.session.add(user)
         db.session.commit()
 
-        flash("Welcome, %s" % user.username)
+        session['user_id'] = user.id
+        
+        flash("Welcome, %s" % user.first_name)
 
-        next_url = form.next or url_for('main.index')
+        next_url = form.next.data or url_for('main.index')
         return redirect(next_url)
 
     return render_template("signup.html", form=form)

File foodoptimizr/views/main.py

-from flask import Module, render_template
+from datetime import date
+from dateutil.relativedelta import relativedelta
+
+from flask import Module, render_template, g
+
+from foodoptimizr.decorators import login_required
 
 main = Module(__name__)
 
+@main.route("/<int:month>/<int:year>/")
 @main.route("/")
-def index():
-    return render_template("index.html")
+@login_required
+def index(month=None, year=None):
+
+    if month is None or year is None:
+        today = date.today()
+    else:
+        today = date(day=1, month=month, year=year)
+
+    last_month = today + relativedelta(months=-1)
+    next_month = today + relativedelta(months=1)
+
+    entries = g.user.entries.for_month(month, year)
+
+    return render_template("index.html", 
+                           today=today,
+                           last_month=last_month,
+                           next_year=next_year,
+                           entries=entries)
 
 
 @main.route("/add-food/")
+import settings
+
+from flaskext.script import Manager, prompt_bool
+
+from foodoptimizr import create_app
+from foodoptimizr.models import db
+
+manager = Manager(lambda: create_app(settings))
+
+@manager.command
+def resetdb():
+    if prompt_bool("Are you sure you want to lose all your data"):
+        db.drop_all()
+        db.create_all()
+
+if __name__ == "__main__":
+    manager.run()