100 Days of Code
This journey is based on the Udemy course 100 Days of Code - The Python Pro Bootcamp by Dr. Angela Yu.
My code repo is here ๐: 100DaysOfCode-Python
I document my progress in this post: programming tasks, and notes about things that made an impression.
Day 72 - Visualization
Converting string to date
pd.to_datetime(df.DATE)
Pivot pandas dataframe
df.pivot(index = 'DATE', columns = 'TAG', values = 'POST')
Data visualization with matplotlib
1
2
3
4
5
6
7
8
9
10
11
12
plt.figure(figsize=(16,20))
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.xlabel('Date', fontsize=14)
plt.ylabel('Number of post', fontsize=14)
plt.ylim(0, 35000)
for column in df.columns:
plt.plot(df.index, df[column],
linewidth=3, label=df[column].name)
plt.legend(fontsize=16)
Day 71 - Data Exploration with Pandas
This day starts the Data Analytics exercises.
Pandas ๐ผ
The notebook is here Data Exploration Pandas College Major
Noteworthy Pandas functions include:
.findna()
- Looks for NaN (not a number).dropna()
- drops the NaNdf[['col1','col2']]
- access multiple columns.idxmax()
- gets row index of the max value.idxmin()
- gets row index of the min value
Day 70 - Deployed blog in Heroku
Deployed blog ๐: Blog.
The Github repo is here ๐: my-blog-flask
Since Iโve done this part ahead, itโs only a matter of changing the underlying database from SQLite to PostgreSQL. This ensures that the data will not be wiped out periodically. More information here.
A Postgres database was added from the Resources in Heroku. Databases can be viewed in Heroku Data. The Postgres database URL (DATABASE_URL
) is automatically added as a variable in Config Vars; hence the script should be modified as:
1
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite:///blog.db")
The second argument in os.environ.get
is the default value if DATABASE_URL
is not available, i.e. in the local deployment.
I also removed the SQLAlchemy binding for the two other tables since these tables will be created in the Postgres database and not in separate databases (unlike in SQLite which is text based). Not doing so resulted in this error (psycopg2.errors.UndefinedTable) relation "table_name" does not exist
. (This actually took a while to figure out ๐
)
I also learned how to write a custom flask CLI command while digging around for a solution to this error.
1
2
3
4
5
6
7
8
9
import click
from flask.cli import with_appcontext
@click.command(name="create_tables")
@with_appcontext
def create_tables():
db.create_all()
app.cli.add_command(create_tables)
Rename main.py
to app.py
.
In Heroku, run the custom Flask CLI command using the console:
1
flask create_tables
This turned out unnecessary but convenient when doing multiple deploys; I donโt have to comment and uncomment db.create_all()
in the script since I can just do this using the Heroku commandline.
Day 69 (Capstone Part 4) - Blog with Users
Added authentication and users to blog ๐: Blog
Multiple databases declared in Flask
1
2
3
4
5
6
7
8
9
10
11
12
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config["SQLALCHEMY_BINDS"] = {
'db2': 'sqlite:///users.db'
}
class User(UserMixin, db.Model):
__bind_key__ = "db2"
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100))
name = db.Column(db.String(1000))
Create an admin_only
decorator. Patterned from the login_required
decorator. Error pages using abort()
1
2
3
4
5
6
7
8
9
from functools import wraps
from flask import abort
def admin_only(function):
@wraps(function)
def wrapper(*args, **kwargs):
if current_user.id != 1:
return abort(403)
return function(*args, **kwargs)
return wrapper
Define a one-to-many relationship between user (author) and posts using relationship()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User(UserMixin, db.Model):
__bind_key__ = "db2"
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100))
name = db.Column(db.String(1000))
posts = relationship("BlogPost", back_populates="author")
# connection
class BlogPost(db.Model):
__tablename__ = "blog_posts"
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
# Foreign Key to user.id
author = relationship("User", back_populates="posts")
# connection
title = db.Column(db.String(250), unique=True, nullable=False)
subtitle = db.Column(db.String(250), nullable=False)
date = db.Column(db.String(250), nullable=False)
body = db.Column(db.Text, nullable=False)
img_url = db.Column(db.String(250), nullable=False)
Since post.author
is now a User
object, access author name as post.author.name
1
2
3
<p class="post-meta">Posted by
<a href="#">{{post.author.name}}</a>
on {{post.date}}
Flask gravatar to show image by email
1
<img src="{{ post.author.email | gravatar }}"/>
Day 68 - Flask with Authentication
Flask website with authentication ๐: Flask with Authentication
Why authenticate
- Create / login users
- Restrict access
Levels of encryption
- 1: Storing the password as plain text in the database
- 2: Encryption - scrambling the original message with a key to decode it i.e. password + key (cipher method) -> cipher text; weak, easy to figure out the original text even without a key (think Mary, Queen of Scots but methaphorically. Still ๐ โโ๏ธ)
- 3: Hashing - removes the need for an encryption key; hash function turns the password into a hash and the hash is stored in the database; Hashes are mathematical equations that make it almost impossible (far too long) to turn a hash back to a password; e.g. MD5 or bcrypt (the latter takes longer to hash out; industry standard)
- 4: Hashing and Salting - salt or random string of characters is appended to the password which generates different hashes for the same passwords; salt is stored in the database along with the hash; bcrypt has salt-rounds or how many times the password is salted e.g. hash from round 1 gets salted with the same salt then repeat; increase rounds to overcome Mooreโs Law (every year computing power increases)
A hash table of passwords can be created from:
- Common passwords
- All words from a dictionary (approxo 150,000) -> dictionary attack
- All numbers form a telephone book (approx 5,000,000)
- All combinations of characters up to 6 places (19,770,609,664)
โ Total: 19,775,759,664 combinations which only takes 0.9s seconds to generate the hash for using one of the latest GPUs
โ But as the number of characters of your password increases, the combination of times it takes to crack it increases exponentialy.
Hash password using Werkzeug helper function
1
2
3
4
5
6
7
from werkzeug.security import generate_password_hash
new_user = User(
email = request.form.get("email"),
password = generate_password_hash(request.form.get("password"), \
method='pbkdf2:sha256', salt_length=8),
name = request.form.get("name")
Login authentication using Flask_Login package. The User class is implemented with UserMixin. Mixin is a way to provide multiple inheritance in Python
1
2
3
4
5
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100))
name = db.Column(db.String(1000))
Configure the flask app to use Flask_login
1
2
3
4
5
6
app = Flask(__name__)
# more configs ...
login_manager = LoginManager()
login_manager.init_app(app)
Create a user loader function. Flask_Login creates a cookie ๐ช that contains the user.id
. Flask uses this to create a User
object to access information in the succeeding pages. eg. printing the userโs name secrets.html
. This function is in fact used in every page even though it was not explicitly called in main.py
1
2
3
@login_manager.user_loader
def load_user(user_id):
return User.get(int(user_id))
Check password agains the database. Unhash using check_password_hash
1
2
3
4
5
6
7
8
9
10
@app.route('/login', methods=["GET", "POST"])
def login():
if request.method == "POST":
email = request.form.get("email")
password = request.form.get("password")
user = User.query.filter_by(email=email).first()
if check_password_hash(user.password, password):
login_user(user)
return redirect(url_for("secrets"))
return render_template("login.html")
Some pages are only viewable when authenticated using @login_required
. Loggin user is identified by current_user.is_authenticated
. current_user
is imported from flask_login
. is_authenticated
is from the Mixin
send_from_directory()
method is used to download files
1
2
3
4
5
6
7
8
9
10
@app.route('/secrets')
@login_required
def secrets():
return render_template("secrets.html", name=current_user.name, \
logged_in=current_user.is_authenticated)
@app.route('/download')
@login_required
def download():
return send_from_directory("static", filename="files/cheat_sheet.pdf")
Immediately login user after registration using login_user()
1
2
3
4
5
# add new_user to the db...
db.session.add(new_user)
db.session.commit()
login_user(new_user)
Use flash
to display feedback on forms
1
flash("Password incorrect, please try again")
and in the HTML code
1
2
3
4
5
6
7
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<p>{{ message }}<p>
{% endfor %}
{% endif %}
{% endwith %}
Resources
- Cryptii; works just like the Caesar Cipher (Letter substitution by shifting the letters)
- How the Enigma Machine Works
- The Flaw in the Enigma Machine
- Plain Text Offenders
- Have I been Pawned Passwords
- Most common passwords; usually gleaned from attacks
- Password complexity checker; also gives the time it takes for the password to be cracked (brute force)
- Hacker Typer
Day 67 (Capstone Part 3) - Blog with RESTful Routing
Added SQL database and CRUD functionalities to the blog project ๐: Blog
New features:
- Add new post
- Edit post
- Delete post
Flask-CDKEditor renders styling toolbar in the body text area
1
2
3
4
5
6
7
8
9
10
11
from flask_ckeditor import CKEditor, CKEditorField
# more code ...
app.config['CKEDITOR_PKG_TYPE'] = "basic"
ckeditor = CKEditor(app)
class CreatePostForm(FlaskForm):
# some code ..
body = CKEditorField("Blog Content", validators=[DataRequired()])
And in the html
1
2
3
4
5
6
7
8
9
10
11
12
13
{% import "bootstrap/wtf.html" as wtf %}
#...more html code ..
# Load ckeditor
{{ ckeditor.load() }}
# Configure the ckeditor to tell it which field in WTForm will need to be a CKEditor.
{{ ckeditor.config(name="body") }}
# Add WTF quickfor
# button_map to render "primary" button styling to the submit button
{{ wtf.quick_form(form, novalidate=True, button_map={"submit": "primary"}) }}
Remove HTML tags from CDK editor using the Jinja filter: safe
1
{{ Jinja expression | Jinja filter }}
as in
1
{{post.body|safe}}
Write date with month as string
1
2
3
4
from datetime import datetime
date = datetime.now().strftime("%B %d, %Y"),
# August 14, 2021
Also if form.validate_on_submit():
as opposed to if request.method == "POST"
, and using form.body.data
as opposed to request.form.get("body")
when form
is supplied in render_template
as in:
1
2
3
4
5
form = CreatePostForm()
# more code ..
return render_template("make-post.html", form=form)
Day 66 - Cafe and Wifi with REST API
An update to the Cafe and Wifi Project โ๐ถ: Cafe and Wifi with RESTful API
Skipped Day 65 on design since Iโve picked up that course a few months back. And also because Iโm excited about this Dayโs topic ๐
Analogy:
- customer: client
- order: request
- api: language (can take many forms)
- waiter: server
Other examples of protocols are HTTP, HTTPS, and FTP.
REST stands for REpresentational State Transfer and is the gold standard guidelines to communicate with a web API / A set of rules that web developers can follow when building web APIs. A RESTful website follows the REST principles.
Serialization is the process of turning an SQL object into JSON โ the structure of the data returned by the endpoints.
Two important features of REST:
- Use HTTP Request Verbs
- GET, POST, PUT, PATCH (new!), DELETE
- Use Specific Pattern of Routes/Endpoint URLs
- e.g.
/artiles
,/articles/about-me
the latter is a specific article
- e.g.
Resources: Postman to manage API calls and make documentations!
My take at the Cafe & Wifi documentation
Iโm still confused about when to use request.args.get()
and request.form.get()
. The former is for parsing a URL e.g. /search/?loc=Peckham
or /update-cafe/<int:id>
.. def update_cafe(id)
. The latter is for POST
when a form is created even if itโs implicit (e.g. when using Postman) e.g. Cafe(location=request.form.get("location"))
where the equivalent HTML has an attribute name
with value location
as in <input type="text" name="location"/>
.
โ Use of request.form.get("key")
returns None if key
is not present. This is preferrable to request.form["key"]
which returns an error if key
is not found.
Get a variable from link e.g. http://127.0.0.1:5000/search/?loc=Peckham
1
loc = request.args.get("loc")
A method to convert a Model to a dictionary
1
2
3
4
5
6
class Cafe ()
# more code here
def to_dict(self):
cafe_dictionary = {column.name: getattr(self, column.name) for column in self.__table__.columns}
return cafe_dictionary
1
2
3
4
5
@app.route("/random", methods=["GET"]) # can also remove methods because all routes have GET
def get_random_cafe():
cafes = Cafe().query.all()
random_cafe = choice(cafes)
return jsonify(cafe = random_cafe.to_dict())
For POST
:
1
2
3
4
5
6
7
8
9
10
11
12
from distutils.utils import strtobool
# strtobool converts a string to a boolean
# (e.g. "false" and"False" are False or 0, "true" & "True" is True or 1)
@app.route("/add", methods=["POST"])
def post_new_cafe():
new_cafe = Cafe(
name=request.form.get("name"),
map_url=request.form.get("map_url")
has_sockets=strtobool(request.form.get("has_sockets")),
# more code
)
Example of a PATCH
1
2
3
4
5
6
@app.route("/update-price/<int:cafe_id>", methods=["PATCH"])
def path_update_price(cafe_id):
cafe = Cafe.query.get(cafe_id)
if cafe:
cafe.coffee_price = request.args.get("new_price")
db.session.commit()
Example of responses
1
2
3
return jsonify(response={"success": "Successfully deleted the cafe."}), 200
return jsonify(error={"Not Found": "Sorry a cafe with that id was not found in the database"}), 404
return jsonify(error={"Forbidden": "Sorry, that's not allowed. Make sure you have the correct API key"}), 403
Day 64 - My Top Movies
Here is the app ๐ฝ: My top Movies - Deployed
Features:
- Displays movies according to rating, top-rated first
- Can edit both rating and review
- Can delete entries
- Movie details populated through the The Movie Database (TMDB) API
I populated it with my favorite movies (see below ๐) but the db might get deleted or changed overtime.
The code in Replit: My Top Movies and the Github repo: my-top-movies. In the deployed and the github repo, I changed the view of the cards to be in a responsive grid.
This project has some interconnectedness that was a bit confusing. So when I got stuck, I found that back-tracking and thinking about each connection one at a time helped.
Order database entries by column
1
movies = Movie.query.order_by(Movie.rating).all()
My top movies! These are my go-to movies when Iโm bored, sad, or just in general. I like animations (Studio Ghibli), horror (The Conjuring Universe), rom-com (yep!), and the Hunger Games!
Day 63 - Library
Flask with SQL functionality using SQL Alchemy ๐: Library
A model using SQL Alchemy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from flask import Flask, render_template, request, redirect, url_for, redirect
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/ new-books-collection.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
author = db.Column(db.String(250), nullable=False)
rating = db.Column(db.Float, nullable=False)
# create the database
db.create_all()
# output contents of the database
Books.query.all()
CRUD functions:
Create an entry
1
2
3
4
5
6
7
8
9
def add():
if request.method == "POST":
book = Books(title = request.form["title"],
author = request.form["author"],
rating = request.form["rating"])
db.session.add(book)
db.session.commit()
return redirect(url_for("home"))
return render_template("add.html")
Update an entry
1
2
3
4
5
6
7
@app.route("/edit/<id>", methods=["GET", "POST"])
def edit_rating(id):
book_to_update = Books.query.get(id)
if request.method == "POST":
book_to_update.rating = request.form["rating"]
db.session.commit()
return redirect(url_for("home"))
Read
1
Books.query.all()
then in the HTML, use curly braces and dot notation
1
2
3
4
5
6
<form action="{{ url_for('edit_rating', id=book.id) }}", method="post">
<h1>Book name: {{ book.title }}</h1>
<h1>Current rating: {{book.rating }} </h1>
<input type="text" placeholder="New Rating" name="rating">
<button>Change Rating</button>
</form>
Delete an entry
1
<a href="{{ url_for('delete', id=book.id) }}">Delete </a>
1
2
3
4
id = request.args.get("id")
book_to_delete = Books.query.get(id)
db.session.delete(book_to_delete)
db.session.commit()
Resources:
Redirects are a bit confusing
Day 62 - Coffee & Wifi
Cofee and Wifi โ๐ถ: Coffee & WiFi
Useful Jinja feature that I discovered while digging: For loop index and attributes in Jinja
Day 61 - Flask-WTForms
Template code using Flask-WTForms and Flask-Bootstrap: flask-wtforms
Resources:
Quick form
1
2
3
{% extends "bootstrap/base.html"%}
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form, novalidate=True) }}
Day 60 - Contact Form
Added functionality to the contact form for the blog: ๐Blog
Template contact form with some functions: Contact Form
Using Flask request
1
2
3
4
5
6
7
8
from flask import request
@app.route("/login", methods=["POST"])
def received_data():
username = request.form["username"]
password = request.form["password"]
return f"<h1>Name: {username}, Password: {password}</h1>"
In the HTML file, add action="<redirect>" method=<"get" or "post">
. Add value for name in input tag: name="username"
1
2
3
4
5
6
7
<form action="/login" method="post">
<labe >Name</label>
<input type="text" placeholder="name" name="username">
<label >Pasword</label>
<input type="text" placeholder="password" name="password">
<button type="submit">Ok</button>
</form>
Day 59 (Capstone Part 2) - Blog with Styling
Stub Blog using Flask / Jinja and deployed using Heroku: ๐Blog
The repo for this project is here: my-blog-flask
Ways to serve static files:
1
url({{ url_for('static', filename='assets/img/home-bg.jpg')}})
also
1
2
3
4
app = Flask(__name__,
static_url_path = "",
static_folder = "static",
templates_folder = "templates")
Resources:
Useful resource on how to setup a flask app to deploy in Heroku:
Day 58 - Tindog
Iโve come around to front-end web development (again!) and, admittedly, I sat on this for daysโฆ But here it is! ๐ ๐
Landing page for a dating site for dogs ๐ถ: Tindog
The repo for this project is here: tindog
CDN - Content Delivery Network; Instead of hosting a website in just one location, there are multiple locations that can deliver the website; Cuts down on the latency i.e. how long the website will load up
Bootstrap uses maxcdn
; Look for the shortest route to download the CSS file; Browser caches (saves local copy) so browser does not have to download it again which further cuts down the latency
Workflow:
- Plan first!
- Wireframe - low-fidelity representation of a website
- Mockup (optional) - high-fidelity representation of the your app or web design
- Prototype (optional) - animated version of your website
Resources:
- Awwwards
- UI Patterns
- Dribble
- Sneak Peek It
- Balsamiq
- Fonts
- Font awesome
- Bootstrap examples
- Bootsnipp
- Mobile-friendly
Javascript is responsible for the behaviour of the website; CSS for the appearance.
Responsive does not mean fast; it means that the website respond to the size of the viewport i.e. desktop, tablet, or phone.
Positioning:
- Sequential (top is closer to the back)
- Heirarchical (child sits over parents)
position
- absolute - take divs outside of the flow, bottom div at the top
- z-index - positive: forwards; negative: backwards; only works with
position
at the parent div; by default all elements have a z-index of 0
Refactoring: neat, tidy, readable code By order of importance
- Readability
- Modularity
- Efficiency
- Length
.container.title
- targets an element with two classes class="container title"
.container .title
- targets an element with class .title
inside another element with class .container
Consider using classes for specific styling rather than targetting a tag itself.
Day 57 (Capstone Part 1) - Blog
Simple blog template ๐: Blog
Jinja - used for templating in Python; already installed with Flask
1
2
<h1>{{ 5 * 6 }}</h1>
<h1>Hey {{ name.title() }},</h1>
1
2
3
4
5
6
7
8
9
from flask import Flask, render_template
import random
app = Flask(__name__)
@app.route("/")
def home():
random_number = random.randint(1,10)
return render_template("index.html", num=random_number)
1
<h3>Random Number: {{ num }}</h3>
Multi-line python codes in HTML
1
2
3
4
5
6
{% for blog_post in posts: %}
{% if blog_post["id"] == 2: %}
<h1>{{ blog_post["title"] }}</h1>
<h2>{{ blog_post["subtitle"] }}</h2>
{% endif %}
{% endfor %}
URL building
1
<a href="{{ url_for('get_blog', num=3) }}">Go to blog</a>
Where get_blog
is a function in app.py
that renders a page, and num
is a keyword argument that can be passed:
1
2
3
4
@app.route("/blog/<num>")
def get_blog(num):
# some code here
return render_template("blog.html", posts=all_posts)
Resources:
- Update your footer
- Genderize.io - predict the gender of a name
- Agify.io - predict the age of a name
- Npoint.io - create own API endpoint
Day 56 - Name Card
Name card flask template: Name Card
HTML Source: Identity
How to render HTML and statifiles:
HTML files should be inside the folder templates
.
Static files like images, CSS, videos etc should be in the folder static
and linked accordingly in the HTML files e.g. static/<image_name>.png
1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("index.html")
if __name__ == "__main__":
app.run()
Chrome tends to cache static files so in order to view changes in CSS, need to do a hard reload: Shift + Reload
Free templates: HTML5
How to trigger editing a webpage in Chrome developer:
In console (JS):
1
document.body.contentEditable = true
Then save the webpage.
Resource for images: Unsplash
Day 55 - Higher Lower Web Game
Higher-lower game in web form: Higher Lower Web Game
Parse a URL in Flask
1
2
3
@app.route("/username/<name>/1") # can add before or after or leave as /<name>
def greet(name):
return f"Hello {name}"
Run on debug mode to reload server automatically; Errors are outputed in View
1
2
if __name__ == "__main__":
app.run(debug=True)
Can open an interactive shell (via icon on right), enter debugging PIN to continue.
Converter - converts url variable name to a datatype
1
2
3
4
5
6
7
8
9
10
11
@app.route("/username/<path:name>")
def greet(name):
return f"Hello there {name}"
# <local:5000>/name/1
# Hello there name/1
@app.route("/username/<name>/<int:number>")
def greet(name, number):
return f"Hello there {name}, you are {number} years old"
#<local:5000>/name/2
# Hello there name, you are 2 years old
Render HTML
1
2
3
4
5
@app.route("/")
def hello_world():
return "<h1 style='text-align: center'>Hello, World!</h1> \
<p>This is a paragraph</p> \
<img width=200px src='https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif'>"
Render HTML tags in decorators
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def make_bold(function):
def wrapper():
return f"<b>{function()}</b>"
return wrapper
def make_emphasis(function):
def wrapper():
return f"<em>{function()}</em>"
return wrapper
def make_underlined(function):
def wrapper():
return f"<u>{function()}</u>"
return wrapper
@app.route("/bye")
@make_bold
@make_emphasis
@make_underlined
def say_bye():
return "Bye"
Advanced decorator - pass arguments as *args
and/or **kwargs
to wrapper function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User:
def __init__(self, name):
self.name = name
self.is_logged_in = False
def is_authenticated_decorator(function):
def wrapper(*args, **kwargs):
if args[0].is_logged_in == True:
function(args[0])
return wrapper
@is_authenticated_decorator
def create_blog_post(user):
print(f"This is {user.name}'s new blog post.")
new_user = User("my_name")
new_user.is_logged_in = True
create_blog_post(new_user)
Another example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Create the logging_decorator() function
def logging_decorator(function):
def wrapper(*args):
result = function(*args)
print(f"Function name: {function.__name__}, arguments: {args} ")
print(f"Result: {result}")
return wrapper
# Use the decorator
@logging_decorator
def sum(n1, n2):
return n1 + n2
# Call function
sum(3,2)
# Function name: sum, arguments: (3, 2)
# Result: 5
Day 54 - Introduction to Flask
Starting code here ๐: Introduction to Flask
Full Stack = Front-End + Back-End
Front: HTML, CSS, JS; Frameworks: Angular, React โฆ
Back-end: JS, Python, Ruby, etc..; Frameworks: Node, Flask โฆ
Frameworks: Tools with pre-built functionalities
Examples of frameworks in Python:
- Flask
- Django
- Cherrypy
- Pyramid
Backend:
- Client
- Server
- Datbase
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "Hello, World!"
@app.route("/bye")
def say_bye():
return "Bye"
if __name__ == "__main__":
app.run()
Run as:
1
flask run
__name__
is a Special Attribute in Python which corresponds to the current class or module name; __main__
is the name of the scope in which the top-level code runs.
Python functions can be treated as first-class objects - they can be passed around as arguments e.g. int/string/float etc
1
2
3
4
5
6
7
8
9
10
11
def add(n1, n2):
return n1 + n2
def subtract(n1, n2):
return n1 - n2
def multiply(n1, n2):
return n1 * n2
def divide(n1, n2):
return n1 / n2
Functions as first-class objects
1
2
3
4
5
6
def calculate(calc_function, n1, n2):
return calc_function(n1, n2)
result = calculate(add, 2, 3)
print(result)
# 5
Nested functions
1
2
3
4
5
6
7
8
9
10
11
12
def outer_function():
print("I'm outer")
def nested_function():
print("I'm inner")
nested_function()
outer_function()
# I'm outer
# I'm inner
Functions can be returned from other functions
1
2
3
4
5
6
7
8
9
10
11
12
def outer_function():
print("I'm outer")
def nested_function():
print("I'm inner")
return nested_function
inner_function = outer_function()
inner_function() # add parenthesis (calls variable as a function)
# I'm outer
# I'm inner
Simple decorator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import time
current_time = time.time()
print(current_time)
def speed_calc_decorator(function):
def wrapper_function():
start_time = time.time()
function()
end_time = time.time()
print(f"{function.__name__} run speed: {end_time - start_time}s")
return wrapper_function
@speed_calc_decorator
def fast_function():
for i in range(10000000):
i * i
@speed_calc_decorator
def slow_function():
for i in range(100000000):
i * i
fast_function()
slow_function()
Day 53 (Capstone) - Data Entry Automation
Automated data entry ๐ฆ: Data entry automation
Goes through Zillow (a website containing listings of properties), scrapes all results for a certain search, then fills up a Google Form with these information.
Pass a header
in requests in order to access the website without CAPTCHA. Supply headers of the default browser.
Since BeautifulSoup can only parse the first 9 listings, Iโve used Selenium all throughout:
- Clicked / unclicked an element on the right panel (I used the dropdown element since itโs clickable; trying this on any other element raises an exception)
- Scrolled down using
Key.DOWN
for n seconds (this hopefully gets the page to load until the end; otherwise increase n)- I tried scrolling until an element but all the listing information do not tend to load with the speed of the scroll
- Maximizing the window also helped with the speed
I also scraped until the end of the results (e.g. until page 20). I can only get until page 20 (800 listings) even though Zillow indicates there are >1,600 properties.
Time:
1
2
3
real 32m40.976s
user 0m18.578s
sys 0m3.234s
Zillow still checks for a human now and again so the script above fails when that happens.
I skipped Days 50-52 because I didnโt want to deal with social media right now. Hereโs some interesting links:
- Does Tweeting at Companies Really Work?
- This person does not exist
- Translate URL to human-readable form: URL Decoder/Encoder
Day 49 - Automate Job Application
Automate LinkedIn job application (template) code ๐ผ: Automate Job Applications
Used XPaths generously, and also time.sleep()
function which waits for the page to load before executing the next part of the code
Day 48 - Game Clicker Bot
Cookie world domination here ๐ช: Game Clicker Bot
Automates the game (click and purchase) for five minutes.
Got the following score:
1
2
Total score: 1239
Cookies per second: cookies/second : 97.4
Uses Selenium web driver.
Download the Chrome Driver that matches Chrome browserโs version: ChromeDriver WebDriver for Chrome
Read the docs for locating elements in Selenium
1
2
3
4
5
6
7
8
9
10
11
12
13
14
chrome_driver_path="/path/to/exec"
driver = webdriver.Chrome(executable_path=chrome_driver_path)
url = "https://www.amazon.com/Legend-Zelda-Skyward-Sword-Nintendo-Switch/dp/B08WWFWRY6"
# open the url in browser
driver.get(url)
# get element
price = driver.find_element_by_id("priceblock_ourprice")
print(price.text)
# quit tab or program
# driver.close() # quits particular tab
driver.quit() # quits entire program
BeautifulSoup scrapes data from website HTML/XML but gets stuck if website uses Javascript to load (or other conditions) whereas Selenium would do the same thing as humans i.e. opening websites, grabbing the needed info. Can do a lot more using Selenium.
1
2
3
4
5
6
7
8
9
10
11
12
13
# url = "python.org"
search_bar = driver.find_element_by_name("q")
print(search_bar) # a Selenium object
print(search_bar.tag_name)
print(search_bar.get_attribute("placeholder"))
logo = driver.find_element_by_class_name("logo")
print(logo.size)
documentation_link = driver.find_elements_by_css_selector(".documentation-widget a") # list
print(documentation_link.text)
XPath - What is XPath
1
2
bug_link = driver.find_element_by_xpath('//*[@id="site-map"]/div[2]/div/ul/li[3]/a')
print(bug_link.text)
To get more than one elements, use find_elments*
in method name, as in e.g. .find_elements_by_xpath
or find_elements_by_name
Interactions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Selenium import webdriver
from Selenium.webdriver.common.keys import Keys
chrome_driver_path="/mnt/d/Programs/Chromedriver_win32/chromedriver.exe"
driver = webdriver.Chrome(executable_path=chrome_driver_path)
url = "https://en.wikipedia.org/wiki/Main_Page"
driver.get(url)
# selecting through tag
article_count = driver.find_element_by_css_selector("#articlecount a")
article_count.click()
# selecting through linked text (text inside link tag)
all_portals = driver.find_element_by_link_text("All portals")
all_portals.click()
# write in input bar
search = driver.find_element_by_name("search")
search.send_keys("Python")
# enter in keyboard
search.send_keys(Keys.ENTER)
Day 47 - Amazon Price Tracker
Get sales alert here ๐: Amazon Price Tracker
Sends an email when an item in Amazon is equal to or below a price. (May be adapted to other websites.)
See current browserโs headers here: MY HTTP Header. Pass the entire string as values in headers.
Web article on the difference between HTML and XML.
Day 46 - Music Time Machine
Travel back in time through music! ๐ต: Music Time Machine
This is so cool! โญ And I get to have new playlists while working.
This code uses the spotipy
library. I had some hiccups with authentication (my systemsโ config). Otherwise the documentation is pretty good.
This is the Developer page for Spotify.
Hereโs the spotipy
Github repo which contains code examples.
Day 45 - Greatest Movies
Top 100 Greatest Movies according to Empire Online ๐ฝ: Greatest Movies
(I actually havenโt been able to sit through The Godfather try as I may ๐ )
The script is a bit complex because the source website has transitioned out of vanilla HTML โ essentially, contents were loaded using Javascript on an almost empty HTML plate. Movie names had to be parsed from the <script>
tag, and then further re-formatted.
Iโve skipped over several days since days 41-44 were about HTML and CSS (which I feel at this point Iโm already familiar with).
Is Web Scraping legal? ๐ค
- Web scraping is now legal: Hereโs what that means for Data Scientists
- โ Publicly available
- โ Not copyrighted
- โ Using data privately
- โ Using data for commercial purposes (business)
- โ Data behind authentication (e.g. scraping from social media sites where you need to login - see fine print)
โ Just because itโs legal, doesnโt mean you can do it - CAPTCHA / reCAPTCHA
๐ญ Ethics: think about if itโs right or wrong; putting aside whether itโs legal or not.
๐ Go for the API if possible
๐ Respect the owner - donโt scrape every few ms (e.g. try < 1 per minute)
๐ Check <website>/robots.txt
. e.g https://news.ycombinator.com/robots.txt
specifies which endpoints are not allowed to be scraped
1
2
3
4
5
6
7
8
9
10
11
User-Agent: * # person or bot scraping
Disallow: /x?
Disallow: /r?
Disallow: /vote?
Disallow: /reply?
Disallow: /submitted?
Disallow: /submitlink?
Disallow: /threads?
Crawl-delay: 30
# the number of seconds that to wait each time
# the website is accessed
Compare this to LinkInโs robot.txt
statement
Read a website using requests
1
2
3
response = requests.get("https://news.ycombinator.com")
yc_combinator = response.text
soup = BeautifulSoup(yc_webpage, "html.parser")
๐ Can use html.parser
or lxml
which needs to be imported import lxml
; sometimes lxml
works better than html.parser
Some syntax reference:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from bs4 import BeautifulSoup
# import lxml
with open("./website.html") as file:
contents = file.read()
soup = BeautifulSoup(contents, "html.parser") #or "lxml"
print(soup.title)
# <title>Angela's Personal Site</title
print(soup.title.name)
# title
print(soup.title.string)
# Angela's Personal Site
# matches first instance
print(soup.p)
# pretty printing
print(soup.prettify())
# list of all anchor tags
all_anchor_tags = soup.find_all(name="a")
for tag in all_anchor_tags:
print(tag.getText())
print(tag.get("href"))
# isolate by name tag
heading = soup.find(name="h1", id="name")
print(heading)
# <h1 id="name">Angela Yu</h1>
# same for isolating by class name
section_heading = soup.find(name="h3", class_="heading")
# "class" is a reserved word in Python
print(section_heading)
# <h3 class="heading">Books and Teaching</h3>
# using selector
company_url = soup.select_one(selector="p a")
print(company_url)
# <a href="https://www.appbrewery.co/">The App Brewery</a>
# using selector
name = soup.select_one(selector="#name")
print(name)
# <h1 id="name">Angela Yu</h1>
headings = soup.select(".heading")
print(headings)
# [<h3 class="heading">Books and Teaching</h3>,
# <h3 class="heading">Other Pages</h3>]
Day 40 (Capstone Part 2) - Flight club
Join the flight club here ๐ค๐ซ: Flight club
This sends a text and an email (a user can be added) when there are flights cheaper than a certain price. If direct flights are not avialable, it looks for flights with at most one (1) stop-over.
Flights are limited at the moment, so testing was challenging.
I also encountered a weird bug because I was passing an entire class as an argument.
โ Sheety has a cap of 200 requests per month in its free tier.
Historic low prices for airfare: Fare Detective
Day 39 (Capstone Part 1) - Flight deal finder
Flight deal finder ๐ซ: Flight deal finder
Sends a message when flights are equal to or cheaper than a set price
This took longer than I thought: getting through the API documentation and writing models.
Flight search API: Tequila
Day 38 - Workout Tracker
Workout tracker using Google Sheets ๐โโ๏ธ : Workout tracker
This tracker uses NLP capabilities from OpenAI API.
This was a lot of fun to make and certainly challenging in terms of reading documentations.
API resources:
Storing .env
in Replit: Storing secrets in .env
Day 37 - Pixela Habit Tracker
Template for the Pixela habit tracker here๐งโโ๏ธ: Pixela habit tracker
Advanced authentication and POST / PUT / DELETE Requests
1
2
3
4
5
6
7
8
# get data
requests.get()
# post data
requests.post(url=endpoint, json=params)
# update a piece of data
requests.put()
# delete a piece of data
requests.delete()
Website: Pixela, API Documentation
.strftime()
to specific date string formats
1
2
today = datetime.now().strftime("%Y%m%d")
yesterday = datetime(year=2020, month=7, day=12).strftime("%Y%m%d")
Day 36 - Stock Trading News Alert
A script to send stock updates ๐: Stock trading news alert
* sends a text when stock price of a certain company changes 5% or more between the present day and the day before
API sources:
Day 35 - Rain Alert App
Rain Alert app โ: Rain Alert
API authentication using API key: way for API providers to track usage and/or deny access past limit
Weather related websites:
Visual parsing of JSON texts: Online JSON Viewer
Send text messages using Twillio
Retrieve variables from environment
1
2
3
import os
API_KEY = os.environ.get("API_KEY")
In the commandline
1
export API_KEY="thisIsASecret"
Day 34 - Quizzler App
Play the Quizzler App here ๐: Quizzler app
HTML Entities - a way to replace characters in text so they are not confused with HTML characters
HTML Escape to convert into human-readable format
Or using the html
module
1
2
3
import html
text = "It's <tag>"
q_text = html.unescape(text)
Specify paramater as type: โClassโ
1
2
3
4
from quiz_brain import QuizBrain
def __init__(self, quiz_brain: QuizBrain):
# code here
Usage in main.py
1
2
3
quiz = QuizBrain()
quiz_ui = QuizInterface(quiz)
Type hints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
age: int
name: str
height: float
is_human: bool
def police_check(age: int) -> bool:
if age > 18:
can_drive = True
else:
can_drive = False
return can_drive
if print(police_check(19)):
print("You may pass")
else:
print("Pay a fine")
# will crash code
if print(police_check("twelve")):
print("You may pass")
else:
print("Pay a fine")
Day 33 - ISS Overhead
Send an email if the International Space Station is overhead ๐: ISS Overhead
API (Application Programming Interface): a set of commands, functions, protocols, and objects that programmers can use to create software or interact with an external system
API Endpoint: where to extract data; e.g. url api.coinbase.com
API Request: get a piece of data from a website
API Parameters: inputs
International Space Station Current Location
- 1xx: Hold on
- 2xx: Here you go
- 3xx: Go away
- 4xx: You screwed up
- 5xx: I screwed up
Latitude-Longitude To Address Address to Latitude-Longitude
Day 32 - Birthday Greeting
Email a birthday greeting ๐: Birthday greeting
SMTP stands for Simple Mail Transfer Protocol
1
2
3
4
Gmail(smtp.gmail.com)
Yahoo(smtp.mail.yahoo.com)
Hotmail(smtp.live.com)
Outlook(smtp-mail.outlook.com)
Template code for sending an email
1
2
3
4
5
6
7
8
9
10
11
12
13
import smtplib
my_email = "test@gmail.com"
password = "abc123()"
with smtplib.SMTP("smtp.gmail.com") as connection:
connection.starttls()
connection.login(user=my_email, password=password)
connection.sendemail(
from_addr=my_email,
to_addrs="test@yahoo.com",
msg="Subject:Hello\n\nThis is the body of my email."
)
Some datetime
codes
1
2
3
4
5
6
7
8
import datetime as dt
now = dt.datetime.now()
year = now.year
print(year)
print(now.weekday())
date_of_birth = dt.datetime(year = 1995, month = 12, day = 15, hour=4)
print(date_of_birth)
Host code in Python Anywhere
Day 31 - Flash Card App
Flash card app: Flash card
Itโs another capstone project!
Timer in tkinter
1
2
3
4
# set the timer
flip_timer = window.after(3000, func=flip_card)
# stop the timer
window.after_cancel(flip_timer)
Day 30 - Password Manager with Search
๐๐ Password manager with search
Catching exceptions
1
2
3
4
5
6
7
8
try:
# something that might cause an exception
except:
# do this if there was an exception
else:
# do this if there were no exceptions
finally:
# do this no matter what happens
Use Exceptions if there is no other way (i.e. an easy alternative) to handle an error or if the error happens in exceptional cases. Otherwise, an if-else
statement can be used.
Error handling with recursions
1
2
3
4
5
6
7
8
9
10
def generate_phonetic():
word = input("Enter a word: ").upper()
try:
output_list = [phonetic_dict[letter] for letter in word]
except KeyError:
print("Sorry, only letters in the alphabet please.")
generate_phonetic()
else:
print(output_list)
generate_phonetic()
JSON - update()
and dump()
are a bit confusing
1
2
3
4
5
6
7
8
9
import json
new_file = {"key":"value"}
with open("file.json") as data_file:
# read a json file
data = json.load(data_file)
# update a json file
data.update(new_data)
# write in a json file
json.dump(new_data, data_file)
Day 29 - Password Manager
Password manager/ generator ๐ : Password manager
Also learned about pyperclip, a clipboard module in Python.
Day 28 - Pomodoro Timer
Tomato timer here ๐ : Pomodoro
Event Driven programs watch the screen โ a while loop before the mainloop()
program will result in an error since the program will stop listening when it encounters the while loop.
Recursion
1
2
3
4
def count_down(count):
print(count)
if count > 0:
window.after(1000, count_down, count-1)
Dynaminc typing
1
2
3
count_down = 0
count_down = "00"
# change type from int to str
Python is a strongly, dynamically typed language
Day 27 - Miles to Kilometer Converter
Convert miles to kilometer ๐ฃ: Miles to Kilometer
Moving on to GUIโจ using tKinter
Tk commands: Tk commands
Steps to add components on the screen:
- Make a component
- Specify how that component will be laid out on the screen
1 2
my_label = tkinter.Label(text="I am a label") my_label.pack() # The Packer
tkinter layouts:
- Pack
- Place
- Grid
1 2 3 4
my_label.pack(side="left") my_label.place(x=0,y=0) my_label.place(x=100, y=100) # x moves to right, y moves down my_label.grid(column=0, row=0)
Pack and place are incompatible
Advanced arguments ๐ก๐ข๐
Unlimited positional arguments: *args
-> tuple
1
2
3
def add(*args):
print(sum(args))
add(3,5,8,10,12) # 38
Unlimited keyword arguments: **kwargs
-> dictionary
1
2
3
4
5
6
def calculate(n, **kwargs):
n += kwargs["add"]
n *= kwargs["multiply"]
print(n)
calculate(2, add=3, multiply=5) # (2+3)*5 = 25
Use in a class:
1
2
3
4
5
6
7
8
9
10
class Car:
def __init__(self, **kw) :
self.make = kw.get("make")
self.model = kw.get("model")
self.color = kw.get("color")
self.seats = kw.get("seats")
my_car = Car(make="Nissan", model="Skyline")
print(my_car.color)
# None
Mixed
1
2
3
4
5
def all_aboard(a, *args, **kw):
print(a, args, kw)
all_aboard(4, 7, 3, 0, x=10, y=64)
# 4 (7,3,5) {'x: 10, 'y': 64}
Day 26 - NATO Alphabet
Translate any word to the NATO alphabet here: NATO alphabet
This is actually helpful for me! (Also, isnโt spelling out the letters easier?)
List comprehension
1
new_list = [new_item for item in list if test]
Dictionary comprehension
1
2
new_dict = {new_key:new_value for (key,value) in dict.items if test}
Iterate over a Pandas DataFrame: use iterrows()
1
2
3
4
5
6
for (index, row) in df.iterrows():
print(index)
print(row)
# each row is a pandas.Series object so dot-notation can be used
print(row.col1)
print(row.col2)
Day 25 - Guess US States
Guess US States here ๐: US States
Website for quizzes: Sporkle
Code to get coordinates of mouseclick in turtle
1
2
3
4
def get_mouse_click_coor(x,y):
print(x,y)
turtle.onscreenclick(get_mouse_click_coor)
turtle.mainloop() # instead of `exitonclick()`
Day 24 - Snake with High Score
๐ Snake but you can retain the high scores: Snake High Score
๐ง Mail merge - an automatic letter generator: Mail merge
Mail merge reminds me of the time I wrote a script in Python/Latex that created name tags automatically. It took a bit to generate the code but worth the effort for the number of events x participants that we had.
Optimized readability > Premature optimization (Efficient code as soon as possible)
โYou write so that people understand what you say.โ
Day 23 (Capstone) - Turtle crossing
๐ฃ๐ข: Turtle crossing
Itโs another capstone, another landmark! ๐ This game brings back a lot of memories!
๐ก Using random chance to slow down the generation of cars can be done this way:
1
2
3
4
def create_car(self):
random_chance = random.randint(1, 6)
if random_chance == 1:
# generate cars
Needed help on the car manager bit.. but it was interesting. Got confused about when to inherit or not.
Programming is a way to test oneโs thinking! ๐ต๐ซ
Day 22 - Pong
Play Pong here ๐ฎ: Pong
To dos for this game:
- Create the screen
- Create and move a paddle
- Create another paddle
- Create the ball and make it move
- Detect collision with wall and bounce
- Detect collision with paddle
- Detect when paddle misses
- Keep score
Day 21 - Snake Game Part 2
The full Snake Game here ๐: Snake game
- Detect collision with food
- Create a scoreboard
- Detect collision with wall
- Detect collision with tail
Inheritance
1
2
3
4
5
6
class Fish(Animal):
def __init__(self):
super().__init__()
def breathe(self):
super().breathe()
# add something
Day 20 - Snake Game Part 1
Snake Game Part 1 here ๐: Snake game Part 1
To dos for this game:
- Create a body
- Move the snake
- Control the snake
- Detect collision with food
- Create a scoreboard
- Detect collision with wall
- Detect collision with tail
๐ก Focus on understanding, not on memorising.
Getting the hangout of OOP. Also need to consider how to make future modifications of the code easier โ CONSTANTS (at the top of the code) help!
Day 19 - Etch-A-Sketch App
๐ข Turtle Race
Etch-A-Sketch
I had a great time making the turtle race game! ๐ข
Higher order functions
1
2
def calculator(n1, n2, func):
return func(n1, n2)
States, different versions of the same Object
1
2
3
timmy.color = green
tommy.color = purple
Day 18 - Hirst Painting
See it here ๐จ: Hirst painting
Trinket
Colorgram.py
RGB Calculator
Day 17 - Quiz Game
Play the game here: quiz-game
Generate trivia questions here ๐ Open Trivia Database
Run for the bus! ๐๐โ
Different types of cases:
- PascalCase
- camelCase
- snake_case
Attaching an attribute to an object
1
2
3
4
5
6
7
class User:
speed = 4
user_1 = User()
user_1.id = "001"
user_1.username = "angela"
print(user_1.username)
Constructor function __init__(self)
initializes the Classes:
1
2
3
4
5
class Car:
def __init__(self, seats):
self.seats = seats
my_car = Car(5)
Classes, attributes, methods, objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User:
def __init__(self, user_id, username):
# attributes
self.id = user_id
self.username = username
self.followers = 0 # default value
self.following = 0
# method
def follow(self, user):
user.followers += 1
user.following += 1
# objects
user_1 = User("001", "angela")
user_2 = User("002", "jack")
user_1.follow(user_2)
print(user_1.followers)
print(user_1.following)
print(user_2.followers)
print(user_2.following)
Day 16 - OOP Coffee Machine
Code here: oop-coffee-machine
This day was a bit more challenging. But Iโve ventured into the Intermediate level so ๐. Between going over the stub code and reading the documentation, there were a few snags. But the main program looks clean with the help of Python classes.
Thereโs this article about the Four Programming Styles in Python:
- Functional
- Imperative
- Object-oriented
- Procedural
When I work, I tend to use 1, 2, and 4, and seldom object-oriented. Today (and until September/October this year 2021), we are learning about web development using Django in WWCode Manila. The codes are heavily OOP because we are using a framework.
So, this lesson and a few more after this, is something that I look forward to. There is also an element of code design which is fun.
Day 15 - Coffee Machine
Get your own coffee here โ: coffee-machine
Imagine the amount of coffee I ordered to get this to work. Had a hard time ordering โcappuccinoโ since the spelling is difficult (I then added a condition). I thought about being in a real cafรฉ โ what would the transactions be โ and wrote that in the code.
Ahh ๐ญ good times.
Day 14 - Higher-lower
Play my solution ๐ฎ: higher-lower
โ Might need to refactor this code. Was so sleepy when I wrote this.
Day 13 - Debugging
๐๐ซ Tips:
- Describe the problem, challenge your assumptions!
- Reproduce the error, notice when it happens! See if the code can be changed so it reproduces the error.
- Play computer. Evaluate each line to figure out the problem.
- Fix the erros as they come. Watch out the red underlines. Watch out for silent errors.
- Use your friend
print()
- Use a debugger e.g. Python Tutor
- Take a break. ๐ด
- Ask a real human friend (not
print()
). - Run your code often. Confirm that the code runs like itโs intended.
- Ask StackOverflow (other developers) but only if you think that the bug is unique. Otherwise, check existing solutions.
โ The more bugs you solve, the better you get at it.
Day 12 - Guess the number
Play my solution ๐ฎ: guess the number
Todayโs topic is scope: Global, local, block (same as enclosed scope?).
- Using and not using
global
1 2 3 4 5 6 7
enemies = 1 def increase_enemies(): global enemies enemies +=1 return enemies print(increase_enemies())
1 2 3 4
enemies = 1 def increase_enemies(): return enemies + 1 enemies = increase_enemies()
For the first version of the code, I was able to use
global
but final version went for a functional code which looks like the second one. Because of enclosed scoping, I cannot call the variable out of a nested function. - Constants written in capital letters. ๐ Something that still does not come to me automatically.
1 2
PI = 3.14159 URL = "google.com"
ASCII fonts ๐
- โจFunctional codingโจ, and writing Docstrings!
Day 11 (Capstone) - Blackjack
Play my solution ๐ฎ: blackjack
You win if you score 21 or closer to 21 than the dealer. You lose if you score over 21. Full game rules are here. In this version, the deck is infinite.
Sample without replacement:
1
random.sample(seq, n)
- Difference between append and extend:
- both add to the end of a list, but e.g. given a list
l = [1,2]
l.append([3]])
adds the object as is e.g.[1,2,[3]]
l.extend(3)
unpacks the object and will result in[1,2,3]
.extend()
only accepts iterables
In the code I used
.extend()
to add a card since the result ofrandom.sample(seq,n)
from a list is a list. - both add to the end of a list, but e.g. given a list
While loops, should still be wary of while loops!
- Formulating the
evaluation
function (if/else statements) to decide the game was nuanced.
Day 10 - Calculator
๐ก Functions inside dictionaries.
The functions:
1
2
3
4
5
6
7
8
9
10
11
12
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a / b
The dictionary:
1
2
3
4
5
6
operators = {
"+": add,
"-": subtract,
"*": multiply,
"/": divide,
}
Finally, calling the functions
1
result = operators[operator](a, b)
Day 9 - Blind auction
Day 8 - Caesar cipher
TIL That Caesar was into ciphers!
Day 7 - Hangman
Importing from an external python code
1
2
from hangman_art import stages, logo
from hangman_words import word_list
and using them inside the code
1
2
3
print(logo)
print(stages[lives])
chosen_word = random.choice(word_list)
Day 6 - Reeborg Maze
The Reeborgโs World website fun coding puzzles! ๐ค
Day 5 - Password generator
Day 4 - Rock paper scissors
Day 3 - Treasure Island
Asci art ๐จ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
___ __
(_ ( . ) )__ '. \ : / .'
'(___(_____) __ '. \ : / .'
/. _\ '. \ : / .'
.--.|/_/__ -----____ _ _____-----
_______________''.--o/___ \_______________(_)___________
~ /.'o|_o '.| ~ ~ ~
~ |/ |_| ~' ~
' ~ |_| ~ ~ ~ ~
~ ~ |_|O ~ ~
~ ___|_||_____ ~ ~ ~
~ ~ .'':. .|_|A:. ..::''.
/:. .:::|_|.\ .:. :.:\ ~
~ :..:. .:. .::..: .: ..:. ~ ~ ~
\.: .: :. .: ..:: .lcf/
~ ~ ~ ~ ~ ~
~ ~ ~ ~ ~
~ ~ ~ ~ ~
~ ~ ~ ~ ~