1- from bottle import Bottle , run , template , static_file , request , route , redirect , error
2- import os
3- import sys
4- import datetime
1+ import bottle
2+ import os , sys , datetime
3+ import string , random
4+
55from collections import defaultdict , namedtuple
66import shelve
77
88path = os .path .abspath (__file__ )
99dir_path = os .path .dirname (path )
10- app = Bottle ()
10+ app = bottle . Bottle ()
1111
1212database_path = "submission_record.db"
13+ user_db = "user_record.db"
14+ sessions_db = "session_record.db"
1315questions = {}
1416contests = {}
1517question_dir = "files/questions"
1618
1719Question = namedtuple ("Question" , "output statement" )
1820Submission = namedtuple ("Submission" , "question time output is_correct contest" )
19- # questions, code, description, start_time, end_time
2021Contest = namedtuple ("Contest" , "description questions start_time end_time" )
22+ User = namedtuple ("User" , "password" )
2123
2224# dummy contests
2325contests ["PRACTICE" ] = Contest (
4850for i in os .listdir (question_dir ):
4951 if not i .isdigit ():
5052 continue
51- # read the correct output as bytes object
5253 with open (os .path .join (question_dir , i , "output.txt" ), "rb" ) as fl :
5354 output = fl .read ()
5455 with open (os .path .join (question_dir , i , "statement.txt" ), "r" ) as fl :
5556 statement = fl .read ()
5657 questions [i ] = Question (output = output , statement = statement )
5758
5859
60+ def login_required (function ):
61+ def login_redirect (* args , ** kwargs ):
62+ if not logggedIn ():
63+ return bottle .template ("home.html" , message = "Login required." )
64+ return function (* args , ** kwargs )
65+ return login_redirect
66+
5967@app .route ("/" )
6068def changePath ():
61- return redirect ("/dashboard" )
69+ return bottle .redirect ("/home" )
70+
71+
72+ @app .get ("/home" )
73+ def home ():
74+ if logggedIn ():
75+ return bottle .redirect ("/dashboard" )
76+ return bottle .template ("home.html" , message = "" )
6277
6378
6479@app .get ("/dashboard" )
80+ @login_required
6581def dashboard ():
66- return template ("dashboard.html" , contests = contests )
82+ return bottle . template ("dashboard.html" , contests = contests )
6783
6884
6985@app .get ("/contest/<code>/<number>" )
86+ @login_required
7087def contest (code , number ):
7188 if not code in contests :
7289 return "Contest does not exist"
7390 if contests [code ].start_time > datetime .datetime .now ():
7491 return "The contest had not started yet."
7592 statement = questions [number ].statement
76- return template (
77- "index .html" , question_number = number , contest = code , question = statement
93+ return bottle . template (
94+ "question .html" , question_number = number , contest = code , question = statement
7895 )
7996
8097
8198@app .get ("/contest/<code>" )
99+ @login_required
82100def contest (code ):
83101 if not code in contests :
84102 return "Contest does not exist"
85103 if contests [code ].start_time > datetime .datetime .now ():
86104 return "The contest had not started yet."
87- return template ("contest.html" , code = code , contest = contests [code ])
105+ return bottle . template ("contest.html" , code = code , contest = contests [code ])
88106
89107
90108@app .get ("/question/<path:path>" )
91109def download (path ):
92- return static_file (path , root = question_dir )
110+ return bottle . static_file (path , root = question_dir )
93111
94112
95113@app .get ("/static/<filepath:path>" )
96114def server_static (filepath ):
97- return static_file (filepath , root = os .path .join (dir_path , "static" ))
115+ return bottle . static_file (filepath , root = os .path .join (dir_path , "static" ))
98116
99117
100118@app .get ("/ranking/<code>" )
@@ -124,7 +142,7 @@ def contest_ranking(code):
124142 order .sort (key = lambda x : x [1 ], reverse = True )
125143 order = [entry for entry in order if entry [1 ] > 0 ]
126144 order = [(user , score , rank ) for rank , (user , score ) in enumerate (order , start = 1 )]
127- return template ("rankings.html" , people = order )
145+ return bottle . template ("rankings.html" , people = order )
128146
129147
130148@app .get ("/ranking" )
@@ -149,12 +167,68 @@ def rankings():
149167 order = [(user , score , rank ) for rank , (user , score ) in enumerate (order , start = 1 )]
150168 return template ("rankings.html" , people = order )
151169
170+ def logggedIn ():
171+ if not bottle .request .get_cookie ("s_id" ):
172+ return False
173+ with shelve .open (sessions_db ) as sessions :
174+ return bottle .request .get_cookie ("s_id" ) in sessions
175+
176+
177+ def createSession (username ):
178+ session_id = "" .join (
179+ random .choice (string .ascii_letters + string .digits ) for i in range (20 )
180+ )
181+ bottle .response .set_cookie (
182+ "s_id" ,
183+ session_id ,
184+ expires = datetime .datetime .now () + datetime .timedelta (days = 30 ),
185+ )
186+ with shelve .open (sessions_db ) as sessions :
187+ sessions [session_id ] = username
188+ return bottle .redirect ("/dashboard" )
189+
190+
191+ @app .post ("/login" )
192+ def login ():
193+ username = bottle .request .forms .get ("username" )
194+ password = bottle .request .forms .get ("password" )
195+ with shelve .open (user_db ) as users :
196+ if not username in users :
197+ return bottle .template ("home.html" , message = "User does not exist." )
198+ if users [username ].password != password :
199+ return bottle .template ("home.html" , message = "Incorrect password." )
200+ return createSession (username )
201+
202+
203+ @app .post ("/register" )
204+ def register ():
205+ username = bottle .request .forms .get ("username" )
206+ password = bottle .request .forms .get ("password" )
207+ with shelve .open (user_db ) as users :
208+ if username in users :
209+ return bottle .template (
210+ "home.html" ,
211+ message = "Username already exists. Select a different username" ,
212+ )
213+ users [username ] = User (password = password )
214+ return createSession (username )
215+
216+
217+ @app .get ("/logout" )
218+ def logout ():
219+ with shelve .open (sessions_db ) as sessions :
220+ del sessions [bottle .request .get_cookie ("s_id" )]
221+ bottle .response .delete_cookie ("s_id" )
222+ return bottle .redirect ("/home" )
223+
152224
153225@app .post ("/check/<code>/<number>" )
226+ @login_required
154227def file_upload (code , number ):
155- u_name = request .forms .get ("username" ) # accepting username
228+ with shelve .open (sessions_db ) as sessions :
229+ u_name = sessions [bottle .request .get_cookie ("s_id" )]
156230 time = datetime .datetime .now ()
157- uploaded = request .files .get ("upload" ).file .read ()
231+ uploaded = bottle . request .files .get ("upload" ).file .read ()
158232 expected = questions [number ].output
159233 expected = expected .strip ()
160234 uploaded = uploaded .strip ()
@@ -164,7 +238,6 @@ def file_upload(code, number):
164238 submissions = (
165239 [] if u_name not in submission_record else submission_record [u_name ]
166240 )
167- # submissions = submission_record.get(u_name, list())
168241 submissions .append (
169242 Submission (
170243 question = number ,
@@ -186,5 +259,4 @@ def file_upload(code, number):
186259def error404 (error ):
187260 return template ("error.html" ,errorcode = error .status_code , errorbody = error .body )
188261
189-
190- run (app , host = "localhost" , port = 8080 )
262+ bottle .run (app , host = "localhost" , port = 8080 )
0 commit comments