diff --git a/joint_teapot/app.py b/joint_teapot/app.py index 17aae24..6cbe4eb 100644 --- a/joint_teapot/app.py +++ b/joint_teapot/app.py @@ -172,14 +172,14 @@ def upload_assignment_grades(assignments_dir: Path, assignment_name: str) -> Non @app.command( - "create-channels-on-mm", + "create-group-channels-on-mm", help="create channels for student groups according to group information on" " gitea", ) -def create_channels_on_mm( +def create_group_channels_on_mm( prefix: str = Option(""), suffix: str = Option(""), - invite_teaching_team: bool = Option(False), + invite_teaching_team: bool = Option(True), ) -> None: groups = { group_name: members @@ -195,6 +195,16 @@ def create_channels_on_mm( tea.pot.mattermost.create_channels_for_groups(groups, suffix, invite_teaching_team) +@app.command( + "create-personal-channels-on-mm", + help="create channels for every student", +) +def create_personal_channels_on_mm( + invite_teaching_team: bool = Option(True), +) -> None: + tea.pot.create_channels_for_individuals(invite_teaching_team) + + @app.command( "create-webhooks-for-mm", help="create a pair of webhooks on gitea and mm for all student groups on gitea, " diff --git a/joint_teapot/config.py b/joint_teapot/config.py index 2c0bbb1..3855f67 100644 --- a/joint_teapot/config.py +++ b/joint_teapot/config.py @@ -10,7 +10,7 @@ class Settings(BaseSettings): """ # canvas - canvas_domain_name: str = "jicanvas.com" + canvas_domain_name: str = "oc.sjtu.edu.cn" canvas_suffix: str = "/" canvas_access_token: str = "" canvas_course_id: int = 0 diff --git a/joint_teapot/teapot.py b/joint_teapot/teapot.py index e1a946d..e838042 100644 --- a/joint_teapot/teapot.py +++ b/joint_teapot/teapot.py @@ -13,7 +13,7 @@ _T = TypeVar("_T") def for_all_methods( - decorator: Callable[[Callable[[_T], _T]], Any] + decorator: Callable[[Callable[[_T], _T]], Any], ) -> Callable[[_T], _T]: @functools.wraps(decorator) def decorate(cls: Any) -> Any: @@ -204,6 +204,13 @@ class Teapot: f"{commit_count} commit(s), {issue_count} issue(s)" ) + def create_channels_for_individuals( + self, invite_teaching_teams: bool = True + ) -> None: + return self.mattermost.create_channels_for_individuals( + self.canvas.students, invite_teaching_teams + ) + if __name__ == "__main__": teapot = Teapot() diff --git a/joint_teapot/utils/main.py b/joint_teapot/utils/main.py index 669fd30..9545fdb 100644 --- a/joint_teapot/utils/main.py +++ b/joint_teapot/utils/main.py @@ -30,8 +30,8 @@ def percentile( def default_repo_name_convertor(user: User) -> str: - login_id, name = user.login_id, user.name + sis_id, name = user.sis_id, user.name eng = re.sub("[\u4e00-\u9fa5]", "", name) eng = eng.replace(",", "") eng = eng.title().replace(" ", "").replace("\xa0", "") - return f"{eng}{login_id}" + return f"{eng}{sis_id}" diff --git a/joint_teapot/workers/canvas.py b/joint_teapot/workers/canvas.py index fa450c0..2c818d4 100644 --- a/joint_teapot/workers/canvas.py +++ b/joint_teapot/workers/canvas.py @@ -1,4 +1,5 @@ import os +import re from glob import glob from typing import cast @@ -33,6 +34,10 @@ class Canvas: types = ["student"] def patch_student(student: User) -> User: + student.name = re.sub( + r"[^\x00-\x7F]+", "", student.name + ).strip() # We only care english name + student.sis_id = student.login_id student.login_id = student.email.split("@")[0] return student diff --git a/joint_teapot/workers/mattermost.py b/joint_teapot/workers/mattermost.py index 260a640..f47f6a3 100644 --- a/joint_teapot/workers/mattermost.py +++ b/joint_teapot/workers/mattermost.py @@ -1,6 +1,7 @@ from typing import Dict, List import focs_gitea +from canvasapi.paginated_list import PaginatedList from mattermostdriver import Driver from joint_teapot.config import settings @@ -47,7 +48,7 @@ class Mattermost: self, groups: Dict[str, List[str]], suffix: str = "", - invite_teaching_team: bool = False, + invite_teaching_team: bool = True, ) -> None: for group_name, members in groups.items(): channel_name = group_name + suffix @@ -75,6 +76,12 @@ class Mattermost: logger.warning( f"User {member} is not found on the Mattermost server" ) + self.endpoint.posts.create_post( + { + "channel_id": channel["id"], + "message": f"User {member} is not found on the Mattermost server", + } + ) continue # code for adding student to mm, disabled since there is no need to do that # try: @@ -88,6 +95,75 @@ class Mattermost: ) except Exception: logger.warning(f"User {member} is not in the team") + self.endpoint.posts.create_post( + { + "channel_id": channel["id"], + "message": f"User {member} is not in the team", + } + ) + logger.info(f"Added member {member} to channel {channel_name}") + + def create_channels_for_individuals( + self, + students: PaginatedList, + invite_teaching_team: bool = True, + ) -> None: + for student in students: + display_name = student.name + channel_name = student.sis_id + try: + channel = self.endpoint.channels.create_channel( + { + "team_id": self.team["id"], + "name": channel_name, + "display_name": display_name, + "type": "P", # create private channels + } + ) + logger.info( + f"Added channel {display_name} ({channel_name}) to Mattermost" + ) + except Exception as e: + logger.warning( + f"Error when creating channel {channel_name}: {e} Perhaps channel already exists?" + ) + continue + members = [student.login_id] + if invite_teaching_team: + members.extend(settings.mattermost_teaching_team) + for member in members: + try: + mmuser = self.endpoint.users.get_user_by_username(member) + except Exception: + logger.warning( + f"User {member} is not found on the Mattermost server" + ) + self.endpoint.posts.create_post( + { + "channel_id": channel["id"], + "message": f"User {member} is not found on the Mattermost server", + } + ) + continue + # code for adding student to mm, disabled since there is no need to do that + # try: + # mmuser = self.endpoint.users.create_user({'email':f"{member}@sjtu.edu.cn", 'username':member, auth_service:"gitlab"}) + # except e: + # logger.error(f"Error creating user {member}") + # continue + try: + self.endpoint.channels.add_user( + channel["id"], {"user_id": mmuser["id"]} + ) + except Exception: + logger.warning(f"User {member} is not in the team") + self.endpoint.posts.create_post( + { + "channel_id": channel["id"], + "message": f"User {member} is not in the team", + } + ) + logger.info(f"Added member {member} to channel {channel_name}") def create_webhooks_for_repos(self, repos: List[str], gitea: Gitea) -> None: