CS50 notes

Scratch

try

  • hello people
    • what’s you name
    • hi,response
  • meow
    • meow module

C

VSCode debug

  1. Open Developer Command Prompt for Visual Studio:
  • Open the “Developer Command Prompt for Visual Studio” from the Start menu. This sets up the necessary environment variables for Visual C++ development.
  1. Start Visual Studio Code from Developer Command Prompt:
  • After opening the Developer Command Prompt, navigate to your project directory using the cd command.
  • Launch Visual Studio Code from this command prompt:
    1
    code .
    This ensures that Visual Studio Code inherits the environment variables set up by the Developer Command Prompt.
  1. Configure Environment Variables in Visual Studio Code:
  • In Visual Studio Code, you can configure environment variables in the .vscode/settings.json file.
  • Open the settings.json file by clicking on the gear icon in the lower-left corner and selecting “Settings.”
  • Click on the “Open Settings (JSON)” icon in the top-right corner.
  • Add or modify the “env” settings to include the necessary paths. For example:
    1
    2
    3
    4
    5
    {
    "env": {
    "PATH": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64"
    }
    }
    Make sure to adjust the path to match your Visual Studio installation.
  1. Install the C/C++ Extension:
  • Make sure you have the “C/C++” extension installed in Visual Studio Code. This extension provides IntelliSense, debugging, and build support for C and C++ languages.
  1. Verify Compiler Path:
  • In your Visual Studio Code project, create or check the .vscode/c_cpp_properties.json file to ensure the correct path to the compiler is set. For example:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     {
    "configurations": [
    {
    "name": "Win32",
    "includePath": ["${workspaceFolder}/**"],
    "defines": ["_DEBUG", "UNICODE", "_UNICODE"],
    "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe",
    "cStandard": "c11",
    "cppStandard": "c++17",
    "intelliSenseMode": "msvc-x64"
    }
    ],
    "version": 4
    }
    Adjust the compilerPath according to your Visual Studio installation.
    After these steps, you should be able to build and debug your C++ code within Visual Studio Code, and it should recognize the compiler and tools from your Visual Studio installation.

remove a PATH from this cmd

1
set PATH=%PATH:C:\cygwin64\bin;=%
1
2
3
4
5
6
7
8
9
// hello.c
# include "cs50.h"
# include <stdio.h>
int main(void) {
// printf("hello, world");

string answer = get_string("What's your name?");
printf("hello answer");
}
1
2
3
4
5
# build static library
gcc -shared -o cs50lib.dll cs50.c

# build hello.c and link library
gcc -o hello.exe .\hello.c -L. -lcs50lib

Valgrind

Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. You can also use Valgrind to build new tools.

add MSYS2 path

1
set PATH=%PATH%;D:\app\msys64\ucrt64\bin

python

install

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# install cs50
pip3 install cs50
# install pyttsx3
pip install pyttsx3
# install flask
pip install flask
# install flask_session
pip install flask_session
# upgrade pip
python.exe -m pip install --upgrade pip
# install pillow
pip install pillow

# install by py
py -m pip install pillow

# None
session["name"] = None

vscode debug

setting
1
2
3
4
# vscode plugin install 
Python IntelliSense (Pylance), Linting, Debugging (multi-threaded, remote), code formatting, refactoring, unit tests, and more.
# mouse right --> run python -> run python in terminal
# F5:debug
launch.json - args for parameter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": ["maze1.txt"]
}
]
}

some code

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# print
answer = get_string("What's your name?")
print(f"hello, {answer}")

#x: 1
#y: 3
#0.33333333333333331482961625624739099293947219848633
z = x / y
print(f"{z:.50f}")

# main()
def main():
for i in range(3):
meow()
def meow():
print("meow")
main()

# try
def main():
height = get_height()
for i in range(height):
print("#")
# def get_height():
# while True:
# try :
# n = int(input("Height: "))
# if n > 0:
# return n
# except ValueError:
# print("Not an integer")
def get_height():
while True:
try :
n = int(input("Height: "))
except ValueError:
print("Not an integer")
else:
if n > 0:
return n
main()

# same line
for i in range(4):
print("?", end="")
print()
# same line
print("?" * 4)

# list
scores = [72, 73, 33]
average = sum(scores) / len(scores)
print(f"Average: {average}")
# list - 2
from cs50 import get_int
# scores = list() - same
scores = []
for i in range(3):
score = get_int("Score: ")
# scores.append(score)
scores += [score]
average = sum(scores) / len(scores)
print(f"Average: {average}")
# list -3
for arg in argv[0:1]:

# sys module
from sys import argv
if len(argv) == 2:
print(f"hello, {argv[1]}")
else:
print("hello, world")
from sys import argv
# sys module - 2
for i in range(len(argv)):
print(argv[i])
# ----------------
for arg in argv:
print(arg)
# sys module - 3
import sys
if len(sys.argv) != 2:
print("Missing command-line argument")
sys.exit(1)
print(f"hello, {sys.argv[1]}")
sys.exit(0)

# dict
# people = dict() -- same -- people ={}
people = {
"Carter": "+1-617-495-1000",
"David": "+1-949-468-2750"
}
name = input("Name: ")
if name in people:
print(f"Number: {people[name]}")
# for pie, price in pizzas.items():
# print("A whole {} pizza costs ${}".format(price))

# swap
x = 1
y =2
print(f"x is {x}, y is {y}")
x, y = y, x
print(f"x is {x}, y is {y}")

# talk
import pyttsx3
engine = pyttsx3.init()
name = input("Name: ")
engine.say(f"hello, {name}")
engine.runAndWait()

# qr code
import os
import qrcode
img = qrcode.make("https://www.youtube.com/watch?v=xvFZjo5PgG0&ab_channel=Duran")
img.save("qr.png", "PNG")
# windows not support
# os.system("open qr.png")

# CSV
import csv
# file = open("phonebook.csv", "a")
# name = input("Name: ")
# number = input("number: ")
# writer = csv.writer(file)
# writer.writerow([name, number])
# file.close()
with open("phonebook.csv", "a") as file:
name = input("Name: ")
number = input("number: ")
# writer = csv.writer(file)
# writer.writerow([name, number])
writer = csv.DictWriter(file, filenames=["name", "number"])
writer.writerow({"name": name, "number": number})
#with open("phonebook.csv") as file:
# file_reader = csv.DictReader(file)
# for book in file_reader:
# print(book)
#

# tuple - constent
# list of tuples
presidents = [
("George Washington", 1789,),
("John Adams", 1797,),
("Thomas Jefferson", 1801,),
]

for prez, year in presidents:
print("In {1}, {0} took office".format(prez, year))

# run main
if __name__ == "__main__"
main()

# boject
class Student():
def __init__(self, name, id):
self.name = name
self.id = id

def changeID(self, id):
self.id = id

def print(self):
print("{} - {}".format(self.name, self.id))

jane = Student("Jane", 10)
jane.print()
jane.changeID(11)
jane.print()

# direct run - linux (also need set file can execute)
#!/usr/bin/env pythone

# lambda 使用於僅使用一次 function
import csv
with open("favorites.csv", "r") as file:
reader = csv.DictReader(file)
counts = {}
for row in reader:
favorite = row["language"]
if favorite in counts:
counts[favorite] += 1
else:
counts[favorite] = 1
# def get_value(language):
# return counts[language]
#
# for favorite in counts:
# for favorite in sorted(counts, reverse=True):
# get_value 無 () 表不馬上執行
# for favorite in sorted(counts, key=get_value, reverse=True):
# lambda 使用於僅使用一次 function
for favorite in sorted(counts, key=lambda language: counts[language], reverse=True):
print(f"{favorite}: {counts[favorite]}")
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
from cs50 import SQL
db = SQL("sqlite:///favorites.db")
favorite = input("Favorite: ")
rows = db.execute("SELECT (*) FROM favorites WHERE problem = 'Mario'")
for row in rows:
print(row["Timestamp"])

from cs50 import SQL
db = SQL("sqlite:///favorites.db")
favorite = input("Favorite: ")
#rows = db.execute("SELECT COUNT(*) AS n FROM favorites WHERE problem = 'Mario'")
rows = db.execute("SELECT COUNT(*) AS n FROM favorites WHERE problem = ?", favorite)
row = rows[0]
print(row["n"])

# race
db.execute("BEGIN TRANSACTION")
rows = db.execute("SELECT likes FROM posts WHERE id = ?", id)
likes = roes[0]["likes]
db.execute("UPDATE posts SET likes = ? WHERE id = ?", likes + 1, id)
db.execute("COMMIT)

# inject
# -- ignore right
# rows = db.execute(f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'")
# rows = db.execute(f"SELECT * FROM users WHERE username = 'mmalan@harvad.edu'--' AND password = '{password}'")
rows = db.execute("SELECT * FROM users WHERE username = ? AND password = ?", username, password )
if row:
...
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# tuple
self.start = (i, j)
if (i, j) == self.start:
print("A", end="")

# set
# 建立空集合的方法是 set1 = set() 而不是 set1 = {}
set1 = set()# 建立空的集合
set2 = {1, 2, 3, 4, 5}
# 從串列 (List) 來建立集合
set3 = set([i for i in range(20, 30)])
# 從數組 (Tuple) 來建立集合
set4 = set((10, 20, 30, 40, 50))
# 集合不會包含重複的資料, 你可以從 set5 印出來的結果得知
set5 = set((1, 1, 1, 2, 2, 3))
set2.add(6) # 在 set2 中加入 6
set4.remove(

# 與串列 (List) 和數組 (Tuple) 一樣可以使用以下函式
# len() 回傳長度
# sum() 回傳總和
# max() 回傳最大值
# min() 回傳最小值
set1 = {2, 4, 6, 8, 10}
print(len(set1))
print(sum(set1))
print(max(set1))
print(min(set1))

# raise 例外
# raise empty frontier
def remove(self):
if self.empty():
raise Exception("empty frontier")
else:
node = self.frontier[-1]
self.frontier = self.frontier[:-1]
return node

# any
# any(a list with at least one non-zero entry) returns True
list_1 = [0,0,0,1,0,0,0,0]
print(any(list_1))

# all - alpha
my_string = "coding**is**cool"
are_all_letters = [char.isalpha() for char in my_string]
print(all(are_all_letters))
# Output
False
print(are_all_letters)
# Output
[True, True, True, True, True, True, False, False, True, True, False, False,
True, True, True, True]
# all - number
my_string = "56456278"
are_all_digits = [char.isdigit() for char in my_string]
print(all(are_all_digits))
# Output
True
print(are_all_digits)
# Output
[True, True, True, True, True, True, True, True]

# reverse
actions = []
while node.parent is not None:
actions.append(node.action)
cells.append(node.state)
node = node.parent
actions.reverse()

# class 繼承
class StackFrontier():
def __init__(self):
self.frontier = []
def add(self, node):
self.frontier.append(node)
def contains_state(self, state):
return any(node.state == state for node in self.frontier)
def empty(self):
return len(self.frontier) == 0
def remove(self):
if self.empty():
raise Exception("empty frontier")
else:
node = self.frontier[-1]
self.frontier = self.frontier[:-1]
return node
class QueueFrontier(StackFrontier):
def remove(self):
if self.empty():
raise Exception("empty frontier")
else:
node = self.frontier[0]
self.frontier = self.frontier[1:]
return node

SQL

basic

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# CRUD
# WHERE
# LIKE
# ORDER BY
# GROUP BY
cs50>sqlite3 favorites.db
SQLite version 3.44.2 2023-11-24 11:41:44 (UTF-16 console I/O)
Enter ".help" for usage hints.
sqlite> .mode csv
sqlite> .import favorites.csv favorites
sqlite> .schema
CREATE TABLE IF NOT EXISTS "favorites"(
"Timestamp" TEXT, "language" TEXT, "problem" TEXT);
sqlite> SELECT * FROM favorites;
"10/24/2022 8:33:26",C,Credit
...

cs50>sqlite3 favorites.db
SQLite version 3.44.2 2023-11-24 11:41:44 (UTF-16 console I/O)
Enter ".help" for usage hints.
sqlite> SELECT * FROM favorites;
10/24/2022 8:33:26|C|Credit
10/24/2022 10:32:26|Python|Runoff
10/24/2022 11:10:47|Python|Mario
10/24/2022 11:22:35|Python|Scratch
10/24/2022 11:39:06|Python|Readability
10/24/2022 11:53:00|Scratch|Scratch
......

sqlite> SELECT language from favorites;

sqlite> SELECT COUNT(*) from favorites;
430

sqlite> SELECT DISTINCT(language) FROM favorites;
C
Python
Scratch

sqlite> SELECT COUNT(DISTINCT(language)) FROM favorites;
3

# change output to n
sqlite> SELECT COUNT(DISTINCT(language)) as n FROM favorites;
3

sqlite> SELECT COUNT(*) FROM favorites WHERE language = 'C';
98
sqlite> SELECT COUNT(*) FROM favorites WHERE language = 'C' AND problem = 'Mario' ;
3

sqlite> SELECT language,COUNT(*) FROM favorites GROUP BY language;
C|98
Python|270
Scratch|62

sqlite> SELECT language,COUNT(*) FROM favorites GROUP BY language ORDER BY COUNT(*);
Scratch|62
C|98
Python|270
sqlite> SELECT language,COUNT(*) FROM favorites GROUP BY language ORDER BY COUNT(*) DESC;
Python|270
C|98
Scratch|62
sqlite> SELECT language,COUNT(*) FROM favorites GROUP BY language ORDER BY COUNT(*) ASC;
Scratch|62
C|98
Python|270
sqlite> SELECT language,COUNT(*) FROM favorites GROUP BY language ORDER BY COUNT(*) DESC LIMIT 1;
Python|270
sqlite> SELECT * FROM students ORDER BY house, student_name;

sqlite> INSERT INTO favorites (language, problem) VALUES('SQL', 'Fiftyville');
sqlite> SELECT language FROM favorites GROUP by language;
C
Python
SQL
Scratch

sqlite> UPDATE favorites SET language = 'C++' WHERE language = 'C';
sqlite> SELECT * FROM favorites WHERE language = 'C++';
10/24/2022 8:33:26|C++|Credit
10/24/2022 13:26:23|C++|Bulbs
10/24/2022 13:37:30|C++|Speller

sqlite> DELETE FROM favorites WHERE problem = 'Tideman';

command and word

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.quit
.shell cls
.shell clear
.headers ON
.timer on

# SQLite
# BLOB
# INTERGER
# NUMERIC
# REAL
# TEXT

# NOT NULL
# UNIQUE
# PRIMARY KEY
# FOREIGN KEY

# JOIN
# UNION
# BEGIN TRANSACTION
# COMMIT
# ROLLBACK

example #1

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
sqlite3 shows.db
sqlite> .schema
CREATE TABLE genres (
show_id INTEGER NOT NULL,
genre TEXT NOT NULL,
FOREIGN KEY(show_id) REFERENCES shows(id)
);
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE ratings (
show_id INTEGER NOT NULL,
rating REAL NOT NULL,
votes INTEGER NOT NULL,
FOREIGN KEY(show_id) REFERENCES shows(id)
);
CREATE TABLE shows (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
episodes INTEGER,
PRIMARY KEY(id)
);
CREATE TABLE stars (
show_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(show_id) REFERENCES shows(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE writers (
show_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(show_id) REFERENCES shows(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);

SELECT * FROM shows LIMIT 10;
SELECT * from stars LIMIT 20;
SELECT * FROM genres LIMIT 20;
SELECT * FROM genres WHERE genre = 'Comedy';
SELECT * FROM genres WHERE genre = 'Comedy' LIMIT 10;
SELECT * FROM shows WHERE id ='62614';

SELECT show_id WHERE genre = 'Comedy';
SELECT show_id FROM genres WHERE genre = 'Comedy';
SELECT COUNT(show_id) FROM genres WHERE genre = 'Comedy';
SELECT title FROM shows WHERE id IN (SELECT show_id FROM genres WHERE genre = 'Comedy');
SELECT title FROM shows WHERE id IN (SELECT show_id FROM genres WHERE genre = 'Comedy') LIMIT 10;

SELECT * from people WHERE name = 'Steve Carell';
SELECT * FROM stars WHERE person_id IN (SELECT id from people WHERE name = 'Steve Carell');
SELECT title FROM shows WHERE id IN (SELECT show_id FROM stars WHERE person_id IN (SELECT id from people WHERE name = 'Steve Carell'));
SELECT title FROM shows WHERE id IN (SELECT show_id FROM stars WHERE person_id IN (SELECT id from people WHERE name = 'Steve Carell')) ORDER BY title;

sqlite> SELECT * FROM shows JOIN genres ON shows.id = genres.show_id WHERE title = 'The Office';
112108|The Office|1995|6|112108|Comedy
290978|The Office|2001|14|290978|Comedy
290978|The Office|2001|14|290978|Drama
386676|The Office|2005|188|386676|Comedy
1791001|The Office|2010|30|1791001|Comedy
20877972|The Office|2022|20|20877972|Comedy
2186395|The Office|2012|8|2186395|Comedy
8305218|The Office|2019|28|8305218|Comedy

sqlite> SELECT * FROM shows JOIN ratings ON shows.id = ratings.show_id WHERE title = 'The Office';
id|title|year|episodes|show_id|rating|votes
112108|The Office|1995|6|112108|7.5|45
290978|The Office|2001|14|290978|8.5|112944
386676|The Office|2005|188|386676|9.0|585206
1791001|The Office|2010|30|1791001|4.7|56
2186395|The Office|2012|8|2186395|6.0|12
8305218|The Office|2019|28|8305218|5.7|5718

sqlite> SELECT title FROM people
...> JOIN stars ON people.id = stars.person_id
...> JOIN shows ON stars.show_id = shows.id
...> WHERE name = 'Steve Carell';
title
The Dana Carvey Show
Over the Top
Watching Ellie
Come to Papa
The Office
Entertainers with Byron Allen
The Naked Trucker and T-Bones Show
Some Good News
ES.TV HD
Mark at the Movies
Inside Comedy
Rove LA
Metacafe Unfiltered
Fabrice Fabrice Interviews
Riot
Séries express
Hollywood Sessions
First Impressions with Dana Carvey
LA Times: The Envelope
Space Force

sqlite> SELECT title FROM people, stars, shows
...> WHERE people.id = stars.person_id
...> AND stars.show_id = shows.id
...> AND name = 'Steve Carell';
title
The Dana Carvey Show
Over the Top
Watching Ellie
Come to Papa
The Office
Entertainers with Byron Allen
The Naked Trucker and T-Bones Show
Some Good News
ES.TV HD
Mark at the Movies
Inside Comedy
Rove LA
Metacafe Unfiltered
Fabrice Fabrice Interviews
Riot
Séries express
Hollywood Sessions
First Impressions with Dana Carvey
LA Times: The Envelope
Space Force

sqlite> SELECT * FROM people WHERE name = 'Steve';
id|name|birth
1880811|Steve|
6109489|Steve|
10470749|Steve|
11859694|Steve|
12105385|Steve|
12363756|Steve|
12596781|Steve|
13574708|Steve|
13744353|Steve|
sqlite> SELECT * FROM people WHERE name = 'Steve C%';
sqlite> SELECT * FROM people WHERE name LIKE 'Steve C%';
id|name|birth
34335|Steve Cassling|
127472|Steve Caballero|1964
131409|Steve Comisar|1961

sqlite> .timer on
sqlite> SELECT * FROM shows WHERE title = 'The Office';
id|title|year|episodes
112108|The Office|1995|6
...
Run Time: real 0.023 user 0.000000 sys 0.015625
sqlite> CREATE INDEX title_index ON shows (title);
Run Time: real 0.587 user 0.078125 sys 0.046875
sqlite> SELECT * FROM shows WHERE title = 'The Office';
id|title|year|episodes
...
Run Time: real 0.003 user 0.000000 sys 0.000000

example #2

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
sqlite> DROP TABLE houses;

sqlite> SELECT house,COUNT(*) FROM students GROUP by house;

sqlite> CREATE TABLE houses (
(x1...> id INTERGER NOT NULL,
(x1...> house TEXT NOT NULL,
(x1...> head TEXT NOT NULL,
(x1...> PRIMARY KEY(id)
(x1...> );

sqlite> CREATE TABLE houses (
(x1...> id INTERGER,
(x1...> house TEXT NOT NULL,
(x1...> head TEXT NOT NULL,
(x1...> PRIMARY KEY(id)
(x1...> );
sqlite> SELECT * FROM houses;
sqlite> INSERT INTO houses (house, head)
...> VALUES ('Gryffindor', 'McGonagall');
sqlite> SELECT * FROM houses;
id|house|head
|Gryffindor|McGonagall

# roster.db 3 table
SELECT id FROM houses WHERE house = 'Gryffindor';
SELECT COUNT(student_id) FROM assignments WHERE house_id = 3;
SELECT COUNT(student_id) FROM assignments WHERE house_id = (SELECT id FROM houses WHERE house = 'Gryffindor');

SELECT * FROM assignments JOIN houses ON assignments.house = houses.id;
SELECT COUNT(student_id) FROM assignments JOIN houses ON assignments.house = houses.id WHERE houses.house = 'Gryffindor';
SELECT house, COUNT(student_id) FROM assignment JOIN houses ON houses.id = assignments.house_id GROUP BY house;
SELECT * FROM students JOIN assignments ON students.id = saaignments.student_id JOIN houses ON houses.id = assignments.house_id;

HTML/CSS/JavaScript

HTTP query

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
C:\Users\win10>curl -I https://www.harvard.edu/
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 170804
Cache-Control: public, max-age=1200
Content-Type: text/html; charset=UTF-8
Link: <https://www.harvard.edu/>; rel=shortlink
Permissions-Policy: geolocation=(self)
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
Strict-Transport-Security: max-age=31622400
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Pantheon-Styx-Hostname: styx-fe1-a-6c75bb66d9-bfxbg
X-Styx-Req-Id: 329cdaf9-9f3f-11ee-9576-02ab3da1061f
X-Xss-Protection: 1; mode=block
Age: 584
Accept-Ranges: bytes
Via: 1.1 varnish, 1.1 varnish, 1.1 varnish, 1.1 varnish
Date: Wed, 20 Dec 2023 14:03:32 GMT
X-Served-By: cache-chi-klot8100144-CHI, cache-qpg1273-QPG, cache-qpg1237-QPG, cache-qpg1283-QPG
X-Cache: HIT, HIT, MISS, MISS
X-Cache-Hits: 29, 1, 0, 0
X-Timer: S1703081013.594465,VS0,VE9
Vary: Accept-Encoding, Cookie, Cookie, orig-host

C:\Users\win10>curl -I http://www.harvard.edu/
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 0
Server: Varnish
Retry-After: 0
Location: https://www.harvard.edu/
Accept-Ranges: bytes
Date: Wed, 20 Dec 2023 14:06:34 GMT
Via: 1.1 varnish
X-Served-By: cache-qpg1240-QPG
X-Cache: HIT
X-Cache-Hits: 0
Strict-Transport-Security: max-age=300

C:\Users\win10>curl -I https://harvard.edu/
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 0
Server: Varnish
Retry-After: 0
location: https://www.harvard.edu/
Accept-Ranges: bytes
Date: Wed, 20 Dec 2023 14:08:45 GMT
Via: 1.1 varnish
X-Served-By: cache-qpg1222-QPG
X-Cache: HIT
X-Cache-Hits: 0
X-Timer: S1703081326.829721,VS0,VE0
Strict-Transport-Security: max-age=300

# google search
https://www.google.com/search?q=dog
# re-direct
http://safetyschool.org/


C:\Users\win10>curl -I http://safetyschool.org/
HTTP/1.1 301 Moved Permanently
Server: Sun-ONE-Web-Server/6.1
Date: Wed, 20 Dec 2023 14:21:14 GMT
Content-length: 122
Content-type: text/html
Location: http://www.yale.edu
Connection: close

1st example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="CS50">
<meta property="og:description" content="Introduct to the CS50">
<meta property="og:image" content="cat.jpg">
<title>paragraphs</title>
</head>
<body>
Visit <a href="https://www.harvard.edu/">Harvard</a>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>search</title>
</head>
<body>
<form action="https://www.google.com/search" method="get">
<!-- autocomplete="off" 不顯示之前輸入 -->
<input autocomplete="off" autofocus name='q' placeholder="Query" type="search">
<input type="submit" value="Google Search">
</form>
</body>
</html>

greet response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="home.js"></script>
<title>home</title>
</head>
<body>

<form>
<input autocomplete="off" autofocus id ="name" placeholder="Name" type="text">
<button type="submit">Greet</button>
</form>
</body>
</html>
1
2
3
4
5
6
7
document.addEventListener('DOMContentLoaded', function(){
document.querySelector('form').addEventListener('submit', function(event) {
let name = document.querySelector('#name').value;
alert('hello, ' + name);
event.preventDefault();
});
});

input quickly response

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
<!DOCTYPE html>
<!-- Demonstrates keyup and template literals -->
<html lang="en">
<head>
<script>

document.addEventListener('DOMContentLoaded', function() {
let input = document.querySelector('input');
input.addEventListener('keyup', function(event) {
let name = document.querySelector('p');
if (input.value) {
name.innerHTML = `hello, ${input.value}`;
}
else {
name.innerHTML = 'hello, whoever you are';
}
});
});

</script>
<title>hello</title>
</head>
<body>
<form>
<input autocomplete="off" autofocus placeholder="Name" type="text">
</form>
<p></p>
</body>
</html>

Flask

1
2
3
4
5
6
7
Jinja
Flask

app.y
requirements.txt
static/
templates/

debug mode - auto reload

linux
1
2
3
export FLASK_APP=your_application.py
export FLASK_ENV=development
flask run
windows
1
2
3
set FLASK_APP=your_application.py
set FLASK_ENV=development
flask run

hello

hello #1
auto.bat
1
2
3
set FLASK_APP=app.py
set FLASK_ENV=development
flask run
app.py
1
2
3
4
5
6
7
8
9
10
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
return render_template("index.html")

# def index():
# return "hello world"
templates/index.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hello</title>
</head>
<body>
hello, world
</body>
</html>
run
1
2
3
4
5
6
PS D:\work\run\python\python_100ds\flask\hello> flask run
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [22/Dec/2023 09:11:16] "GET / HTTP/1.1" 200 -
hello #2
app.py
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
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
# if "name" in request.args:
# name = request.args["name"]
# else:
# name = "world"
name = request.args.get("name", "world2")
return render_template("index.html", name=name)

# def index():
# if "name" in request.args:
# name = request.args["name"]
# else:
# name = "world"
# return render_template("index.html", placeholder=name)
#
# <body>
# hello, {{ placeholder }}
# </body>

# http://127.0.0.1:5000/?name=Robert
templates/index.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hello</title>
</head>
<body>
hello, {{ name }}
</body>
</html>
hello #3
app.py
1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
return render_template("index.html")

@app.route("/greet")
def greet():
return render_template("greet.html", name=request.args.get("name", "world"))
templates/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hello</title>
</head>
<body>
<form action="/greet" method="get">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<button type="submit">Greet</button>
</form>
</body>
</html>
templates/greet.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hello</title>
</head>
<body>
hello, {{ name }}
</body>
</html>
hello #4 - layout
templates/layout.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hello</title>
</head>
<body>
{% block body %}{% endblock%}
</body>
</html>
templates/index.html
1
2
3
4
5
6
7
8
9
10
{% extends "layout.html" %}

{% block body %}

<form action="/greet" method="get">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<button type="submit">Greet</button>
</form>

{% endblock %}
templates/greet.html
1
2
3
4
5
6
7
{% extends "layout.html" %}

{% block body %}

hello3, {{ name }}

{% endblock %}
hello #5 - no show name at URL(POST)
app.py
1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
return render_template("index.html")

# @app.route("/greet", methods=["GET"]) - default
@app.route("/greet", methods=["POST"])
def greet():
return render_template("greet.html", name=request.args.get("name", "world"))
templates/index.html
1
2
3
4
5
6
7
8
9
10
{% extends "layout.html" %}

{% block body %}

<form action="/greet" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<button type="submit">Greet</button>
</form>

{% endblock %}
hello #6 - / for GET and POST
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("index.html")
elif request.method == "POST":
# return render_template("greet.html", name=request.args.get("name", "world"))
# request.args.get - for GET, request.form.get for POST
return render_template("greet.html", name=request.form.get("name", "world"))


# @app.route("/greet", methods=["POST"])
# def greet():
# return render_template("greet.html", name=request.args.get("name", "world"))
templates/index.html
1
2
3
4
5
6
7
8
9
10
{% extends "layout.html" %}

{% block body %}

<form action="/" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<button type="submit">Greet</button>
</form>

{% endblock %}

froshims

froshims #1
app.py
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

app = Flask(__name__)

REGISTRANTS = {}

@app.route("/")
def index():
return render_template("index.html")

@app.route("/register", methods=["POST"] )
def register():
name = request.form.get("name")
sport = request.form.get("sport")
REGISTRANTS[name] = sport
return render_template("success.html")

@app.route("/registrants")
def registrants():
return render_template("registrants.html", registrants = REGISTRANTS)
layout.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>froshims</title>
</head>
<body>
{% block body %} {% endblock %}
</body>
</html>
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% extends "layout.html" %}

{% block body %}
<form action="/register" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<select name="sport">
<option disabled selected>Sport</option>
<option value="Baseketball">Baseketball</option>
<option value="Socker">Socker</option>
<option value="Ultimate Frisbee">Ultimate Frisbe</option>
</select>
<button type="submit">Register</button>
</form>
{% endblock %}
success.html
1
2
3
4
5
{% extends "layout.html" %}

{% block body %}
You are gegistered.
{% endblock %}
registrants.html
1
2
3
4
5
6
7
8
9
10
{% extends "layout.html" %}

{% block body %}
<ul>
{% for name in registrants %}
<li>{{ name }} is registered for {{ registrants[name] }}</li>
{% endfor %}
</ul>

{% endblock %}
froshims #2 - add some check and protect
app.py
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
from flask import Flask, render_template, request

app = Flask(__name__)

REGISTRANTS = {}

SPORTS = [
"Baseketball",
"Socker",
"Ultimate Frisbee"
]

@app.route("/")
def index():
return render_template("index.html", sports = SPORTS)

@app.route("/register", methods=["POST"] )
def register():
name = request.form.get("name")
sport = request.form.get("sport")
if not name:
return render_template("failure.html")
if sport not in SPORTS:
return render_template("failure.html")
REGISTRANTS[name] = sport
return render_template("success.html")

@app.route("/registrants")
def registrants():
return render_template("registrants.html", registrants = REGISTRANTS)
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% extends "layout.html" %}

{% block body %}
<form action="/register" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" required type="text">
<select name="sport">
<option disabled selected>Sport</option>
{% for sport in sports %}
<option value="{{ sport }}">{{ sport }}</option>
{% endfor %}
</select>
<button type="submit">Register</button>
</form>
{% endblock %}
failure.html
1
2
3
4
5
{% extends "layout.html" %}

{% block body %}
You are not gegistered.
{% endblock %}
froshims4 - add to SQL
create DB
1
2
3
4
5
6
7
8
9
D:\work\run\python\python_100ds\flask\froshims4>sqlite3 froshims.db
SQLite version 3.44.2 2023-11-24 11:41:44 (UTF-16 console I/O)
Enter ".help" for usage hints.
sqlite> .schema
sqlite> CREATE TABLE registrants (id INTEGER, name TEXT NOT NULL, sport text NOT NULL, PRIMARY KEY(id));
sqlite> .schema
CREATE TABLE registrants (id INTEGER, name TEXT NOT NULL, sport text NOT NULL, PRIMARY KEY(id));
sqlite> SELECT * from registrants;
sqlite> .quit
app.py
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
51
# Implements a registration form, storing registrants in a SQLite database, with support for deregistration

from cs50 import SQL
from flask import Flask, redirect, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///froshims.db")

SPORTS = [
"Basketball",
"Soccer",
"Ultimate Frisbee"
]


@app.route("/")
def index():
return render_template("index.html", sports=SPORTS)


@app.route("/deregister", methods=["POST"])
def deregister():

# Forget registrant
id = request.form.get("id")
if id:
db.execute("DELETE FROM registrants WHERE id = ?", id)
return redirect("/registrants")


@app.route("/register", methods=["POST"])
def register():

# Validate submission
name = request.form.get("name")
sport = request.form.get("sport")
if not name or sport not in SPORTS:
return render_template("failure.html")

# Remember registrant
db.execute("INSERT INTO registrants (name, sport) VALUES(?, ?)", name, sport)

# Confirm registration
return redirect("/registrants")


@app.route("/registrants")
def registrants():
registrants = db.execute("SELECT * FROM registrants")
return render_template("registrants.html", registrants=registrants)
index.html
1
2
3
4
5
6
7
8
9
10
11
12
{% extends "layout.html" %}

{% block body %}
<h1>Register</h1>
<form action="/register" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
{% for sport in sports %}
<input name="sport" type="radio" value="{{ sport }}"> {{ sport }}
{% endfor %}
<button type="submit">Register</button>
</form>
{% endblock %}
registrants.html
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
{% extends "layout.html" %}

{% block body %}
<h1>Registrants</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Sport</th>
<th></th>
</tr>
</thead>
<tbody>
{% for registrant in registrants %}
<tr>
<td>{{ registrant.name }}</td>
<td>{{ registrant.sport }}</td>
<td>
<form action="/deregister" method="post">
<input name="id" type="hidden" value="{{ registrant.id }}">
<button type="submit">Deregister</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

login

app.py
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
from flask import Flask, redirect, render_template, request, session
from flask_session import Session

# Configure app
app = Flask(__name__)

# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)


@app.route("/")
def index():
if not session.get("name"):
return redirect("/login")
return render_template("index.html")


@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
session["name"] = request.form.get("name")
return redirect("/")
return render_template("login.html")


@app.route("/logout")
def logout():
session["name"] = None
return redirect("/")
index.html
1
2
3
4
5
6
7
8
9
10
11
{% extends "layout.html" %}

{% block body %}

{% if session["name"] %}
You are logged in as {{ session["name"] }}. <a href="/logout">Log out</a>.
{% else %}
You are not logged in. <a href="/login">Log in</a>.
{% endif %}

{% endblock %}
login.html
1
2
3
4
5
6
7
8
9
10
{% extends "layout.html" %}

{% block body %}

<form action="/login" method="post">
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<button type="submit">Log In</button>
</form>

{% endblock %}

store

app.py
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
from cs50 import SQL
from flask import Flask, redirect, render_template, request, session
from flask_session import Session

# Configure app
app = Flask(__name__)

# Connect to database
db = SQL("sqlite:///store.db")

# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)


@app.route("/")
def index():
books = db.execute("SELECT * FROM books")
return render_template("books.html", books=books)


@app.route("/cart", methods=["GET", "POST"])
def cart():

# Ensure cart exists
if "cart" not in session:
session["cart"] = []

# POST
if request.method == "POST":
id = request.form.get("id")
if id:
session["cart"].append(id)
return redirect("/cart")

# GET
books = db.execute("SELECT * FROM books WHERE id IN (?)", session["cart"])
return render_template("cart.html", books=books)
books.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% extends "layout.html" %}

{% block body %}

<h1>Books</h1>
{% for book in books %}
<h2>{{ book["title"] }}</h2>
<form action="/cart" method="post">
<input name="id" type="hidden" value="{{ book['id'] }}">
<button type="submit">Add to Cart</button>
</form>
{% endfor %}

{% endblock %}
cart.html
1
2
3
4
5
6
7
8
9
10
11
12
{% extends "layout.html" %}

{% block body %}

<h1>Cart</h1>
<ol>
{% for book in books %}
<li>{{ book["title"] }}</li>
{% endfor %}
</ol>

{% endblock %}

show2(API)

app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Searches for shows using Ajax with JSON

from cs50 import SQL
from flask import Flask, jsonify, render_template, request

app = Flask(__name__)

db = SQL("sqlite:///shows.db")


@app.route("/")
def index():
return render_template("index.html")


@app.route("/search")
def search():
q = request.args.get("q")
if q:
shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%")
else:
shows = []
return jsonify(shows)
index.html
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
<!DOCTYPE html>

<html lang="en">
<head>
<meta name="viewport" content="initial-scale=1, width=device-width">
<title>shows</title>
</head>
<body>

<input autocomplete="off" autofocus placeholder="Query" type="text">

<ul></ul>

<script>

let input = document.querySelector('input');
input.addEventListener('input', async function() {
let response = await fetch('/search?q=' + input.value);
let shows = await response.json();
let html = '';
for (let id in shows) {
let title = shows[id].title.replace('<', '<').replace('&', '&');
html += '<li>' + title + '</li>';
}
document.querySelector('ul').innerHTML = html;
});

</script>

</body>
</html>

library

app.py
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
from cs50 import SQL
from flask import Flask, render_template, request
from helpers import random_string
import random

app = Flask(__name__)

db = SQL("sqlite:///history.db")

app.config["TEMPLATES_AUTO_RELOAD"] = True


@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
page = request.form.get("page")
try :
page = int(page)
except ValueError:
return render_template("index.html", random_string="Enter a number")

if (page < 0):
return render_template("index.html", random_string="Type in a positive number")
random.seed(page)
db.execute("INSERT INTO history (page) VALUES(?)", page)
string = random_string(1000)
rows = db.execute("SELECT * FROM history;")
print(rows)
return render_template("index.html", random_string=string, history=rows )
helpers.py
1
2
3
4
5
6
7
8
9
10
11
import random
from string import ascii_letters


def random_string(len):
"""Generate a random lowercase string of length `len`."""

string = ""
for _ in range(len):
string += random.choice(ascii_letters)
return string.lower()
index.html
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
{% extends "layout.html" %}

{% block body %}
<h1 class="mt-5">Library of Babol</h1>

<form action="/" method="post">
<input name="page" type="text">
<button type="submit">Go to page</button>
</form>

<p class="text-break mt-3">
{{ random_string }}
</p>

<!-- TODO: Implement a search history table -->
<table class="table">
<thead>
<tr>
<th>
Search History
</th>
</tr>
</thead>
<tbody>
{% for row in history %}
<tr>
<td>{{ row["page"] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

Ref