Skip to content

Commit 6c5deb0

Browse files
authored
Frontend (#55)
* fix pipeline 21st may * started header bar * about page finished, nav finished, footer added, ready for subcom to work * calendar and home page split up * database helper functions basically done * implemented the drop everything in db function
1 parent 909cd07 commit 6c5deb0

File tree

2,167 files changed

+283137
-28163
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,167 files changed

+283137
-28163
lines changed

backend/Pipfile.lock

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/common/database.py

+185-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,190 @@ def get_connection():
2020

2121
return conn
2222

23+
conn = get_connection()
24+
cur = conn.cursor()
25+
26+
# IMPORTANT: executing a query is expensive, so we would rather write more functions than write more execute queries.
27+
28+
# Get all the information about a question given its day number
29+
# Returns all information in the form of a dictionary
30+
# You might want to use this function to find the total number of parts in a question, and then use getPartInfo
31+
def getQuestionInfo(compName, dayNum):
32+
query = f"""
33+
select * from Questions q
34+
join Competitions c on q.cid = c.cid
35+
where q.dayNum = {dayNum} and c.name = {compName};
36+
"""
37+
cur.execute(query)
38+
39+
# only one entry should be returned since day number is unique
40+
t = cur.fetchone()
41+
return t
42+
43+
# Get all the parts given a day number of a question
44+
# Returns all information in the form of a list of dictionaries
45+
def getQuestionParts(compName, dayNum):
46+
query = f"""
47+
select * from Parts p
48+
join Questions q on p.qid = q.qid
49+
join Competitions c on q.cid = c.cid
50+
where q.dayNum = {dayNum} and c.name = {compName};
51+
"""
52+
cur.execute(query)
53+
54+
partsList = []
55+
for t in cur.fetchall():
56+
partsList.append(t)
57+
58+
# sort the list based off the part number
59+
sortedList = sorted(partsList, key=lambda x: x['partNum'])
60+
return sortedList
61+
62+
# Get all the information about a part of a question (e.g. day 1 part 2) given the day number and part number
63+
# Same as above but more specific
64+
# Returns all information in the form of a dictionary
65+
def getPartInfo(compName, dayNum, partNum):
66+
query = f"""
67+
select * from Parts p
68+
join Questions q on p.qid = q.qid
69+
join Competitions c on q.cid = c.cid
70+
where q.dayNum = {dayNum} and p.partNum = {partNum} and c.name = {compName};
71+
"""
72+
cur.execute(query)
73+
74+
# only one entry should be returned since day number is unique
75+
t = cur.fetchone()
76+
return t
77+
78+
# Get all the questions that pertain to a certain competition, by name
79+
# Returns None if the competition does not exist
80+
def getCompetitionQuestions(compName):
81+
query = f"""
82+
select * from Parts p
83+
join Questions q on p.qid = q.qid
84+
join Competitions c on q.cid = c.cid
85+
where c.name = {compName};
86+
"""
87+
cur.execute(query)
88+
89+
# only one entry should be returned since day number is unique
90+
return cur.fetchall()
91+
92+
# Unfinished function.
93+
# Dynamically generates a new input for a user and day number
94+
def generateInput(dayNum, uid):
95+
pass
96+
97+
# Gets the input for a day number and user, if it exists
98+
# Returns the input string, else returns None
99+
def getInput(compName, dayNum, uid):
100+
query = f"""
101+
select i.input from Inputs i
102+
join Questions q on i.qid = q.qid
103+
join Competitions c on q.cid = c.cid
104+
where q.dayNum = {dayNum} and i.uid = {uid} and c.name = {compName};
105+
"""
106+
cur.execute(query)
107+
108+
t = cur.fetchone()
109+
return t['input'] if t is not None else t
110+
111+
# Gets the input for a day number and user, if it exists
112+
# Returns a tuple:
113+
# the tuple is None if the value does not exist
114+
# the first entry of the tuple is True if the solution is correct (and exists), False otherwise
115+
# the second entry of the tuple is a string outlining the reason if the solution was incorrect
116+
# not sure what to put here so just leaving as empty string for now
117+
def checkInput(compName, dayNum, uid, solution):
118+
query = f"""
119+
select i.input, i.solution from Inputs i
120+
join Questions q on i.qid = q.qid
121+
join Competitions c on q.cid = c.cid
122+
where q.dayNum = {dayNum} and i.uid = {uid} and c.name = {compName};
123+
"""
124+
cur.execute(query)
125+
126+
t = cur.fetchone()
127+
if t is None:
128+
return None
129+
elif t['solution'].lower() == solution.strip().lower():
130+
# can change this later, but iirc advent of code is also not case sensitive
131+
return (True, "")
132+
else:
133+
return (False, "")
134+
135+
# note: for more advanced processing, we might consider having a timeout if a user tries too many things too quickly
136+
# but idk how to implement this too well
137+
138+
# Get all the information about a user given their uid
139+
# Returns all information in the form of a dictionary
140+
def getUserInfo(uid):
141+
query = f"""
142+
select * from Users where uid = {uid};
143+
"""
144+
cur.execute(query)
145+
146+
# only one entry should be returned since day number is unique
147+
t = cur.fetchone()
148+
return t
149+
150+
# Get all the information about a user's stats in a certain competition
151+
# Returns all information in the form of a list of 'solved objects'
152+
def getUserStatsPerComp(compName, uid):
153+
154+
# A right outer join returns all the results from the parts table, even if there is no solves
155+
# Best to look up examples :D
156+
# Use this information to deduce whether a user has solved a part or not
157+
query = f"""
158+
select q.dayNum, p.partNum, s.points, s.solveTime from Solves s
159+
right outer join Parts p on s.pid = p.pid
160+
join Questions q on p.qid = q.qid
161+
join Competitions c on q.cid = c.cid
162+
where i.uid = {uid} and c.name = {compName};
163+
"""
164+
cur.execute(query)
165+
166+
return cur.fetchall()
167+
168+
# Could be very large
169+
def getAllUsers():
170+
query = f"""
171+
select * from Users;
172+
"""
173+
cur.execute(query)
174+
175+
return cur.fetchall()
176+
177+
# Could be very large
178+
def getAllCompetitions():
179+
query = f"""
180+
select * from Competitions;
181+
"""
182+
cur.execute(query)
183+
184+
return cur.fetchall()
185+
186+
# Pre conditions assume we have already checked that noone has that username
187+
# No idea whether this works lol never done something like this before
188+
def updateUsername(username, uid):
189+
query = f"""
190+
update Users
191+
set username = {username}
192+
where uid = {uid};
193+
"""
194+
cur.execute(query)
195+
conn.commit()
196+
197+
# DO NOT EVER EXECUTE THIS FUNCTION BRUH
198+
def dropDatabase():
199+
query = f"""
200+
SELECT 'DROP TABLE IF EXISTS "' || tablename || '" CASCADE;'
201+
from
202+
pg_tables WHERE schemaname = 'advent';
203+
"""
204+
cur.execute(query)
205+
conn.commit()
206+
23207
def clear_database():
24208
conn = get_connection()
25209
cursor = conn.cursor()
@@ -30,4 +214,4 @@ def clear_database():
30214
conn.commit()
31215

32216
cursor.close()
33-
conn.close()
217+
conn.close()

database/setup.sql

+18-12
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,52 @@ CREATE TABLE Users (
33
uid SERIAL PRIMARY KEY,
44
email TEXT UNIQUE NOT NULL,
55
username TEXT UNIQUE NOT NULL,
6-
numStars INTEGER NOT NULL,
7-
score INTEGER NOT NULL,
86
password TEXT NOT NULL
97
);
108

119
DROP TABLE IF EXISTS Questions;
1210
CREATE TABLE Questions (
1311
qid SERIAL PRIMARY KEY,
12+
cid FOREIGN KEY references Competitions(cid),
13+
numParts INTEGER NOT NULL,
1414
name TEXT NOT NULL,
15+
dayNum INTEGER UNIQUE NOT NULL
16+
);
17+
18+
DROP TABLE IF EXISTS Parts;
19+
CREATE TABLE Parts (
20+
qid FOREIGN KEY references Questions(qid),
1521
description TEXT NOT NULL,
1622
partNum INTEGER NOT NULL,
17-
numParts INTEGER NOT NULL,
1823
numSolved INTEGER NOT NULL,
1924
bestTime TIME,
20-
dayNum INTEGER UNIQUE NOT NULL
25+
PRIMARY KEY (qid, partNum)
2126
);
2227

2328
DROP TABLE IF EXISTS Competitions;
2429
CREATE TABLE Competitions (
2530
cid SERIAL PRIMARY KEY,
26-
name TEXT NOT NULL,
31+
name TEXT UNIQUE NOT NULL,
2732
numUsers INTEGER NOT NULL,
28-
questions TEXT
33+
numQuestions INTEGER NOT NULL
2934
);
3035

3136
DROP TABLE IF EXISTS Inputs;
3237
CREATE TABLE Inputs (
3338
iid SERIAL PRIMARY KEY,
34-
qid INTEGER,
35-
solution TEXT NOT NULL,
36-
FOREIGN KEY (qid) REFERENCES Questions(qid)
39+
qid FOREIGN KEY references Questions(qid),
40+
uid FOREIGN KEY references Users(id),
41+
input TEXT NOT NULL,
42+
solution TEXT NOT NULL
3743
);
3844

3945
DROP TABLE IF EXISTS Solves;
4046
CREATE TABLE Solves (
4147
uid INTEGER,
42-
qid INTEGER,
48+
pid INTEGER,
4349
solveTime TIME NOT NULL,
4450
points INTEGER NOT NULL,
45-
PRIMARY KEY (uid, qid),
51+
PRIMARY KEY (uid, pid),
4652
FOREIGN KEY (uid) REFERENCES Users(uid),
47-
FOREIGN KEY (qid) REFERENCES Questions(qid)
53+
FOREIGN KEY (pid) REFERENCES Parts(pid)
4854
);

0 commit comments

Comments
 (0)