Implemented submissions. Works, although in a pretty nasty manner.

This commit is contained in:
Boyan 2024-04-08 21:40:52 +02:00
parent b819305704
commit 3c63a64eac
3 changed files with 195 additions and 120 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
# Config - Testing
config.py
baller.py
tests/
#Doc env
.docs_env

View File

@ -1,8 +1,11 @@
from bs4 import BeautifulSoup
from exceptions.IllegalAction import IllegalAction
import re
from json import loads
from time import sleep
class ExerciseGroup():
class ExerciseGroup:
def __init__(self, url: str, soup, session, parent):
self.url = url
self.name = soup.text
@ -10,14 +13,14 @@ class ExerciseGroup():
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')
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']
return "ass-submitable" in self.__raw["class"]
def submit(self):
if not self.amExercise:
@ -28,7 +31,7 @@ class ExerciseGroup():
# Test cases
@property
def testCases(self):
section = self.soup.find_all('div', class_="subsec round shade")
section = self.soup.find_all("div", class_="subsec round shade")
tcs = []
for div in section:
res = div.find("h4", class_="info")
@ -61,27 +64,25 @@ class ExerciseGroup():
# Files
@property
def files(self):
details = self.soup.find('div', id=lambda x: x and x.startswith('details'))
details = self.soup.find("div", id=lambda x: x and x.startswith("details"))
cfg_lines = details.find_all('div', class_='cfg-line')
cfg_lines = details.find_all("div", class_="cfg-line")
link_list = []
for line in cfg_lines:
key = line.find('span', class_='cfg-key')
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')
links = line.find_all("span", class_="cfg-val")
for link in links:
a = link.find_all('a')
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}")
@ -90,37 +91,110 @@ class ExerciseGroup():
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")
section = self.soup.find("div", class_="ass-children")
try:
submittables = section.find_all('a', class_="ass-submitable")
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]
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")
section = self.soup.find("div", class_="ass-children")
try:
folders = section.find_all('a', class_="ass-group")
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]
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
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)
# 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
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)

View File

@ -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)