summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2013-04-11 01:14:49 +0200
committerRené 'Necoro' Neumann <necoro@necoro.net>2013-04-11 01:14:49 +0200
commit0567318344330295512176569a84afc9748d79c5 (patch)
treeb2ccd2e5ba3e254a945143060f31bdd4d42c921c
parent5bf8a03dae494f1625d3d5eeee5ffd6396b730fa (diff)
downloadkosten-0567318344330295512176569a84afc9748d79c5.tar.gz
kosten-0567318344330295512176569a84afc9748d79c5.tar.bz2
kosten-0567318344330295512176569a84afc9748d79c5.zip
First part of the transition to flask
Diffstat (limited to '')
-rw-r--r--__init__.py2
-rw-r--r--app/__init__.py13
-rw-r--r--app/model.py (renamed from model.py)104
-rw-r--r--app/views.py32
-rwxr-xr-xindex.py75
-rw-r--r--settings.py11
-rw-r--r--templates/menu.jinja7
-rw-r--r--templates/menu.mako8
-rw-r--r--templates/root.jinja56
-rw-r--r--templates/root.mako56
10 files changed, 179 insertions, 185 deletions
diff --git a/__init__.py b/__init__.py
index c3189d1..d099b92 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1 @@
-from index import app
+from app import app
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..6ba7025
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,13 @@
+from flask import Flask
+
+# create app
+app = Flask("kosten")
+
+# force autoescape in all files
+app.jinja_env.autoescape = True
+
+# load config
+app.config.from_pyfile("settings.py")
+
+from .model import db
+from . import views
diff --git a/model.py b/app/model.py
index 7a07ff0..92b3710 100644
--- a/model.py
+++ b/app/model.py
@@ -1,68 +1,63 @@
-from sqlalchemy import types as T
-from sqlalchemy import sql, Index, Column, ForeignKey, create_engine
-from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker,\
- column_property
-from sqlalchemy.ext.declarative import declarative_base, declared_attr
-from sqlalchemy.ext.hybrid import hybrid_property
+from flask.ext.sqlalchemy import SQLAlchemy
+from sqlalchemy import sql
+from sqlalchemy.ext.declarative import declared_attr
import datetime
import decimal
from functools import partial
from collections import namedtuple
-__all__ = ["Category", "SingleExpense", "ConstExpense", "CatExpense", "MonthExpense",
- "Session"]
+from . import app
-#
-# DB Setup
-#
+db = SQLAlchemy(app)
-engine = create_engine("sqlite:///test.sqlite")
-engine.echo = True
+__all__ = ["db", "Category", "SingleExpense", "ConstExpense", "CatExpense", "MonthExpense"]
-Session = scoped_session(sessionmaker(bind=engine))
+ReqColumn = partial(db.Column, nullable = False)
+ExpNum = db.Numeric(scale = 2, precision = 10)
+
+def to_exp(d):
+ """Converts decimal into expense"""
+ return d.quantize(decimal.Decimal('.01'), rounding = decimal.ROUND_UP)
#
-# Global definitions
-#
+# Database Entities
+class Model (db.Model):
+ __abstract__ = True
+
+ id = db.Column(db.Integer, primary_key=True)
-class Base(object):
@declared_attr
def __tablename__ (cls):
return cls.__name__.lower()
- id = Column(T.Integer, primary_key=True)
-
- query = Session.query_property()
-
@classmethod
def get_by (cls, *args, **kwargs):
return cls.query.filter_by(*args, **kwargs).first()
@classmethod
+ def get_by_or_404 (cls, *args, **kwargs):
+ return cls.query.filter_by(*args, **kwargs).first_or_404()
+
+ @classmethod
def get (cls, *args, **kwargs):
return cls.query.get(*args, **kwargs)
+ @classmethod
+ def get_or_404 (cls, *args, **kwargs):
+ return cls.query.get_or_404(*args, **kwargs)
-Base = declarative_base(cls=Base)
-
-ReqColumn = partial(Column, nullable = False)
-ExpNum = T.Numeric(scale = 2, precision = 10)
-
-def to_exp(d):
- """Converts decimal into expense"""
- return d.quantize(decimal.Decimal('.01'), rounding = decimal.ROUND_UP)
-
-#
-# Database Entities
-#
+class Category (Model):
+ name = ReqColumn(db.Unicode(50), unique = True)
+ parent_id = db.Column(db.Integer, db.ForeignKey('category.id'))
-class Category (Base):
- name = ReqColumn(T.Unicode(50), unique = True)
- parent_id = Column(T.Integer, ForeignKey('category.id'))
+ children = db.relationship('Category',
+ backref=db.backref('parent', remote_side="Category.id"))
- children = relationship('Category',
- backref=backref('parent', remote_side="Category.id"))
+ def __init__ (self, name, parent_id = None):
+ Model.__init__(self)
+ self.name = name
+ self.parent_id = parent_id
def __repr__ (self):
if self.parent:
@@ -70,24 +65,24 @@ class Category (Base):
else:
return '<Category "%s">' % self.name
-class Expense (Base):
+class Expense (Model):
__abstract__ = True
- description = Column(T.Unicode(50))
+ description = db.Column(db.Unicode(50))
expense = ReqColumn(ExpNum)
@declared_attr
def category_id(cls):
- return ReqColumn(T.Integer, ForeignKey(Category.id))
+ return ReqColumn(db.Integer, db.ForeignKey(Category.id))
@declared_attr
def category(cls):
- return relationship(Category, innerjoin = True)
+ return db.relationship(Category, innerjoin = True)
class SingleExpense (Expense):
- year = ReqColumn(T.Integer)
- month = ReqColumn(T.SmallInteger)
- day = ReqColumn(T.SmallInteger)
+ year = ReqColumn(db.Integer)
+ month = ReqColumn(db.SmallInteger)
+ day = ReqColumn(db.SmallInteger)
@classmethod
def of_month (cls, month, year):
@@ -108,13 +103,13 @@ class SingleExpense (Expense):
self.day = d.day
class ConstExpense (Expense):
- months = ReqColumn(T.SmallInteger)
- start = ReqColumn(T.Date, index = True)
- end = ReqColumn(T.Date, index = True)
- prev_id = Column(T.Integer, ForeignKey('constexpense.id'))
+ months = ReqColumn(db.SmallInteger)
+ start = ReqColumn(db.Date, index = True)
+ end = ReqColumn(db.Date, index = True)
+ prev_id = db.Column(db.Integer, db.ForeignKey('constexpense.id'))
- prev = relationship('ConstExpense', remote_side = "ConstExpense.id", uselist = False,
- backref=backref('next', uselist = False))
+ prev = db.relationship('ConstExpense', remote_side = "ConstExpense.id", uselist = False,
+ backref=db.backref('next', uselist = False))
@property
def monthly(self):
@@ -168,8 +163,5 @@ class MonthExpense (namedtuple('MonthExpense', 'date catexps')):
# Extra indizes have to be here
#
-Index('idx_single_date', SingleExpense.year, SingleExpense.month)
-Index('idx_start_end', ConstExpense.start, ConstExpense.end)
-
-if __name__ == "__main__":
- Base.metadata.create_all(engine)
+db.Index('idx_single_date', SingleExpense.year, SingleExpense.month)
+db.Index('idx_start_end', ConstExpense.start, ConstExpense.end)
diff --git a/app/views.py b/app/views.py
new file mode 100644
index 0000000..7450946
--- /dev/null
+++ b/app/views.py
@@ -0,0 +1,32 @@
+from flask import render_template, request, url_for
+import flask
+
+from . import app, db
+
+# check for mobile visitors
+mobile_checks = ["J2ME", "Opera Mini"]
+
+@app.before_request
+def handle_mobile():
+ ua = request.environ.get("HTTP_USER_AGENT", "")
+
+ flask.g.is_mobile = any((x in ua) for x in mobile_checks)
+
+
+@app.template_filter("static_url")
+def static_url(s):
+ return url_for("static", filename=s)
+
+
+#@app.errorhandler(404)
+#def page_not_found (error):
+# print request.path
+
+@app.route("/")
+@app.route("/index")
+def index():
+ return render_template("root.jinja")
+
+@app.route("/add")
+def addExp():
+ return render_template("root.jinja")
diff --git a/index.py b/index.py
index d4ec756..c3ad9fa 100755
--- a/index.py
+++ b/index.py
@@ -1,69 +1,16 @@
#!/usr/bin/python
+import sys
-import web
-import controller
-import model
+from app import app, db
-#
-# URL Mappings
-#
-urls = (
- "/add/?", controller.Add,
- "/edit/(\d+)", controller.Edit,
- "/const/?", controller.Const,
- "/const/(\d+)", controller.Const,
- "/const/add/?", controller.ConstAdd,
- "/const/add/from/(\d+)", controller.ConstAdd,
- "/const/edit/(\d+)", controller.ConstEdit,
- "/categories", controller.Cat,
- "/(\d\d\d\d)/(\d\d?)/?", controller.Show,
- "/", controller.Show,
- "/(.*)", controller.FourOhFour
- )
-#
-# ORM
-#
-def handle_sql(handler):
- web.ctx.orm = model.Session()
+try:
+ cmd = sys.argv[1]
+except IndexError:
+ cmd = None
- try:
- try:
- h = handler()
- except web.HTTPError:
- web.ctx.orm.commit()
- raise
- except:
- web.ctx.orm.rollback()
- raise
- else:
- web.ctx.orm.commit()
- return h
- finally:
- model.Session.remove()
-
-#
-# Check for mobile (at least somewhat)
-#
-mobile_checks = ["J2ME", "Opera Mini"]
-def handle_mobile():
- ua = web.ctx.env.get("HTTP_USER_AGENT", "")
-
- web.ctx.is_mobile = any((x in ua) for x in mobile_checks)
-
-#
-# The App
-#
-app = web.application(urls, globals())
-app.notfound = controller.FourOhFour.catch
-
-# add processors
-app.add_processor(handle_sql)
-app.add_processor(web.loadhook(handle_mobile))
-
-# debug for the moment
-web.config.debug = True
-
-#
-# And go!
-if __name__ == "__main__":
+if cmd == "create":
+ db.create_all()
+elif cmd == "drop":
+ db.drop_all()
+else:
app.run()
diff --git a/settings.py b/settings.py
new file mode 100644
index 0000000..0effb96
--- /dev/null
+++ b/settings.py
@@ -0,0 +1,11 @@
+import os
+
+# set this to False on productive systems
+DEBUG = True
+
+# just generate a new secret key on each startup
+SECRET_KEY = os.urandom(42)
+
+# database
+SQLALCHEMY_DATABASE_URI = "sqlite:///test.sqlite"
+SQLALCHEMY_ECHO = True
diff --git a/templates/menu.jinja b/templates/menu.jinja
new file mode 100644
index 0000000..b96d542
--- /dev/null
+++ b/templates/menu.jinja
@@ -0,0 +1,7 @@
+{% set menu = [
+ ("index", "Kosten"),
+ ("addExp", "Neu"),] %}
+ {# ("/const", "Konstante Kosten"),
+ ("/categories", "Kategorien")
+ ]
+#}
diff --git a/templates/menu.mako b/templates/menu.mako
deleted file mode 100644
index 98e5904..0000000
--- a/templates/menu.mako
+++ /dev/null
@@ -1,8 +0,0 @@
-<%!
- menu = [
- ("/", "Kosten"),
- ("/add", "Neu"),
- ("/const", "Konstante Kosten"),
- ("/categories", "Kategorien")
- ]
-%>
diff --git a/templates/root.jinja b/templates/root.jinja
new file mode 100644
index 0000000..7debb6a
--- /dev/null
+++ b/templates/root.jinja
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8" >
+ {% if not g.is_mobile %}
+ {% block js %}
+ <script type="text/javascript" src="{{ "js/jquery-1.4.2.js" | static_url }}"></script>
+ <script type="text/javascript" src="{{ "js/lib.js" | static_url }}"></script>
+ {% endblock %}
+ {% endif %}
+ <link rel="shortcut icon" href="{{ "images/currency.png" | static_url }}" type="image/icon">
+ <title>{% block title -%}Kostenverwaltung{%- endblock %}</title>
+ {% block style %}
+ <link href="{{ "css/style.css" | static_url }}" rel="stylesheet" type="text/css">
+ {% endblock %}
+ </head>
+
+ <body>
+ <div id="wrapper">
+ <div id="logo">
+ <img src="{{ "images/currency.png" | static_url }}">
+ <h1><a href="{{ url_for("index") }}">Kosten</a></h1>
+ <h2>»   vom Nec</h2>
+ </div>
+
+ <div id="header">
+ <div id="menu">
+ {% block menu %}
+ {% import "menu.jinja" as m %}
+ <ul>
+ {% for uri, page in m.menu %}
+ <li><a href="{{ url_for(uri) }}">{{page}}</a></li>
+ {% endfor %}
+ </ul>
+ {% endblock %}
+ </div>
+ </div>
+ </div>
+
+ <div id="page">
+ <div class="content">
+ {% block content %}{% endblock %}
+ </div>
+ <div style="clear: both;"></div>
+ </div>
+ <div id="footer">
+ {% block footer %}
+ <p id="legal">
+ powered by <a href="http://flask.pocoo.org">Flask</a> |
+ © 2010-2013. All Rights Reserved. René Neumann |
+ with the help of <a href="http://www.freecsstemplates.org/">Free CSS Templates</a>.
+ </p>
+ {% endblock %}
+ </div>
+ </body>
+</html>
diff --git a/templates/root.mako b/templates/root.mako
deleted file mode 100644
index 19af96e..0000000
--- a/templates/root.mako
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
- % if not w.ctx.is_mobile:
- <%block name="js">
- <script type="text/javascript" src=${"/static/js/jquery-1.4.2.js" | url}></script>
- <script type="text/javascript" src=${"/static/js/lib.js" | url}></script>
- </%block>
- % endif
- <link rel="shortcut icon" href=${"/static/images/currency.png" | url} type="image/icon">
- <title><%block name="title">Kostenverwaltung</%block></title>
- <%block name="style">
- <link href=${"/static/css/style.css" | url} rel="stylesheet" type="text/css" />
- </%block>
- </head>
-
- <body>
- <div id="wrapper">
- <div id="logo">
- <img src=${"/static/images/currency.png" | url} />
- <h1><a href=${"/" | url}>Kosten</a></h1>
- <h2>»   vom Nec</h2>
- </div>
-
- <div id="header">
- <div id="menu">
- <%block name="menu">
- <%namespace file="menu.mako" name="m" />
- <ul>
- % for uri, page in m.attr.menu:
- <li><a href=${uri | url}>${page}</a></li>
- % endfor
- </ul>
- </%block>
- </div>
- </div>
- </div>
-
- <div id="page">
- <div class="content">
- ${next.body()}
- </div>
- <div style="clear: both;"></div>
- </div>
- <div id="footer">
- <%block name="footer">
- <p id="legal">
- powered by <a href="http://www.makotemplates.org">mako</a> &amp; <a href="http://www.webpy.org">web.py</a> |
- © 2010-2012. All Rights Reserved. René Neumann |
- with the help of <a href="http://www.freecsstemplates.org/">Free CSS Templates</a>.
- </p>
- </%block>
- </div>
- </body>
-</html>