prepare.py
The snippet can be accessed without any authentication.
Authored by
Quentin Bramas
Script permettant de télécharger et extraire automatiquement les exercices de programmation C.
#!/usr/bin/python3
import signal, sys, re
import tarfile, io, os
import getpass
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
try:
import requests
pass
except ImportError:
print('Vous devez installer le package requests pour faire fonctionner ce programme:\n')
print(' pip3 install requests')
exit(1)
def safe_get(url, *args, **kwargs):
try:
r = requests.get(url, *args, **kwargs, timeout=8)
if not r:
print("An error occurred (are you connected to the Internet?)")
exit(1)
return r
except:
print("An error occurred (are you connected to the Internet?)")
exit(1)
if '--help' in sys.argv:
print("Options:")
print(" --help: display this message")
print(" --update: update this script")
print(" --remove-key: remove the current key from the keyring")
print()
print("""To start the program with a specific key:
MOODLE_API_KEY=your_key pyhon3 prepare.py
To store the key in you .bashrc file:
echo 'export MOODLE_API_KEY=your_key' >> ~/.bashrc
source ~/.bashrc
and then simply run:
python3 prepare.py
""")
exit(0)
if '--update' in sys.argv:
print("Updating 📑")
r = safe_get('https://git.unistra.fr/-/snippets/132/raw')
with open(__file__, 'w') as f:
f.write(r.text)
print("Done 🎉")
exit(0)
from abc import ABC, abstractmethod
from datetime import datetime, timezone
'''
classes from
https://github.com/lukewarlow/PythonConsoleMenu
'''
class OperationError(Exception):
def __init__(self):
super().__init__("Invalid operation")
class AbstractMenu(ABC):
def __init__(self, title: str):
self.title = title
self.menu_items = []
self.initialise()
@abstractmethod
def initialise(self):
pass
def update_menu_items(self):
pass
def item_text(self, item: 'MenuItem'):
return "%s" % item.description
def item_line(self, index: int, item: 'MenuItem'):
return "%s. %s" % (index, self.item_text(item))
def display(self):
repeat: bool = True
while repeat:
self.update_menu_items()
print()
print(self.title)
for i in range(0, len(self.menu_items)):
if self.menu_items[i].isVisible:
print(self.item_line(i, self.menu_items[i]))
inp = input("Select Option: ")
try:
menu_item = self.menu_items[int(inp)]
if menu_item.isVisible:
repeat = menu_item.run()
else:
raise OperationError()
except ValueError:
print("Invalid option, you need to enter a number.", inp)
repeat = True
except IndexError:
print("Invalid option. Option {0} doesn't exist.".format(inp))
repeat = True
except OperationError:
print("Invalid option. Option at {0} is hidden.".format(inp))
repeat = True
def add_menu_item(self, menu_item: 'MenuItem'):
if not self.menu_items.__contains__(menu_item):
self.menu_items.append(menu_item)
else:
raise ValueError("Menu item with id {0} already exists!.".format(menu_item.id))
def add_hidden_menu_item(self, menu_item: 'MenuItem'):
self.add_menu_item(menu_item.hide())
def show_menu_item(self, item_id: int):
try:
menu_item = MenuItem(item_id)
index = self.menu_items.index(menu_item)
self.menu_items[index].show()
except ValueError:
print("Error showing menu item. Menu item with ID {0} hasn't been added to this menu.".format(item_id))
def hide_menu_item(self, item_id: int):
try:
menu_item = MenuItem(item_id)
index = self.menu_items.index(menu_item)
self.menu_items[index].hide()
except ValueError:
print("Error hiding menu item. Menu item with ID {0} hasn't been added to this menu.".format(item_id))
class MenuItem:
def __init__(self, id: int, description: str = "", action: callable(None) = None, menu: AbstractMenu = None, args: list = []):
self.id: int = id
self.description: str = description
self.action = action
self.menu: AbstractMenu = menu
self.isExitOption: bool = False
self.isVisible: bool = True
self.args: list = args
def hide(self) -> 'MenuItem':
self.isVisible = False
return self
def show(self) -> 'MenuItem':
self.isVisible = True
return self
def set_as_exit_option(self) -> 'MenuItem':
self.isExitOption = True
return self
def run(self) -> bool:
if self.action is not None:
self.action(*self.args)
elif self.menu is not None:
self.menu.display()
return not self.isExitOption
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.id == other.id
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
url_args = []
for i in range(len(sys.argv) - 1):
if sys.argv[i] == '--code':
url_args.append('code='+sys.argv[i+1])
for i in range(len(sys.argv)):
if sys.argv[i] == '--force':
url_args.append('force=1')
url_args = "&".join(url_args)
print('Téléchargement de la liste des problèmes ...')
#r = safe_get('https://api.tps.bramas.fr/api/problems'+code)
r = safe_get('https://tps.bramas.fr/problems?'+url_args)
problems = r.json()
problemsGrouped = {}
for p in problems:
s = p['name'].split('/')
if s[0] not in problemsGrouped:
problemsGrouped[s[0]] = []
problemsGrouped[s[0]].append(p)
def download(path, url):
print('Téléchargement de '+path+' ...', end='')
z_r = safe_get(url, stream=True)
t = tarfile.open(fileobj=z_r.raw, mode='r:gz')
if os.path.exists(path):
print('Attention, le dossier existe, certains fichiers risquent d\être remplacés.\n')
print('Voulez vous écraser les fichiers existant (sans risque si vous avez commit vos modifications)\n(y/n)')
if input() != 'y':
print('Annulé!!')
input('appuyer sur Entrée pour continuer')
return
t.extractall(path)
print('OK\n')
input('appuyer sur Entrée pour continuer')
def remainingTime(dt):
if not dt: return ''
dat = datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S%z")
#dat = datetime.fromisoformat(dt)
#dat = dat.replace(tzinfo=timezone.utc)
diff = dat - datetime.now(dat.tzinfo)
if diff.total_seconds() < 0:
return 'Passé'
return str(diff).split('.')[0]
class GroupMenuSubMenu(AbstractMenu):
group = None
_hideExisting = True
_toggleExistingVisibilityItem = None
def __init__(self, group):
self.group = group
super().__init__("Liste "+str(group))
def toggleExistingVisibility(self):
if self._hideExisting:
self.showExisting()
else:
self.hideExisting()
def hideExisting(self):
self._hideExisting = True
self._toggleExistingVisibilityItem.description = "Show existing problems"
for p in problemsGrouped[self.group]:
if os.path.exists(p['name']):
self.hide_menu_item(p['menu_id'])
def showExisting(self):
self._hideExisting = False
self._toggleExistingVisibilityItem.description = "Hide existing problems"
for p in problemsGrouped[self.group]:
self.show_menu_item(p['menu_id'])
def initialise(self):
self.add_menu_item(MenuItem(0, "<< Back").set_as_exit_option())
self._toggleExistingVisibilityItem = MenuItem(1, "", action=self.toggleExistingVisibility)
self.add_menu_item(self._toggleExistingVisibilityItem)
idx = 1
for p in problemsGrouped[self.group]:
idx += 1
text = ("*"*p['difficulty']).ljust(6,' ') +' '+p['name']
if p['deadline']:
text += ' ('+remainingTime(p['deadline'])+')'
m = MenuItem(idx, text, lambda: download(p['name'], p['url']))
self.add_menu_item(m)
p['menu_id'] = idx
self.hideExisting()
class GroupMenu(AbstractMenu):
subMenus = []
_hideExisting = True
_toggleExistingVisibilityItem = None
def __init__(self):
super().__init__("Welcome to the test menu.")
def showGroup(self, group):
if group in self.groupVisible:
self.groupVisible[group] = False
self.groupVisible[group] = True
for p in problemsGrouped[group]:
self.hide_menu_item(p['menu_id'])
print('show', group)
def hideExisting(self):
self._hideExisting = True
self._toggleExistingVisibilityItem.description = "Show existing problems"
for m in self.subMenus:
m.menu.hideExisting()
def showExisting(self):
self._hideExisting = False
self._toggleExistingVisibilityItem.description = "Hide existing problems"
for m in self.subMenus:
m.menu.showExisting()
def toggleExistingVisibility(self):
if self._hideExisting:
self.showExisting()
else:
self.hideExisting()
def initialise(self):
self.add_menu_item(MenuItem(0, "Exit menu").set_as_exit_option())
self._toggleExistingVisibilityItem = MenuItem(1, "", action=self.toggleExistingVisibility)
self.add_menu_item(self._toggleExistingVisibilityItem)
idx = 1
for group, problems in problemsGrouped.items():
#if not displayAll and os.path.exists(p['name']) and not self.seeAll:
# continue
idx += 1
m = MenuItem(idx, group, menu=GroupMenuSubMenu(group))
self.add_menu_item(m)
self.subMenus.append(m)
self.hideExisting()
class ListMenu(AbstractMenu):
_hideExisting = True
_toggleExistingVisibilityItem = None
def __init__(self):
super().__init__("Bienvenu dans le gestionnaire de TP")
def updateVisibility(self):
if self._hideExisting:
for p in problems:
if os.path.exists(p['name']):
self.hide_menu_item(p['menu_id'])
else:
for p in problems:
self.show_menu_item(p['menu_id'])
def hideExisting(self):
self._hideExisting = True
self._toggleExistingVisibilityItem.description = \
"\t 🙉 Montrer les problèmes existants"
self.updateVisibility()
def showExisting(self):
self._hideExisting = False
self._toggleExistingVisibilityItem.description = \
"\t 🙈 Cacher les problèmes existants"
self.updateVisibility()
def toggleExistingVisibility(self):
if self._hideExisting:
self.showExisting()
else:
self.hideExisting()
def selectProblem(self, name, url):
download(name, url)
self.updateVisibility()
def initialise(self):
self.add_menu_item(MenuItem(0, "\t ❌ Quitter").set_as_exit_option())
self._toggleExistingVisibilityItem = MenuItem(1, "", action=self.toggleExistingVisibility)
self.add_menu_item(self._toggleExistingVisibilityItem)
idx = 1
for p in problems:
idx += 1
text = ("⭐"*p['difficulty']).ljust(6,' ') +'\t'
rtime = remainingTime(p['deadline'])
if rtime != '':
text += '🕓 '
else:
text += ' '
text += p['name']
if p['deadline']:
text += ' ('+rtime+')'
m = MenuItem(idx, text, action=self.selectProblem, args=[p['name'], p['url']])
self.add_menu_item(m)
p['menu_id'] = idx
self.hideExisting()
if '--download' in sys.argv:
name = 'TP-sans-nom'
if '--name' in sys.argv:
name = sys.argv[sys.argv.index('--name')+1]
url = sys.argv[sys.argv.index('--download')+1]
download(name, url)
exit(0)
demoMenu = ListMenu()
demoMenu.display()
Please register or sign in to comment