Skip to content
Commits on Source (3)
[metadata]
name = teach_auto
version = attr: teach_auto.__version__
author = Cyril Matthey-Doret
author_email = cyril.matthey-doret@epfl.ch
license = GPLv3
description = Utilities to automate classroom management with Gitlab and Renku.
keywords = automation, git, gitlab, teaching
url = https://renkulab.io/gitlab/learn-renku/teaching-on-renku/advanced-teaching-automation
classifiers =
Development Status :: 3 - Alpha
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Intended Audience :: Education
Topic :: Software Development :: Version Control :: Git
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
\ No newline at end of file
......@@ -5,8 +5,7 @@ with open("requirements.txt", "r") as f:
setup(
name="teach_auto",
version="0.1",
packages=find_packages(),
install_requires=REQUIREMENTS,
license="GPLv3",
entry_points={"console_scripts": ["teach-auto=teach_auto.cli:cli"]},
)
from operator import inv
import click
from teach_auto import __version__
from teach_auto.clone_forks import clone_forks
from teach_auto.collect_forks import collect_forks
from teach_auto.create_groups import create_groups
from teach_auto.invite_students import invite_students
from teach_auto.send_feedback import send_feedback
@click.group()
@click.version_option(version=__version__)
def cli():
"""
teach-auto: Classroom automation utilities for Gitlab + Renku.
"""
...
cli.add_command(clone_forks, name="clone-forks")
cli.add_command(collect_forks, name="collect-forks")
cli.add_command(create_groups, name="create-groups")
cli.add_command(invite_students, name="invite-students")
cli.add_command(send_feedback, name="send-feedback")
......@@ -23,10 +23,15 @@ def clone_fork(url: str, out_path: Path, commit: str):
@click.command()
@click.argument("out_dir", type=click.Path(file_okay=False, exists=False))
@click.argument("forks", type=click.File(mode="r"), default=sys.stdin)
def main(out_dir, forks, token):
def clone_forks(out_dir, forks, token):
"""Clone all forks from input JSON into target directory. The input json should be produced by collect-forks."""
forks = json.load(forks)
for fork in forks:
url, group, commit = [fork.get(key) for key in ["url", "group", "commit"]]
path = Path(out_dir) / group
clone_fork(url, path, commit)
if __name__ == "__main__":
clone_forks()
......@@ -15,8 +15,8 @@ import requests
import click
from datetime import datetime
import pytz
from class_auto.common_requests import parse_repo_url, get_project_id
from class_auto.utils import ingest_token
from teach_auto.common_requests import parse_repo_url, get_project_id
from teach_auto.utils import ingest_token
def validate_iso_date(date: str) -> str:
......@@ -139,7 +139,13 @@ def format_fork_metadata(
type=click.Path(exists=True),
help="Path to a file containing the Gitlab API token. If not provided, you will be prompted for the token.",
)
def main(repo_url, token_path, deadline=None):
@click.option(
"--group-only",
is_flag=True,
help="Only consider forks belonging to a group (and not an individual user).",
)
def collect_forks(repo_url, token_path, deadline=None, group_only=False):
"""Gather metadata about all forks of input project into a JSON file."""
token = ingest_token(token_path)
# Check for valid deadline format
if deadline is not None:
......@@ -149,7 +155,8 @@ def main(repo_url, token_path, deadline=None):
header = {"PRIVATE-TOKEN": token}
forks = collect_forks(repo_url, header)
# Only keep those which belong to a group
forks = filter_group_forks(forks)
if group_only:
forks = filter_group_forks(forks)
# Reformat metadata for convenience
meta = map(lambda f: format_fork_metadata(f, header, deadline), forks)
......@@ -157,4 +164,4 @@ def main(repo_url, token_path, deadline=None):
if __name__ == "__main__":
main()
collect_forks()
......@@ -57,7 +57,7 @@ def generate_student_code(first_name: str, last_name: str) -> str:
type=click.Path(exists=True),
help="Path to a file containing the Gitlab API token. If not provided, you will be prompted for the token.",
)
def main(student_table, parent_url, token_path):
def create_groups(student_table, parent_url, token_path):
"""Given a parent-group URL and a CSV file of students exported from Moodle,
create one private subgroup for each student in the parent group and invite students
in their personal group. The student table must contain columns: "Email address", "First name"
......@@ -85,4 +85,4 @@ def main(student_table, parent_url, token_path):
if __name__ == "__main__":
main()
create_groups()
......@@ -5,8 +5,8 @@ import re
from typing import Dict
import requests
import click
from class_auto.common_requests import parse_repo_url, get_group_id
from class_auto.utils import ingest_token
from teach_auto.common_requests import parse_repo_url, get_group_id
from teach_auto.utils import ingest_token
def invite_student_email(
......@@ -52,7 +52,7 @@ def find_email(line: str) -> str:
type=click.Path(exists=True),
help="Path to a file containing the Gitlab API token. If not provided, you will be prompted for the token.",
)
def main(emails_file, group_url, token_path):
def invite_students(emails_file, group_url, token_path):
"""
Send invitations to join input gitlab group to all emails in the emails_file.
The file should have one email per line."""
......@@ -71,4 +71,4 @@ def main(emails_file, group_url, token_path):
if __name__ == "__main__":
main()
invite_students()
......@@ -15,8 +15,8 @@ import requests
from pathlib import Path
import click
import pandas as pd
from class_auto.common_requests import parse_repo_url
from class_auto.utils import ingest_token
from teach_auto.common_requests import parse_repo_url
from teach_auto.utils import ingest_token
FEEDBACK_TEMPLATE = """
......@@ -114,7 +114,7 @@ def gather_fill_data(fork: Dict, feedback_csv: Optional[Path] = None) -> Dict:
@click.option(
"--dry-run", is_flag=True, help="Print feedback to stdout instead of opening issues"
)
def main(forks, feedback_csv, token_path, template, dry_run):
def send_feedback(forks, feedback_csv, token_path, template, dry_run):
"""
Send feedback to repositories in input JSON by opening issues.
"""
......@@ -149,4 +149,4 @@ def main(forks, feedback_csv, token_path, template, dry_run):
if __name__ == "__main__":
main()
send_feedback()