diff --git a/work/036-pyramid-intro/billtracker/billtracker/__init__.py b/work/036-pyramid-intro/billtracker/billtracker/__init__.py
deleted file mode 100644
index 8cc91dc..0000000
--- a/work/036-pyramid-intro/billtracker/billtracker/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from pyramid.config import Configurator
-
-
-def main(global_config, **settings):
- """ This function returns a Pyramid WSGI application.
- """
- with Configurator(settings=settings) as config:
- config.include('pyramid_chameleon')
- config.include('.routes')
- config.scan()
- return config.make_wsgi_app()
diff --git a/work/036-pyramid-intro/billtracker/billtracker/routes.py b/work/036-pyramid-intro/billtracker/billtracker/routes.py
deleted file mode 100644
index 25504ad..0000000
--- a/work/036-pyramid-intro/billtracker/billtracker/routes.py
+++ /dev/null
@@ -1,3 +0,0 @@
-def includeme(config):
- config.add_static_view('static', 'static', cache_max_age=3600)
- config.add_route('home', '/')
diff --git a/work/036-pyramid-intro/billtracker/billtracker/templates/base.pt b/work/036-pyramid-intro/billtracker/billtracker/templates/base.pt
deleted file mode 100644
index a320665..0000000
--- a/work/036-pyramid-intro/billtracker/billtracker/templates/base.pt
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
Pyramid Starter project
+
Bill Tracker
404 Page Not Found
diff --git a/work/037-pyramid-intro/billtracker/billtracker/templates/base.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/base.pt
new file mode 100644
index 0000000..592430b
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/templates/base.pt
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
Bill Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a demo site from our #100DaysOfWeb course. Please don't really track your bills here. ;)
+
+
+
+ Copyright 2018 © Talk Python Training
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work/037-pyramid-intro/billtracker/billtracker/templates/bill_detail.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/bill_detail.pt
new file mode 100644
index 0000000..7b588a7
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/templates/bill_detail.pt
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
Bill for ${bill.description} .
+
${ bill.user.name } / ${ bill.user.email }
+
+
+
+
+
+
+
+ Date created
+
+
+ ${bill.created_date.date().isoformat()}
+
+
+
+
+
+
+ Total amount
+
+
+ ${'${:,.0f}'.format(bill.total)}
+
+
+
+
+
+
+ Amount owed
+
+
+ ${'${:,.0f}'.format(max(bill.total-bill.paid,0))}
+
+
+
+
+
+
+
Make a payment
+
+
+ ${error}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work/037-pyramid-intro/billtracker/billtracker/templates/home.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/home.pt
new file mode 100644
index 0000000..a5c0428
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/templates/home.pt
@@ -0,0 +1,75 @@
+
+
+
+
+
Bill Tracker Pro Demo
+
+
+
+
+
+
+
+
+
Hi ${user.name} .
+ Welcome to Bill Tracker Pro Demo Site .
+
+
+
+
+
+
+
Unpaid bills (total: ${"${:,.0f}".format(user.total_owed)})
+
+
+
+
+
Date
+
Total
+
Still owe
+
+ Category
+
+
+
+
+
+
${b.created_date.date().isoformat()}
+
${"${:,.0f}".format(b.total)}
+
${"${:,.0f}".format(b.total-b.paid)}
+
+ ${b.description}
+
+
+
+
+
+
+
+
Past bills (total: ${"${:,.0f}".format(user.total_paid_off)})
+
+
+
+
+
Date
+
Total
+
+ Category
+
+
+
+
+
+
${b.created_date.date().isoformat()}
+
${"${:,.0f}".format(b.total)}
+
+ ${b.description}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/work/037-pyramid-intro/billtracker/billtracker/templates/login.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/login.pt
new file mode 100644
index 0000000..a08d5e5
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/templates/login.pt
@@ -0,0 +1,24 @@
+
+
+
+
+ Login
+
+ ${ message }
+
+
+
+
\ No newline at end of file
diff --git a/work/037-pyramid-intro/billtracker/billtracker/templates/welcome.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/welcome.pt
new file mode 100644
index 0000000..be69d42
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/templates/welcome.pt
@@ -0,0 +1,17 @@
+
+
+
+
+
Welcome to
+ Bill Tracker Pro Demo
+
+
+
+
+
\ No newline at end of file
diff --git a/work/036-pyramid-intro/billtracker/billtracker/tests.py b/work/037-pyramid-intro/billtracker/billtracker/tests.py
similarity index 100%
rename from work/036-pyramid-intro/billtracker/billtracker/tests.py
rename to work/037-pyramid-intro/billtracker/billtracker/tests.py
diff --git a/work/037-pyramid-intro/billtracker/billtracker/views/__init__.py b/work/037-pyramid-intro/billtracker/billtracker/views/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/work/037-pyramid-intro/billtracker/billtracker/views/auth.py b/work/037-pyramid-intro/billtracker/billtracker/views/auth.py
new file mode 100644
index 0000000..f34c32a
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/views/auth.py
@@ -0,0 +1,57 @@
+from pyramid.httpexceptions import HTTPFound
+from pyramid.security import (
+ remember,
+ forget,
+)
+from pyramid.view import (
+ forbidden_view_config,
+ view_config,
+)
+
+
+from billtracker.data import repository
+
+
+@view_config(
+ route_name="login",
+ renderer="../templates/login.pt",
+)
+def login(request):
+ if request.user is not None:
+ raise HTTPFound(location="/")
+
+ next_url = request.params.get("next", request.referrer)
+ if not next_url or next_url == "/login":
+ next_url = "/"
+ message = ""
+ login = ""
+ if "form.submitted" in request.params:
+ login = request.params["login"]
+ password = request.params["password"]
+ # Login needs to be the ID
+ # (this is just because I dont have to name look up yet)
+ user = repository.get_user_by_email(login)
+ if user is not None and user.check_password(password):
+ headers = remember(request, user.id)
+ return HTTPFound(location=next_url, headers=headers)
+ message = "Login Failed"
+
+ return {
+ "message": message,
+ "url": request.route_url("login"),
+ "next_url": next_url,
+ "login": login,
+ }
+
+
+@view_config(route_name="logout")
+def logout(request):
+ headers = forget(request)
+ next_url = request.route_url("login")
+ return HTTPFound(location=next_url, headers=headers)
+
+
+@forbidden_view_config()
+def forbidden_view(request):
+ next_url = request.route_url("login", _query={"next": request.url})
+ return HTTPFound(location=next_url)
diff --git a/work/037-pyramid-intro/billtracker/billtracker/views/bill.py b/work/037-pyramid-intro/billtracker/billtracker/views/bill.py
new file mode 100644
index 0000000..d98ffee
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/views/bill.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+"""Defines bill view functions for the billtracker app."""
+
+from typing import Optional, Dict, List
+
+from pyramid.httpexceptions import (
+ HTTPNotFound,
+ HTTPBadRequest,
+ HTTPFound,
+ HTTPForbidden,
+)
+from pyramid.request import Request
+from pyramid.view import view_config
+
+from billtracker.data import repository
+from billtracker.data.models.users import User
+from billtracker.data.models.bill import Bill
+
+
+@view_config(
+ route_name="bill_detail",
+ renderer="../templates/bill_detail.pt",
+ request_method="GET",
+)
+def bill_detail_get(request: Request) -> Dict:
+ """Render home template with given project name."""
+ # noqa: DAR101, DAR201
+ user = request.user
+
+ bill = get_bill_or_client_error(
+ bill_id=request.matchdict.get("bill_id"),
+ )
+
+ verfiy_user_access_to_bill(user, bill)
+
+ return {
+ "bill": bill,
+ "user": user,
+ "error": "",
+ }
+
+
+@view_config(
+ route_name="bill_detail",
+ renderer="../templates/bill_detail.pt",
+ request_method="POST",
+)
+def bill_detail_post(request: Request) -> Dict:
+ """Render home template with given project name."""
+ # noqa: DAR101, DAR201
+ user = request.user
+
+ bill = get_bill_or_client_error(
+ bill_id=request.matchdict.get("bill_id"),
+ )
+
+ verfiy_user_access_to_bill(user, bill)
+
+ errors: List = []
+ amount_str: str = request.POST.get("amount", "")
+ try:
+ amount: int = int(amount_str)
+ except ValueError:
+ errors.append("Amount has to be integer.")
+ else:
+ if amount < 0:
+ errors.append("Amount has to larger than zero.")
+ if amount > bill.open:
+ errors.append("Amount should be small than open amount.")
+
+ if errors:
+ return {
+ "bill": bill,
+ "user": user,
+ "error": " ".join(errors),
+ "amount": amount_str,
+ }
+
+ repository.add_payment(amount=amount, bill_id=bill.id)
+ raise HTTPFound(location="/bill/{0}".format(bill.id))
+
+
+def get_bill_or_client_error(bill_id):
+ try:
+ bill_id: int = int(bill_id)
+ except ValueError:
+ raise HTTPBadRequest()
+ else:
+ bill: Optional[Bill] = repository.get_bill_by_id(bill_id)
+ if bill is None:
+ raise HTTPNotFound()
+ return bill
+
+
+def verfiy_user_access_to_bill(user, bill):
+ """Raise HTTPForbidden if use has no access."""
+ if user is None or user.id != bill.user.id:
+ raise HTTPForbidden()
diff --git a/work/037-pyramid-intro/billtracker/billtracker/views/home.py b/work/037-pyramid-intro/billtracker/billtracker/views/home.py
new file mode 100644
index 0000000..fe35ce6
--- /dev/null
+++ b/work/037-pyramid-intro/billtracker/billtracker/views/home.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+"""Defines home view functions for the billtracker app."""
+
+from typing import Optional, Dict
+
+from pyramid.httpexceptions import HTTPForbidden, HTTPFound
+from pyramid.view import view_config
+
+from billtracker.data import repository
+from billtracker.data.models.users import User
+
+
+@view_config(route_name="home", renderer="../templates/home.pt")
+def home(request) -> Dict:
+ """Render home template with given project name."""
+ # noqa: DAR101, DAR201
+ # user_id: int = 1
+ # user: Optional[User] = repository.get_user_by_id(user_id)
+
+ user = request.user
+ if user is None:
+ raise HTTPFound(location="/welcome")
+
+ return {
+ "user": user,
+ }
+
+
+@view_config(route_name="welcome", renderer="../templates/welcome.pt")
+def welcome(request) -> Dict:
+ """Render landing page for not logged in visitors."""
+ if request.user is not None:
+ raise HTTPFound(location="/")
+ return {}
diff --git a/work/036-pyramid-intro/billtracker/billtracker/views/notfound.py b/work/037-pyramid-intro/billtracker/billtracker/views/notfound.py
similarity index 100%
rename from work/036-pyramid-intro/billtracker/billtracker/views/notfound.py
rename to work/037-pyramid-intro/billtracker/billtracker/views/notfound.py
diff --git a/work/036-pyramid-intro/billtracker/development.ini b/work/037-pyramid-intro/billtracker/development.ini
similarity index 95%
rename from work/036-pyramid-intro/billtracker/development.ini
rename to work/037-pyramid-intro/billtracker/development.ini
index 4c576eb..dd033de 100644
--- a/work/036-pyramid-intro/billtracker/development.ini
+++ b/work/037-pyramid-intro/billtracker/development.ini
@@ -13,6 +13,9 @@ pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
+pyramid.reload_all = true
+
+auth.secret = seekrit
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
diff --git a/work/036-pyramid-intro/billtracker/production.ini b/work/037-pyramid-intro/billtracker/production.ini
similarity index 100%
rename from work/036-pyramid-intro/billtracker/production.ini
rename to work/037-pyramid-intro/billtracker/production.ini
diff --git a/work/036-pyramid-intro/billtracker/pytest.ini b/work/037-pyramid-intro/billtracker/pytest.ini
similarity index 100%
rename from work/036-pyramid-intro/billtracker/pytest.ini
rename to work/037-pyramid-intro/billtracker/pytest.ini
diff --git a/work/036-pyramid-intro/billtracker/setup.py b/work/037-pyramid-intro/billtracker/setup.py
similarity index 100%
rename from work/036-pyramid-intro/billtracker/setup.py
rename to work/037-pyramid-intro/billtracker/setup.py