Big Idea 3, Simulations and SQLite Lesson Notes
My notes on the lesson by Drew, Vivian, Aliya, Sri and Sreeja
- Review Topics
- Selection
- Introduction to Algorithms
- Iteration and Simulations
- Databases
- Fitness Full Stack Database with everything including frontend
- Setting up DB
- The Model File
- API File
- Professors Database with CRUD
- Creating the Database
- Create Function
- Read Function
- Update Function
- Delete Function
- Grading
Review Topics
All of the topics below are things that have been heavily covered and used throughout the class. We will mostly be focusing on more complicated uses and mechanics of these topics.
Lists
- What are Lists?
- Lists are an ordered sequence of elements, where each element is a variable
- Unlike dictionaries, lists' keys are all integers that describe the order of the list
Some examples of lists:
- Playlist of songs
- names of students in a class
-
contacts on your phone
-
Each element of a string is referenced by an index (which is a number) and they generally start 0 but for the AP Exam it starts at 1.
- AP Exam: 1,2,3,4 etc.
- Python: 0,1,2,3 etc.
How do lists Manage Complexity of a program?
- We may not need as many variables. For example:
- One Variable that holds all students would be better than having a variable for EACH student
- There can also be a list of test scores so if they need to be curved then the same calculation can be applied to the list (that has all the students) instead of doing the calculations one at a time
Answer the following questions about the code block below:
- Why do you think lists are helpful? What word does College Board like to use to describe the function of lists?
Lists facilitate operations like addition or removal of elements, accessing specific elements by index, and iterating over all elements. The College Board uses the term "abstraction" to refer to the role of lists in programming. Lists obscure the intricacies of how individual elements are stored in memory, enabling programmers to work with groups of elements as a single entity. This simplifies the process of writing and comprehending code that deals with multiple values simultaneously, without having to be concerned about the underlying technicalities of how they are represented in the computer's memory.
# variable of type string
name = "Sri Kotturi"
print("name", name, type(name))
# variable of type integer
age = 16
print("age", age, type(age))
# variable of type float
score = 90.0
print("score", score, type(score))
print()
# variable of type list (many values in one variable)
langs = ["Python", "JavaScript", "Java", "Bash", "html"]
print("langs", langs, type(langs))
print("- langs[2]", langs[2], type(langs[2]))
print()
# variable of type dictionary (a group of keys and values)
person = {
"name": name,
"age": age,
"score": score,
"langs": langs
}
print("person", person, type(person))
print('- person["name"]', person["name"], type(person["name"]))
grade1 = 10
grade2 = grade1
average_grade = (grade1 + grade2) // 2 #what are these two slashes?
print(average_grade)
What is the value of num1, num2, and num3? Explain how each number ended up what it was.
- num1 = 4096 because 4^6
- num2 = 455 because 4097/9
- num3 = 1 because 4096%5 = 1
num1 = 2
num2 = 4
num3 = 6
num1 = num2 ** num3
num3 = num1 % 5
num2 = (num1 + num3) // 9
print(num1)
print(num2)
print(num3)
Selection
Selection refers to the process of making decisions in a program based on certain conditions. It is normally done with conditional statements.
Conditionals
What is a conditional?:
- Statement that allows code to execute different instructions if a certain condition is true or false
- Allows program to make decisions based on data and input
What are the main types of conditional statements?:
- if
- elif
- else
If statements
- The if statement is used to check if a certain condition is true. The condition can be any expression that evaulates to a boolean value, True or False. If the condition is True, then it executes a code block.
- If (condition) then (consequence)
- Example:
x = int(input("Enter a number"))
if x > 0: # if condition, check if this is true of false
print("x is positive") # code that will execute if condition is met
Else
- The else statemnt executes a code block when the if condition is False.
- If (condition) then (consequence A), else (consequence B)
Elif
- The elif statement can check multiple conditions in a sequence, and execute a certain block of code if any of the conditions are true.
-
If (condition) then (consequence A), elif (condition) then (consequence B), else (consequence C)
-
Example adding onto the code from before to take negative numbers and 0 into account
x = int(input("Enter a number, x:"))
if x > 0: # if condition, check if this is true of false
print("x is positive") # code that will execute if condition is met
elif x < 0: # if previous condition not true... elif condition, check if this is true of false
print("x is negative")# code that will execute if condition is met
else: # everything else, in this case it is if x == 0
print("x is zero") # only executes if all previous conditions are not met
x = int(input("Enter a number, x:"))
if x % 2 == 0:
print("x is even divisible by 2")
# only ever checks is x is divisble by 3 if x is even. nested conditional
if x % 3 == 0:
print("x is divisible by 3")
else:
print("x is not divisible by 3")
else:
print("x is odd")
Indentation
When using conditionals and nested conditionals in Python, it is important to pay attention to the level of indentation in the code. The code inside the if, elif, and else blocks must be indented so they are nested wihtin the outer statements. This way, Python knows which code belongs to which block.
Binary Search
What is binary search and what is it used for?:
- Searching algorithm
- Find and select a specific element in a sorted list of elements
How does binary search work?:
- Repeatedly divides the search interval in half to find the middle element and compares the middle value to the target value, if not the same then it continues on to either the lower or upper half
- Eliminate half of the remaining search interval elements each time
- Efficient way to search for element in large dataset
What is the time complexity and why?:
- O(log(N))
- The maximum number of iterations is the amount of times the list can be divided in half until it reaches 1 number
-
Dividing by 2, so it is log2(N), logarigthm of n base 2
-
You may recognize the example below from the binary lesson last Friday
import random
def binary_search_game():
low = 1
high = 100
target = random.randint(low, high)
while True:
guess = (low + high) // 2
print(f"Is your number {guess}?")
response = input("Enter 'higher', 'lower', or 'yes': ")
# conditional statements to check target number and guess
if response == 'yes':
print(f"I guessed your number {guess}!")
break
elif response == 'higher':
low = guess + 1
elif response == 'lower':
high = guess - 1
else:
print("Invalid input, please enter 'higher', 'lower', or 'yes'.")
binary_search_game()
questions = {
"Who is the best NBA player?": "Lebron James",
"What is the tallest NBA player?": "Manute bolt",
"Who is the best lakers player?": "Kobe Bryamt",
"What year did warriors win the chip?": "2022"
}
# Keep track of the user's score
score = 0
# Loop through the questions and ask them one by one
for question, answer in questions.items():
user_answer = input(question + " ")
# Check if the user's answer is correct and add to the score if it is
if user_answer.lower() == answer.lower():
print("Correct!")
score += 1
else:
print("Incorrect.")
# Offer the user a hint if they get the answer wrong
if question == "Who is the best NBA player?":
print("It starts with the letter 'L'")
elif question == "What is the tallest NBA player?":
print("It starts with the letter 'M'")
elif question == "Who is the best lakers player?":
print("His number was 24 and 8!")
elif question == "What year did warriors win the chip?":
print("Year before this year maybe?")
# Print the user's final score
print("You got", score, "out of", len(questions), "questions correct.")
if score == len(questions):
print("Well done, you got all the questions right!")
else:
print("Better luck next time!")
Introduction to Algorithms
- an algorithm is a set of instructions that describes how to solve a problem or perform a specific task using a computer program.
- It is a precise sequence of computational steps that take an input and produce an output
How do Algorithms relate to data structures?
- Algorithms often rely on specific data structures to solve problems efficiently.
- Sorting algorithms require a data structure such as an array or a linked list to store and manipulate data.
- Searching algorithms such as binary search require data structures like arrays or trees to organize and search through data.
Important Terms
What is an algorithm?
- it is a finite set of instructions that accomplishes a specific task
Sequencing
- means that there is an order in which to do things
Selection
- Helps to choose two different outcomes based off of a decision that the programmer wants to make
Iteration
- Repeat something until the condition is met. (also referred to as repetition)
Calling and Developing Procedures
- A procedure is a sequence of instructions that performs a specific task.
- To call a procedure, you need to know its name and any arguments it requires.
- When a procedure is called, the program jumps to its instruction and starts executing it.
- The arguments passed to a procedure can be used within the procedure to perform tasks or calculations.
- After the procedure has completed its task, it returns control back to the calling program.
def add_numbers(a, b):
sum = a + b
print("The sum of", a, "and", b, "is", sum)
# Call the procedure with arguments 5 and 7
add_numbers(5, 7)
- The result of the procedure can be stored in a variable, printed to the screen, or used in any other way that is required by the program.
- Procedures can be defined within the same program or in external files, and can be reused across multiple parts of the program.
- To avoid errors and improve code readability, it's important to define and call procedures with proper syntax and conventions that are appropriate for the programming language you're using.
def calculate_average(numbers):
total = sum(numbers)
count = len(numbers)
average = total / count
return average
# Call the procedure with a list of numbers
numbers_list = [10, 20, 30, 40, 50]
result = calculate_average(numbers_list)
# Display the result
print("The average of", numbers_list, "is", result)
Algorithmic Efficiency
- Algorithmic efficiency refers to the amount of time and resources needed to execute an algorithm.
- The efficiency of an algorithm can be measured in terms of its time complexity and space complexity.
- Time complexity refers to the amount of time required by an algorithm to complete its task as a function of its input size.
- Space complexity refers to the amount of memory required by an algorithm to complete its task as a function of its input size.
- can be analyzed using Big O notation, which provides an upper bound on the worst-case time and space complexity of the algorithm.
What is the time complexity of the following code:
- O(N)
- O(N*log(N))
- O(N * Sqrt(N))
- O(N*N)
Below is O(N^2) ; O(NN) because it is iterating N times for N possibilites, which gives it a time complexity equivalent to NN
a = 0
for i in range(N):
for j in reversed(range(i, N)):
a = a + i + j
What will be the time complexity of the following code?
- n
- (n+1)
- n(n-1)
- n(n+1)
n(n-1) because it is iterating n times for n-1 possibilities, which gives it a time complexity of n(n-1)
value = 0
for i in range(n): #iterates "n" times, with "i" taking on values from 0 to n-1.
for j in range(i): # iterates "i" times, with "j" taking on values from 0 to i-1.
value=value+1
- Efficiency can be improved by optimizing algorithms or by using more efficient data structures and algorithms.
- Some common techniques for improving efficiency include reducing the size of input data, caching results, and parallelizing tasks.
- Understanding algorithmic efficiency is important in software development, as it can impact the performance of applications and their ability to scale with larger data sets.
Iteration and Simulations
Simulations are models of real-world phenomena or systems that use mathematical algorithms and computer programs simulate the real behavior and aspects of the subject being modeled.
Simulations are most often used to model complex or time-consuming things that would be difficult to test in real life, such as modeling the spread of diseases in certain ecosystems or testing the functionality of a potential product before it is made.
In this lesson, we will be looking at lists, iteration, and random values through the lens of simulations.
PLEASE RUN THE CODE BELOW BEFORE INTERACTING WITH THE CODE SEGMENTS IN THIS SECTION!
class Card:
def __init__(self, suit, val):
self.suit = suit
self.val = val
if val == 11:
self.kind = "Ace"
elif val == 12:
self.kind = "Jack"
elif val == 13:
self.kind = "Queen"
elif val == 14:
self.kind = "King"
else:
self.kind = str(self.val)
#return a formatted string version of a card
def show(self):
return f"{self.kind} of {self.suit}"
#adjust aces to prevent breaking
def ace_adj(self):
if self.kind == "Ace":
self.val = 1
For Loops
For loops are probably the most well-known type of iterative loop used in code. Most of us know about the for variable in list format.
One helpful tool not a lot of people know about is the enumerate() function. When used in conjunction with a for loop, you can always have access to the index and value of each selected list entry.
numlist = [3, 5, 68, 203]
for key, num in enumerate(numlist):
print(f"This entry's index is {str(key)}, but its value is {str(num)}.")
print(f"The difference between the value and the index is {num - key}.")
QUESTION: How is the key, num in enumerate(list)
format similar to the format used when applying a for
loop to a dictionary?
Answer: The key, num in enumerate(list) format is similar to the format used when applying a for loop to a dictionary in that both allow you to iterate over the elements of a collection and access both the keys and the values of the elements.
List Comprehension
You may also see for
loops used within a list like below. We went over this in class fairly recently. In this case, it is used to show the cards in the hand of a player.
player_hand = [] # the player's hand is represented as a list
# because lists are mutable (can change), they can be added to, like drawing a card
# assume the deck below is a a deck of shuffled cards
deck = [Card("Hearts", 3), Card("Spades", 12), Card("Diamonds", 11)]
def draw_card(hand, deck):
hand.append(deck.pop())
#try it out
draw_card(player_hand, deck)
print([card.show() for card in player_hand])
def fibonacci(terms):
if terms <= 1:
return terms
return fibonacci(terms-1) + fibonacci(terms-2)
fibonacci(5)
def build(deck):
for suit in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for val in range(2, 15): #HINT: try replacing this function
deck.append(Card(suit, val))
While Loops
While loops aren't used in the program, but they offer a different way to repeat a set of instructions in a program. The procedure below the while [condition] line will occur until the condition is made not true.
Student Interaction: How could this build
function be altered to function with a while loop within it?
def build(deck):
for suit in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for val in range(2, 15):
deck.append(Card(suit, val))
#HINT: you may want to make an incrementing i variable
Response:
def build(deck):
suits = ["Spades", "Clubs", "Diamonds", "Hearts"]
val = 2
while val <= 14:
for suit in suits:
deck.append(Card(suit, val))
val += 1
While loops also alter an alternative way to loop a set of instructions forever, until a precise thing occurs to break the loop. See the code below.
import random
i = 0
while True:
i += 1
ch = random.randint(1, 11)
if ch == 10:
print(f"It took {str(i)} random generations to get 10.")
break
49 random generations is a lot more than it would normally take, but it's important for code to be able to model unlikely, yet possible scenarios. Speaking of random values...
Random Values
Because unpredictable randomness occurs in the real world, it's important to have a way to represent it. Simulations are able to use randomization, which could be in the form of random number generation or other methods like shuffle.
Card decks are a great example of how random values can be used to represent real-world scenarios. In the card simulation, the random module's shuffle function is used to quite literally shuffle the deck, seen below.
def shuffle(deck):
random.shuffle(deck)
Often, random selection methods use functions like randint or randrange as ways to select certain indexes in lists, or might use the random numbers in some other way.
random
module functions could be used to get a random card from the deck? Do so in the code cell below.
QUESTION: Without shuffling the card order of the deck, can you think of a way that the aforementioned import random
#find another random function that could pull a random card from a deck of UNSORTED cards
import random
class Card:
def __init__(self, suit, val):
self.suit = suit
self.value = val
def __str__(self):
if self.value == 11:
value = "Jack"
elif self.value == 12:
value = "Queen"
elif self.value == 13:
value = "King"
elif self.value == 14:
value = "Ace"
else:
value = str(self.value)
return value + " of " + self.suit
class Deck:
def __init__(self):
self.cards = []
for suit in ["Spades", "Clubs", "Diamonds", "Hearts"]:
for val in range(2, 15):
self.cards.append(Card(suit, val))
def draw_card(self):
return random.choice(self.cards)
deck = Deck()
random_card = deck.draw_card()
print("Randomly drawn card from the deck:", random_card)
Simulation Homework
Now that you've learned about simulations and how they're used, it's time to apply that knowledge by creating a (basic) simulation of a real-world scenario. It can be something in nature, like the changes in the wildlife population of a certain area; it can be a game, like Uno (no blackjack though, that's taken); or it can be something completely random and unique.
The simulation must include...
- Use of at least one random value
- At least one list or similar data type (dictionary, set, etc.)
- Efficient use of iteration (must support the purpose of the simualtion)
- Selection (use of conditionals)
Do this in your student copy in the cell provided. This is worth 0.9 (or more with extra credit) out of the 3 possible points.
Explanaton: This code simulates the spread of the virus for 30 days. The simulation initializes the population, defines the interact() function to simulate contact between people, defines the update_population() function to simulate the virus's spread, and defines the simulate() function to iterate through the simulation for a certain number of days and track the counts of healthy, infected, and recovered people. The simulate() function also prints
import random
HEALTHY = "healthy"
INFECTED = "infected"
RECOVERED = "recovered"
population = [HEALTHY] * 990 + [INFECTED] * 10 + [RECOVERED] * 0
def interact(population):
indices = random.sample(range(len(population)), 2)
person1 = population[indices[0]]
person2 = population[indices[1]]
if person1 == INFECTED and person2 == HEALTHY:
if random.random() < 0.5:
population[indices[1]] = INFECTED
elif person1 == HEALTHY and person2 == INFECTED:
if random.random() < 0.5:
population[indices[0]] = INFECTED
def update_population(population):
for i in range(len(population)):
if population[i] == INFECTED:
if random.randint(1, 14) == 14:
if random.random() < 0.9:
population[i] = RECOVERED
else:
population[i] = HEALTHY
def simulate(days):
healthy_count = []
infected_count = []
recovered_count = []
for i in range(days):
healthy_count.append(population.count(HEALTHY))
infected_count.append(population.count(INFECTED))
recovered_count.append(population.count(RECOVERED))
for j in range(100):
interact(population)
update_population(population)
print("Final Counts:")
print("Healthy: ", healthy_count[-1])
print("Infected: ", infected_count[-1])
print("Recovered: ", recovered_count[-1])
simulate(30)
Explanation: In this simulation, the player and computer take turns guessing whether the coin will land on heads or tails. A random value is generated for the computer's guess, and the winner of each round is determined based on the player and computer's guesses. The simulation uses a for loop to iterate through a set number of tosses, and conditionals to determine the winner of each round and the overall winner. A list (coin_sides) is used to store the possible outcomes of the coin toss, and the player and computer scores are stored in variables.
import random
# Initialize variables
num_of_tosses = 5
coin_sides = ["heads", "tails"]
player_score = 0
computer_score = 0
# Main game loop
for i in range(num_of_tosses):
# Get the player's guess
player_guess = input("Enter your guess (heads or tails): ")
# Ensure the player's guess is valid
while player_guess not in coin_sides:
player_guess = input("Invalid guess. Enter again (heads or tails): ")
# Flip the coin
computer_guess = random.choice(coin_sides)
print(f"Computer guessed {computer_guess}")
# Determine the winner of the round
if player_guess == computer_guess:
print("You win!")
player_score += 1
else:
print("Computer wins!")
computer_score += 1
# Print the final scores
print(f"Final score - Player: {player_score}, Computer: {computer_score}")
if player_score > computer_score:
print("Congratulations, you win!")
elif player_score < computer_score:
print("Sorry, you lost. Better luck next time!")
else:
print("It's a tie!")
Databases
We have already gone over databases in this class, but here is a refresher. A database contains data that's stored in columns and rows. The information in this database can then be pulled from the database and can be used in a program.
Setting Up the Database
Run the code cell below to prepare SQLite to create the database. If your system is struggling with the flask functions, verify that you're in the correct Python environment. REMEMBER: You should only db.init_app(app) ONCE during the process!
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
database = 'sqlite:///sqlite.db' # path and filename of database
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = database
app.config['SECRET_KEY'] = 'SECRET_KEY'
db = SQLAlchemy()
# This belongs in place where it runs once per project
db.init_app(app)
import os, base64
import json
from sqlalchemy.exc import IntegrityError
# Define the User class to manage actions in the 'users' table
class User(db.Model):
__tablename__ = 'players' # table name is plural, class name is singular
# Define the User schema with "vars" from object
id = db.Column(db.Integer, primary_key=True)
_username = db.Column(db.String(255), unique=False, nullable=False)
_streak = db.Column(db.Integer, unique=True, nullable=False)
# constructor of a User object, initializes the instance variables within object (self)
def __init__(self, username, streak):
self._username = username
self._streak = streak
# a username getter method, extracts username from object
@property
def username(self):
return self._username
# a setter function, allows username to be updated after initial object creation
@username.setter
def username(self, username):
self._username = username
# a getter method, extracts streak from object
@property
def streak(self):
return self._streak
# a setter function, allows streak to be updated after initial object creation
@streak.setter
def streak(self, streak):
self._streak = streak
# output content using str(object) in human readable form, uses getter
# output content using json dumps, this is ready for API response
def __str__(self):
return json.dumps(self.read())
# CRUD create/add a new record to the table
# returns self or None on error
def create(self):
try:
# creates a person object from User(db.Model) class, passes initializers
db.session.add(self) # add prepares to persist person object to Users table
db.session.commit() # SqlAlchemy "unit of work pattern" requires a manual commit
return self
except IntegrityError:
db.session.remove()
return None
# CRUD read converts self to dictionary
# returns dictionary
def read(self):
return {
"id": self.id,
"username": self.username,
"streak": self.streak
}
# CRUD update: updates user name, password, phone
# returns self
def update(self, username, streak):
"""only updates values with length"""
if len(username) > 0:
self.username = username
if streak > 0:
self.streak = streak
db.session.commit()
return self
# CRUD delete: remove self
# None
def delete(self):
db.session.delete(self)
db.session.commit()
return None
"""Database Creation and Testing """
# Builds working data for testing
def initUsers():
with app.app_context():
"""Create database and tables"""
db.create_all()
"""Tester data for table"""
u1 = User(username="Mr. Cards", streak=5)
u2 = User(username="Kard Kowntre", streak=10)
u3 = User(username="Un Bea Table", streak=15)
users = [u1, u2, u3]
"""Builds sample user/note(s) data"""
for user in users:
try:
user.create()
print(f'Created user with username "{user.username}".')
except IntegrityError:
'''fails with bad or duplicate data'''
db.session.remove()
print(f"Records exist, duplicate email, or error: {user.username}")
def __init__(self, username, streak):
self._username = username
self._score = streak
@property
def streak(self)
return self._stereak
@streak.setter
def streak(self, streak):
self._streak = streak
@property
def streak(self):
return self._streak
import json
from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource # used for REST API building
user_api = Blueprint('user_api', __name__,
url_prefix='/api/users')
api = Api(user_api)
class UserAPI:
class _CRUD(Resource): # User API operation for Create, Read. THe Update, Delete methods need to be implemeented
def post(self): # Create method
''' Read data for json body '''
body = request.get_json()
''' Avoid garbage in, error checking '''
# validate name
username = body.get('username')
if username is None or len(username) < 1:
return {'message': f'Username is missing, or is less than a character'}, 400
# validate uid
streak = body.get('streak')
if streak is None or streak < 1:
return {'message': f'Streak is missing, or is less than 1'}, 400
''' #1: Key code block, setup USER OBJECT '''
uo = User(username=username,
streak=streak)
''' #2: Key Code block to add user to database '''
# create user in database
user = uo.create()
# success returns json of user
if user:
return jsonify(user.read())
# failure returns error
return {'message': f'Processed {username}, either a format error or a duplicate'}, 400
def get(self): # Read Method
users = User.query.all() # read/extract all users from database
json_ready = [user.read() for user in users] # prepare output in json
return jsonify(json_ready) # jsonify creates Flask response object, more specific to APIs than json.dumps
def put(self):
body = request.get_json() # get the body of the request
id = body.get('id')
username = body.get('username')
streak = body.get('streak') # get the UID (Know what to reference)
user = User.query.get(id) # get the player (using the uid in this case)
user.update(username=username, streak=streak)
return f"{user.read()} Updated"
def delete(self):
body = request.get_json()
id = body.get('id')
player = User.query.get(id)
player.delete()
return f"{player.read()} Has been deleted"
# building RESTapi endpoint
api.add_resource(_CRUD, '/')
This is important particularly in a full flask respository context, but in this case, you'll just need to run the initUsers()
function.
initUsers()
An Alternative Method of Making SQLite Databases
In a previous lesson, we went over using the cursor
object in SQLite3. Rather than go over all of that here, this lesson goes over it thoroughly. (You may use this method for the homework below.)
Database Homework
For this assignment, we'd like you to make your own database file as instructed above. Remember, the API file isn't necessary in this case; you'll be focusing on making the model and the init
function.
Your database must include these things:
- A class with at least four attributes (if not the cursor method)
- Setters and getters for this class (if not the cursor method)
- Each of the CRUD functions
- An
init
function with at least four entries - A screenshot showing proof that your SQLite file has been created correctly
Feel free to base your database on the model provided above! Ask our group if you have any questions or concerns.
Fitness Full Stack Database with everything including frontend
Watch this video in order to see proof of functioning database and extra credit of frontend. https://youtu.be/ZCrt3BK-DfM
Repository to Frontend Code that works with this DB: https://github.com/DerekSol/dejmofrontend/edit/gh-pages/fitness_review/dietfitness.md
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
"""
These object can be used throughout project.
1.) Objects from this file can be included in many blueprints
2.) Isolating these object definitions avoids duplication and circular dependencies
"""
# Setup of key Flask object (app)
app = Flask(__name__)
# Setup SQLAlchemy object and properties for the database (db)
dbURI = 'sqlite:///volumes/sqlite.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = dbURI
app.config['SECRET_KEY'] = 'SECRET_KEY'
db = SQLAlchemy()
Migrate(app, db)
# Images storage
app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # maximum size of uploaded content
app.config['UPLOAD_EXTENSIONS'] = ['.jpg', '.png', '.gif'] # supported file types
app.config['UPLOAD_FOLDER'] = 'volumes/uploads/' # location of user uploaded content
from sqlite3 import IntegrityError
from sqlalchemy import Column, Integer, String
from __init__ import db
import random
class FitnessEntry(db.Model):
__tablename__ = "fitness"
id = db.Column(db.Integer, primary_key=True)
_username = Column(db.String(255), nullable=False)
_diet_name = Column(db.String(255), nullable=False)
_calories = Column(db.Integer, nullable=False)
_protein = Column(db.Integer, nullable=False)
_fat = Column(db.Integer, nullable=False)
_carbs = Column(db.Integer, nullable=False)
_extra_notes = Column(db.String(255), nullable=False)
def __init__(self, username, diet_name, calories, protein, fat, carbs, extra_notes):
self._username = username
self._diet_name = diet_name
self._calories = calories
self._protein = protein
self._fat = fat
self._carbs = carbs
self._extra_notes = extra_notes
def __repr__(self):
return (
"<FitnessEntry(username='%s', calories='%s', protein='%s', fat='%s', carbs='%s', extra_notes='%s')>"
% (
self.username,
self.calories,
self.protein,
self.fat,
self.carbs,
self.extra_notes,
)
)
@property
def username(self):
return self._username
@username.setter
def username(self, value):
self._username = value
@property
def diet_name(self):
return self._diet_name
@diet_name.setter
def diet_name(self, value):
self._diet_name = value
@property
def calories(self):
return self._calories
@calories.setter
def calories(self, value):
self._calories = value
@property
def protein(self):
return self._protein
@protein.setter
def protein(self, value):
self._protein = value
@property
def fat(self):
return self._fat
@fat.setter
def fat(self, value):
self._fat = value
@property
def carbs(self):
return self._carbs
@carbs.setter
def carbs(self, value):
self._carbs = value
@property
def extra_notes(self):
return self._extra_notes
@extra_notes.setter
def extra_notes(self, value):
self._extra_notes = value
def create(self):
try:
# creates a player object from Player(db.Model) class, passes initializers
db.session.add(self) # add prepares to persist person object to Users table
db.session.commit() # SqlAlchemy "unit of work pattern" requires a manual commit
return self
except IntegrityError:
db.session.remove()
return None
# CRUD read converts self to dictionary
# returns dictionary
def read(self):
return {
"id" : self.id,
"username": self.username,
"calories": self.calories,
"protein": self.protein,
"fat": self.fat,
"carbs": self.carbs,
"extra_notes": self.extra_notes,
"diet_name": self.diet_name,
}
# CRUD update: updates name, uid, password, tokens
# returns self
def update(self, dictionary):
# {k:v, v:w, x:r}
"""only updates values in dictionary with length"""
for key in dictionary:
if key == "name":
self._diet_name = dictionary[key]
print("changed " + key)
if key == "calories":
self._calories = dictionary[key]
print("changed " + key)
if key == "protein":
self._protein
print("changed " + key)
if key == "fat":
self._fat = dictionary[key]
print("changed " + key)
if key == "carbs":
self._carbs = dictionary[key]
print("changed " + key)
if key == "notes":
self._extra_notes = dictionary[key]
print("changed " + key)
db.session.commit()
return self
# CRUD delete: remove self
# return self
def delete(self):
player = self
db.session.delete(self)
db.session.commit()
return player
def fitness_table_empty():
return len(db.session.query(FitnessEntry).all()) == 0
def initDiets():
if not fitness_table_empty():
return
entry1 = FitnessEntry("Martin", "Cutting", 2000, 150, 70, 150, "had a big lunch")
entry2 = FitnessEntry(
"Ethan", "Bulking", 1700, 120, 50, 100, "ate a lot of veggies"
)
entry3 = FitnessEntry("Derek", "Training", 1500, 200, 80, 200, "had a heavy dinner")
fitness_entries = [entry1, entry2, entry3]
for entry in fitness_entries:
try:
entry.create()
except Exception as e:
print("error while creating entries: " + str(e))
db.session.rollback()
from flask import Blueprint, jsonify, request
from flask_restful import Api, Resource, reqparse
from __init__ import db
from model.fitness import FitnessEntry
from flask import Blueprint, jsonify # jsonify creates an endpoint response object
from flask_restful import Api, Resource # used for REST API building
diets_api = Blueprint('diets_api', __name__,
url_prefix='/api/diets')
api = Api(diets_api)
class DietAPI(Resource):
class _ReadAll(Resource):
def get(self):
query = FitnessEntry.query.all() # read/extract all players from database
json_ready = [player.read() for player in query] # prepare output in json body
return jsonify(json_ready) # jsonify creates Flask response object, more specific to APIs than json.dumps
class _ReadPerson(Resource):
def get(self, username):
query = FitnessEntry.query.all() # read/extract all players from database
json_ready = [player.read() for player in query if player._username == username] # prepare output in json
return jsonify(json_ready) # jsonify creates Flask response object, more specific to APIs than json.dumps
class _Update(Resource):
def put(self, username, diet):
update = request.get_json()["update"]
query = FitnessEntry.query.all()
json_ready = [person for person in query if person._username == username and diet == person._diet_name] # prepare output in json
if len(json_ready) <= 0:
return {"message" : "There is nothing like this!"}
for entry in json_ready:
entry.update(update)
return {"message" : "Action Complete!"} # jsonify creates Flask response object, more specific to APIs than json.dumps
class _Post(Resource):
def post(self):
# username, dietname, calories, protein, fat, carbs, notes
body = request.get_json()
# validate name
name = body.get('username')
if name is None or len(name) < 2:
return {'message': f'Name is missing, or is less than 2 characters'}, 210
dietname = body.get('diet_name')
if name is None or len(name) < 2:
return {'message': f'Diet Name is missing, or is less than 2 characters'}, 210
cals = body.get('calories')
protein = body.get('protein')
fat = body.get('fat')
carbs = body.get('carbs')
notes = body.get('extra_notes')
''' #1: Key code block, setup PLAYER OBJECT '''
fit = FitnessEntry(username=name,
diet_name=dietname,
calories=cals,
protein=protein,
fat=fat,
carbs=carbs,
extra_notes=notes)
print(fit)
''' #2: Key Code block to add user to database '''
# create player in database
player = fit.create()
print("posted")
# success returns json of player
if player:
return jsonify(player.read())
# failure returns error
return {'message': f'Processed {name}, either a format error or User ID is duplicate'}, 210
class _Delete(Resource):
def delete(self, id):
query = FitnessEntry.query.all() # read/extract all players from database
entries = [player for player in query if player.id == id] # prepare output in json
for entry in entries:
entry.delete()
return jsonify({"message" : 'deleted'}) # jsonify creates Flask response object, more specific to APIs than json.dumps
api.add_resource(_ReadAll, "/")
api.add_resource(_ReadPerson, "/<string:username>")
api.add_resource(_Update, "/<string:username>_<string:diet>")
api.add_resource(_Post, "/post")
api.add_resource(_Delete, "/delete/<int:id>")
import sqlite3
def create_database():
# Connect to the database (will create it if it doesn't exist)
connection = sqlite3.connect('instance/professors.db')
cursor = connection.cursor()
# Create the professors table if it doesn't already exist
cursor.execute('''CREATE TABLE IF NOT EXISTS professors (
name TEXT,
field TEXT,
rating REAL,
reviews TEXT
)''')
# Commit changes and close the connection
connection.commit()
connection.close()
# Call the function to create the database
create_database()
Create Function
The _ function allows users to input information about a coding professor and store it in a SQLite database named 'professors.db'. This script prompts the user for the professor's name, field of expertise, rating out of 10, and any reviews or comments about the professor. It then establishes a connection to the SQLite database and creates a cursor object for executing SQL commands.
import sqlite3
def create():
database = 'instance/professors.db'
name = input("Enter the professor's name: ")
field = input("Enter the professor's field of expertise: ")
rating = input("Enter the professor's rating (out of 10): ")
reviews = input("Enter any reviews or comments about the professor: ")
# Connect to the database and create a cursor to execute SQL commands
connection = sqlite3.connect(database)
cursor = connection.cursor()
try:
# Execute SQL to insert record into db
cursor.execute("INSERT INTO professors (name, field, rating, reviews) VALUES (?, ?, ?, ?)", (name, field, rating, reviews))
# Commit the changes
connection.commit()
print(f"{name} has been added to the list of coding professors.")
except sqlite3.Error as error:
print("Error while inserting record", error)
# Close cursor and connection
cursor.close()
connection.close()
create()
Read Function
This code demonstrates how to read data from a SQLite database using Python and the _. The first step is to establish a connection to the database and create a cursor object to execute SQL commands. Then, a SELECT query is executed to fetch all records from the "professors" table. If there are any records, the code iterates through each record and prints out the name, field of expertise, rating, and reviews for each coding professor. If there are no records in the table, a message indicating so is printed.
import sqlite3
def read():
try:
# Open a connection to the database and create a cursor
connection = sqlite3.connect('instance/professors.db')
cursor = connection.cursor()
# Fetch all records from the professors table
cursor.execute("SELECT * FROM professors")
rows = cursor.fetchall()
# If there are any records, print them
if len(rows) > 0:
print("List of coding professors:")
for row in rows:
print(f"Name: {row[0]}\nField of expertise: {row[1]}\nRating: {row[2]}\nReviews: {row[3]}\n")
else:
print("There are no coding professors in the list.")
except sqlite3.Error as error:
print("Error while connecting to the database:", error)
finally:
# Close the cursor and the connection to the database
cursor.close()
connection.close()
read()
Update Function
This is an implementation of an update function for the professors database using the sqlite3 module in Python. The function first establishes a connection to the database file 'instance/professors.db' and creates a cursor object to execute commands. It prompts the user to enter the name of the professor to update and retrieves the corresponding record from the database using a SELECT statement with a WHERE clause to match the professor's name. If the professor is found in the database, the user is prompted to enter new information for the professor's field of expertise, rating, and reviews. The function then executes an statement with the new information to update the record in the database.
import sqlite3
def update():
database = 'instance/professors.db'
connection = sqlite3.connect(database)
cursor = connection.cursor()
try:
# Get the professor's name to update
name = input("Enter the name of the professor to update: ")
# Retrieve the current record from the database
cursor.execute("SELECT * FROM professors WHERE name=?", (name,))
record = cursor.fetchone()
# If the professor is found, update the record
if record:
print("Enter the new information for the professor:")
field = input(f"Current field: {record[1]}\nNew field: ")
rating = input(f"Current rating: {record[2]}\nNew rating: ")
reviews = input(f"Current reviews: {record[3]}\nNew reviews: ")
# Execute SQL to update the record
cursor.execute("UPDATE professors SET field=?, rating=?, reviews=? WHERE name=?", (field, rating, reviews, name))
connection.commit()
print(f"{name}'s record has been updated.")
# If the professor is not found, notify the user
else:
print(f"No record found for {name}.")
except sqlite3.Error as error:
print("Error while updating record", error)
# Close cursor and connection
cursor.close()
connection.close()
update ()
Delete Function
This code is a Python function for __ a record from a SQLite database. The function prompts the user to input the name of the professor they want to delete. It then uses a SQL query to search for the professor in the database. If the professor is found, the user is prompted to confirm the deletion. If the user confirms, the function executes a SQL command to delete the record from the database. The function also prints a message confirming that the professor has been deleted from the list of coding professors. If the professor is not found in the database, the function prints a message indicating that the professor is not in the list.
import sqlite3
def delete():
# Connect to the database and create a cursor
connection = sqlite3.connect('instance/professors.db')
cursor = connection.cursor()
# Prompt the user for the name of the professor to delete
name = input("Enter the name of the professor you want to delete: ")
# Use a SQL query to find the professor with the given name
cursor.execute("SELECT * FROM professors WHERE name=?", (name,))
row = cursor.fetchone()
# If the professor exists, confirm deletion and delete the record
if row:
confirm = input(f"Are you sure you want to delete {name}? (y/n): ")
if confirm.lower() == 'y':
cursor.execute("DELETE FROM professors WHERE name=?", (name,))
connection.commit()
print(f"{name} has been deleted from the list of coding professors.")
else:
print(f"{name} not found in the list of coding professors.")
# Close the cursor and the connection to the database
cursor.close()
connection.close()
delete()
Grading
Your submission will be graded based on the following criteria:
- Filling in the blank throughout the lesson and providing code in the given cells when applicable (0.9)
- Simulation homework (0.9)
- Database homework (0.9)
Here are some ideas for ways to increase your score above a 2.7:
- Make a frontend version of your simulation that can be interacted with on your blog
- Connect your simulation to the database you create
- Create a menu that allows a user to make an entry in your database (CRUD functions within it)
- You can establish a relationship between two classes/tables in your database (see the relationship between the User and Note classes in the Nighthawk Coders flask repository)