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 @@ - - - - - - - - - - - Cookiecutter Starter project for the Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
No content
-
-
- -
- -
-
-
- - - - - - - - diff --git a/work/036-pyramid-intro/billtracker/billtracker/templates/home.pt b/work/036-pyramid-intro/billtracker/billtracker/templates/home.pt deleted file mode 100644 index 981492a..0000000 --- a/work/036-pyramid-intro/billtracker/billtracker/templates/home.pt +++ /dev/null @@ -1,17 +0,0 @@ -
-
-
-

Pyramid Starter project

-

- Welcome to ${project}, a Pyramid - application generated by
Cookiecutter and me. -

-

- Some Items: -

    -
  1. ${i} is an item!
  2. -
-

-
-
-
diff --git a/work/036-pyramid-intro/billtracker/billtracker/views/home.py b/work/036-pyramid-intro/billtracker/billtracker/views/home.py deleted file mode 100644 index 76205ae..0000000 --- a/work/036-pyramid-intro/billtracker/billtracker/views/home.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Defines view functions for the billtracker app.""" - -from pyramid.view import view_config - - -@view_config(route_name="home", renderer="../templates/home.pt") -def home(request): - """Render home template with given project name.""" - # noqa: DAR101, DAR201 - return { - "project": "Bill Tracker", - "items": [ - "item1", - "item2", - "item3", - "item4", - ], - } diff --git a/work/036-pyramid-intro/billtracker/.coveragerc b/work/037-pyramid-intro/billtracker/.coveragerc similarity index 100% rename from work/036-pyramid-intro/billtracker/.coveragerc rename to work/037-pyramid-intro/billtracker/.coveragerc diff --git a/work/036-pyramid-intro/billtracker/.gitignore b/work/037-pyramid-intro/billtracker/.gitignore similarity index 100% rename from work/036-pyramid-intro/billtracker/.gitignore rename to work/037-pyramid-intro/billtracker/.gitignore diff --git a/work/036-pyramid-intro/billtracker/CHANGES.txt b/work/037-pyramid-intro/billtracker/CHANGES.txt similarity index 100% rename from work/036-pyramid-intro/billtracker/CHANGES.txt rename to work/037-pyramid-intro/billtracker/CHANGES.txt diff --git a/work/036-pyramid-intro/billtracker/MANIFEST.in b/work/037-pyramid-intro/billtracker/MANIFEST.in similarity index 100% rename from work/036-pyramid-intro/billtracker/MANIFEST.in rename to work/037-pyramid-intro/billtracker/MANIFEST.in diff --git a/work/036-pyramid-intro/billtracker/README.txt b/work/037-pyramid-intro/billtracker/README.txt similarity index 100% rename from work/036-pyramid-intro/billtracker/README.txt rename to work/037-pyramid-intro/billtracker/README.txt diff --git a/work/037-pyramid-intro/billtracker/billtracker/__init__.py b/work/037-pyramid-intro/billtracker/billtracker/__init__.py new file mode 100644 index 0000000..637a60f --- /dev/null +++ b/work/037-pyramid-intro/billtracker/billtracker/__init__.py @@ -0,0 +1,25 @@ +# -*- conding: utf-8 -*- + +"""App entry point.""" + +import os + +from pyramid.config import Configurator + +from billtracker.bin.load_base_data import load_starter_data +from billtracker.data.db_session import DbSession + + +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.include(".security") + config.scan() + + current_dir = os.path.dirname(os.path.abspath(__file__)) + db_file = os.path.join(current_dir, "db/billtracker.sqlite3") + DbSession.global_init(db_file) + load_starter_data() + return config.make_wsgi_app() diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/__init__.py b/work/037-pyramid-intro/billtracker/billtracker/bin/__init__.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/data/__init__.py rename to work/037-pyramid-intro/billtracker/billtracker/bin/__init__.py diff --git a/work/037-pyramid-intro/billtracker/billtracker/bin/load_base_data.py b/work/037-pyramid-intro/billtracker/billtracker/bin/load_base_data.py new file mode 100644 index 0000000..c2c79e3 --- /dev/null +++ b/work/037-pyramid-intro/billtracker/billtracker/bin/load_base_data.py @@ -0,0 +1,65 @@ +import json +import os +import random +from typing import List + +import dateutil.parser +from sqlalchemy.orm import Session + +from billtracker.data.db_session import DbSession +from billtracker.data.models.bill import Bill +from billtracker.data.models.users import User + + +def load_starter_data(): + print("Loading starter data...") + session = DbSession.create_session() + if session.query(Bill).count() > 0: + session.close() + print("Data already loaded...") + return + + session.expire_on_commit = False + + users = add_users(session) + add_bills(users) + + session.commit() + session.close() + + +def add_users(session: Session) -> List[User]: + users = [] + data_file = os.path.join(DbSession.db_folder, 'MOCK_USERS.json') + with open(data_file, 'r', encoding='utf-8') as fin: + data = json.load(fin) + + for u in data: + user = User() + users.append(user) + user.email = u.get('email') + user.name = u.get('name') + user.created_date = dateutil.parser.parse(u.get('created_date')) + user.last_login = dateutil.parser.parse(u.get('last_login')) + user.last_login = dateutil.parser.parse(u.get('last_login')) + user.hashed_password = u.get('hashed_password') + session.add(user) + + return users + + +def add_bills(users: List[User]): + data_file = os.path.join(DbSession.db_folder, 'MOCK_PAYMENTS.json') + with open(data_file, 'r', encoding='utf-8') as fin: + data = json.load(fin) + + for p in data: + user = random.choice(users) + + bill = Bill() + bill.created_date = dateutil.parser.parse(p.get('created_date')) + bill.description = p.get('description') + bill.total = int(p.get('total')) + bill.paid = min(bill.total, int(p.get('paid'))) + + user.bills.append(bill) diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/__all_models.py b/work/037-pyramid-intro/billtracker/billtracker/data/__all_models.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/data/__all_models.py rename to work/037-pyramid-intro/billtracker/billtracker/data/__all_models.py diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/models/__init__.py b/work/037-pyramid-intro/billtracker/billtracker/data/__init__.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/data/models/__init__.py rename to work/037-pyramid-intro/billtracker/billtracker/data/__init__.py diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/db_session.py b/work/037-pyramid-intro/billtracker/billtracker/data/db_session.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/data/db_session.py rename to work/037-pyramid-intro/billtracker/billtracker/data/db_session.py diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/modelbase.py b/work/037-pyramid-intro/billtracker/billtracker/data/modelbase.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/data/modelbase.py rename to work/037-pyramid-intro/billtracker/billtracker/data/modelbase.py diff --git a/work/036-pyramid-intro/billtracker/billtracker/views/__init__.py b/work/037-pyramid-intro/billtracker/billtracker/data/models/__init__.py similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/views/__init__.py rename to work/037-pyramid-intro/billtracker/billtracker/data/models/__init__.py diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/models/bill.py b/work/037-pyramid-intro/billtracker/billtracker/data/models/bill.py similarity index 87% rename from work/036-pyramid-intro/billtracker/billtracker/data/models/bill.py rename to work/037-pyramid-intro/billtracker/billtracker/data/models/bill.py index 0590832..c2ad0cc 100644 --- a/work/036-pyramid-intro/billtracker/billtracker/data/models/bill.py +++ b/work/037-pyramid-intro/billtracker/billtracker/data/models/bill.py @@ -20,3 +20,9 @@ class Bill(SqlAlchemyBase): @property def is_paid(self): return self.total <= self.paid + + @property + def open(self): + """Return the open amount.""" + return self.total - self.paid + diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/models/users.py b/work/037-pyramid-intro/billtracker/billtracker/data/models/users.py similarity index 77% rename from work/036-pyramid-intro/billtracker/billtracker/data/models/users.py rename to work/037-pyramid-intro/billtracker/billtracker/data/models/users.py index ca46aa5..3944a1d 100644 --- a/work/036-pyramid-intro/billtracker/billtracker/data/models/users.py +++ b/work/037-pyramid-intro/billtracker/billtracker/data/models/users.py @@ -35,3 +35,13 @@ def total_owed(self) -> float: @property def total_paid_off(self) -> float: return sum(b.total for b in self.paid_bills) + + def check_password(self, password): + # Just a stub that accepts anything as the password. + print("Password to check: {0}".format(password)) + print("Password to match: {0}".format(self.hashed_password)) + if password == self.hashed_password: + print("Passwords match.") + return True + print("Passwords do not match.") + return False diff --git a/work/036-pyramid-intro/billtracker/billtracker/data/repository.py b/work/037-pyramid-intro/billtracker/billtracker/data/repository.py similarity index 72% rename from work/036-pyramid-intro/billtracker/billtracker/data/repository.py rename to work/037-pyramid-intro/billtracker/billtracker/data/repository.py index 2d79abb..1201a8e 100644 --- a/work/036-pyramid-intro/billtracker/billtracker/data/repository.py +++ b/work/037-pyramid-intro/billtracker/billtracker/data/repository.py @@ -21,11 +21,26 @@ def get_user_by_id(user_id: int, include_bills=True) -> Optional[User]: session.close() +def get_user_by_email(email: int, include_bills=False) -> Optional[User]: + session = DbSession.create_session() + try: + if not include_bills: + return session.query(User).filter(User.email == email).first() + else: + return session.query(User) \ + .options(subqueryload(User.bills)) \ + .filter(User.id == user_id) \ + .first() + finally: + session.close() + + def get_bill_by_id(bill_id: int) -> Optional[Bill]: session = DbSession.create_session() try: return session.query(Bill) \ .filter(Bill.id == bill_id) \ + .options(subqueryload(Bill.user)) \ .first() finally: session.close() diff --git a/work/036-pyramid-intro/billtracker/billtracker/db/MOCK_PAYMENTS.json b/work/037-pyramid-intro/billtracker/billtracker/db/MOCK_PAYMENTS.json similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/db/MOCK_PAYMENTS.json rename to work/037-pyramid-intro/billtracker/billtracker/db/MOCK_PAYMENTS.json diff --git a/work/036-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json b/work/037-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json similarity index 98% rename from work/036-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json rename to work/037-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json index d698783..7bff9ac 100644 --- a/work/036-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json +++ b/work/037-pyramid-intro/billtracker/billtracker/db/MOCK_USERS.json @@ -1,4 +1,4 @@ -[{"id":1,"name":"Dannie Easom","email":"deasom0@macromedia.com","hashed_password":"86rV3X82LC6VFFxb3hRX98B66tTMOM7QPW73Yhx2p40C2A12fSPGLDCB6KOUep4m9HEQU","created_date":"2017-12-23T13:31:30Z","last_login":"2018-04-09T05:04:37Z"}, +[{"id":1,"name":"Dannie Easom","email":"deasom0@macromedia.com","hashed_password":"test","created_date":"2017-12-23T13:31:30Z","last_login":"2018-04-09T05:04:37Z"}, {"id":2,"name":"Vicki Minchenton","email":"vminchenton1@behance.net","hashed_password":"15vZB1FH9AJUGHsr0mWKBLU72wVGGBYM54MXHnv1eZ4JXG45aXGGDWLETPCRum1y6ENV4","created_date":"2018-05-11T03:04:49Z","last_login":"2018-05-18T19:44:24Z"}, {"id":3,"name":"Jami Mayte","email":"jmayte2@liveinternet.ru","hashed_password":"37eG627P025U9Vbl3iEBZQS92fIOME80WRUZLty4cWS4HL88oDKAKR02M64Rlg4wW45CF","created_date":"2017-12-14T20:43:16Z","last_login":"2018-02-11T20:59:17Z"}, {"id":4,"name":"Dre Dunston","email":"ddunston3@etsy.com","hashed_password":"75xETKLMGHOVB4dy3b1UGY452lO1REVL6SI7Mfz4wD2NNW50bMJ7SPDKFEWKtr4l2LTQG","created_date":"2018-09-02T09:54:46Z","last_login":"2018-05-07T17:35:12Z"}, diff --git a/work/037-pyramid-intro/billtracker/billtracker/routes.py b/work/037-pyramid-intro/billtracker/billtracker/routes.py new file mode 100644 index 0000000..493a5fe --- /dev/null +++ b/work/037-pyramid-intro/billtracker/billtracker/routes.py @@ -0,0 +1,7 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('home', '/') + config.add_route('welcome', '/welcome') + config.add_route('bill_detail', '/bill/{bill_id}') diff --git a/work/037-pyramid-intro/billtracker/billtracker/security.py b/work/037-pyramid-intro/billtracker/billtracker/security.py new file mode 100644 index 0000000..836a284 --- /dev/null +++ b/work/037-pyramid-intro/billtracker/billtracker/security.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +"""Defines the security functionality for the app.""" + +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy + + +from billtracker.data import repository + + +class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): + """Defines the auth policy for the app.""" + + def authenticated_user(self, request): + """Return id of authenticated user from request.""" + user = request.user + if user is not None: + return user.id + + +def get_user(request): + """Return User object from database with given id.""" + user_id = request.unauthenticated_userid + if user_id is not None: + user = repository.get_user_by_id(user_id) + return user + + +def includeme(config): + settings = config.get_settings() + authn_policy = MyAuthenticationPolicy( + settings["auth.secret"], + hashalg="sha512", + ) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.add_request_method(get_user, "user", reify=True) + diff --git a/work/037-pyramid-intro/billtracker/billtracker/static/css/site.css b/work/037-pyramid-intro/billtracker/billtracker/static/css/site.css new file mode 100644 index 0000000..a5025d9 --- /dev/null +++ b/work/037-pyramid-intro/billtracker/billtracker/static/css/site.css @@ -0,0 +1,128 @@ +body { + color: #aaa; + background-color: #777; +} + +#main-content { + color: black; + background-color: white; +} + +a { + color: dodgerblue; +} + +a:hover { + color: darkblue; +} + +footer { + text-align: center; + padding-top: 50px; + line-height: 2.5em; + font-size: 16px; +} + +footer a { + color: black; + margin: 10px; +} + +footer a:hover { + color: dodgerblue; +} + +#hero { + padding-top: 50px; + padding-bottom: 50px; + text-align: center; + color: #f8f8f8; + background: white url(/static/img/finance.png) no-repeat center center; + background-size: cover; + min-height: 400px; +} + +#hero .heading-wrapper { + padding: 50px; + display: inline-block; + font-size: 3em; + background-color: rgba(0, 0, 0, 0.41); + border-radius: 5px; +} + +.content { + padding-top: 50px; + margin-bottom: 50px; +} + +.paid { + background-color: #bef0ba; +} + +.owe { + background-color: #f0adae; +} + +#eop { + margin-top: 50px; +} + +.amount { + text-align: right; +} + +.row.grid-entry > div { + padding-top: 5px; + padding-bottom: 5px; + height: 40px; +} + +.row.grid-entry:hover > .values { + background-color: #eee; +} + +.row.grid-entry:hover .filler { + background-color: white; +} + + + +#bill-header { + padding-top: 50px; + padding-bottom: 50px; + text-align: center; + color: #f8f8f8; + background: white url(/static/img/finance.png) no-repeat center center; + background-size: cover; + min-height: 200px; +} + +#bill-header h1 { + padding: 50px; + background-color: rgba(0, 0, 0, 0.41); + display: inline-block; + border-radius: 5px; + font-size: 3em; +} + +.back { + padding: 20px; +} + +.details { + line-height: 2em; +} + +.details form > * { + margin: 5px; +} + +.error { + color: red; + font-weight: bold; +} + +form > * { + margin-top: 5px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/work/036-pyramid-intro/billtracker/billtracker/static/css/theme.css b/work/037-pyramid-intro/billtracker/billtracker/static/css/theme.css similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/static/css/theme.css rename to work/037-pyramid-intro/billtracker/billtracker/static/css/theme.css diff --git a/work/037-pyramid-intro/billtracker/billtracker/static/img/finance.png b/work/037-pyramid-intro/billtracker/billtracker/static/img/finance.png new file mode 100644 index 0000000..5559669 Binary files /dev/null and b/work/037-pyramid-intro/billtracker/billtracker/static/img/finance.png differ diff --git a/work/036-pyramid-intro/billtracker/billtracker/static/img/pyramid-16x16.png b/work/037-pyramid-intro/billtracker/billtracker/static/img/pyramid-16x16.png similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/static/img/pyramid-16x16.png rename to work/037-pyramid-intro/billtracker/billtracker/static/img/pyramid-16x16.png diff --git a/work/036-pyramid-intro/billtracker/billtracker/static/img/pyramid.png b/work/037-pyramid-intro/billtracker/billtracker/static/img/pyramid.png similarity index 100% rename from work/036-pyramid-intro/billtracker/billtracker/static/img/pyramid.png rename to work/037-pyramid-intro/billtracker/billtracker/static/img/pyramid.png diff --git a/work/036-pyramid-intro/billtracker/billtracker/templates/404.pt b/work/037-pyramid-intro/billtracker/billtracker/templates/404.pt similarity index 67% rename from work/036-pyramid-intro/billtracker/billtracker/templates/404.pt rename to work/037-pyramid-intro/billtracker/billtracker/templates/404.pt index 3e82f4b..ea9e591 100644 --- a/work/036-pyramid-intro/billtracker/billtracker/templates/404.pt +++ b/work/037-pyramid-intro/billtracker/billtracker/templates/404.pt @@ -2,7 +2,7 @@
-

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 + + + + + + + + + +
+

+ Login +

+

+ ${request.user.name} Logout +

+
+
+
No content
+
+ + + + + + + \ 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 Details

+
+
+
+
+ +
+
+
+
+
+
+

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

+
+
+
+
+ Login +
+
+
+
+
+
\ 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