Skip to content
Snippets Groups Projects

prepare.py

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    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.

    Edited
    prepare.py 12.21 KiB
    #!/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()
    0% or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment