mirror of
https://github.com/Code-For-Groningen/temmies.git
synced 2025-03-15 15:10:15 +01:00
Implemented submissions. Works, although in a pretty nasty manner.
This commit is contained in:
parent
b819305704
commit
3c63a64eac
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
# Config - Testing
|
||||
config.py
|
||||
baller.py
|
||||
tests/
|
||||
|
||||
#Doc env
|
||||
.docs_env
|
||||
|
@ -1,126 +1,200 @@
|
||||
from bs4 import BeautifulSoup
|
||||
from exceptions.IllegalAction import IllegalAction
|
||||
import re
|
||||
import re
|
||||
from json import loads
|
||||
from time import sleep
|
||||
|
||||
class ExerciseGroup():
|
||||
def __init__(self, url:str, soup, session, parent):
|
||||
self.url = url
|
||||
self.name = soup.text
|
||||
self.__raw = soup
|
||||
self.session = session
|
||||
self.parent = parent # This is unnecessary, but I'll keep it for now
|
||||
self.request = self.session.get(self.url)
|
||||
self.soup = BeautifulSoup(self.request.text, 'lxml')
|
||||
|
||||
def __str__(self):
|
||||
return f"ExerciseGroup {self.name} in folder {self.parent.name}"
|
||||
|
||||
@property
|
||||
def amExercise(self):
|
||||
return "ass-submitable" in self.__raw['class']
|
||||
|
||||
def submit(self):
|
||||
if not self.amExercise:
|
||||
raise IllegalAction(message="You are submitting to a folder.")
|
||||
|
||||
# Logic for submitting
|
||||
|
||||
# Test cases
|
||||
@property
|
||||
def testCases(self):
|
||||
section = self.soup.find_all('div', class_="subsec round shade")
|
||||
tcs = []
|
||||
for div in section:
|
||||
res = div.find("h4", class_="info")
|
||||
if not res:
|
||||
continue
|
||||
|
||||
class ExerciseGroup:
|
||||
def __init__(self, url: str, soup, session, parent):
|
||||
self.url = url
|
||||
self.name = soup.text
|
||||
self.__raw = soup
|
||||
self.session = session
|
||||
self.parent = parent # This is unnecessary, but I'll keep it for now
|
||||
self.request = self.session.get(self.url)
|
||||
self.soup = BeautifulSoup(self.request.text, "lxml")
|
||||
|
||||
def __str__(self):
|
||||
return f"ExerciseGroup {self.name} in folder {self.parent.name}"
|
||||
|
||||
@property
|
||||
def amExercise(self):
|
||||
return "ass-submitable" in self.__raw["class"]
|
||||
|
||||
def submit(self):
|
||||
if not self.amExercise:
|
||||
raise IllegalAction(message="You are submitting to a folder.")
|
||||
|
||||
# Logic for submitting
|
||||
|
||||
# Test cases
|
||||
@property
|
||||
def testCases(self):
|
||||
section = self.soup.find_all("div", class_="subsec round shade")
|
||||
tcs = []
|
||||
for div in section:
|
||||
res = div.find("h4", class_="info")
|
||||
if not res:
|
||||
continue
|
||||
|
||||
if "Test cases" in res.text:
|
||||
for case in div.find_all("div", class_="cfg-line"):
|
||||
if link := case.find("a"):
|
||||
tcs.append(link)
|
||||
return tcs
|
||||
return None
|
||||
|
||||
def downloadTCs(self, path="."):
|
||||
# Logic for downloading test cases(if any)
|
||||
# In a div with class "subsec round shade", where there is an h4 with text "Test cases"
|
||||
if not self.amExercise:
|
||||
raise IllegalAction(message="You are downloading test cases from a folder.")
|
||||
|
||||
for tc in self.testCases:
|
||||
url = f"https://themis.housing.rug.nl{tc['href']}"
|
||||
|
||||
print(f"Downloading {tc.text}")
|
||||
# download the files
|
||||
with open(f"{path}/{tc.text}", "wb") as f:
|
||||
f.write(self.session.get(url).content)
|
||||
|
||||
return self.testCases
|
||||
|
||||
# Files
|
||||
@property
|
||||
def files(self):
|
||||
details = self.soup.find("div", id=lambda x: x and x.startswith("details"))
|
||||
|
||||
cfg_lines = details.find_all("div", class_="cfg-line")
|
||||
|
||||
link_list = []
|
||||
|
||||
for line in cfg_lines:
|
||||
key = line.find("span", class_="cfg-key")
|
||||
|
||||
if key and "Downloads" in key.text.strip():
|
||||
# Extract all links in the cfg-val span
|
||||
links = line.find_all("span", class_="cfg-val")
|
||||
for link in links:
|
||||
a = link.find_all("a")
|
||||
for a in a:
|
||||
link_list.append(a)
|
||||
|
||||
return link_list if link_list else None
|
||||
|
||||
def downloadFiles(self, path="."):
|
||||
for file in self.files:
|
||||
print(f"Downloading file {file.text}")
|
||||
url = f"https://themis.housing.rug.nl{file['href']}"
|
||||
with open(f"{path}/{file.text}", "wb") as f:
|
||||
f.write(self.session.get(url).content)
|
||||
return self.files
|
||||
|
||||
@property
|
||||
def exercises(self) -> list:
|
||||
if self.amExercise:
|
||||
return self
|
||||
|
||||
section = self.soup.find("div", class_="ass-children")
|
||||
try:
|
||||
submittables = section.find_all("a", class_="ass-submitable")
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return [
|
||||
ExerciseGroup(
|
||||
f"https://themis.housing.rug.nl{x['href']}", x, self.session, self
|
||||
)
|
||||
for x in submittables
|
||||
]
|
||||
|
||||
@property
|
||||
def folders(self) -> list:
|
||||
section = self.soup.find("div", class_="ass-children")
|
||||
try:
|
||||
folders = section.find_all("a", class_="ass-group")
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return [
|
||||
ExerciseGroup(f"https://themis.housing.rug.nl{x['href']}", x, session, self)
|
||||
for x in folders
|
||||
]
|
||||
|
||||
def __parseTable(self, soup, url):
|
||||
cases = soup.find_all('tr', class_='sub-casetop')
|
||||
fail_pass = {}
|
||||
i = 1
|
||||
for case in cases:
|
||||
status = case.find('td', class_='status-icon')
|
||||
# queued status-icon
|
||||
if "queued" in status.get("class"):
|
||||
sleep(1) # <- 🗿
|
||||
return self.__waitForResult(url)
|
||||
if "Passed" in status.text:
|
||||
fail_pass[i] = True
|
||||
elif "Wrong output" in status.text:
|
||||
fail_pass[i] = False
|
||||
elif ("No status" or "error") in status.text:
|
||||
fail_pass[i] = None
|
||||
i += 1
|
||||
return fail_pass
|
||||
|
||||
if "Test cases" in res.text:
|
||||
for case in div.find_all("div", class_="cfg-line"):
|
||||
if link := case.find("a"):
|
||||
tcs.append(link)
|
||||
return tcs
|
||||
return None
|
||||
|
||||
def downloadTCs(self, path="."):
|
||||
# Logic for downloading test cases(if any)
|
||||
# In a div with class "subsec round shade", where there is an h4 with text "Test cases"
|
||||
if not self.amExercise:
|
||||
raise IllegalAction(message="You are downloading test cases from a folder.")
|
||||
|
||||
for tc in self.testCases:
|
||||
url= f"https://themis.housing.rug.nl{tc['href']}"
|
||||
|
||||
print(f"Downloading {tc.text}")
|
||||
# download the files
|
||||
with open(f"{path}/{tc.text}", "wb") as f:
|
||||
f.write(self.session.get(url).content)
|
||||
|
||||
return self.testCases
|
||||
|
||||
# Files
|
||||
@property
|
||||
def files(self):
|
||||
details = self.soup.find('div', id=lambda x: x and x.startswith('details'))
|
||||
def __waitForResult(self, url):
|
||||
# This waits for result and returns a bundled info package
|
||||
r = self.session.get(url)
|
||||
soup = BeautifulSoup(r.text, "lxml")
|
||||
return self.__parseTable(soup, url)
|
||||
|
||||
cfg_lines = details.find_all('div', class_='cfg-line')
|
||||
|
||||
link_list = []
|
||||
|
||||
for line in cfg_lines:
|
||||
key = line.find('span', class_='cfg-key')
|
||||
|
||||
if key and "Downloads" in key.text.strip():
|
||||
# Extract all links in the cfg-val span
|
||||
links = line.find_all('span', class_='cfg-val')
|
||||
for link in links:
|
||||
a = link.find_all('a')
|
||||
for a in a:
|
||||
link_list.append(a)
|
||||
|
||||
return link_list if link_list else None
|
||||
|
||||
# Submit
|
||||
def submit(self, files: list, judge=True, wait=True):
|
||||
|
||||
# Find the form with submit and store the action as url
|
||||
# Store then the data-suffixes as file_types - dictionary
|
||||
|
||||
|
||||
def downloadFiles(self, path="."):
|
||||
for file in self.files:
|
||||
print(f"Downloading file {file.text}")
|
||||
url = f"https://themis.housing.rug.nl{file['href']}"
|
||||
with open(f"{path}/{file.text}", "wb") as f:
|
||||
f.write(self.session.get(url).content)
|
||||
return self.files
|
||||
|
||||
# idea exercises and folders are identical, maybe merge them?
|
||||
@property
|
||||
def exercises(self) -> list:
|
||||
if self.amExercise:
|
||||
return self
|
||||
|
||||
section = self.soup.find('div', class_="ass-children")
|
||||
try:
|
||||
submittables = section.find_all('a', class_="ass-submitable")
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return [
|
||||
ExerciseGroup(f"https://themis.housing.rug.nl{x['href']}",
|
||||
x,
|
||||
self.session,
|
||||
self)
|
||||
for x in submittables]
|
||||
|
||||
@property
|
||||
def folders(self) -> list:
|
||||
section = self.soup.find('div', class_="ass-children")
|
||||
try:
|
||||
folders = section.find_all('a', class_="ass-group")
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
return [
|
||||
ExerciseGroup(f"https://themis.housing.rug.nl{x['href']}",
|
||||
x,
|
||||
session,
|
||||
self)
|
||||
for x in folders]
|
||||
|
||||
form = self.soup.find("form")
|
||||
if not form:
|
||||
raise IllegalAction(message="You cannot submit to this assignment.")
|
||||
|
||||
url = "https://themis.housing.rug.nl" + form["action"]
|
||||
file_types = loads(form["data-suffixes"])
|
||||
|
||||
if isinstance(files, str):
|
||||
temp = []
|
||||
temp.append(files)
|
||||
files = temp
|
||||
|
||||
# Package the files up into files[]
|
||||
|
||||
packaged_files = []
|
||||
data = {}
|
||||
found_type = ""
|
||||
for file in files:
|
||||
for t in file_types:
|
||||
if t in file:
|
||||
found_type = file_types[t]
|
||||
break
|
||||
if not found_type:
|
||||
raise IllegalAction(message="Illegal filetype for this assignment.")
|
||||
|
||||
packaged_files.append((f"files[]", (file, open(file, "rb"), "text/x-csrc")))
|
||||
|
||||
data = {"judgenow": "true" if judge else "false", "judgeLanguage": found_type}
|
||||
|
||||
resp = self.session.post(url, files=packaged_files, data=data)
|
||||
|
||||
# Close each file
|
||||
i = 0
|
||||
for f in packaged_files:
|
||||
if i == 1:
|
||||
f[1].close()
|
||||
i = 0
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if not wait:
|
||||
return resp.url if "@submissions" in resp.url else None
|
||||
|
||||
return self.__waitForResult(resp.url)
|
||||
|
@ -31,6 +31,7 @@ class Themis:
|
||||
# get the csrf token and add it to payload
|
||||
csrfToken = soup.find('input',attrs = {'name':'_csrf'})['value']
|
||||
data['_csrf'] = csrfToken
|
||||
data['sudo'] = user.lower()
|
||||
|
||||
# Login
|
||||
r = s.post(url,data=data,headers = headers)
|
||||
|
Loading…
x
Reference in New Issue
Block a user