forked from talkpython/htmx-python-course
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Starter code for chapter 7, infinite scroll.
- Loading branch information
1 parent
36d0869
commit f2b32b9
Showing
140 changed files
with
50,488 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import sys | ||
|
||
import flask | ||
import jinja_partials | ||
|
||
from services import video_service | ||
|
||
app = flask.Flask("app") | ||
|
||
|
||
def configure(): | ||
print("Configuring Flask app:") | ||
|
||
register_template_ops() | ||
print("Registered template helpers") | ||
|
||
register_blueprints() | ||
print("Registered blueprints") | ||
|
||
setup_db() | ||
print("DB setup completed.") | ||
print("", flush=True) | ||
|
||
|
||
def register_template_ops(): | ||
jinja_partials.register_extensions(app) | ||
helpers = { | ||
'len': len, | ||
'isinstance': isinstance, | ||
'str': str, | ||
'type': type | ||
} | ||
app.jinja_env.globals.update(**helpers) | ||
|
||
|
||
def register_blueprints(): | ||
from views import home | ||
from views import videos | ||
from views import feed | ||
|
||
app.register_blueprint(home.blueprint) | ||
app.register_blueprint(videos.blueprint) | ||
app.register_blueprint(feed.blueprint) | ||
|
||
|
||
def setup_db(): | ||
video_service.load_data() | ||
|
||
|
||
if __name__ == '__main__': | ||
configure() | ||
being_debugged = sys.gettrace() is not None | ||
app.run(debug=being_debugged) | ||
else: | ||
configure() |
256 changes: 256 additions & 0 deletions
256
code/ch7_infinite_scroll/ch7_final_video_collector/db/videos.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
[ | ||
{ | ||
"category": "Python", | ||
"image": "python.jpg", | ||
"videos": [ | ||
{ | ||
"id": "rDYKZG6Fn8o", | ||
"title": "Go Python, Go!", | ||
"url": "https://www.youtube.com/watch?v=rDYKZG6Fn8o", | ||
"author": "Python Bytes", | ||
"views": 347 | ||
}, | ||
{ | ||
"id": "QltSJUlHbpw", | ||
"title": "A Space Filled Episode with Dr. Becky", | ||
"url": "https://www.youtube.com/watch?v=QltSJUlHbpw", | ||
"author": "Python Bytes", | ||
"views": 365 | ||
}, | ||
{ | ||
"id": "4wjqsPtj2QY", | ||
"title": "HTMX - Clean Dynamic HTML Pages", | ||
"url": "https://www.youtube.com/watch?v=4wjqsPtj2QY", | ||
"author": "Talk Python", | ||
"views": 756 | ||
}, | ||
{ | ||
"id": "cfrUF-UGehk", | ||
"title": "Python and Flask at the US Federal Election Commission", | ||
"url": "https://www.youtube.com/watch?v=cfrUF-UGehk", | ||
"author": "Talk Python", | ||
"views": 511 | ||
}, | ||
{ | ||
"id": "36yw8VC3KU8", | ||
"title": "PyCon: Let's talk Databases in Python: SQLAlchemy and Alembic", | ||
"url": "https://www.youtube.com/watch?v=36yw8VC3KU8", | ||
"author": "Hannah Stepanek", | ||
"views": 10302 | ||
}, | ||
{ | ||
"id": "pvaIi0l1GME", | ||
"title": "What we learned from Papermill to operationalize notebooks", | ||
"url": "https://www.youtube.com/watch?v=pvaIi0l1GME", | ||
"author": "Alan Yu and Vasu Bhog", | ||
"views": 789 | ||
}, | ||
{ | ||
"id": "PnxlHfGdihI", | ||
"title": "A Python Developer Explores Apple's M1", | ||
"url": "https://www.youtube.com/watch?v=PnxlHfGdihI", | ||
"author": "Michael Kennedy", | ||
"views": 4654 | ||
} | ||
] | ||
}, | ||
{ | ||
"category": "Apple", | ||
"image": "apple.jpg", | ||
"videos": [ | ||
{ | ||
"id": "R5BQ6yhhRJc", | ||
"title": "Andy's Pick & Pull - WWDC 2021", | ||
"url": "https://www.youtube.com/watch?v=R5BQ6yhhRJc", | ||
"author": "MacBreak Weekly", | ||
"views": 13720 | ||
}, | ||
{ | ||
"id": "PnxlHfGdihI", | ||
"title": "A Python Developer Explores Apple's M1", | ||
"url": "https://www.youtube.com/watch?v=PnxlHfGdihI", | ||
"author": "Michael Kennedy", | ||
"views": 4654 | ||
}, | ||
{ | ||
"id": "YisUywtzZK0", | ||
"title": "How Apple Private Relay Kills Data Profiling", | ||
"url": "https://www.youtube.com/watch?v=YisUywtzZK0", | ||
"author": "Rene Ritchie", | ||
"views": 65245 | ||
}, | ||
{ | ||
"id": "vg0AF166eVI", | ||
"title": "Why the M1 Mac is SO FAST — Apple Silicon Explained!", | ||
"url": "https://www.youtube.com/watch?v=vg0AF166eVI", | ||
"author": "Rene Ritchie", | ||
"views": 523014 | ||
}, | ||
{ | ||
"id": "0TD96VTf0Xs", | ||
"title": "WWDC 2021", | ||
"url": "https://www.youtube.com/watch?v=0TD96VTf0Xs", | ||
"author": "Apple", | ||
"views": 6655103 | ||
}, | ||
{ | ||
"id": "4ecN7yGcqcQ", | ||
"title": "M1 iMac Unboxing and Review!", | ||
"url": "https://www.youtube.com/watch?v=4ecN7yGcqcQ", | ||
"author": "iJustine", | ||
"views": 623042 | ||
} | ||
] | ||
}, | ||
{ | ||
"category": "JavaScript", | ||
"image": "javascript.jpg", | ||
"videos": [ | ||
{ | ||
"id": "4wjqsPtj2QY", | ||
"title": "HTMX - Clean Dynamic HTML Pages", | ||
"url": "https://www.youtube.com/watch?v=4wjqsPtj2QY", | ||
"author": "Talk Python", | ||
"views": 756 | ||
}, | ||
{ | ||
"id": "qZXt1Aom3Cs", | ||
"title": "Vue JS Crash Course 2021", | ||
"url": "https://www.youtube.com/watch?v=qZXt1Aom3Cs", | ||
"author": "Traversy Media", | ||
"views": 265783 | ||
}, | ||
{ | ||
"id": "OrxmtDw4pVI", | ||
"title": "Vue.js: The Documentary", | ||
"url": "https://www.youtube.com/watch?v=OrxmtDw4pVI", | ||
"author": "Honeypot", | ||
"views": 932606 | ||
}, | ||
{ | ||
"id": "m_F8wSZT3QY", | ||
"title": "Wat - JavaScript misadventures", | ||
"url": "https://www.youtube.com/watch?v=3se2-thqf-A", | ||
"author": "Gary Bernhardt", | ||
"views": 866 | ||
}, | ||
{ | ||
"id": "ahCwqrYpIuM", | ||
"title": "TypeScript - The Basics", | ||
"url": "https://www.youtube.com/watch?v=ahCwqrYpIuM", | ||
"author": "Fireship", | ||
"views": 772764 | ||
} | ||
] | ||
}, | ||
{ | ||
"category": "EVs", | ||
"image": "ev.jpg", | ||
"videos": [ | ||
{ | ||
"id": "6RhtiPefVzM", | ||
"title": "Are Electric Cars Worse For The Environment? Myth Busted", | ||
"url": "https://www.youtube.com/watch?v=6RhtiPefVzM", | ||
"author": "Engineering Explained", | ||
"views": 1753659 | ||
}, | ||
{ | ||
"id": "QMfxJEfb4lw", | ||
"title": "Rivian - Electric Adventure Vehicle", | ||
"url": "https://www.youtube.com/watch?v=QMfxJEfb4lw", | ||
"author": "Fully Charged Show", | ||
"views": 4029696 | ||
}, | ||
{ | ||
"id": "A6RsmzCYB3I", | ||
"title": "New Hyundai Ioniq 5 full review - the 300-mile Tesla EV rival styled by Minecraft", | ||
"url": "https://www.youtube.com/watch?v=A6RsmzCYB3I", | ||
"author": "The Late Brake Show", | ||
"views": 938994 | ||
}, | ||
{ | ||
"id": "QHOPFp1o3d0", | ||
"title": "Bolt: Best Non Tesla Electric Car?", | ||
"url": "https://www.youtube.com/watch?v=QHOPFp1o3d0", | ||
"author": "TheStraightPipes", | ||
"views": 512330 | ||
}, | ||
{ | ||
"id": "3iRHYIwjFKw", | ||
"title": "Tesla Full Self Driving Beta Test Drive", | ||
"url": "https://www.youtube.com/watch?v=3iRHYIwjFKw", | ||
"author": "Tesla Raj", | ||
"views": 367345 | ||
}, | ||
{ | ||
"id": "KOWz9-5T9pE", | ||
"title": "Formula E: 2021 Puebla E-Prix | Round 8", | ||
"url": "https://www.youtube.com/watch?v=KOWz9-5T9pE", | ||
"author": "ABB Formula E", | ||
"views": 43975 | ||
} | ||
] | ||
}, | ||
{ | ||
"category": "Racing", | ||
"image": "racing.jpg", | ||
"videos": [ | ||
{ | ||
"id": "tvcuGoVhpxw", | ||
"title": "How to Trail Brake: A Step-by-Step Guide", | ||
"url": "https://www.youtube.com/watch?v=tvcuGoVhpxw", | ||
"author": "Driver61", | ||
"views": 210861 | ||
}, | ||
{ | ||
"id": "eUdaWbr1KB4", | ||
"title": "Trail Braking: 5 Mistakes DESTROYING Your Lap Time", | ||
"url": "https://www.youtube.com/watch?v=eUdaWbr1KB4", | ||
"author": "Driver61", | ||
"views": 581747 | ||
}, | ||
{ | ||
"id": "F5igCWjmeAU", | ||
"title": "My Very First Start in IndyCar", | ||
"url": "https://www.youtube.com/watch?v=F5igCWjmeAU", | ||
"author": "Romain Grosjean", | ||
"views": 960145 | ||
}, | ||
{ | ||
"id": "KOWz9-5T9pE", | ||
"title": "Formula E: 2021 Puebla E-Prix | Round 8", | ||
"url": "https://www.youtube.com/watch?v=KOWz9-5T9pE", | ||
"author": "ABB Formula E", | ||
"views": 43975 | ||
}, | ||
{ | ||
"id": "EU73gjQJXU4", | ||
"title": "IndyCar - Road America 2021 Highlights", | ||
"url": "https://www.youtube.com/watch?v=EU73gjQJXU4", | ||
"author": "Motorsports on NBC", | ||
"views": 43295 | ||
}, | ||
{ | ||
"id": "9i2iA0R6IdA", | ||
"title": "The First Corner: Do's and Don'ts", | ||
"url": "https://www.youtube.com/watch?v=9i2iA0R6IdA", | ||
"author": "Driver61", | ||
"views": 46432 | ||
}, | ||
{ | ||
"id": "X0WuN3poyVs", | ||
"title": "Biggest Sim Racing Tips (sim driver analysis)", | ||
"url": "https://www.youtube.com/watch?v=X0WuN3poyVs", | ||
"author": "Driver61", | ||
"views": 78817 | ||
}, | ||
{ | ||
"id": "2wzy3HiW7SE", | ||
"title": "CAN YOU TELL THIS ISN'T REAL? - Corvette C8.R - Nordschleife", | ||
"url": "https://www.youtube.com/watch?v=2wzy3HiW7SE", | ||
"author": "Boosted Media", | ||
"views": 60007 | ||
} | ||
] | ||
} | ||
] |
35 changes: 35 additions & 0 deletions
35
code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/request_dict.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import flask | ||
from werkzeug.datastructures import MultiDict | ||
|
||
|
||
class RequestDictionary(dict): | ||
def __init__(self, *args, default_val=None, **kwargs): | ||
self.default_val = default_val | ||
super().__init__(*args, **kwargs) | ||
|
||
def __getattr__(self, key): | ||
return self.get(key, self.default_val) | ||
|
||
|
||
def create(default_val=None, **route_args) -> RequestDictionary: | ||
request = flask.request | ||
|
||
# Adding this retro actively. Some folks are experiencing issues where they | ||
# are getting a list rather than plain dict. I think it's from multiple | ||
# entries in the multidict. This should fix it. | ||
args = request.args | ||
if isinstance(request.args, MultiDict): | ||
args = request.args.to_dict() | ||
|
||
form = request.form | ||
if isinstance(request.args, MultiDict): | ||
form = request.form.to_dict() | ||
|
||
data = { | ||
**args, # The key/value pairs in the URL query string | ||
**request.headers, # Header values | ||
**form, # The key/value pairs in the body, from a HTML post form | ||
**route_args # And additional arguments the method access, if they want them merged. | ||
} | ||
|
||
return RequestDictionary(data, default_val=default_val) |
42 changes: 42 additions & 0 deletions
42
code/ch7_infinite_scroll/ch7_final_video_collector/infrastructure/view_modifiers.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from functools import wraps | ||
|
||
import flask | ||
import werkzeug | ||
import werkzeug.wrappers | ||
|
||
|
||
def response(*, mimetype: str = None, template_file: str = None): | ||
def response_inner(f): | ||
|
||
@wraps(f) | ||
def view_method(*args, **kwargs): | ||
response_val = f(*args, **kwargs) | ||
|
||
if isinstance(response_val, werkzeug.wrappers.Response): | ||
return response_val | ||
|
||
if isinstance(response_val, flask.Response): | ||
return response_val | ||
|
||
if isinstance(response_val, dict): | ||
model = dict(response_val) | ||
else: | ||
model = dict() | ||
|
||
if template_file and not isinstance(response_val, dict): | ||
raise Exception( | ||
"Invalid return type {}, we expected a dict as the return value.".format(type(response_val))) | ||
|
||
if template_file: | ||
response_val = flask.render_template(template_file, **response_val) | ||
|
||
resp = flask.make_response(response_val) | ||
resp.model = model | ||
if mimetype: | ||
resp.mimetype = mimetype | ||
|
||
return resp | ||
|
||
return view_method | ||
|
||
return response_inner |
Oops, something went wrong.