Exemplo n.º 1
0
def main(*args, **kwargs):
    global bot
    loop = asyncio.get_event_loop()

    async def run_bot():
        try:
            await bot.start(*args, **kwargs)
        finally:
            if not bot.is_closed():
                bot.close()

    asyncio.ensure_future(run_bot(), loop=loop)
    try:
        loop.run_forever()
    except GatewayNotFound as e:
        print_exc(f"Gateway wasn't found, likely a Discord API error.", e)
    except ConnectionClosed as e:
        print_exc(f"Connection closed by Discord.", e)
    except LoginFailure as e:
        print_exc(
            f"You gave me the wrong credentials, so Discord didn't let me log in.",
            e)
    except HTTPException as e:
        print_exc(f"Some weird HTTP error happened. More details below.", e)
    except KeyboardInterrupt:
        pass
    except Exception as e:
        print_exc(
            f"Something else went wrong and I'm not sure what. More details below.",
            e)
Exemplo n.º 2
0
    def find_many(self, terms: list[str], by: str) -> list[Subject]:
        """
        Busca por matérias dentro da lista de termos especificada.
        ### Params
        - `terms: list[str]`: Uma lista de strings contendo cada termo de pesquisa;
        - `by: str`: O critério de pesquisa. Dever ser um dos valores:
          - `'code'`: Busca por código de matéria;
          - `'name'`: Busca por nome, parcial ou completo;
          - `'semester'`: Busca por semestre. `terms` deverá ser um `int` ou poder ser convertido para `int`.

        ### Retorno
        - Uma lista de instâncias de `Subject` correspondentes a todos os termos especificados, ou uma lista vazia.

        ### Levanta
        - `SyntaxError` nos seguintes casos:
          - `by` não é uma string com um dos valores válidos acima;
          - Caso `by` seja `'code'` ou `'name'`:
              - Pelo menos um elemento de `terms` não é uma `str` nem pode ser convertido para `str`.
          - Caso `by` seja `'semester'`:
              - Pelo menos um elemento de `terms` não é um `int` nem pode ser convertido para `int`.
        """
        if not isinstance(by, str) or by.lower() not in ('code', 'name',
                                                         'semester'):
            raise SyntaxError(
                "Argument 'by' is not a string with a valid value")

        by = by.lower()
        if by in ('code', 'name'):
            try:
                if by == 'code':
                    terms = tuple(
                        [str(term).upper() for term in terms if bool(term)])
                else:
                    terms = tuple([str(term) for term in terms if bool(term)])
            except Exception:
                raise SyntaxError(
                    "At least one argument in 'terms' is not a str and cannot be cast to str"
                )

        if by == 'semester':
            try:
                terms = tuple([int(term) for term in terms])
            except Exception:
                raise SyntaxError(
                    "At least one argument in 'terms' is not an int and cannot be cast to int"
                )

        try:
            q: Query = self._session.query(Subject)
            if by == 'code':
                q = q.filter(Subject.code.in_(terms))
            elif by == 'name':
                q = q.filter(Subject.fullname.in_(terms))
            else:
                q = q.filter(Subject.semester.in_(terms))

            return q.all()
        except Exception:
            print_exc()
            return []
Exemplo n.º 3
0
    def delete(self, discord_id: int) -> int:
        """
        Exclui um estudante do banco de dados.
        ### Params
        - `discord_id: int`: O ID Discord do estudante a ser excluído.

        ### Retorna
        - `0`: Operação bem-sucedida;
        - `1`: Exceção levantada, transação sofreu rollback.

        ### Levanta
        - `SyntaxError` caso `discord_id` não for um `int` nem poder ser convertido para `int`.
        """
        try:
            if not isinstance(discord_id, int):
                discord_id = int(discord_id)
        except Exception:
            raise SyntaxError("Discord ID is not an instance of 'int' and could not be cast to 'int' either")

        tr = None
        try:
            tr = self._session.begin_nested()
            deleted_student = self._session.query(Student).filter(Student.discord_id == discord_id).first()
            self._session.delete(deleted_student)

            self._gcommit(tr)
            return 0
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return 1
Exemplo n.º 4
0
    def find_all(self) -> list[Subject]:
        """
        Retorna uma lista enorme contendo todas as matérias cadastradas no banco de dados.
        - AVISO: NÃO tente imprimir essa lista completa em um chat - não só a lista pode exceder o limite de 2000 chars
          imposto pelo Discord, mas também pode causar muito spam!
        - Em vez de poluir o seu chat, considere usar o método `find()`.
        """
        # -> this text is left here as a lesson learned kind of thing. how to bypass the orm somewhat and connect to the
        #    DB API on a lower level

        # This is preferred over the usual session.query(Subject).all()
        # because the former issues multiple SELECT statements for each id.

        # Actually nevermind, it was the rollback at the end of every function causing this.
        # Leaving this here for reference on how to use DBAPI-like querying.
        # Also note: put a rollback BEFORE every command call that uses the DB.
        # Not only it avoids the problem mentioned above, it has 2 outcomes:
        # 1. if there is a current bad transaction in progress, it will be rolled back.
        # 2. if there are no active transactions, the method is a pass-through.

        # cnx = engin.connect()
        # res = []
        # for row in cnx.execute(Subject.__table__.select()):
        #     res.append(Subject(id=row['id'], code=row['code'], fullname=row['fullname'], semester=row['semester']))
        # return res
        try:
            return self._session.query(Subject).all()
        except Exception:
            print_exc()
            return []
Exemplo n.º 5
0
    async def add_student(self, ctx: commands.Context, *, arguments=''):
        """
        Cadastra o usuário que invocou o comando.
        - O sistema não permite o cadastro de um usuário já cadastrado.
        - Sintaxe: `st cadastrar matricula nome completo`
          - Exemplo: `st cadastrar 2020123456 Celso Souza`
        """
        arguments: list = arguments.split()
        if len(arguments) < 2 or not str(arguments[0]).isnumeric():
            await ctx.send("Sintaxe inválida. Exemplo: `>>st cadastrar 2020123456 Celso Souza`.")
            return

        msg: Message = await ctx.send('Cadastrando...')
        try:
            statuses = (
                "Algo deu errado - cadastro não realizado. Consulte o log para mais detalhes.",
                "Sintaxe inválida. Exemplo: `>>st cadastrar 2020123456 Celso Souza`.",
                "Você já está cadastrado."
            )
            result = self.stdao.insert(name=' '.join(arguments[1::]), registry=arguments[0], discord_id=ctx.author.id)
            if 'err' in result.keys():
                await msg.edit(content=statuses[result.get('err') - 1])
            else:
                await msg.edit(content=f'Cadastro realizado com sucesso. ```{smoothen(str(result.get(ctx.author.id)))}```')

        except Exception:
            print_exc()
            await msg.edit(content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 6
0
 async def cog_command_error(self, ctx: commands.Context, error):
     if isinstance(error, commands.CheckFailure):
         await ctx.send(
             "Você não está cadastrado. Use o comando `>>st cadastrar` para usar os subcomandos de `sc`."
         )
     else:
         print_exc(f"Exception raised:")
Exemplo n.º 7
0
    async def find_subject(self, ctx: commands.Context, *, arguments=''):
        """
        Procura uma matéria existente.
        - Sintaxe: `mt buscar <por> <filtro>`, onde:
          - `por` pode ser:
              - 'nome' (busca por nome completo ou parcial);
              - 'cod' (busca por código exato);
              - 'sem' (busca por semestre);
          - Em caso de busca por semestre, `filtro` deve ser um valor numérico inteiro de 0 a 8.
          - Matérias elo (eletivas) têm semestre 0, mas `mt buscar sem elo` também funciona.
        - Exemplos: `mt buscar nome banco`, `mt buscar cod est`, `mt buscar sem 4`.
        """
        arguments: list = arguments.split()
        if len(arguments) < 2 or arguments[0].lower() not in ('nome', 'cod',
                                                              'sem'):
            await ctx.send(
                "Sintaxe inválida. Exemplos: `>>mt buscar nome banco` ou `>>mt buscar cod bd2`.\nUse `>>help mt buscar` para ajuda."
            )
        else:
            try:
                terms = ' '.join(arguments[1::])
                if arguments[0].lower() == 'nome':
                    by = 'name'
                elif arguments[0].lower() == 'cod':
                    by = 'code'
                else:
                    by = 'semester'
                    terms = 0 if terms.lower() == 'elo' else int(terms)
            except Exception:
                await ctx.send(
                    "Sintaxe inválida. Exemplos: `>>mt buscar nome banco` ou `>>mt buscar cod bd2`."
                )
                return

            msg: Message = await ctx.send('Buscando matéria...')
            try:
                matches = self.sbdao.find(terms=terms,
                                          by=by,
                                          single_result=False)

                if matches is None:
                    await msg.edit(
                        content="Algo deu errado. Consulte o log para detalhes."
                    )
                elif len(matches) == 0:
                    await msg.edit(
                        content=
                        "Nenhuma matéria foi encontrado para esse critério.")
                else:
                    await msg.edit(
                        content=f"Encontrada(s): ```{smoothen(matches)}```")

            except Exception:
                print_exc()
                await msg.edit(
                    content="Algo deu errado. Consulte o log para detalhes.")
Exemplo n.º 8
0
    def insert(self, discord_id: int, name: str, registry: int) -> dict:
        """
        Cadastra um estudante novo.
        Caso o `discord_id` já exista no banco de dados, o estudante é tido como já cadastrado, e nada é feito.
        ### Params
        - `discord_id: int`: O ID Discord do estudante a ser cadastrado;
        - `name: str`: O nome completo do estudante a ser cadastrado;
        - `registry: int`: O número de matrícula do estudante a ser cadastrado.

        ### Retorno
        - Um dict no formato `{std.discord_id: std}` em caso de sucesso.
        - Casos excepcionais:
            - `{'err': 1}`: Erro desconhecido, usuário não foi cadastrado;
            - `{'err': 2}`: Erro de sintaxe nos argumentos passados;
            - `{'err': 3}`: Usuário já existente.

        ### Levanta
        - `SyntaxError` caso os argumentos não possam ser convertidos para seus respectivos tipos.
        """
        try:
            if not isinstance(discord_id, int):
                discord_id = int(discord_id)
            if not isinstance(name, str):
                name = str(name)
            if not isinstance(registry, int):
                registry = int(registry)
        except Exception:
            raise SyntaxError("Arguments could not be cast to their intended types")

        try:
            #    no name      or first name only or                   registry not in 20xxxxxxxx format
            if len(name) == 0 or ' ' not in name or (len(str(registry)) != 10 and not str(registry).startswith('20')):
                return {'err': 2}
        except Exception:
            print_exc()
            return {'err': 1}

        if self.find(discord_id, by='id') is not None:
            return {'err': 3}
        else:
            tr = None
            try:
                tr = self._session.begin_nested()
                new_student = Student(name=name, registry=registry, discord_id=discord_id)
                self._session.add(new_student)
                self._gcommit(tr)
                return {new_student.discord_id: new_student}
            except Exception:
                print_exc()
                if tr is not None:
                    tr.rollback()
                return {'err': 1}
Exemplo n.º 9
0
    def update(self, student: Union[int, Student], name=None, registry=None) -> dict:
        """
        Atualiza o cadastro de um estudante.
        ### Params
        - `student: int | Student`: O estudante (ou seu ID Discord) a ser cadastrado;
        - `name: str`: O novo nome do estudante. Se `None`, não vai ser alterado.
        - `registry: int`: O novo número de matrícula do estudante. Se `None`, não vai ser alterado.
        - Pelo menos um dentre `name` e `registry` precisa não ser `None`.

        ### Retorno
        - Um dict no formato `{std.discord_id: std}` em caso de sucesso.
        - Casos excepcionais:
            - `{'err', 1}`: Exceção levantada, transação sofreu rollback;
            - `{'err', 2}`: Estudante inexistente;
            - `{'err', 3}`: Nada a modificar (`name` e `registry` ambos são `None`).

        ### Levanta
        - `SyntaxError` caso `student` não seja uma instância de `int` nem `Student`, nem possa ser convertido para `int`.
        """
        try:
            if not isinstance(student, Union[Student, int].__args__):
                student = int(student)
        except Exception:
            raise SyntaxError("Argument 'student' is neither an instance of Student nor int, nor can it be casted to int")

        if all([name is None, registry is None]):
            return {'err', 3}

        tr = None
        try:
            tr = self._session.begin_nested()
            cur_student = self.find(student if isinstance(student, int) else student.id, by='id')
            if cur_student is None:
                return {'err', 2}

            if name is not None:
                cur_student.name = str(name)
            if registry is not None:
                cur_student.registry = int(registry)

            self._gcommit(tr)
            return {cur_student.discord_id: cur_student}
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return {'err': 1}
Exemplo n.º 10
0
    async def enroll(self, ctx: commands.Context, *, arguments=''):
        """
        Matricula um estudante numa matéria.
        - Se essa matéria for uma matéria trancada, ela é reativada.
        - Se essa matéria for uma matéria antiga, ela é atualizada e todos os seus trabalhos redefinidos para o padrão.
        """
        arguments: list = arguments.split()
        if not arguments:
            await ctx.send(
                "Sintaxe inválida. Exemplo: `>>sc matricular mt1 mt2 ...`")
        else:
            msg: Message = await ctx.send('Efetuando matrícula...')
            try:
                arguments = list(filter(lambda x: len(x) == 3, arguments))
                if len(arguments) > 8:
                    arguments = arguments[0:7:
                                          1]  # up to 8 at once only, uni rules

                result = self.scdao.register(student=ctx.author.id,
                                             subjects=arguments)
                if 'err' in result.keys():
                    if result['err'] == 3:
                        await msg.edit(
                            content=
                            "Matéria(s) inexistente(s).\nUse o comando `>>mt todas` ou `>>mt buscar` para verificar o código da(s) matéria(s) desejada(s)."
                        )
                    elif result['err'] == 2:
                        await msg.edit(
                            content=
                            "Sintaxe inválida. Exemplo: `>>sc matricular mt1 mt2 ...`"
                        )
                    else:
                        msg.edit(
                            content=
                            "Algo deu errado. Consulte o log para mais detalhes."
                        )
                else:
                    await msg.edit(
                        content=
                        f"Matrícula registrada nas matérias a seguir:\n```{smoothen(tuple([f'{x}: {y}' for x, y in result.items()]))}```"
                    )
            except Exception:
                print_exc()
                await msg.edit(
                    content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 11
0
    async def check_student(self, ctx: commands.Context, *, arguments=''):
        """
        Busca os dados de um estudante.
        - O 'filtro' pode ser uma matrícula (numérica) ou um nome (string).
        - Sintaxe: `st buscar filtro`
          - Exemplos: `st buscar 2019123456` ou `st buscar Silva`
        """
        if len(arguments) == 0:
            await ctx.send("Sintaxe inválida. Exemplo: `>>st buscar 2019123456` (matrícula) ou `>>st buscar João Carlos`.")
            return

        q = None
        comedias = False
        roger = bool("roger" in arguments.lower())
        msg: Message = await ctx.send('Buscando estudantes...')

        try:
            if arguments.lower().startswith("comédia"):
                q = list(self.stdao.find_all())
                comedias = True
            elif arguments.lower() == "todos":
                q = list(self.stdao.find_all())

            elif len(arguments) == 10 and arguments.isnumeric():
                q = [self.stdao.find(int(arguments), by='registry')]
            else:
                q = list(self.stdao.find(arguments, by='name'))

            if len(q) == 0:
                await msg.edit(content="Estudante(s) não encontrado(s).")
                return
            else:
                if comedias:
                    results = "Os comédia dessa porra:"
                elif roger:
                    results = "O divino meme em charme e osso:"
                else:
                    results = "Encontrado(s):"
                await msg.edit(content=f"{results}```{smoothen(q)}```")
        except Exception:
            print_exc()
            await msg.edit(content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 12
0
    async def lock_enrollment(self, ctx: commands.Context, *, arguments=''):
        """
        Tranca uma, várias ou todas as matérias matriculadas pelo estudante que chamar o comando.
        - Sintaxe: `sc trancar mt1 mt2 ...`
          - Exemplo: `sc trancar POO CGR TCP`
          - `mt1`, `mt2` etc. são códigos de matérias a ser trancadas. Case insensitive, mas precisam necessariamente ter comprimento 3.
          - Caso `mt1 = 'todas'`, todas as matérias matriculadas são trancadas.
        """
        arguments: list = arguments.split()
        invalid_syntax = "Sintaxe: `>>sc trancar mt1 mt2 ...` - caso `mt1 = 'todas'`, todas as matérias ativas do estudante serão trancadas."
        if not arguments:
            await ctx.send(invalid_syntax)
        else:
            msg: Message = await ctx.send('Trancando matrículas...')
            try:
                if arguments[0] == 'todas':
                    lock_all = True
                else:
                    lock_all = False

                result = self.scdao.lock(ctx.author.id,
                                         arguments,
                                         lock_all=lock_all)

                msgs = ("Você trancou todas as suas matrículas.",
                        "Algo deu errado. Consulte o log para mais detalhes.",
                        "Nenhuma matéria válida fornecida. Nada foi trancado.")
                if 'err' not in result.keys():
                    if not lock_all:
                        await msg.edit(
                            content=
                            f"Matrículas trancadas com sucesso: ```{smoothen([f'-> {x}' for x in result.get(0)])}```"
                        )
                    else:
                        await msg.edit(content=msgs[0])
                else:
                    await msg.edit(content=msgs[result.get('err')])
            except Exception:
                print_exc()
                await msg.edit(
                    content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 13
0
    async def edit_student(self, ctx: commands.Context, *, arguments=''):
        """
        Edita o cadastro do usuário que invocou o comando.
        - Sintaxe: `editar campo novo valor`, onde campo pode ser:
          - `nome`: Edita o nome do estudante;
          - `mtr`: Edita a matrícula do estudante.
          - Exemplos: `st editar nome Carlos Eduardo` ou `st editar mtr 2020048596`
        """
        arguments = arguments.split()
        if len(arguments) < 2:
            await ctx.send("Sintaxe inválida. Exemplo: `>>st editar mtr 2019246852` ou `>>st editar nome Carlos Eduardo`.")
            return

        msg: Message = await ctx.send('Editando...')
        try:
            if str(arguments[0]).lower() == 'nome':
                await msg.edit(content='Editando nome...')
                result = self.stdao.update(ctx.author.id, name=' '.join(arguments[1::]))

            elif str(arguments[0]).lower() == 'mtr':
                await msg.edit(content='Editando matrícula...')
                result = self.stdao.update(ctx.author.id, registry=int(arguments[1]))

            else:
                await msg.edit(content="Campo inválido - o campo pode ser `nome` ou `mtr`.")
                return

            statuses = (
                'Algo deu errado. Consule o log para detalhes.',
                'Você não está cadastrado.',
                'Nenhuma modificação a fazer.'
            )

            if 'err' in result.keys():
                await msg.edit(content=statuses[result.get('err') - 1])
            else:
                await msg.edit(content=f'Dados alterados. ```{smoothen(str(result.get(ctx.author.id)))}```')
        except Exception as e:
            await msg.edit(content='Algo deu errado. Consule o log para detalhes.')
            print_exc()
Exemplo n.º 14
0
    def find_enrollments(self,
                         std_id: int,
                         previous=False,
                         active=True) -> list[Registered]:
        """
        Busca todas as matrículas correspondentes a um ID de estudante.
        ### Params
        - `std_id: int`: O ID Discord do estudante cujas matrículas vão ser retornadas;
        - `previous: bool`: Quando `True`, retorna matérias de semestres anteriores, senão, somente matérias do semestre atual;
        - `active: bool`: Quando `False`, retorna também matérias trancadas, senão, somente matérias ativas.

        ### Retorno
        - Uma `list`a com instâncias de `Registered` para todos os resultados encontrados.
          - Se nenhum for encontrado, ou se alguma exceção for lançada durante a execução, retorna uma lista vazia.

        ### Levanta
        - `SyntaxError` caso `std_id` não for uma instância de `int`, nem puder ser convertido para `int`.
          - Isso inclui o caso de `std_id` ser `None`.
        """
        if not isinstance(std_id, int):
            try:
                std_id = int(std_id)
            except Exception:
                raise SyntaxError(
                    "Student ID is not an instance of 'int' and cannot be cast to 'int'"
                )
        try:
            q = self._session.query(Registered).filter(
                Registered.std_id == std_id)
            if active:
                q = q.filter(Registered.active == 'true')
            if previous:
                q = q.filter(Registered.semester <= self.cur_semester)
            else:
                q = q.filter(Registered.semester == self.cur_semester)
            return q.all()
        except Exception as e:
            print_exc()
            return []
Exemplo n.º 15
0
    async def roger_foto(self, ctx: commands.Context):
        """Você perguntou? O Roger aparece!"""
        msg: Message = await ctx.send("Invocando o Roger...")
        try:
            roger_img = self._fetch_roger_image()
            embed = Embed(description=roger_img[0], colour=Colour(randint(0x000000, 0xFFFFFF)))
            embed.set_image(url=roger_img[1])

            if roger_img[0].lower() == "julio_cobra":
                cobra = True
                ct = 'Cacilda, agora a cobra fumou. Você tirou o julio_cobra.'
            else:
                cobra = False
                ct = None
            await msg.edit(content=ct, embed=embed)

            if cobra and ctx.guild.id == 567817989806882818:
                await self._aprisionar(ctx)

        except Exception as e:
            await msg.edit("Ih, deu zica.")
            print_exc("Zica thrown:")
Exemplo n.º 16
0
    async def add_subject(self, ctx: commands.Context, *, arguments=''):
        """
        Adiciona uma matéria nova.
        - Sintaxe: `mt add CDE SM Nome da Matéria`, onde:
          - `CDE`: Código, uma sigla de precisamente 3 letras, única entre todas as matérias registradas.
          - `SM`: Semestre, um número inteiro de 0-8 indicando o semestre.
            - Semestre 0 significa que a matéria é eletiva (elo).
          - Exemplo: `mt add BD2 4 Banco de Dados II`
        """
        arguments = arguments.split()
        if len(arguments) <= 2:
            await ctx.send(
                "Sintaxe inválida. Exemplo: `>>mt add BD2 4 Banco de Dados II`."
            )
        else:
            msg: Message = await ctx.send('Adicionando matéria...')
            try:
                statuses = (
                    "Algo deu errado. Consulte o log para detalhes.",
                    "Sintaxe inválida. Exemplo: `>>mt add BD2 4 Banco de Dados II`.",
                    "O código especificado para a matéria já existe.")
                result = self.sbdao.insert(code=arguments[0],
                                           fullname=' '.join(arguments[2::]),
                                           semester=abs(int(arguments[1])))

                if 'err' in result.keys():
                    await msg.edit(content=statuses[result.get('err') - 1])
                else:
                    await msg.edit(
                        content=
                        f"Matéria adicionada com sucesso.\n```{tuple(result.values())[0]}```"
                    )

            except Exception:
                print_exc()
                await msg.edit(
                    content="Algo deu errado. Consulte o log para detalhes.")
Exemplo n.º 17
0
    def update(self,
               code: Union[str, Subject],
               newcode=None,
               fullname=None,
               semester=None) -> dict:
        """
        Atualiza as informações de uma matéria.
        ### Params
        - `code: Subject | str`: A matéria (ou o código dela) a ser atualizada;
        - `newcode: str`: O novo código;
        - `fullname: str`: O novo nome completo;
        - `semester: int`: O novo semestre.
        - Pelo menos um dos 3 acima não deve ser `None`. A matéria permanecerá inalterada nos atributos que forem `None`.

        ### Retorno
        - Um dict no formato `{0: sbj}` em caso de sucesso.
        - Casos excepcionais:
            - `{'err': 1}`: Exceção lançada, transação sofreu rollback;
            - `{'err': 2}`: Erro de sintaxe nos argumentos passados:
                - `newcode` ou `fullname` é uma string vazia;
                - `semester` não é um `int` dentro do intervalo [0, 11[
            - `{'err': 3}`: Matéria inexistente.

        ### Levanta
        - `SyntaxError` caso `code` não seja uma instância de `str` nem `Subject`, nem possa ser convertido para `str`.
        """
        if not isinstance(code, Union[str, Subject].__args__):
            try:
                code = str(code)
            except Exception:
                raise SyntaxError(
                    "Argument 'code' is not an instance of 'str' nor 'Subject', nor can it be cast to 'str'"
                )

        try:
            if any([
                    all([newcode is None, fullname is None, semester is None]),
                    any([
                        newcode is not None and len(str(newcode)) != 3,
                        fullname is not None and len(fullname) < 3,
                        semester is not None
                        and int(semester) not in range(0, 11)
                    ])
            ]):
                return {'err': 2}
        except Exception:
            print_exc()
            return {'err': 2}

        tr = None
        try:
            cur_sbj = self.find(
                code.upper() if isinstance(code, str) else code.id, by='code')
            if cur_sbj is None:
                return {'err': 3}
            else:
                tr = self._session.begin_nested()
                if newcode is not None:
                    cur_sbj.code = str(newcode).upper()

                if fullname is not None:
                    cur_sbj.fullname = str(fullname)

                if semester is not None:
                    cur_sbj.semester = int(semester)

                self._gcommit(tr)
                return {0: cur_sbj}
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return {'err': 1}
Exemplo n.º 18
0
 async def cog_command_error(self, ctx: commands.Context, error):
     if isinstance(error, commands.NotOwner):
         await ctx.send(
             "Somente o proprietário do bot pode usar esse comando.")
     else:
         print_exc()
Exemplo n.º 19
0
    async def edit_subject(self, ctx: commands.Context, *, arguments=''):
        """
        Edita uma matéria em específico.
        - Sintaxe: `mt editar <campo> <código> <novo valor>`, onde:
          - `código`: Código da matéria;
          - `campo`: Campo a editar (`nome`, `cod`, `sem` ou `todos`);
          - `novo valor`: Novo valor;
          - No caso de `campo == 'todos'`, a ordem dos novos valores é `<código> <semestre> <nome completo>`.
        - Exemplos: `mt editar cod AL1 ALG`, `mt editar todos AL1 ALG 0 Algoritmos 1`.
        """
        arguments: list = arguments.split()
        syntax_error = "Sintaxe inválida. Exemplos: `mt editar cod AL1 ALG`, `mt editar todos AL1 ALG 0 Algoritmos 1`."
        if len(arguments) < 3 or any([
                arguments[0].lower() not in ('nome', 'cod', 'sem', 'todos'),
                len(arguments[1]) != 3
        ]):
            await ctx.send(syntax_error)
        else:
            msg: Message = await ctx.send('Editando matéria...')
            err_msgs = ("Algo deu errado. Consulte o log para detalhes.",
                        syntax_error, "A matéria informada não existe.")
            try:
                field = arguments[0].lower()
                code = arguments[1].upper()
                if field == 'todos':
                    new_value = arguments[2::]
                    if len(new_value) < 3:
                        raise SyntaxError(
                            '(SubjectController.update) Number of arguments passed is lower than minimum for option \'todos\''
                        )
                elif field == 'cod':
                    new_value = arguments[2].upper()
                elif field == 'sem':
                    new_value = int(arguments[2])
                else:
                    new_value = ' '.join(arguments[2::])
            except Exception:
                await msg.edit(content=syntax_error +
                               '\nPara mais detalhes, use `>>help mt editar`.')
                return

            try:
                update_kwargs = {'code': code}
                if field == 'todos':
                    update_kwargs['newcode'] = new_value[0]
                    update_kwargs['semester'] = new_value[1]
                    update_kwargs['fullname'] = ' '.join(new_value[2::])
                else:
                    update_kwargs[
                        'newcode'] = new_value if field == 'cod' else None
                    update_kwargs[
                        'semester'] = new_value if field == 'sem' else None
                    update_kwargs[
                        'fullname'] = new_value if field == 'nome' else None

                result = self.sbdao.update(**update_kwargs)
                if 'err' in result.keys():
                    await msg.edit(content=err_msgs[result.get('err') - 1])
                else:
                    await msg.edit(
                        content=
                        f'Matéria editada: ```{smoothen(str(result.get(0)))}```'
                    )

            except Exception:
                print_exc()
                await msg.edit(
                    content="Algo deu errado. Consulte o log para detalhes.")
Exemplo n.º 20
0
    def find(self,
             terms: Union[int, str],
             by: str,
             single_result=True) -> Union[list[Subject], Subject, None]:
        """
        Busca uma matéria baseado num filtro.
        ### Params
        - `terms: str | int`: Os termos de pesquisa;
        - `single_result: bool`: Caso `True`, retorna o primeiro resultado ou None. Senão, retorna uma lista com todos os resultados. Opcional.
        - `by: str`: O critério de pesquisa. Deve ser um dos valores:
          - `'code'`: Busca por código de matéria;
          - `'name'`: Busca por nome, parcial ou completo;
          - `'semester'`: Busca por semestre. `terms` deverá ser um `int` ou poder ser convertido para `int`.

        ### Retorno
        - Caso `single_result == True`, retorna uma instância de `Subject`, ou `None` se nada for encontrado;
        - Senão, retorna uma lista de instâncias de `Subject`, ou uma lista vazia.
        - Em ambos os casos, retorna `None` se uma exceção for lançada.

        ### Levanta
        - `SyntaxError` nos seguintes casos:
          - `by` não é uma string com um dos valores válidos acima;
          - Caso `by` seja `'code'` ou `'name'`:
              - `terms` não é uma `str` nem pode ser convertido para `str`.
          - Caso `by` seja `'semester'`:
              - `terms` não é um `int` nem pode ser convertido para `int`.
        """
        if not isinstance(by, str) or by.lower() not in ('code', 'name',
                                                         'semester'):
            raise SyntaxError(
                "Argument 'by' is not a string with a valid value")

        if by in ('code', 'name') and not isinstance(terms, str):
            try:
                terms = str(terms)
            except Exception:
                raise SyntaxError(
                    "Argument 'terms' is not a str and cannot be cast to str")

        if by == 'semester' and not isinstance(terms, int):
            try:
                terms = int(terms)
            except Exception:
                raise SyntaxError(
                    "Argument 'terms' is not an int and cannot be cast to int")

        try:
            q: Query = self._session.query(Subject)
            if by == 'code':
                q = q.filter(Subject.code == terms.upper())
            elif by == 'name':
                q = q.filter(Subject.fullname.ilike(f'%{terms}%'))
            else:
                q = q.filter(Subject.semester == terms)

            if single_result:
                return q.first()
            else:
                return q.all()
        except Exception:
            print_exc()
            return None
Exemplo n.º 21
0
    async def update_grade(self, ctx: commands.Context, *, arguments=''):
        """
        Atualiza a nota de uma matéria em específico.
        - O comando rejeita trabalhos pendentes - somente trabalhos com status `OK` são alterados!
        - Sintaxe: `sc nota codigo trabalho novanota [antigo]`
          - Se `antigo = 'sim'` então o comando altera a nota de uma matéria de um semestre anterior, ou de alguma matéria trancada.
          - Senão, o comando só vai alterar notas de matérias ativas do semestre atual.
          - Exemplo: `sc nota AL1 AV1 9.5 sim`
        """
        arguments: list = arguments.split()
        invalid_syntax = "Sintaxe: `>>sc nota codigo trabalho novanota antigo?` - 'antigo' é opcional: sim ou não.\nExemplo: `>>sc nota AL1 AV1 9.5`"
        invalid_syntax += "\nSe tentar mudar a nota para uma matéria de um semestre anterior, terá que especificar 'sim' em antigo."
        if len(arguments) < 3:
            await ctx.send(invalid_syntax)
        else:
            exam_grades = {
                'AV1': [8.0, 1],
                'APS1': [2.0, 2],
                'AV2': [8.0, 3],
                'APS2': [2.0, 4],
                'AV3': [10.0, 5]
            }
            msg: Message = await ctx.send('Atualizando nota...')
            try:
                if len(arguments) > 4:
                    arguments = arguments[0:3:1]
                if len(arguments) == 4:
                    current = not bool(arguments[3].lower() == 'sim')
                else:
                    current = True
                sbj_code = arguments[0].upper()
                assignment = arguments[1].upper()
                grade = abs(float(arguments[2]))

                if exam_grades.get(assignment) is None:
                    await msg.edit(
                        content=f'*Trabalho inválido!*\n{invalid_syntax}')
                    return

                if grade > exam_grades.get(assignment)[0]:
                    await msg.edit(
                        content=
                        f"*A nota especificada é mais alta que o permitido:* `{exam_grades.get(assignment)[0]}`"
                    )
                    return

                result = self.scdao.update(
                    student=ctx.author.id,
                    subject=sbj_code,
                    exam_type=exam_grades.get(assignment)[1],
                    newval=grade,
                    grade=True,
                    current=current,
                    active=current)
                err_responses = (
                    "Algo deu errado. Consulte o log para mais detalhes.",
                    invalid_syntax,
                    "O trabalho para a matéria especificada não foi encontrado.",
                    "Você não pode alterar a nota para um trabalho que não foi entregue ainda."
                )

                if 'err' in result.keys():
                    await msg.edit(content=err_responses[result.get('err') - 1]
                                   )
                else:
                    newmsg = f"Nota alterada: ```{smoothen(f'{tuple(result.keys())[0]} | {assignment}: {nround(grade, 1)}')}```"
                    await msg.edit(content=newmsg)

            except Exception:
                print_exc()
                await msg.edit(
                    content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 22
0
    async def russian_roulette(self, ctx: commands.Context, *, arguments):
        """
        Roleta russa com N opções. Se o trigger cair no meio delas, você vai morrer por (opções / 2) minutos. 6 opções por padrão.\n
        - Sintaxe: `rr opções`\n
        - Exemplo: `rr 10`\n
        P.S. Não chame o bot de diabo e mande ele morrer ao mesmo tempo, ele fica nervoso.
        """
        arguments = arguments.split()
        options = 6
        if len(arguments) > 0 and str(
                arguments[0]).isnumeric() and int(arguments[0]) > 1:
            options = abs(int(arguments[0]))

        trigger = randint(1, options)

        morre_diabo = False
        if len(arguments) >= 2:
            morre_diabo = 'morre' in arguments[0].lower(
            ) and 'diabo' in arguments[1].lower()
        dprint(f"Roleta para {' '.join(arguments)}: {options} opções.")

        if trigger == options // 2 or morre_diabo:
            original_roles = [
                x for x in ctx.author.roles if x.name != '@everyone'
            ]
            if not morre_diabo:
                await ctx.send(u'<:ikillu:700684891251277904> \U0001F4A5')
            else:
                await ctx.send(
                    u"<:morrediabo:779864127249055795><:ikillu:700684891251277904> \U0001F4A5"
                )
            try:
                respawn = (options // 2) * 60
                response = None
                if morre_diabo:
                    response = "ENTÃO MORRE, DIABO!"
                    dprint(f"O DIABO {str(ctx.author.name).upper()} MORREU!!")
                else:
                    response = f"{ctx.author.mention} morreu! Respawn em {respawn} segundos..."

                for role in original_roles:
                    await ctx.author.remove_roles(role)
                await ctx.send(response)
                await ctx.author.add_roles(
                    ctx.guild.get_role(778774271869583480))
                await sleep(respawn)
                try:
                    await ctx.author.remove_roles(
                        ctx.guild.get_role(778774271869583480))
                    for role in original_roles:
                        await ctx.author.add_roles(role)
                    if not morre_diabo:
                        await ctx.send(f"{ctx.author.mention}: Levante e ande!"
                                       )
                    else:
                        await ctx.send(
                            f"{ctx.author.mention}: EU QUERO QUE VOCÊ SE FODA, SEU FILHO DE UMA PUTA! <:morrediabo:779864127249055795>"
                        )
                except Exception as e:
                    if 'unknown member' in str(e).lower():
                        await ctx.send(
                            f"Parece que {ctx.author.name} morreu de vez...")
                    else:
                        print_exc()
            except Exception as e:
                if 'missing permisisons' in str(e).lower():
                    await ctx.send(
                        f"A arma atirou, mas parece que {ctx.author.name} é imortal..."
                    )
                else:
                    print_exc()

        else:
            await ctx.send(u'<:ikillu:700684891251277904> \U0001F389')
            await ctx.send(
                f"{ctx.author.mention} deu sorte! A bala era de mentira!")
Exemplo n.º 23
0
    async def view_self(self, ctx: commands.Context, *, arguments=''):
        """
        Verifica se a pessoa que invocou o comando está cadastrada no banco de dados.
        - Se sim, então os dados da pessoa são mostrados, juntamente com suas provas e respectivos status.
        - Aceita um argumento, sendo este o tipo de exibição das matérias:
          - `completo`: mostra trabalhos como `TRB: STS (nota)`
          - `notas`: mostra trabalhos como `TRB: nota`
          - `médias` ou `medias`: ao invés de trabalhos, mostra a média atual da matéria.
            - Caso a AV2 já tenha sido marcada como OK, mostra também um status "aprovado" ou "reprovado", dependendo da média.
          - Nada ou qualquer outra coisa além dos já citados: mostra trabalhos como o padrão, `TRB: STS`
        - Sintaxe: `st ver exibicao`
          - Exemplo: `st ver notas`
        """
        msg: Message = await ctx.send('Buscando...')
        cur_student: Student = self.stdao.find(ctx.author.id, by='id')
        if cur_student is None:
            await msg.edit(content="Você não está cadastrado.")
        else:
            start = time()
            command = next(iter(arguments.split()), None)
            if command is not None:
                command = command.lower()

            try:
                enrollments = list(self.scdao.find_enrollments(cur_student.id))
                cur_strings = []  # parse to strings afterward, if applicable

                if enrollments:
                    semester_avg = []  # insert class averages here

                    for registry in enrollments:
                        composite = str(registry.subject.code) + ' | '

                        # MT1 | AV1: STS (10.0) | APS1: STS (10.0) | AV2: STS (10.0) | APS2: STS (10.0) | AV3: STS (10.0)
                        if command == 'completo':
                            composite += ' | '.join([f"{exam.show_type()}: {exam.show_status()} ({exam.show_grade()})" for exam in registry.exams])

                        # MT1 | AV1: 10.0 | APS1: 10.0 | AV2: 10.0 | APS2: 10.0 | AV3: 10.0
                        elif command == 'notas':
                            composite += ' | '.join([f"{exam.show_type()}: {exam.show_grade()}" for exam in registry.exams])

                        # MT1 | Média: 10.0 | Status: Aprovado (se AV2 OK)
                        elif command == 'médias' or command == 'medias':
                            average = uni_avg(*[exam.grade for exam in registry.exams])
                            semester_avg.append(average)
                            composite += ' | '.join([f"Média: {average}"])
                            for exam in registry.exams:
                                if exam.exam_type == 2 and exam.status == 1:
                                    composite += f" | Status: {'Aprovado' if average >= 7.0 else 'Reprovado'}"
                                    break

                        # MT1 | AV1: STS | APS1: STS | AV2: STS | APS2: STS | AV3: STS
                        else:
                            composite += ' | '.join([f"{exam.show_type()}: {exam.show_status()}" for exam in registry.exams])
                        cur_strings.append(composite)

                    enrollments.clear()  # memory cleanup

                    if command == 'médias' or command == 'medias':
                        semester_avg = nround(avg(semester_avg), 2)
                        savg_msg = f"CR do semestre: {semester_avg}"
                        cur_strings.extend(('---', savg_msg))

                final_msg = [str(cur_student), '---']
                if cur_strings:
                    final_msg.extend(cur_strings)
                else:
                    final_msg.append('-- aluno não matriculado em nenhuma matéria --')
                final_msg = tuple(final_msg)
            except Exception as e:
                final_msg = 'Algo deu errado. Consulte o log para detalhes.'
                print_exc()
            else:
                final_msg = f"Seus dados: ```{smoothen(final_msg)}```"
            finally:
                end = round(time() - start, 2)
                await msg.edit(content=final_msg + f"Tempo de execução: {end} seg.")
                dprint(f"------------------ Time taken: {end} sec ------------------")
Exemplo n.º 24
0
    def update(self,
               student: Union[Student, int],
               subject: Union[Subject, str],
               exam_type: int,
               newval,
               grade: bool,
               current=True,
               active=True) -> dict:
        """
        Atualiza um dado em um trabalho.
        ### Params
        - `student: Student | int`: O estudante (ou o ID Discord dele) cujo trabalho deverá ser atualizado;
        - `subject: Subject | str`: A matéria (ou o código dela) cujo trabalho deverá ser atualizado;
        - `newval`: O novo valor do status ou da nota do trabalho/prova;
        - `grade: bool`: Caso `True`, atualiza a nota do trabalho/prova com `newval`, senão, atualiza o status.
        - `current: bool`: Caso `True`, busca entre provas do semestre atual somente;
        - `active: bool`: Caso `True`, busca entre provas somente em matérias não trancadas;
        - `exam_type: int`: O tipo da prova ou trabalho a ser pesquisado(a):
          - `1`: Prova AV1
          - `2`: Trabalho APS1
          - `3`: Prova AV2
          - `4`: Trabalho APS2
          - `5`: Prova AV3

        ### Retorno
        - Um dict no formato `{sbj.fullname: 0}` no caso de sucesso.
        - Casos excepcionais:
            - `{'err': 1}`: Exceção lançada, transação sofreu rollback;
            - `{'err': 2}`: Erro de sintaxe nos argumentos passados.
            - `{'err': 3}`: Trabalho não encontrado.
            - `{'err': 4}`: (Atualização de nota) Trabalho ainda não entregue. Não se atualiza notas de trabalhos não entregues.

        ### Levanta
        - `SyntaxError` nos casos a seguir:
          - `student` não é uma instância de `Student` nem `int` e não pode ser convertido para `int`;
          - `subject` não é uma instância de `Subject` nem `str` e não pode ser convertido para `str`;
          - `exam_type` não é um `int` entre 1 e 5.
        """
        if not isinstance(student, Union[Student, int].__args__):
            try:
                student = int(student)
            except Exception:
                raise SyntaxError(
                    "Argument 'student' not an instance of Student nor int, cannot be cast to int"
                )

        if not isinstance(subject, Union[Subject, str].__args__):
            try:
                subject = str(subject)
            except Exception:
                raise SyntaxError(
                    "Argument 'subject' not an instance of Subject nor str, cannot be cast to str"
                )

        if not isinstance(exam_type, int):
            try:
                exam_type = int(exam_type)
            except Exception:
                raise SyntaxError(
                    "Argument 'exam_type' is not an instance of int, nor can it be cast to int"
                )

        if exam_type not in range(1, 6):
            raise SyntaxError(
                "Argument 'exam_type' is not an integer between 1 and 5")

        tr = None
        try:
            tr = self._session.begin_nested()
            if isinstance(student, int):
                student = self._session.query(Student).filter(
                    Student.discord_id == student).first()
            if isinstance(subject, str):
                subject = self._session.query(Subject).filter(
                    Subject.code == subject.upper()).first()

            exam = self.find_exam(student=student,
                                  subject=subject,
                                  exam_type=exam_type,
                                  current=current,
                                  active=active)

            if exam is None:
                return {'err': 3}
            else:
                if grade:
                    if int(exam.status) != 1:
                        tr.rollback()
                        return {'err': 4}
                    else:
                        exam.grade = nround(newval, 1)
                else:
                    if isinstance(newval, str):
                        exam.status = ('OK', 'EPN', 'PND').index(newval - 1)
                    elif isinstance(newval, int):
                        exam.status = newval
                    else:
                        tr.rollback()
                        return {'err': 2}
                self._gcommit(tr)
                return {subject.fullname: 0}
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return {'err': 1}
Exemplo n.º 25
0
    async def update_status(self, ctx: commands.Context, *, arguments=''):
        """
        Altera o status de um trabalho. Também reconhecido como o comando `sts`.
        - Sintaxe: `sc status codigo_materia trabalho novostatus`; onde `novostatus` pode ser:
          - OK;
          - PND (pendente);
          - EPN (envio/entrega pendente).
          - Exemplo: `sc status AL1 AV1 OK`
        """
        arguments: list = arguments.split()
        invalid_syntax = "Sintaxe: `>>sc status codigo trabalho novostatus`.\nExemplo: `>>sc status AL1 AV1 OK`"
        if len(arguments) < 3:
            await ctx.send(invalid_syntax)
        else:
            msg: Message = await ctx.send('Atualizando status...')
            statuses = ('OK', 'EPN', 'PND')
            exam_types = ('AV1', 'APS1', 'AV2', 'APS2', 'AV3')

            sbj_code = arguments[0].upper()
            assignment = arguments[1].upper()
            status = arguments[2].upper()

            if assignment not in exam_types:
                await msg.edit(content=invalid_syntax)
                return
            else:
                assignment = exam_types.index(assignment) + 1

            if status not in statuses:
                await msg.edit(content=invalid_syntax)
                return

            if len(arguments) >= 4 and ' '.join(
                    arguments[2:4:]).lower() in ('envio pendente',
                                                 'entrega pendente'):
                status = 2
            elif status == 'pendente':
                status = 3
            else:
                status = statuses.index(status) + 1

            try:
                result = self.scdao.update(student=ctx.author.id,
                                           subject=sbj_code,
                                           exam_type=assignment,
                                           newval=status,
                                           grade=False)
                err_responses = (
                    "Algo deu errado. Consulte o log para mais detalhes.",
                    invalid_syntax,
                    "O trabalho para a matéria especificada não foi encontrado."
                )

                if 'err' in result.keys():
                    await msg.edit(content=err_responses[result.get('err') - 1]
                                   )
                else:
                    newmsg = f"Status alterado: ```{smoothen(f'{tuple(result.keys())[0]} | {exam_types[assignment - 1]}: {statuses[status - 1]}')}```"
                    await msg.edit(content=newmsg)

            except Exception:
                print_exc()
                await msg.edit(
                    content='Algo deu errado. Consulte o log para detalhes.')
Exemplo n.º 26
0
    def register(self, student: Union[Student, int],
                 subjects: Union[list[str], tuple[str], set[str]]) -> dict:
        """
        Matricula um estudante em uma ou várias matérias.\n
        Se a matéria for trancada, ela é reativada.
        Se a matéria for de um semestre anterior, todos os seus trabalhos são redefinidos.
        ### Params
        - `student: Student | int`: O estudante (ou seu ID Discord) ingressando nas matérias especificadas;
        - `subjects: list | tuple | set`: Um iterável contendo os códigos (todos `str`) das matérias a serem registradas.

        ### Retorno
        - Um dict com o formato `sbj.code: sbj.fullname` para cada item.
        - Casos excepcionais:
          - `{'err': 1}`: Exceção lançada, transação sofreu rollback;
          - `{'err': 2}`: Erro de sintaxe nos argumentos passados:
              - Estudante nulo ou inexistente;
              - Lista de matérias vazia
          - `{'err': 3}`: Lista de matérias válidas vazia, nada a matricular.
              - Isso é diferente do erro 2, onde o argumento `subjects` é vazio ou nulo. No erro 3, nenhuma matéria com os códigos informados foi encontrada.

        ### Levanta
        - `SyntaxError` nos seguintes casos:
            - Caso `student` não seja uma instância de `Student` ou `int`, nem possa ser convertido para `int`;
            - Caso os valores de `subject` não sejam todos `str`s nem possam ser convertidas para tal.
        """
        if not all([bool(student), bool(subjects)]):
            return {'err': 2}

        try:
            if not isinstance(student, Union[Student, int].__args__):
                student = int(student)
        except Exception:
            raise SyntaxError(
                "Argument 'student' not an instance of Student nor int, cannot be cast to int"
            )

        try:
            subjects = [str(x).upper() for x in subjects if bool(x)]
        except Exception:
            raise SyntaxError(
                "At least one object in argument 'subjects' is not a string and cannot be cast to string"
            )

        tr = None
        try:
            student = self._session.query(Student).filter(
                Student.discord_id ==
                (student if isinstance(student, int) else student.id)).first()
            if student is None or len(subjects) == 0:
                return {'err': 2}

            tr = self._session.begin_nested()
            modified_subjects = dict({})
            enrollments = self.find_enrollments(student.id,
                                                previous=True,
                                                active=False)
            for reg in enrollments:
                if reg.subject.code in subjects:
                    modified_subjects[reg.subject.code] = reg.subject.fullname

                    while reg.subject.code in subjects:
                        subjects.remove(reg.subject.code)

                    if not reg.active:  # reactivating a locked subject
                        reg.active = True

                    if reg.semester != self.cur_semester:  # retrying a failed subject
                        reg.semester = self.cur_semester
                        for exam in reg.exams:
                            exam.reset()
                    # no need to add reg to session here, it already is in the session

            loaded_subjects = self._session.query(Subject).filter(
                Subject.code.in_(subjects)).all()
            if not loaded_subjects and not modified_subjects:
                return {'err': 3}

            for subj in loaded_subjects:
                registry = Registered(semester=self.cur_semester, active=True)
                registry.subject = subj
                registry.student = student
                for x in range(1, 6):
                    exam = Exam(exam_type=x, status=3, grade=0.0)
                    exam.registry = registry
                    registry.exams.append(exam)
                student.registered_on.append(registry)
                modified_subjects[subj.code] = subj.fullname

            self._gcommit(tr)
            self._session.expire_all()
            return modified_subjects
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return {'err': 1}
Exemplo n.º 27
0
    def find_exam(self,
                  student: Union[Student, int],
                  subject: Union[Subject, str],
                  exam_type: int,
                  current=True,
                  active=True) -> Union[Exam, None]:
        """
        Busca uma prova em específico, para um estudante e matéria em específico.
        ### Params
        - `student: int | Student`: O estudante (ou o ID Discord dele) cuja prova deve ser pesquisada;
        - `subject: str | Subject`: A matéria (ou o código dela) cuja prova deve ser pesquisada;
        - `current: bool`: Caso `True`, busca provas do semestre atual somente;
        - `active: bool`: Caso `True`, busca provas somente em matérias não trancadas;
        - `exam_type: int`: O tipo da prova ou trabalho a ser pesquisado(a):
          - `1`: Prova AV1
          - `2`: Trabalho APS1
          - `3`: Prova AV2
          - `4`: Trabalho APS2
          - `5`: Prova AV3
        - Por padrão, `current == True` e `active == True`. Isso realiza uma pesquisa entre as matérias ativas do semestre atual.

        ### Retorno
        - A primeira instância de `Exam` encontrada, ou `None` caso uma exceção for lançada, ou nada for encontrado.

        ### Levanta
        - `SyntaxError` nos seguintes casos:
          - `student` não é uma instância de `Student` nem `int` e não pode ser convertido para `int`;
          - `subject` não é uma instância de `Subject` nem `str` e não pode ser convertido para `str`;
          - `exam_type` não é um `int` entre 1 e 5.
        """
        if not isinstance(student, Union[Student, int].__args__):
            try:
                student = int(student)
            except Exception:
                raise SyntaxError(
                    "Argument 'student' not an instance of Student nor int, cannot be cast to int"
                )

        if not isinstance(subject, Union[Subject, str].__args__):
            try:
                subject = str(subject)
            except Exception:
                raise SyntaxError(
                    "Argument 'subject' not an instance of Subject nor str, cannot be cast to str"
                )

        if not isinstance(exam_type, int):
            try:
                exam_type = int(exam_type)
            except Exception:
                raise SyntaxError(
                    "Argument 'exam_type' is not an instance of int, nor can it be cast to int"
                )

        if exam_type not in range(1, 6):
            raise SyntaxError(
                "Argument 'exam_type' is not an integer between 1 and 5")

        try:
            if isinstance(student, int):
                student = self._session.query(Student).filter(
                    Student.discord_id == student).first()
            if isinstance(subject, str):
                subject = self._session.query(Subject).filter(
                    Subject.code == subject.upper()).first()

            q: Query = self._session.query(Exam).join(Registered,
                                                      Registered.id == Exam.id)
            q = q.filter(Registered.std_id == student.id,
                         Registered.sbj_id == subject.id,
                         Exam.exam_type == exam_type)
            if current:
                q = q.filter(Registered.semester == self.cur_semester)
            if active:
                q = q.filter(Registered.active == 'true')
            return q.first()
        except Exception:
            print_exc()
            return None
Exemplo n.º 28
0
 async def cog_command_error(self, ctx: commands.Context, error):
     if isinstance(error, commands.CommandOnCooldown):
         await ctx.send("Calma, rapaz. Sem spam.")
     else:
         print_exc()
Exemplo n.º 29
0
    def lock(self,
             student: Union[Student, int],
             subjects: Union[list[str], tuple[str], set[str]],
             lock_all: bool = False) -> dict:
        """
        Tranca uma, várias ou todas as matrículas de um estudante.
        ### Params
        - `student: Student | int`: O estudante (ou o ID Discord dele) cuja(s) matéria(s) deverá(ão) ser trancada(s);
        - `subjects: list[str]`: Um iterável contendo o(s) código(s) da(s) matéria(s) a ser(em) trancada(s);
        - `lock_all: bool`: Caso `True`, tranca todas as matérias do estudante.

        ### Retorno
        - Um dict no formato `{0: [sbj1.fullname, sbj2.fullname, ...]}` em caso de sucesso.
        - Casos excepcionais:
            - `{'err': 1}`: Exceção lançada, transação sofreu rollback;
            - `{'err': 2}`: Nada a trancar.

        ### Levanta
        - `SyntaxError` nos casos a seguir:
          - `student` não é uma instância de `Student` nem `int` e não pode ser convertido para `int`;
          - `subjects` não é uma instância de uma lista, tupla ou conjunto;
          - Pelo menos um elemento de `subject` não é uma `str` nem pode ser convertido para `str`.
        """
        if not isinstance(student, Union[Student, int].__args__):
            try:
                student = int(student)
            except Exception:
                raise SyntaxError(
                    "Argument 'student' not an instance of Student nor int, cannot be cast to int"
                )

        if not isinstance(subjects, Union[list, tuple, set].__args__):
            raise SyntaxError(
                "Argument 'subjects' not an instance of list, tuple or set")

        try:
            subjects = tuple([str(x).upper() for x in subjects])
        except Exception:
            raise SyntaxError(
                "At least one object in argument 'subjects' is not a string and cannot be cast to string"
            )

        if not (subjects or lock_all):
            return {'err': 2}

        tr = None
        try:
            print(
                f'Calling scdao.lock with student {student} | subjects {subjects}'
            )
            tr = self._session.begin_nested()
            changed = False
            if isinstance(student, int):
                student = self._session.query(Student).filter(
                    Student.discord_id == student).first()

            eager_enrollments = self.find_enrollments(student.id)
            modified_enrollments = []

            for reg in eager_enrollments:
                print(f"Current registry's subject code: {reg.subject.code}")
                if lock_all or reg.subject.code in subjects:
                    changed = True
                    reg.active = False
                    modified_enrollments.append(reg.subject.fullname)

            self._gcommit(tr)
            return {0: modified_enrollments} if changed else {'err': 2}
        except Exception:
            print_exc()
            if tr is not None:
                tr.rollback()
            return {'err': 1}
Exemplo n.º 30
0
    def insert(self, code: str, fullname: str, semester: int) -> dict:
        """
        Cadastra uma matéria nova no banco de dados.
        ### Params
        - `code: str`: O código da matéria. Deverá ser uma string full uppercase de 3 chars;
        - `fullname: str`: O nome completo da matéria;
        - `semester: int`: O semestre a qual a matéria pertence.
          - Matérias eletivas são de semestre 0.

        ### Retorno
        - Um dict no formato `{sbj.code: sbj}` em caso de sucesso.
        - Casos excepcionais:
            - `{'err': 1}`: Exceção lançada, transação sofreu rollback;
            - `{'err': 2}`: Erro de sintaxe nos argumentos passados;
            - `{'err': 3}`: O código especificado já existe.

        ### Levanta
        - `SyntaxError` nos seguintes casos:
          - `code` ou `fullname` não são `str`, nem podem ser convertidas para `str`;
          - `semester` não é um `int` e nem pode ser convertido para `int`.
        """
        if not isinstance(code, str):
            try:
                code = str(code)
            except Exception:
                raise SyntaxError(
                    "Argument 'code' is not a string nor can it be converted to string"
                )

        if not isinstance(fullname, str):
            try:
                fullname = str(fullname)
            except Exception:
                raise SyntaxError(
                    "Argument 'fullname' is not a string nor can it be converted to string"
                )

        if not isinstance(semester, int):
            try:
                semester = int(semester)
            except Exception:
                raise SyntaxError(
                    "Argument 'semester' is not an int nor can it be converted to int"
                )

        if any(
            [len(code) != 3,
             len(fullname) == 0, semester not in range(0, 11)]):
            return {'err': 2}
        else:
            tr = None
            try:
                if self.find(terms=code.upper(), by='code') is not None:
                    return {'err': 3}
                else:
                    tr = self._session.begin_nested()
                    new_subject = Subject(code=code.upper(),
                                          fullname=fullname,
                                          semester=abs(semester))
                    self._session.add(new_subject)
                    self._gcommit(tr)
                    return {new_subject.code: new_subject}
            except Exception:
                print_exc()
                if tr is not None:
                    tr.rollback()
                return {'err': 1}