Compare commits
1 Commits
feat/group
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ac29a6d89 |
|
|
@ -35,8 +35,17 @@ tea = Tea() # lazy loader
|
|||
|
||||
|
||||
@app.command("export-users", help="export users from canvas to csv file")
|
||||
def export_users_to_csv(output_file: Path = Argument("students.csv")) -> None:
|
||||
tea.pot.canvas.export_users_to_csv(output_file)
|
||||
def export_users_to_csv(
|
||||
output_file: Path = Argument("students.csv"),
|
||||
group_prefix: str = Option(
|
||||
"", "--group", help="export members in matched canvas group set"
|
||||
),
|
||||
) -> None:
|
||||
try:
|
||||
tea.pot.canvas.export_users_to_csv(output_file, group_prefix=group_prefix)
|
||||
except ValueError as e:
|
||||
logger.error(e)
|
||||
raise Exit(code=1)
|
||||
|
||||
|
||||
@app.command(
|
||||
|
|
|
|||
|
|
@ -16,19 +16,19 @@ class Settings(BaseSettings):
|
|||
canvas_course_id: int = 0
|
||||
|
||||
# gitea
|
||||
gitea_domain_name: str = "focs.ji.sjtu.edu.cn"
|
||||
gitea_domain_name: str = "focs.gc.sjtu.edu.cn"
|
||||
gitea_suffix: str = "/git"
|
||||
gitea_access_token: str = ""
|
||||
gitea_org_name: str = ""
|
||||
gitea_debug: bool = False
|
||||
|
||||
# git
|
||||
git_host: str = "ssh://git@focs.ji.sjtu.edu.cn:2222"
|
||||
git_host: str = "ssh://git@focs.gc.sjtu.edu.cn:2222"
|
||||
repos_dir: str = "./repos"
|
||||
default_branch: str = "master"
|
||||
|
||||
# mattermost
|
||||
mattermost_domain_name: str = "focs.ji.sjtu.edu.cn"
|
||||
mattermost_domain_name: str = "focs.gc.sjtu.edu.cn"
|
||||
mattermost_suffix: str = "/mm"
|
||||
mattermost_access_token: str = ""
|
||||
mattermost_team: str = ""
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ def generate_title_and_comment(
|
|||
f"Generated at {now} from [Gitea Actions #{run_number}]({action_link}), "
|
||||
f"commit {commit_hash}, "
|
||||
f"triggered by @{submitter}, "
|
||||
f"run ID [`{run_id}`](https://focs.ji.sjtu.edu.cn/joj-mon/d/{settings.gitea_org_name}?var-Filters=RunID%7C%3D%7C{run_id}).\n"
|
||||
f"run ID [`{run_id}`](https://focs.gc.sjtu.edu.cn/joj-mon/d/{settings.gitea_org_name}?var-Filters=RunID%7C%3D%7C{run_id}).\n"
|
||||
"Powered by [JOJ3](https://github.com/joint-online-judge/JOJ3) and "
|
||||
"[Joint-Teapot](https://github.com/BoYanZh/Joint-Teapot) with ❤️.\n"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import os
|
|||
import re
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
from typing import Dict, List, Tuple, cast
|
||||
|
||||
from canvasapi import Canvas as PyCanvas
|
||||
from canvasapi.assignment import Assignment
|
||||
from canvasapi.group import GroupCategory
|
||||
from canvasapi.user import User
|
||||
from patoolib import extract_archive
|
||||
from patoolib.util import PatoolError
|
||||
|
|
@ -86,7 +87,73 @@ Teaching Team"""
|
|||
print(f"Subject: [{settings.gitea_org_name}] Important: wrong Canvas email")
|
||||
print(f"Body:\n{SAMPLE_EMAIL_BODY}")
|
||||
|
||||
def export_users_to_csv(self, filename: Path) -> None:
|
||||
def _get_group_set_by_prefix(self, group_prefix: str) -> GroupCategory:
|
||||
group_sets = list(self.course.get_group_categories())
|
||||
exact_match = first(group_sets, lambda group_set: group_set.name == group_prefix)
|
||||
if exact_match is not None:
|
||||
return cast(GroupCategory, exact_match)
|
||||
matched_group_sets = [
|
||||
group_set
|
||||
for group_set in group_sets
|
||||
if group_set.name.startswith(group_prefix)
|
||||
]
|
||||
if len(matched_group_sets) == 0:
|
||||
raise ValueError(
|
||||
f'Canvas group set with prefix "{group_prefix}" not found'
|
||||
)
|
||||
if len(matched_group_sets) > 1:
|
||||
matched_names = ", ".join(group_set.name for group_set in matched_group_sets)
|
||||
raise ValueError(
|
||||
f'Multiple Canvas group sets match prefix "{group_prefix}": {matched_names}'
|
||||
)
|
||||
return matched_group_sets[0]
|
||||
|
||||
def _parse_group_name(self, group_set_name: str, group_name: str) -> Tuple[int, str]:
|
||||
match = re.fullmatch(rf"{re.escape(group_set_name)}\s*(\d+)", group_name)
|
||||
if match is None:
|
||||
raise ValueError(
|
||||
f'Canvas group "{group_name}" in group set "{group_set_name}" '
|
||||
+ f'should be named as "{group_set_name} <number>"'
|
||||
)
|
||||
group_number = int(match.group(1))
|
||||
return group_number, f"{group_set_name}{group_number:02}"
|
||||
|
||||
def _get_group_users_csv_rows(self, group_prefix: str) -> Tuple[str, List[List[str]]]:
|
||||
group_set = self._get_group_set_by_prefix(group_prefix)
|
||||
users_by_id: Dict[int, User] = {user.id: user for user in self.users}
|
||||
rows = []
|
||||
groups = []
|
||||
for group in group_set.get_groups():
|
||||
group_number, normalized_group_name = self._parse_group_name(
|
||||
group_set.name, group.name
|
||||
)
|
||||
groups.append((group_number, normalized_group_name, group))
|
||||
for _, normalized_group_name, group in sorted(groups, key=lambda item: item[0]):
|
||||
group_rows = []
|
||||
for membership in group.get_memberships():
|
||||
user = users_by_id.get(membership.user_id)
|
||||
if user is None:
|
||||
logger.warning(
|
||||
f"user with id {membership.user_id} found in {group.name} "
|
||||
+ "but not found in course users"
|
||||
)
|
||||
continue
|
||||
group_rows.append(
|
||||
[user.name, user.sis_id, user.login_id, normalized_group_name]
|
||||
)
|
||||
rows.extend(sorted(group_rows))
|
||||
return group_set.name, rows
|
||||
|
||||
def export_users_to_csv(self, filename: Path, group_prefix: str = "") -> None:
|
||||
if group_prefix:
|
||||
group_set_name, rows = self._get_group_users_csv_rows(group_prefix)
|
||||
with open(filename, mode="w", newline="") as file:
|
||||
writer = csv.writer(file)
|
||||
for row in rows:
|
||||
writer.writerow(row)
|
||||
logger.info(f'Users in group set "{group_set_name}" exported to {filename}')
|
||||
return
|
||||
|
||||
with open(filename, mode="w", newline="") as file:
|
||||
writer = csv.writer(file)
|
||||
for user in self.users:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user