Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5efb942978 | ||
|
|
74f9527832 | ||
|
|
081471fa5d | ||
|
|
291ba82046 | ||
|
|
14ed1783f9 | ||
|
|
74545a6ad1 | ||
|
|
b6be9c9488 | ||
|
|
716e254905 | ||
|
|
161a4e28db | ||
|
|
5af112ebae | ||
|
|
5d36a06987 | ||
|
|
b4f174be47 | ||
|
|
46acaf2e75 | ||
|
|
28c277f369 | ||
|
|
02146e348d | ||
|
|
9df129681d | ||
|
|
a566adc1df | ||
|
|
aa1a8fee93 | ||
|
|
285914f044 | ||
|
|
da2c779bcd | ||
|
|
7259509d9e | ||
|
|
b085e13c4e | ||
|
|
59e84426af | ||
|
|
97f93ebd8e | ||
|
|
35c8889e77 | ||
|
|
dca7e9d599 | ||
|
|
ce856e5626 | ||
|
|
072032e80e | ||
|
|
235e7b5268 | ||
|
|
22fa096271 | ||
|
|
13eb12bf69 | ||
|
|
efa189d0b6 | ||
|
|
111f551a39 | ||
|
|
e0fd81dc1d | ||
|
|
420194ef54 | ||
|
|
632448c249 | ||
|
|
c3405d56e5 | ||
|
|
338824c028 | ||
|
|
6625a4d6c1 | ||
|
|
b1fccaf628 | ||
|
|
568e890c9f | ||
|
|
ff86e4a7c2 | ||
|
|
0f10f22345 |
Vendored
+7
@@ -7,6 +7,13 @@
|
||||
"program": "simulation/SimulationRunner.py",
|
||||
"justMyCode": true,
|
||||
"args": "./HotdogSimulation.py"
|
||||
},
|
||||
{
|
||||
"name": "Python: File",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[1]:
|
||||
|
||||
|
||||
import requests
|
||||
|
||||
url = 'https://www.sayweee.com/zh/product/Vital-Farms-Pasture-Raised-Eggs--Large-18-count/40709'
|
||||
response = requests.get(url)
|
||||
html_content = response.text
|
||||
html_content
|
||||
|
||||
|
||||
# In[2]:
|
||||
|
||||
|
||||
from lxml import html
|
||||
|
||||
tree = html.fromstring(html_content)
|
||||
script = tree.xpath('//script[@id="__NEXT_DATA__"]/text()')[0]
|
||||
next_data = script.split('__NEXT_DATA__ = ')[1].split(';</script>')[0]
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
import json
|
||||
|
||||
data = json.loads(next_data)
|
||||
price = data['props']['initialState']['pdp']['product']['price']
|
||||
print(price)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[1]:
|
||||
|
||||
|
||||
from google.cloud import pubsub_v1
|
||||
|
||||
|
||||
# In[2]:
|
||||
|
||||
|
||||
# Set the project ID and topic name
|
||||
project_id = "sloetjes-project1"
|
||||
topic_name = "sloetjes-project1"
|
||||
subscription_name = "sloetjes-final-project1"
|
||||
|
||||
|
||||
# In[3]:
|
||||
|
||||
|
||||
# Create a publisher client
|
||||
publisher = pubsub_v1.PublisherClient()
|
||||
|
||||
|
||||
# In[4]:
|
||||
|
||||
|
||||
# Create the fully qualified topic path
|
||||
topic_path = publisher.topic_path(project_id, topic_name)
|
||||
|
||||
|
||||
# In[5]:
|
||||
|
||||
|
||||
# Create the Pub/Sub topic
|
||||
topic = publisher.create_topic(name = topic_path)
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
# Print the topic name
|
||||
print(f"Topic created: {topic.name}")
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
# Create the subscription if it doesn't exist
|
||||
subscription_path = subscriber.subscription_path(project_id, subscription_name)
|
||||
topic_path = publisher.topic_path(project_id, topic_name)
|
||||
|
||||
# Publish a message to the topic
|
||||
message = "Hello, World!"
|
||||
data = message.encode("utf-8")
|
||||
publisher.publish(topic_path, data=data)
|
||||
print(f"Message published: {message}")
|
||||
|
||||
# Pull the message from the subscription
|
||||
response = subscriber.pull(subscription_path, max_messages=1)
|
||||
for received_message in response.received_messages:
|
||||
print(f"Received message: {received_message.message.data}")
|
||||
|
||||
# Acknowledge the message
|
||||
subscriber.acknowledge(subscription_path, [received_message.ack_id])
|
||||
@@ -0,0 +1,28 @@
|
||||
# Question 1:
|
||||
# The international standard for floating-point representation is
|
||||
# called the "IEEE 754 Standard for Floating-Point Arithmetic."
|
||||
|
||||
# Question 2:
|
||||
# 32-bit and 64-bit formats are the most widely used ones in practice.
|
||||
|
||||
# Question 3:
|
||||
# My computer supports all these formats
|
||||
import numpy as np
|
||||
|
||||
supported_formats = np.finfo(np.float32).bits, np.finfo(np.float64).bits
|
||||
print(f"Your computer supports the following floating-point formats: {supported_formats}")
|
||||
|
||||
|
||||
# Question 4:
|
||||
# Using the Pandas multiplication operator (df['Number1'] * df['Number2'])
|
||||
# , the result is automatically converted to a floating-point data type.
|
||||
# On the other hand, when I multiply the numbers using the normal Python
|
||||
# multiplication operator (num1 * num2), the result remains an integer
|
||||
# data type.
|
||||
|
||||
# Question 5:
|
||||
# The size of a floating-point number in memory is primarily determined
|
||||
# by the data type and not the actual value it represents. So the size
|
||||
# of a Python floating-point number does not change as it gets larger.
|
||||
import sys
|
||||
print(sys.getsizeof(70))
|
||||
@@ -0,0 +1,30 @@
|
||||
# Question 7:
|
||||
# 1. Strings in Python are sequences of Unicode characters, while bytes
|
||||
# are sequences of raw binary data.
|
||||
# 2. Strings in Python are immutable, bytes objects are mutable
|
||||
# 3. Strings in Python are encoded using a specific character encoding,
|
||||
# bytes objects do not have an inherent encoding and represent raw binary
|
||||
# data.
|
||||
import sys
|
||||
|
||||
string_example = "Hello"
|
||||
bytes_example = b"Hello"
|
||||
|
||||
string_size = sys.getsizeof(string_example)
|
||||
bytes_size = sys.getsizeof(bytes_example)
|
||||
|
||||
print("Size of string:", string_size)
|
||||
print("Size of bytes:", bytes_size)
|
||||
|
||||
# Emojis are represented using Unicode characters, which require more
|
||||
# bytes to store compared to ASCII characters. Therefore, if you have
|
||||
# a string that contains emojis, its size will be larger than a string
|
||||
# without emojis.
|
||||
|
||||
def print_string_as_numbers(input_string):
|
||||
for char in input_string:
|
||||
print(ord(char), end=' ')
|
||||
|
||||
# Example usage
|
||||
input_string = "Hello, World!"
|
||||
print_string_as_numbers(input_string)
|
||||
@@ -0,0 +1 @@
|
||||
print('Hi')
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[1]:
|
||||
import os
|
||||
from google.cloud import pubsub_v1
|
||||
|
||||
|
||||
# In[2]:
|
||||
# Set the project ID and topic name
|
||||
project_id = 'price-extraction-project'
|
||||
topic_name = 'nfl-zipcodes'
|
||||
|
||||
|
||||
# In[3]:
|
||||
# Create a publisher client
|
||||
publisher = pubsub_v1.PublisherClient()
|
||||
|
||||
|
||||
# In[4]:
|
||||
# Get the topic path
|
||||
topic_path = publisher.topic_path(project_id, topic_name)
|
||||
|
||||
|
||||
# In[5]:
|
||||
# Define the zip codes for all NFL cities
|
||||
nfl_zipcodes = {
|
||||
'Arizona Cardinals': '85305',
|
||||
'Atlanta Falcons': '30363',
|
||||
'Baltimore Ravens': '21230',
|
||||
'Buffalo Bills': '14127',
|
||||
'Carolina Panthers': '28202',
|
||||
'Chicago Bears': '60605',
|
||||
'Cincinnati Bengals': '45202',
|
||||
'Cleveland Browns': '44115',
|
||||
'Dallas Cowboys': '76011',
|
||||
'Denver Broncos': '80204',
|
||||
'Detroit Lions': '48226',
|
||||
'Green Bay Packers': '54304',
|
||||
'Houston Texans': '77054',
|
||||
'Indianapolis Colts': '46225',
|
||||
'Jacksonville Jaguars': '32202',
|
||||
'Kansas City Chiefs': '64129',
|
||||
'Las Vegas Raiders': '89118',
|
||||
'Los Angeles Chargers': '92626',
|
||||
'Los Angeles Rams': '90305',
|
||||
'Miami Dolphins': '33056',
|
||||
'Minnesota Vikings': '55415',
|
||||
'New England Patriots': '02035',
|
||||
'New Orleans Saints': '70112',
|
||||
'New York Giants': '07073',
|
||||
'New York Jets': '07932',
|
||||
'Philadelphia Eagles': '19145',
|
||||
'Pittsburgh Steelers': '15212',
|
||||
'San Francisco 49ers': '95054',
|
||||
'Seattle Seahawks': '98056',
|
||||
'Tampa Bay Buccaneers': '33607',
|
||||
'Tennessee Titans': '37213',
|
||||
'Washington Football Team': '20785'
|
||||
}
|
||||
|
||||
|
||||
# In[6]:
|
||||
# Publish each zip code to the topic
|
||||
for team, zipcode in nfl_zipcodes.items():
|
||||
data = f'{zipcode}'.encode('utf-8')
|
||||
publisher_future = publisher.publish(topic_path, data=data)
|
||||
message_id = publisher_future.result()
|
||||
print(f"message_id = {message_id}")
|
||||
|
||||
print('All zip codes published.')
|
||||
|
||||
|
||||
# In[ ]:
|
||||
import os
|
||||
import time
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from google.cloud import pubsub_v1
|
||||
|
||||
# Set the project ID and subscription name
|
||||
project_id = 'price-extraction-project'
|
||||
topic_name = 'nfl-zipcodes'
|
||||
subscription_name = 'sloetjes'
|
||||
|
||||
# Set the path to your ChromeDriver executable
|
||||
chrome_driver_path = "/Users/sloetjeswong/Downloads/chromedriver_mac_arm64/chromedriver"
|
||||
|
||||
# Set up a Google Cloud Pub/Sub subscriber
|
||||
subscriber = pubsub_v1.SubscriberClient()
|
||||
subscription_path = subscriber.subscription_path(project_id, subscription_name)
|
||||
|
||||
# Set up a ChromeDriver instance
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
chrome_options.add_argument("--headless") # Run Chrome in headless mode
|
||||
chrome_service = Service(chrome_driver_path)
|
||||
chrome_service.start()
|
||||
driver = webdriver.Remote(chrome_service.service_url, options=chrome_options)
|
||||
|
||||
def callback(message):
|
||||
# Get the zip code from the message
|
||||
zip_code = message.data.decode("utf-8")
|
||||
print(f"Received message with zip code: {zip_code}")
|
||||
|
||||
# Open the product page for the rice crackers for the given zip code
|
||||
product_url = f"https://www.sayweee.com/zh/product/Want-Want-Senbei-Rice-Crackers/17119?trace_id=89f7c636-05b8-4f52-abdd-74de3a83f45d&zip={zip_code}"
|
||||
driver.get(product_url)
|
||||
|
||||
# Wait for the price to load
|
||||
price_xpath = "//span[@class='price']"
|
||||
price_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, price_xpath)))
|
||||
|
||||
# Extract the price and print it
|
||||
price = price_element.text
|
||||
print(f"Price for zip code {zip_code}: {price}")
|
||||
|
||||
# Acknowledge the message
|
||||
message.ack()
|
||||
|
||||
# Subscribe to the topic
|
||||
streaming_pull_future = subscriber.subscribe(subscription_path, callback=callback)
|
||||
print(f"Listening for messages on {subscription_path}...")
|
||||
streaming_pull_future.result()
|
||||
|
||||
# Keep the script running
|
||||
while True:
|
||||
time.sleep(60)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 130 KiB |
-165
@@ -1,165 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import sessionmaker, relationship, declarative_base
|
||||
from flask import Flask, render_template
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
# Create a base class for declarative models
|
||||
Base = declarative_base()
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = 'product'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String)
|
||||
SKU_PLU_UPC_number = Column(String)
|
||||
price_histories = relationship("PriceHistory", back_populates="product")
|
||||
|
||||
class Retailer(Base):
|
||||
__tablename__ = 'retailer'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String)
|
||||
products = relationship("Product", secondary="retailer_product")
|
||||
|
||||
class Price(Base):
|
||||
__tablename__ = 'price'
|
||||
id = Column(Integer, primary_key=True)
|
||||
MOQ = Column(Integer)
|
||||
price = Column(Float)
|
||||
|
||||
class ScraperJob(Base):
|
||||
__tablename__ = 'scraper_job'
|
||||
id = Column(Integer, primary_key=True)
|
||||
start_time = Column(DateTime)
|
||||
end_time = Column(DateTime)
|
||||
parameters = Column(String)
|
||||
statistics = Column(String)
|
||||
|
||||
class PriceHistory(Base):
|
||||
__tablename__ = 'price_history'
|
||||
id = Column(Integer, primary_key=True)
|
||||
location = Column(String)
|
||||
zip_code = Column(Integer)
|
||||
city_name = Column(String)
|
||||
price_at_time = Column(DateTime)
|
||||
price = Column(Float)
|
||||
scraper_job_id = Column(Integer, ForeignKey('scraper_job.id'))
|
||||
product_id = Column(Integer, ForeignKey('product.id'))
|
||||
product = relationship("Product", back_populates="price_histories")
|
||||
scraper_job = relationship("ScraperJob")
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'user'
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(String)
|
||||
credentials = Column(String)
|
||||
creation_date = Column(DateTime)
|
||||
modification_date = Column(DateTime)
|
||||
|
||||
class RetailerProduct(Base):
|
||||
__tablename__ = 'retailer_product'
|
||||
retailer_id = Column(Integer, ForeignKey('retailer.id'), primary_key=True)
|
||||
product_id = Column(Integer, ForeignKey('product.id'), primary_key=True)
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
# Create the database engine
|
||||
engine = create_engine('sqlite:///user_database.db')
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
# Create a session factory
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
|
||||
# In[ ]:
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
# Create a command-line tool that parses sys.argv to allow a user to create/edit/delete user objects.
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
app = Flask(__name__)
|
||||
|
||||
# Create a database connection
|
||||
engine = create_engine('sqlite:///user_database.db')
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
# Get all user objects
|
||||
users = session.query(User).all()
|
||||
return render_template('index.html', users=users)
|
||||
|
||||
app.run()
|
||||
|
||||
|
||||
command = sys.argv[1]
|
||||
|
||||
if command == "create":
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: python script.py create [username] [credentials]")
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[2]
|
||||
credentials = sys.argv[3]
|
||||
|
||||
# Create a new user object
|
||||
new_user = User(username=username, credentials=credentials)
|
||||
session.add(new_user)
|
||||
session.commit()
|
||||
print("User created successfully!")
|
||||
|
||||
elif command == "edit":
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: python script.py edit [username] [new_credentials]")
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[2]
|
||||
new_credentials = sys.argv[3]
|
||||
|
||||
# Find the user object
|
||||
user = session.query(User).filter_by(username=username).first()
|
||||
|
||||
if user:
|
||||
# Update user credentials
|
||||
user.credentials = new_credentials
|
||||
session.commit()
|
||||
print("User edited successfully!")
|
||||
else:
|
||||
print("User not found!")
|
||||
|
||||
elif command == "delete":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python script.py delete [username]")
|
||||
sys.exit(1)
|
||||
|
||||
username = sys.argv[2]
|
||||
|
||||
# Find the user object
|
||||
user = session.query(User).filter_by(username=username).first()
|
||||
|
||||
if user:
|
||||
# Delete the user object
|
||||
session.delete(user)
|
||||
session.commit()
|
||||
print("User deleted successfully!")
|
||||
else:
|
||||
print("User not found!")
|
||||
|
||||
else:
|
||||
print("Usage: python script.py [create|edit|delete] [username] [credentials]")
|
||||
@@ -0,0 +1,100 @@
|
||||
# A quick brain teaser
|
||||
print('What has a head and a tail but no body?')
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# A coin
|
||||
@@ -0,0 +1,2 @@
|
||||
import sys
|
||||
print(sys.float_info)
|
||||
@@ -0,0 +1,37 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2580
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;\f1\fnil\fcharset0 Menlo-Regular;}
|
||||
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red0\green0\blue0;}
|
||||
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\c87059;\cssrgb\c0\c0\c0\c3922;}
|
||||
\margl1440\margr1440\vieww11520\viewh8400\viewkind0
|
||||
\deftab720
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
|
||||
\f0\fs32 \cf2 \expnd0\expndtw0\kerning0
|
||||
1. What is the name of the international standard for floating-point representation?\
|
||||
\
|
||||
The IEEE-754 standard describes floating-point formats, a way to represent real numbers in hardware.\
|
||||
\
|
||||
\
|
||||
2. How many bits are the floating point numbers defined by that standard allowed to be?\
|
||||
\
|
||||
The floating-point numbers defined by the international standard can be allowed to be of different lengths depending on the specific interchange format. For binary interchange, the formats can be 16 bits, 32 bits, 64 bits, and any multiple of 32 bits \uc0\u8805 128. For decimal interchange, the formats can be any multiple of 32 bits.\
|
||||
\
|
||||
\
|
||||
3. Does your computer support all those formats? Can you think of an easy way to check with only one line of Python?\
|
||||
\
|
||||
import sys\
|
||||
print(sys.float_info)\
|
||||
\
|
||||
scott@MacBook-Pro-6 python-bootcamp % python3 Floating_Point_Formats.py \
|
||||
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)\
|
||||
\
|
||||
\
|
||||
\pard\pardeftab720\sa320\partightenfactor0
|
||||
\cf2 4. Why doesn't this code give the same answers to the two multiplications?\
|
||||
Because they are different operations. The normal Python multiplication operator will multiply two numbers together and return the product. The Pandas multiplication operator, on the other hand, performs element-wise multiplication on the corresponding columns in a DataFrame. In other words, it multiplies the first element in the first column with the first element in the second column, the second element in the first column with the second element in the second column, and so on. \
|
||||
Floating-point arithmetic has a limited precision due to the finite number of bits used to represent floating-point numbers in binary format. This can lead to rounding errors and loss of precision in arithmetic operations, particularly with very large or very small numbers.\
|
||||
\
|
||||
5. You can use
|
||||
\f1\fs27\fsmilli13600 \cf2 \cb3 sys.getsizeof
|
||||
\f0\fs32 \cf2 \cb1 to see how large a Python object is. Does the size of a Python floating-point number change as it gets larger? Why or why not?\
|
||||
The size of a Python floating-point number does not change as it gets larger. This is because Python uses a fixed number of bytes to represent floating-point numbers, regardless of their value.}
|
||||
@@ -0,0 +1,54 @@
|
||||
# Name three practical differences between Python strings and bytes.
|
||||
# Using sys.getsizeof, see how the sizes may be different.
|
||||
# Do emoji change this answer?
|
||||
|
||||
# Encoding
|
||||
# Strings are created using Unicode character points.
|
||||
# While bytes are used to represent actual bytes in memory, which can be encoded using ASCII or UTF-8.
|
||||
|
||||
# Usage
|
||||
# Strings are usually used to represent text.
|
||||
# While bytes are used to represent binary data.
|
||||
|
||||
# Memory Size
|
||||
# Strings take up more memory than bytes, because they are Unicode-based and can contain any character.
|
||||
# While bytes are limited to binary data.
|
||||
|
||||
|
||||
print("---------------------------------------------")
|
||||
print("This is the Memory Size test print out:")
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
my_string = "Hello, world!"
|
||||
my_bytes = b'\x48\x65\x6c\x6c'
|
||||
|
||||
print(sys.getsizeof(my_string)) # Prints 62
|
||||
print(sys.getsizeof(my_bytes)) # Prints 37
|
||||
|
||||
print()
|
||||
print('Test the emoji:')
|
||||
|
||||
x = 'pizza' # Prints 54
|
||||
y = b'pizza' # Prints 38
|
||||
z = '🍕' #Prints 80
|
||||
|
||||
print(sys.getsizeof(x))
|
||||
print(sys.getsizeof(y))
|
||||
print(sys.getsizeof(z))
|
||||
|
||||
|
||||
print("---------------------------------------------")
|
||||
|
||||
print("This is the String to Sequence of Numbers print out:")
|
||||
|
||||
def String_to_SequenceofNumbers():
|
||||
my_string = input("Enter a string: ")
|
||||
num_sequence = [ord(c) for c in my_string]
|
||||
print(num_sequence)
|
||||
|
||||
if __name__ == "__main__":
|
||||
String_to_SequenceofNumbers()
|
||||
|
||||
print("---------------------------------------------")
|
||||
@@ -0,0 +1,63 @@
|
||||
# 1. Why does ord("😀") give a different number than 0xf09f9880, which is what the hexdump of the same character yields?
|
||||
#
|
||||
# The ord() function returns the Unicode code point of a character as an integer, which is 128512 for the smiley face.
|
||||
# However, the hexdump command, returns the UTF-8 encoding of character.
|
||||
# The hexadecimal representation of 128512 is 0x1F600.
|
||||
# The UTF-8 encoding of the "😀" character is represented by the sequence of bytes F0 9F 98 80 in hexadecimal.
|
||||
# UTF-8 is one of the encoding schemes used to represent Unicode characters as binary data.
|
||||
#
|
||||
#
|
||||
# 8. Hypothetical Situation: If the dataframe is not formatted or cleaned, while the column storing the tomoto sales data isn't named exactly same as 'Tomato_Sales'
|
||||
# It will raise a KeyError instead.
|
||||
#
|
||||
#
|
||||
# 9. The solution of the hypothetical situation from my question 8:
|
||||
|
||||
def read_tomato_sales(file_name):
|
||||
data = pd.read_csv(file_name)
|
||||
if not pd.api.types.is_numeric_dtype(data["Tomato_Sales"]):
|
||||
raise TypeError("Tomato_Sales column should contain only numeric values")
|
||||
total_sales = data.iloc[:, -1].sum() # This is the key
|
||||
print(f"Total tomato sales: {total_sales}")
|
||||
# read_tomato_sales("tomato_sales_error.csv")
|
||||
|
||||
|
||||
# 10. Different human languages can use various character encodings such as UTF-8, UTF-16, or ASCII.
|
||||
# While using bytes, you can work with raw data without worrying about specific encodings.
|
||||
# This allows us to avoid encoding-related errors and ensures compatibility across different language texts.
|
||||
|
||||
def encode_decode_data():
|
||||
print('-' * 20)
|
||||
data = input("Enter a sentence using any language: ")
|
||||
print('-' * 20)
|
||||
print("The sentence you entered is: {}".format(data))
|
||||
print('-' * 20)
|
||||
encoded_data = data.encode('utf-8')
|
||||
print("This sentence after encoded is: {}".format(encoded_data))
|
||||
print('-' * 20)
|
||||
byte_length = len(encoded_data)
|
||||
print("The byte length of this sentence is: {}".format(byte_length))
|
||||
print('-' * 20)
|
||||
decoded_data = encoded_data.decode()
|
||||
print("This sentence after decoded is: {}".format(decoded_data))
|
||||
print('-' * 20)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_function = 'function1'
|
||||
|
||||
if run_function == 'function1':
|
||||
encode_decode_data()
|
||||
elif run_function == 'function2':
|
||||
read_tomato_sales(file_name)
|
||||
|
||||
|
||||
# 2.5. REPL stands for Read-Eval-Print Loop.
|
||||
# A REPL is an interactive programming environment that allows you to read input, evaluate expressions or statements, print output, and repeat the process.
|
||||
# It provides a quick way to experiment with code, test small snippets, and get immediate feedback.
|
||||
# The Python REPL allows you to interactively enter Python code and see the results immediately without needing to write a full script and execute it separately.
|
||||
|
||||
|
||||
# 3. The '_' is a convention that holds the value of the last printed expression or the result of the last evaluated expression.
|
||||
# The actual type of the "_" variable depends on the value assigned to it.
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
# Generate two random 64-bit integers
|
||||
num1 = np.random.randint(0, 10 - 1)
|
||||
num2 = np.random.randint(0, 10 - 1)
|
||||
|
||||
# Multiply them together using the normal Python multiplication operator
|
||||
print(f"num1 * num2 = {num1 * num2}")
|
||||
|
||||
# Create a Pandas DataFrame with the generated numbers
|
||||
data = {'Number1': [num1], 'Number2': [num2]}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# Multiply the numbers together using the Pandas multiplication operator
|
||||
df['Product'] = df['Number1'] * df['Number2']
|
||||
|
||||
print(df)
|
||||
@@ -0,0 +1,4 @@
|
||||
import sys
|
||||
num = 99999999999
|
||||
size = sys.getsizeof(num)
|
||||
print(f"The size of the float is {size} bytes.")
|
||||
@@ -0,0 +1,7 @@
|
||||
import time
|
||||
|
||||
i=0
|
||||
while True:
|
||||
print(f"\015{i=}", end='')
|
||||
i += 1
|
||||
time.sleep(0.2)
|
||||
@@ -0,0 +1,5 @@
|
||||
char1 = "u"
|
||||
result1 = ord(char1)
|
||||
print("ASCII value of the character {} is {}.".format(char1, result1))
|
||||
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
from google.cloud import pubsub_v1
|
||||
from google.api_core.exceptions import DeadlineExceeded
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
import json
|
||||
import time
|
||||
from concurrent.futures import TimeoutError
|
||||
import pandas as pd
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
import selenium.common.exceptions
|
||||
import re
|
||||
from datetime import datetime
|
||||
import arrow
|
||||
import subprocess
|
||||
import concurrent.futures
|
||||
|
||||
# This script is used to load the new price and the timestamp and append such data to an existing key in the HDF5 file "product1.h5"
|
||||
|
||||
def get_element_text(driver, class_name_prefix):
|
||||
'''
|
||||
Return the text in the first element with a given class name.
|
||||
|
||||
Note that XPath doesn't have a function to check if a given word is present
|
||||
in a list of words, so using contains() will match a class name even if
|
||||
the class name is a substring of the class name being matched against.
|
||||
To prevent this, we use spaces as a delimiter at the beginning and end of
|
||||
the class name, and ensure there's only one space before and after all
|
||||
class names in the class name list.
|
||||
|
||||
Example: consider two class names with cache-busting suffixes:
|
||||
* Header_soldStatusNotice__12345
|
||||
* Header_soldStatusNoticeBackground__ABCDE
|
||||
|
||||
Matching against Header_soldStatusNotice using a naive implementation with
|
||||
`contains()` would match both class names, even though only the first one
|
||||
is the correct match.
|
||||
'''
|
||||
# Construct xpath selector.
|
||||
xpath_str = "//*[contains(" # Step 1: search for a class name by matching the beginning of the class name against...
|
||||
xpath_str += "concat(' ', " # Step 2: a leading space, followed by...
|
||||
xpath_str += "normalize-space(@class), " # Step 3: the list of class names, separated by single spaces...
|
||||
xpath_str += "' '), " # Step 4: a trailing space...
|
||||
xpath_str += f"' {class_name_prefix} ')]" # Step 5: compared against the class name with a single space on either side.
|
||||
try:
|
||||
match = driver.find_element(By.XPATH, xpath_str)
|
||||
return match.get_attribute('textContent')
|
||||
except selenium.common.exceptions.NoSuchElementException as nsee:
|
||||
pass
|
||||
|
||||
# We didn't find a match, try again by doing a prefix match and assume
|
||||
# the full class name ends with two underscores followed by a cache-
|
||||
# busting string.
|
||||
xpath_str = "//*[contains(" # Step 1: search for a class name by matching the beginning of the class name against...
|
||||
xpath_str += "concat(' ', " # Step 2: a leading space, followed by...
|
||||
xpath_str += "normalize-space(@class)), " # Step 3: the list of class names, separated by single spaces...
|
||||
xpath_str += f"' {class_name_prefix}__')]" # Step 4: compared against the class name prefixed with a single space and ending with two underscores.
|
||||
try:
|
||||
match = driver.find_element(By.XPATH, xpath_str)
|
||||
return match.get_attribute('textContent')
|
||||
except selenium.common.exceptions.NoSuchElementException as nsee:
|
||||
pass
|
||||
|
||||
# No match, return empty string.
|
||||
return ''
|
||||
|
||||
|
||||
def scrape_product_price(project_id, subscription_name, url):
|
||||
# Create an empty dataframe to store the prices
|
||||
df_new = pd.DataFrame(columns=['Product ID', 'Zip Code', 'Shown Price', 'Minimum Quantity', 'Total Price', 'Unit Price','Sales Count','Sold Status'])
|
||||
print(f"Created a dataframe: {df_new}")
|
||||
|
||||
# Configure the Selenium driver options
|
||||
options = Options()
|
||||
options.add_argument('--disable-gpu')
|
||||
driver = webdriver.Chrome(options=options)
|
||||
|
||||
# Set up Pub/Sub subscription
|
||||
subscriber = pubsub_v1.SubscriberClient()
|
||||
subscription_path = subscriber.subscription_path(project_id, subscription_name)
|
||||
|
||||
# Load the product page and extract the __NEXT_DATA__ data
|
||||
product_id = url.split("/")[-1][:5]
|
||||
driver.delete_all_cookies()
|
||||
driver.get(url)
|
||||
|
||||
# Close the cookies setting window
|
||||
try:
|
||||
cookie_button = WebDriverWait(driver, 2).until(
|
||||
EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[2]/div/div/div[3]/button"))
|
||||
)
|
||||
print(f"cookie_button = {cookie_button}")
|
||||
cookie_button.click()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Clear any modal dialog
|
||||
try:
|
||||
modal_close = WebDriverWait(driver, 2).until(
|
||||
EC.presence_of_element_located((By.XPATH, "//*[contains(concat(' ', normalize-space(@class), ' '), ' icon-icon-close-s ')]"))
|
||||
)
|
||||
print(f"modal_close = {modal_close}")
|
||||
modal_close.click()
|
||||
except:
|
||||
pass
|
||||
|
||||
def callback(message):
|
||||
# Extract zip code from message data
|
||||
zip_code = message.message.data.decode('utf-8')
|
||||
print(f"Received zip code: {zip_code}")
|
||||
|
||||
try:
|
||||
# Clear any modal dialog
|
||||
modal_close = WebDriverWait(driver, 2).until(
|
||||
EC.presence_of_element_located((By.XPATH, "//*[contains(concat(' ', normalize-space(@class), ' '), ' icon-icon-close-s ')]"))
|
||||
)
|
||||
print(f"modal_close = {modal_close}")
|
||||
modal_close.click()
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Clear any notice
|
||||
notice_close = WebDriverWait(driver, 2).until(
|
||||
EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div/div[2]/div/div[2]/div/div/div[1]/div[2]/button"))
|
||||
)
|
||||
print(f"notice_close = {notice_close}")
|
||||
notice_close.click()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Enter zip code in the input field and click the "Check" button
|
||||
zip_button = WebDriverWait(driver, 5).until(
|
||||
EC.presence_of_element_located((By.XPATH, '/html/body/div[2]/div/header/div[3]/div/div/div[2]/div[1]'))
|
||||
)
|
||||
zip_button.click()
|
||||
|
||||
zip_input = WebDriverWait(driver, 2).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//div/label/following-sibling::input'))
|
||||
)
|
||||
zip_input.click()
|
||||
zip_input.send_keys("\b\b\b\b\b")
|
||||
zip_input.send_keys(zip_code)
|
||||
|
||||
check_button = driver.find_element(By.XPATH, "//button[text() = 'Confirm']")
|
||||
check_button = WebDriverWait(driver, 2).until(EC.presence_of_element_located((By.XPATH, "//button[text() = 'Confirm']")))
|
||||
check_button.click()
|
||||
|
||||
# Record sales count, if any
|
||||
sales_count_element = get_element_text(driver, 'Header_count')
|
||||
count_start_index = sales_count_element.index('sold ') + len('sold ')
|
||||
count_end_index = sales_count_element.index('+')
|
||||
sales_count = sales_count_element[count_start_index:count_end_index]
|
||||
|
||||
# Record any sold status notice
|
||||
sold_status = get_element_text(driver, 'UnavailableCartbtn_container')
|
||||
if sold_status == 'Notify me':
|
||||
sold_status = 'Sold Out'
|
||||
|
||||
# Extract the price of the product for this zip code
|
||||
min_quantity = 0
|
||||
total_price = 0
|
||||
unit_price = 0
|
||||
try:
|
||||
minimum_quantity_str = driver.find_element(By.XPATH, "//div[contains(@class, 'minQuantity')]")
|
||||
if minimum_quantity_str:
|
||||
regexp = r"Minimum purchase: (\d+) \(\$([\d.]+)"
|
||||
match = re.search(regexp, minimum_quantity_str.get_attribute('innerHTML'))
|
||||
if match:
|
||||
min_quantity, total_price = match.groups()
|
||||
except selenium.common.exceptions.NoSuchElementException:
|
||||
print("No minimum purchase string found")
|
||||
except Exception as e:
|
||||
print(f"Exception when finding minimum purchase string: {e}")
|
||||
|
||||
ld_json_element = driver.find_element(By.XPATH, "//script[@type='application/ld+json']")
|
||||
ld_json_text = ld_json_element.get_attribute('innerHTML')
|
||||
ld_json_start_index = ld_json_text.index('{')
|
||||
ld_json_end_index = ld_json_text.rindex('}') + 1
|
||||
ld_json_json = ld_json_text[ld_json_start_index:ld_json_end_index]
|
||||
ld_json = json.loads(ld_json_json)
|
||||
ld_json_price = ld_json['offers']['price']
|
||||
print(ld_json_price)
|
||||
|
||||
unit_price_element = driver.find_element(By.XPATH, "//div[contains(@class, 'price_price')]")
|
||||
unit_price = unit_price_element.get_attribute('innerText').replace("$", "")
|
||||
print(f"unit_price = {unit_price}")
|
||||
|
||||
subscriber.acknowledge(ack_ids=[message.ack_id], subscription=subscription_path) # acknowledge receipt of message
|
||||
|
||||
new_row = pd.DataFrame({
|
||||
'Product ID': [product_id],
|
||||
'Zip Code': [zip_code],
|
||||
'Shown Price': [ld_json_price],
|
||||
'Minimum Quantity': [min_quantity],
|
||||
'Total Price': [total_price],
|
||||
'Unit Price': [unit_price],
|
||||
'Sales Count': [sales_count],
|
||||
'Sold Status': [sold_status]
|
||||
})
|
||||
return pd.concat([df_new, new_row], ignore_index=True)
|
||||
|
||||
while True:
|
||||
pull_request = pubsub_v1.types.PullRequest(max_messages=1, subscription=subscription_path)
|
||||
messages = []
|
||||
try:
|
||||
pull_response = subscriber.pull(request=pull_request)
|
||||
messages = pull_response.received_messages
|
||||
except DeadlineExceeded:
|
||||
pass
|
||||
|
||||
if messages:
|
||||
df_new = callback(messages[0])
|
||||
print(f"Updated dataframe: {df_new}")
|
||||
else:
|
||||
print(f"No messages, trying again at {time.asctime()}")
|
||||
break
|
||||
|
||||
return df_new
|
||||
|
||||
|
||||
def scrape_multiple_products(project_id, subscription_name, url_list):
|
||||
df_list = []
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
||||
futures = []
|
||||
for url in url_list:
|
||||
future = executor.submit(scrape_product_price, project_id, subscription_name, url)
|
||||
futures.append(future)
|
||||
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
df = future.result()
|
||||
df_list.append(df)
|
||||
print("Scraped data for URL:", url)
|
||||
|
||||
return df_list
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
project_id = input("Enter the project ID: ")
|
||||
subscription_name = input("Enter the subscription name: ")
|
||||
url_list = [
|
||||
'https://www.sayweee.com/en/product/Want-Want-Senbei-Rice-Crackers/17119',
|
||||
'https://www.sayweee.com/en/product/Lays-Potato-Chips-Cucumber-Flavor/51261',
|
||||
'https://www.sayweee.com/en/product/Glico-Pocky-Cookies-and-Cream-Family-Pack/79467',
|
||||
'https://www.sayweee.com/en/product/Xiang-Na-Lan-Thai-Jasmine-Rice/19566?category=dried01',
|
||||
'https://www.sayweee.com/en/product/Sekka-Premium-Japanese-Medium-Grain-Rice/81224',
|
||||
'https://www.sayweee.com/en/product/Big-Green-Spring-Farm-Organic-Millet/90991?category=dried02',
|
||||
'https://www.sayweee.com/en/product/Liuzhou-Guangxi-Specialty-LuoSiFen-Hot-Spicy-Noodles/95442?category=instant01',
|
||||
'https://www.sayweee.com/en/product/House-Foods-Firm-Tofu-/85175?trace_id=c93f3d1a-80a4-4e1a-8d41-a402ae43186f',
|
||||
'https://www.sayweee.com/en/product/Lactaid-Lactose-Free-2--Reduced-Fat-Milk/95353',
|
||||
'https://www.sayweee.com/en/product/Washington-Fuji-Apples/94817?category=fruits03',
|
||||
'https://www.sayweee.com/en/product/Formosa-Pork-Sung-/23528?trace_id=3c143dd4-4a35-4e8d-93ee-d91f209717e7'
|
||||
]
|
||||
|
||||
df_list = scrape_multiple_products(project_id, subscription_name, url_list)
|
||||
|
||||
# Concatenate all dataframes in the list
|
||||
df_combined = pd.concat(df_list, ignore_index=True)
|
||||
|
||||
# Save the combined dataframe to HDF5 file
|
||||
with pd.HDFStore('product1.h5') as store:
|
||||
store.append('key', df_combined, format='table')
|
||||
|
||||
# Print the combined dataframe
|
||||
print("Combined DataFrame:")
|
||||
print(df_combined)
|
||||
|
||||
# Save the combined dataframe to a CSV file
|
||||
df_combined.to_csv('product1.csv', index=False)
|
||||
|
||||
print("Data scraping and storage completed.")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Question7:
|
||||
# Difference:
|
||||
# 1. Byte objects are sequence of Bytes, whereas Strings are sequence of characters.
|
||||
# 2. Byte objects are in machine readable form internally; Strings are only in human readable form.
|
||||
# 3. strings (str) support various character encodings, such as UTF-8, ASCII, and more. Bytes (bytes), on the other hand, represent raw binary data and can hold any byte values.
|
||||
## Emojis can affect the size difference.
|
||||
import sys
|
||||
string_size = sys.getsizeof("Hello")
|
||||
bytes_size = sys.getsizeof(b"Hello")
|
||||
emoji_size = sys.getsizeof("😀")
|
||||
print(f"String size: {string_size} bytes")
|
||||
print(f"Bytes size: {bytes_size} bytes")
|
||||
print(f"emoji size: {emoji_size} bytes")
|
||||
|
||||
# python routine:
|
||||
def print_string_as_numbers(string):
|
||||
for char in string:
|
||||
print(ord(char))
|
||||
input_string = "Hello, world!"
|
||||
print_string_as_numbers(input_string)
|
||||
@@ -0,0 +1,21 @@
|
||||
# question 1: The international standard for floating-point representation is called IEEE 754.
|
||||
|
||||
# question 2: The IEEE 754 standard allows floating-point numbers to be represented with different bit sizes. The most commonly used formats specified by the standard are single precision (32 bits) and double precision (64 bits).
|
||||
|
||||
# question 3:
|
||||
import sys
|
||||
print(sys.float_info)
|
||||
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
|
||||
|
||||
# question 4: In the first multiplication (num1 * num2), you are using the normal Python multiplication
|
||||
# operator, which operates on native Python integers. Python integers have arbitrary precision, meaning they can
|
||||
# represent very large numbers accurately. However, when the result exceeds the limit of a 64-bit signed integer
|
||||
# (2^63 - 1), an overflow may occur, and the result may not be accurate.
|
||||
# In the second multiplication (df['Number1'] * df['Number2']), you are using the Pandas multiplication operator
|
||||
# on the columns of a DataFrame. By default, Pandas uses NumPy for numerical computations, and NumPy uses
|
||||
# fixed-size data types. In this case, the integers in the DataFrame are most likely represented as 64-bit
|
||||
# signed integers (int64), which have a maximum value of 2^63 - 1. If the result of the multiplication exceeds
|
||||
# this limit, it will overflow and wrap around, leading to incorrect results.
|
||||
|
||||
# Question5: 5. The size of a Python floating-point number is determined by the implementation and is not influenced by the magnitude of the value it represents. So it does not change as the value of the number gets larger.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCbUrTPE/8CUByUtG9rBuMP5zeF3x5sZcPmm61bcxBblvHL5Dx1WwIjujhbZ5mhxnmDqxV07MCFZwMzlPjJ7Ow9erZvaXAe5+jS2iTQqu9apfj+U3x2xEZQmoKprXUpWl8G9YDk2wxBqCn//ugkJ5+erIEopNUGPcVhACucpaCT+kld9mHtXBjdUEKUA85HhXiBkuBsv9k/VqL/oXbvHjY8Z6gFP64LcxdNASuf4Qn1wW0w3kXlQRkSoGdSxpGLztXN1v5rvetOzjA8HXixh7TYVWCc+XCa9KsY3QRk7gFyvtIsXz59NMOTgfpCnUQE2Pk9R5wuuA2fXyr+Kg+7NPkd sloetjeswong@Sloetjess-Air
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,30 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Users Table</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>Credentials</th>
|
||||
<th>Creation Date</th>
|
||||
<th>Modification Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user.id }}</td>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.credentials }}</td>
|
||||
<td>{{ user.creation_date }}</td>
|
||||
<td>{{ user.modification_date }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user