diff --git a/algorw/common/models.py b/algorw/common/models.py new file mode 100644 index 0000000..dac9f70 --- /dev/null +++ b/algorw/common/models.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass, field + + +@dataclass +class Repo: + name: str = field(init=False) + owner: str = field(init=False) + full_name: str + + def __post_init__(self): + self.owner, self.name = self.full_name.split("/", 1) diff --git a/algorw/common/tasks.py b/algorw/common/tasks.py index d552871..b2c7528 100644 --- a/algorw/common/tasks.py +++ b/algorw/common/tasks.py @@ -3,29 +3,40 @@ from pydantic import BaseModel +from .models import Repo + class CorrectorTask(BaseModel): - """Clase que se encola en Redis para procesar por el worker. - """ + """Clase que se encola en Redis para procesar por el corrector. - # Por el momento, esta clase solo actúa de wrapper "legacy" para los - # correos que el sistema de entregas envía a Gmail. Como primer paso - # en la reescritura, el corrector ahora los obtiene de Redis. Gmail - # actúa ahora como backup (se le puede aplicar la etiqueta ‘entregas’ - # a un mail para que el corrector lo vuelva a procesar). A futuro, esta - # clase irá teniendo todos los campos necesarios para que el corrector - # no necesite parsear nada. + La cola de rq es ahora la principal comunicación entre el sistema de + entregas y el corrector. Gmail actua ahora como backup, o “corrector + legacy” (procesa y corrige las entregas, pero no envía el correo de + respuesta a les alumnes; este correo queda solo en la casilla). + """ + # "tp_id es el ID del TP; suele ir en minúsculas, y se usa como nombre + # de directorio en skel. tp_id: str zipfile: bytes legajos: List[str] + + # "orig_headers" son los headers del correo original que envió el sistema + # de entregas. TODO: eliminar de corrector.py toda la lógica que trata la + # entrada como un correo. orig_headers: Dict[str, str] - group_id: Optional[str] = None # Ubicación de la entrega en el repo de entregas. A día de hoy el sistema # de entregas elige la ruta, y el corrector guarda los archivos. Próximamente, # el sistema de entregas guardará los archivos, y el corrector los leerá. repo_relpath: PurePath + # "group_id" está presente para entregas grupales con ID de grupo. "alu_repo", + # si está presente, es el repositorio a donde se debería sincronizar la entrega + # (usando "github_id" para la autoría de los commits, o "wachenbot" si no). + group_id: Optional[str] = None + alu_repo: Optional[Repo] = None + github_id: Optional[str] = None + class Config: arbitrary_types_allowed = True diff --git a/algorw/corrector/alu_repos.py b/algorw/corrector/alu_repos.py index 78b5038..b1b7f0b 100644 --- a/algorw/corrector/alu_repos.py +++ b/algorw/corrector/alu_repos.py @@ -6,7 +6,6 @@ import io import os import pathlib -import random import re import tempfile @@ -34,18 +33,6 @@ GITHUB_TOKEN = os.environ["CORRECTOR_GH_TOKEN"] DEFAULT_GHUSER = os.environ["CORRECTOR_GH_USER"] -# XXX Temporario 2020/1. Maneja a qué personas se les incluye -# el enlace al repo en el mail que envía el corrector. -REVIEWEE_INDIV = { - "54321", -} - -# Poner aquí los padrones de integrantes de grupos, pero *solo* -# si el grupo tiene dos miembros. Si no, ponerlos en REVIEW_INDIV. -REVIEWEE_GRUPAL = { - "543421", -} - class AluRepo: """Clase para manejar los repositorios individuales y grupales. @@ -57,7 +44,7 @@ class AluRepo: DEFAULT_COLUMN = "Repo" def __init__( - self, repo_full: str, legajos: List[str], github_users: List[str] = None, + self, repo_full: str, legajos: List[str], github_user: str = None, ): """Constructor de la clase AluRepo. @@ -70,7 +57,7 @@ def __init__( self.gh_repo = None self.legajos = set(legajos) self.repo_full = repo_full - self.github_users = github_users or [DEFAULT_GHUSER] + self.github_user = github_user or DEFAULT_GHUSER @classmethod def from_legajo( @@ -214,11 +201,6 @@ def ensure_exists(self, *, skel_repo: str = None): # TODO: set up team access # TODO: configure branch protections - def has_reviewer(self): - # TODO: usar la columna Reviewer de la planilla? - reviewees = REVIEWEE_GRUPAL if len(self.legajos) > 1 else REVIEWEE_INDIV - return not self.legajos.isdisjoint(reviewees) - def sync(self, entrega_dir: pathlib.Path, rama: str, *, target_subdir: str = None): """Importa una entrega a los repositorios de alumnes. @@ -239,7 +221,7 @@ def sync(self, entrega_dir: pathlib.Path, rama: str, *, target_subdir: str = Non gh = github.Github(GITHUB_TOKEN) repo = self.gh_repo or gh.get_repo(self.repo_full) gitref = repo.get_git_ref(f"heads/{rama}") - ghuser = random.choice(self.github_users) # ¯\_(ツ)_/¯ Only entregas knows. + ghuser = self.github_user prefix_re = re.compile(re.escape(target_subdir.rstrip("/") + "/")) # Estado actual del repo. diff --git a/algorw/corrector/corrector.py b/algorw/corrector/corrector.py index 5871f3e..6a2174b 100755 --- a/algorw/corrector/corrector.py +++ b/algorw/corrector/corrector.py @@ -163,10 +163,10 @@ def procesar_entrega(task: CorrectorTask): if retcode != 0: raise ErrorInterno(output) - if TODO_OK_REGEX.search(output) and False: + if task.alu_repo is not None: try: # Sincronizar la entrega con los repositorios individuales. - alu_repo = AluRepo.from_legajos(padron.split("_"), tp_id) + alu_repo = AluRepo(task.alu_repo.full_name, task.legajos, task.github_id) alu_repo.ensure_exists(skel_repo="algorw-alu/algo2_tps") alu_repo.sync(moss.location(), tp_id) except (KeyError, ValueError): @@ -174,7 +174,7 @@ def procesar_entrega(task: CorrectorTask): except GithubException as ex: print(f"error al sincronizar: {ex}", file=sys.stderr) else: - if alu_repo.has_reviewer(): + if TODO_OK_REGEX.search(output): # Insertar, por el momento, la URL del repositorio. # TODO: insertar URL para un pull request si es el primer Todo OK. message = "Esta entrega fue importada a:" diff --git a/main.py b/main.py index 0b1383b..3e9d991 100644 --- a/main.py +++ b/main.py @@ -145,7 +145,8 @@ def oauth_credentials(): def post(): # Leer valores del formulario. try: - validate_captcha() + if not cfg.test: + validate_captcha() tp = request.form["tp"] files = get_files() body = request.form["body"] or ""