From 4e119960d3104b9581e427c317219d75296a0629 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 09:19:30 +0530 Subject: [PATCH 01/11] basic login/ register --- .gitignore | 3 ++- server.py | 59 ++++++++++++++++++++++++++++++++++++++++++++----- views/home.html | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 views/home.html diff --git a/.gitignore b/.gitignore index c966d14..3b01ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ venv/ .pytest_cache submission_record.db - +user_record.db +session_record.db \ No newline at end of file diff --git a/server.py b/server.py index e8fa131..9bccbd6 100644 --- a/server.py +++ b/server.py @@ -1,23 +1,25 @@ from bottle import Bottle, run, template, static_file, request, route, redirect -import os -import sys -import datetime +import os, sys, datetime +import string, random from collections import defaultdict, namedtuple import shelve +from http import cookies path = os.path.abspath(__file__) dir_path = os.path.dirname(path) app = Bottle() database_path = "submission_record.db" +user_db = "user_record.db" +sessions_db = "session_record.db" questions = {} contests = {} question_dir = "files/questions" Question = namedtuple("Question", "output statement") Submission = namedtuple("Submission", "question time output is_correct contest") -# questions, code, description, start_time, end_time Contest = namedtuple("Contest", "description questions start_time end_time") +User = namedtuple("User", "password firstname lastname") # dummy contests contests["PRACTICE"] = Contest( @@ -61,6 +63,11 @@ def changePath(): return redirect("/dashboard") +@app.get("/home") +def dashboard(): + return template("home.html") + + @app.get("/dashboard") def dashboard(): return template("dashboard.html", contests=contests) @@ -150,9 +157,50 @@ def rankings(): return template("rankings.html", people=order) +def createSession(username): + session_id = "".join( + random.choice(string.ascii_letters + string.digits) for i in range(20) + ) + + +@app.post("/login") +def login(): + username = request.forms.get("username") + password = request.forms.get("password") + with shelve.open(user_db) as users: + if not username in users: + return "user does not exist." + if users[username].password != password: + return "incorrect password." + return createSession(username) + + +@app.post("/register") +def register(): + username = request.forms.get("username") + password = request.forms.get("password") + cpassword = request.forms.get("cpassword") + firstname = request.forms.get("firstname") + lastname = request.forms.get("lastname") + if password != cpassword: + return "Passwords do not match." + with shelve.open(user_db) as users: + if username in users: + return "User already exists." + users[username] = User( + password=password, firstname=firstname, lastname=lastname + ) + return "Registered." + + +@app.get("/logout") +def logout(): + return "Logged out." + + @app.post("/check//") def file_upload(code, number): - u_name = request.forms.get("username") # accepting username + u_name = request.forms.get("username") time = datetime.datetime.now() uploaded = request.files.get("upload").file.read() expected = questions[number].output @@ -164,7 +212,6 @@ def file_upload(code, number): submissions = ( [] if u_name not in submission_record else submission_record[u_name] ) - # submissions = submission_record.get(u_name, list()) submissions.append( Submission( question=number, diff --git a/views/home.html b/views/home.html new file mode 100644 index 0000000..ba902c9 --- /dev/null +++ b/views/home.html @@ -0,0 +1,44 @@ +% include('base.html', title="PyJudge") + +
+

+ PyJudge +

+
+
+ +
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+ \ No newline at end of file From f3d7bec32d467a580d4e6001b541bbe3a7b8de74 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 12:22:21 +0530 Subject: [PATCH 02/11] sessions working --- server.py | 38 +++++++++++++++++++++++------ views/contest.html | 3 +++ views/dashboard.html | 3 +++ views/home.html | 4 +-- views/{index.html => question.html} | 8 ++---- 5 files changed, 40 insertions(+), 16 deletions(-) rename views/{index.html => question.html} (76%) diff --git a/server.py b/server.py index 9bccbd6..811e286 100644 --- a/server.py +++ b/server.py @@ -1,4 +1,4 @@ -from bottle import Bottle, run, template, static_file, request, route, redirect +from bottle import Bottle, run, template, static_file, request, route, redirect, response import os, sys, datetime import string, random from collections import defaultdict, namedtuple @@ -50,7 +50,6 @@ for i in os.listdir(question_dir): if not i.isdigit(): continue - # read the correct output as bytes object with open(os.path.join(question_dir, i, "output.txt"), "rb") as fl: output = fl.read() with open(os.path.join(question_dir, i, "statement.txt"), "r") as fl: @@ -64,12 +63,16 @@ def changePath(): @app.get("/home") -def dashboard(): +def home(): + if logggedIn()==True: + return redirect("/dashboard") return template("home.html") @app.get("/dashboard") def dashboard(): + if logggedIn()==False: + return redirect("/home") return template("dashboard.html", contests=contests) @@ -81,12 +84,14 @@ def contest(code, number): return "The contest had not started yet." statement = questions[number].statement return template( - "index.html", question_number=number, contest=code, question=statement + "question.html", question_number=number, contest=code, question=statement ) @app.get("/contest/") def contest(code): + if logggedIn()==False: + return template("home.html") if not code in contests: return "Contest does not exist" if contests[code].start_time > datetime.datetime.now(): @@ -156,12 +161,23 @@ def rankings(): order = [(user, score, rank) for rank, (user, score) in enumerate(order, start=1)] return template("rankings.html", people=order) +@app.get("/checklogin") +def logggedIn(): + if not request.get_cookie("s_id"): + return False + with shelve.open(sessions_db) as sessions: + if not request.get_cookie("s_id") in sessions: + return False + return True def createSession(username): session_id = "".join( random.choice(string.ascii_letters + string.digits) for i in range(20) ) - + response.set_cookie("s_id", session_id) + with shelve.open(sessions_db) as sessions: + sessions[session_id] = username + return redirect("/dashboard") @app.post("/login") def login(): @@ -190,17 +206,23 @@ def register(): users[username] = User( password=password, firstname=firstname, lastname=lastname ) - return "Registered." + return createSession(username) @app.get("/logout") def logout(): - return "Logged out." + with shelve.open(sessions_db) as sessions: + del sessions[request.get_cookie("s_id")] + response.set_cookie("s_id","") + return redirect("/home") @app.post("/check//") def file_upload(code, number): - u_name = request.forms.get("username") + if not logggedIn(): + return "You need to log in to submit a solution." + with shelve.open(sessions_db) as sessions: + u_name = sessions[request.get_cookie("s_id")] time = datetime.datetime.now() uploaded = request.files.get("upload").file.read() expected = questions[number].output diff --git a/views/contest.html b/views/contest.html index 053e0b0..c7a3f12 100644 --- a/views/contest.html +++ b/views/contest.html @@ -9,6 +9,9 @@

{{code}}

{{contest.description}}

+
+ Rankings +
% for qno in range(len(contest.questions)): {{qno+1}}
diff --git a/views/dashboard.html b/views/dashboard.html index cddaef1..1349b5c 100644 --- a/views/dashboard.html +++ b/views/dashboard.html @@ -23,5 +23,8 @@

Contests

+
+ Logout +
diff --git a/views/home.html b/views/home.html index ba902c9..5babc99 100644 --- a/views/home.html +++ b/views/home.html @@ -21,7 +21,7 @@



- +
@@ -36,7 +36,7 @@



- +

diff --git a/views/index.html b/views/question.html similarity index 76% rename from views/index.html rename to views/question.html index 8895802..9b76a1a 100644 --- a/views/index.html +++ b/views/question.html @@ -18,12 +18,8 @@

Submission Page

You can submit the code in any language. You can upload the file below.

-
-
- - - -
+ + Output file:
From 2292e3fb08676cee756bbf60bab798e9602024d9 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 12:28:52 +0530 Subject: [PATCH 03/11] cookie expiry added --- server.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/server.py b/server.py index 811e286..7ff08c3 100644 --- a/server.py +++ b/server.py @@ -1,4 +1,13 @@ -from bottle import Bottle, run, template, static_file, request, route, redirect, response +from bottle import ( + Bottle, + run, + template, + static_file, + request, + route, + redirect, + response, +) import os, sys, datetime import string, random from collections import defaultdict, namedtuple @@ -64,14 +73,14 @@ def changePath(): @app.get("/home") def home(): - if logggedIn()==True: + if logggedIn() == True: return redirect("/dashboard") return template("home.html") @app.get("/dashboard") def dashboard(): - if logggedIn()==False: + if logggedIn() == False: return redirect("/home") return template("dashboard.html", contests=contests) @@ -90,7 +99,7 @@ def contest(code, number): @app.get("/contest/") def contest(code): - if logggedIn()==False: + if logggedIn() == False: return template("home.html") if not code in contests: return "Contest does not exist" @@ -161,6 +170,7 @@ def rankings(): order = [(user, score, rank) for rank, (user, score) in enumerate(order, start=1)] return template("rankings.html", people=order) + @app.get("/checklogin") def logggedIn(): if not request.get_cookie("s_id"): @@ -170,15 +180,21 @@ def logggedIn(): return False return True + def createSession(username): session_id = "".join( random.choice(string.ascii_letters + string.digits) for i in range(20) ) - response.set_cookie("s_id", session_id) + response.set_cookie( + "s_id", + session_id, + expires=datetime.datetime.now() + datetime.timedelta(days=30), + ) with shelve.open(sessions_db) as sessions: sessions[session_id] = username return redirect("/dashboard") + @app.post("/login") def login(): username = request.forms.get("username") @@ -213,7 +229,7 @@ def register(): def logout(): with shelve.open(sessions_db) as sessions: del sessions[request.get_cookie("s_id")] - response.set_cookie("s_id","") + response.set_cookie("s_id", "") return redirect("/home") From ce1cde12585493e12892bca1e03c27d78d1e15c0 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 13:06:41 +0530 Subject: [PATCH 04/11] removed http.cookie import --- server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server.py b/server.py index 7ff08c3..dedd067 100644 --- a/server.py +++ b/server.py @@ -12,7 +12,6 @@ import string, random from collections import defaultdict, namedtuple import shelve -from http import cookies path = os.path.abspath(__file__) dir_path = os.path.dirname(path) From bf041098e9a37454649b5eb69cd5be8b1cfefd0b Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 13:20:01 +0530 Subject: [PATCH 05/11] removed checkLogin() handler (used for debugging) --- server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server.py b/server.py index dedd067..65369e2 100644 --- a/server.py +++ b/server.py @@ -170,7 +170,6 @@ def rankings(): return template("rankings.html", people=order) -@app.get("/checklogin") def logggedIn(): if not request.get_cookie("s_id"): return False From 282754ac3d335eb16f34a17358d3f79a940e0d07 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti <23037053+rishabhKalakoti@users.noreply.github.com> Date: Thu, 16 May 2019 15:02:11 +0530 Subject: [PATCH 06/11] Apply suggestions from code review Co-Authored-By: Arjoonn Sharma --- server.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server.py b/server.py index 65369e2..9807610 100644 --- a/server.py +++ b/server.py @@ -72,14 +72,14 @@ def changePath(): @app.get("/home") def home(): - if logggedIn() == True: + if logggedIn(): return redirect("/dashboard") return template("home.html") @app.get("/dashboard") def dashboard(): - if logggedIn() == False: + if not logggedIn(): return redirect("/home") return template("dashboard.html", contests=contests) @@ -98,7 +98,7 @@ def contest(code, number): @app.get("/contest/") def contest(code): - if logggedIn() == False: + if not logggedIn(): return template("home.html") if not code in contests: return "Contest does not exist" @@ -174,7 +174,7 @@ def logggedIn(): if not request.get_cookie("s_id"): return False with shelve.open(sessions_db) as sessions: - if not request.get_cookie("s_id") in sessions: + return request.get_cookie('s_id') in sessions return False return True From eb6ca3580ec649c025eb70d19caeeb49248720f8 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 15:37:20 +0530 Subject: [PATCH 07/11] message display --- server.py | 98 +++++++++++++++++++++++-------------------------- views/home.html | 12 +++--- 2 files changed, 51 insertions(+), 59 deletions(-) diff --git a/server.py b/server.py index 9807610..e50882c 100644 --- a/server.py +++ b/server.py @@ -1,13 +1,4 @@ -from bottle import ( - Bottle, - run, - template, - static_file, - request, - route, - redirect, - response, -) +import bottle import os, sys, datetime import string, random from collections import defaultdict, namedtuple @@ -15,7 +6,7 @@ path = os.path.abspath(__file__) dir_path = os.path.dirname(path) -app = Bottle() +app = bottle.Bottle() database_path = "submission_record.db" user_db = "user_record.db" @@ -27,7 +18,7 @@ Question = namedtuple("Question", "output statement") Submission = namedtuple("Submission", "question time output is_correct contest") Contest = namedtuple("Contest", "description questions start_time end_time") -User = namedtuple("User", "password firstname lastname") +User = namedtuple("User", "password") # dummy contests contests["PRACTICE"] = Contest( @@ -67,21 +58,20 @@ @app.route("/") def changePath(): - return redirect("/dashboard") + return bottle.redirect("/dashboard") @app.get("/home") def home(): if logggedIn(): - return redirect("/dashboard") - return template("home.html") + return bottle.redirect("/dashboard") + return bottle.template("home.html", message="") @app.get("/dashboard") +@login_required def dashboard(): - if not logggedIn(): - return redirect("/home") - return template("dashboard.html", contests=contests) + return bottle.template("dashboard.html", contests=contests) @app.get("/contest//") @@ -91,7 +81,7 @@ def contest(code, number): if contests[code].start_time > datetime.datetime.now(): return "The contest had not started yet." statement = questions[number].statement - return template( + return bottle.template( "question.html", question_number=number, contest=code, question=statement ) @@ -99,22 +89,22 @@ def contest(code, number): @app.get("/contest/") def contest(code): if not logggedIn(): - return template("home.html") + return bottle.template("home.html") if not code in contests: return "Contest does not exist" if contests[code].start_time > datetime.datetime.now(): return "The contest had not started yet." - return template("contest.html", code=code, contest=contests[code]) + return bottle.template("contest.html", code=code, contest=contests[code]) @app.get("/question/") def download(path): - return static_file(path, root=question_dir) + return bottle.static_file(path, root=question_dir) @app.get("/static/") def server_static(filepath): - return static_file(filepath, root=os.path.join(dir_path, "static")) + return bottle.static_file(filepath, root=os.path.join(dir_path, "static")) @app.get("/ranking/") @@ -144,7 +134,7 @@ def contest_ranking(code): order.sort(key=lambda x: x[1], reverse=True) order = [entry for entry in order if entry[1] > 0] order = [(user, score, rank) for rank, (user, score) in enumerate(order, start=1)] - return template("rankings.html", people=order) + return bottle.template("rankings.html", people=order) @app.get("/ranking") @@ -170,75 +160,77 @@ def rankings(): return template("rankings.html", people=order) +def login_required(function): + def new_function(): + if not logggedIn(): + return bottle.template("home.html", message="Login required.") + return function() + + return new_function + + def logggedIn(): - if not request.get_cookie("s_id"): + if not bottle.request.get_cookie("s_id"): return False with shelve.open(sessions_db) as sessions: - return request.get_cookie('s_id') in sessions - return False - return True + return bottle.request.get_cookie("s_id") in sessions def createSession(username): session_id = "".join( random.choice(string.ascii_letters + string.digits) for i in range(20) ) - response.set_cookie( + bottle.response.set_cookie( "s_id", session_id, expires=datetime.datetime.now() + datetime.timedelta(days=30), ) with shelve.open(sessions_db) as sessions: sessions[session_id] = username - return redirect("/dashboard") + return bottle.redirect("/dashboard") @app.post("/login") def login(): - username = request.forms.get("username") - password = request.forms.get("password") + username = bottle.request.forms.get("username") + password = bottle.request.forms.get("password") with shelve.open(user_db) as users: if not username in users: - return "user does not exist." + return bottle.template("home.html", message="User does not exist.") if users[username].password != password: - return "incorrect password." + return bottle.template("home.html", message="Incorrect password.") return createSession(username) @app.post("/register") def register(): - username = request.forms.get("username") - password = request.forms.get("password") - cpassword = request.forms.get("cpassword") - firstname = request.forms.get("firstname") - lastname = request.forms.get("lastname") - if password != cpassword: - return "Passwords do not match." + username = bottle.request.forms.get("username") + password = bottle.request.forms.get("password") with shelve.open(user_db) as users: if username in users: - return "User already exists." - users[username] = User( - password=password, firstname=firstname, lastname=lastname - ) + return bottle.template( + "home.html", + message="Username already exists. Select a different username", + ) + users[username] = User(password=password) return createSession(username) @app.get("/logout") def logout(): with shelve.open(sessions_db) as sessions: - del sessions[request.get_cookie("s_id")] - response.set_cookie("s_id", "") - return redirect("/home") + del sessions[bottle.request.get_cookie("s_id")] + bottle.response.delete_cookie("s_id") + return bottle.redirect("/home") @app.post("/check//") +@login_required def file_upload(code, number): - if not logggedIn(): - return "You need to log in to submit a solution." with shelve.open(sessions_db) as sessions: - u_name = sessions[request.get_cookie("s_id")] + u_name = sessions[bottle.request.get_cookie("s_id")] time = datetime.datetime.now() - uploaded = request.files.get("upload").file.read() + uploaded = bottle.request.files.get("upload").file.read() expected = questions[number].output expected = expected.strip() uploaded = uploaded.strip() @@ -265,4 +257,4 @@ def file_upload(code, number): return "Solved! Great Job! " -run(app, host="localhost", port=8080) +bottle.run(app, host="localhost", port=8080) diff --git a/views/home.html b/views/home.html index 5babc99..afee4e4 100644 --- a/views/home.html +++ b/views/home.html @@ -5,6 +5,12 @@

PyJudge

+ % if message: +
+ + Error! {{message}} +
+ % end
From bae321178c4c2872dcf2b4f4efdb2024e8985a10 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 15:56:56 +0530 Subject: [PATCH 08/11] bug fixes --- server.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/server.py b/server.py index e50882c..d79d9b6 100644 --- a/server.py +++ b/server.py @@ -56,6 +56,13 @@ questions[i] = Question(output=output, statement=statement) +def login_required(function): + def new_function(): + if not logggedIn(): + return bottle.template("home.html", message="Login required.") + return function() + return new_function + @app.route("/") def changePath(): return bottle.redirect("/dashboard") @@ -159,16 +166,6 @@ def rankings(): order = [(user, score, rank) for rank, (user, score) in enumerate(order, start=1)] return template("rankings.html", people=order) - -def login_required(function): - def new_function(): - if not logggedIn(): - return bottle.template("home.html", message="Login required.") - return function() - - return new_function - - def logggedIn(): if not bottle.request.get_cookie("s_id"): return False From 28efb21ba8e052f50c1d63ab311d4ca44d90c14b Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 16:33:26 +0530 Subject: [PATCH 09/11] changed name for decorator function --- server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index d79d9b6..cc2d27b 100644 --- a/server.py +++ b/server.py @@ -57,11 +57,11 @@ def login_required(function): - def new_function(): + def login_redirect(): if not logggedIn(): return bottle.template("home.html", message="Login required.") return function() - return new_function + return login_redirect @app.route("/") def changePath(): From d14f88db71e014280968f659392a95a7cd00a8d4 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti <23037053+rishabhKalakoti@users.noreply.github.com> Date: Thu, 16 May 2019 17:48:31 +0530 Subject: [PATCH 10/11] Apply suggestions from code review Co-Authored-By: Arjoonn Sharma --- server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index cc2d27b..77501b7 100644 --- a/server.py +++ b/server.py @@ -57,10 +57,10 @@ def login_required(function): - def login_redirect(): + def login_redirect(*args, **kwargs): if not logggedIn(): return bottle.template("home.html", message="Login required.") - return function() + return function(*args, **kwargs) return login_redirect @app.route("/") From 395fe242e77d580588d82d81872a37cd1f99ea14 Mon Sep 17 00:00:00 2001 From: Rishabh Kalakoti Date: Thu, 16 May 2019 17:51:08 +0530 Subject: [PATCH 11/11] authentication added to more handlers --- server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.py b/server.py index cc2d27b..3bb727b 100644 --- a/server.py +++ b/server.py @@ -65,7 +65,7 @@ def login_redirect(): @app.route("/") def changePath(): - return bottle.redirect("/dashboard") + return bottle.redirect("/home") @app.get("/home") @@ -82,6 +82,7 @@ def dashboard(): @app.get("/contest//") +@login_required def contest(code, number): if not code in contests: return "Contest does not exist" @@ -94,9 +95,8 @@ def contest(code, number): @app.get("/contest/") +@login_required def contest(code): - if not logggedIn(): - return bottle.template("home.html") if not code in contests: return "Contest does not exist" if contests[code].start_time > datetime.datetime.now():