From c4a34bd7b4733be925e4f50fcf750fdba47e029b Mon Sep 17 00:00:00 2001 From: Matthew Hague <Matthew.Hague@rhul.ac.uk> Date: Thu, 20 Mar 2025 10:37:50 +0000 Subject: [PATCH] add(moodle): extend select users to cope with new email format --- moodle/select-users.py | 84 ++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/moodle/select-users.py b/moodle/select-users.py index 595cbee..18456c4 100644 --- a/moodle/select-users.py +++ b/moodle/select-users.py @@ -36,6 +36,8 @@ import os import re import sys +from dataclasses import dataclass + from typing import Dict, List, Optional, Set from selenium import webdriver @@ -100,7 +102,15 @@ class Browser: for c in cookie_jar ] -class LDAPEmailGetter: +@dataclass(frozen=True, eq=True) +class StudentInfo: + email : str + login : Optional[str] + + def login_email(self): + return f"{self.login}@live.rhul.ac.uk" + +class LDAPStudentInfoGetter: def __init__(self, mod_codes : List[str]): self._mod_codes = mod_codes @@ -109,30 +119,26 @@ class LDAPEmailGetter: PASS_CMD, stderr=subprocess.DEVNULL ).decode("ascii").strip() - self._numbers_to_email = None + self._numbers_to_info = None - def lookup(self, number : str) -> Optional[str]: - numbers_to_email = self._get_numbers_to_email() + def lookup(self, number : str) -> Optional[StudentInfo]: + numbers_to_info = self._get_numbers_to_info() + return numbers_to_info.get(number) - if number in numbers_to_email: - return numbers_to_email[number] - else: - return None - - def _get_numbers_to_email(self) -> Dict[str, str]: + def _get_numbers_to_info(self) -> Dict[str, StudentInfo]: """Get student ID num to email in lower case from LDAP""" - if self._numbers_to_email is not None: - return self._numbers_to_email + if self._numbers_to_info is not None: + return self._numbers_to_info - self._numbers_to_email = dict() + self._numbers_to_info = dict() if len(self._mod_codes) == 0: - return self._numbers_to_email + return self._numbers_to_info if self._username is None: - return self._numbers_to_email + return self._numbers_to_info if self._password is None: - return self._numbers_to_email + return self._numbers_to_info try: with ldap3.Connection( @@ -143,19 +149,22 @@ class LDAPEmailGetter: mod_filter = f"(memberOf=CN=MG_STU_RC_{mod_code}," \ "OU=prog-section,OU=student,OU=Distribution Lists," \ "OU=MIIS Managed,DC=cc,DC=rhul,DC=local)" - student_attrs = ["mail", "extensionAttribute3"] + student_attrs = ["mail", "extensionAttribute3", "extensionAttribute2"] conn.search(LDAP_BASE, mod_filter, attributes=student_attrs) for student in conn.entries: - self._numbers_to_email[student.extensionAttribute3.value] \ - = student.mail.value.lower() + self._numbers_to_info[student.extensionAttribute3.value] \ + = StudentInfo( + student.mail.value.lower(), + student.extensionAttribute2.value.lower() + ) except Exception as e: print("WARNING: LDAP connection failed", e) - return self._numbers_to_email + return self._numbers_to_info def select_users( - browser : Browser, emails : Set[str], page_url : str + browser : Browser, info : Set[StudentInfo], page_url : str ): """Select the users on the given page :param browser: a logged in Moodle browser object @@ -180,41 +189,46 @@ def select_users( email_nodes = browser.driver.find_elements(By.CSS_SELECTOR, email_css) + to_check_emails = set(i.email for i in info) \ + | set(i.login_email() for i in info if i.login) checked_emails = set() for email_node in email_nodes: email = email_node.text.lower() - if email in emails: + if email in to_check_emails: email_node.find_element( By.XPATH, "parent::*//input[@type='checkbox']" ).click() checked_emails.add(email) print(len(checked_emails), "emails selected") - for email in emails - checked_emails: - print("WARNING: did not select", email) - -def get_emails(mod_codes : List[str], id_file_csv : str): + for i in info: + if ( + i.email not in checked_emails + and i.login_email() not in checked_emails + ): + print("WARNING: did not select", i.email) + +def get_info(mod_codes : List[str], id_file_csv : str) -> Set[StudentInfo]: """Returns emails in lower case""" - emails = set() - email_getter = LDAPEmailGetter(mod_codes) + info = set() + info_getter = LDAPStudentInfoGetter(mod_codes) with open(id_file_csv) as f: for row in csv.DictReader(f): id = row["ID"].lower() if "@" in id: - emails.add(id) + info.add(StudentInfo(id, None)) elif re.match(r"\d+", id): - email = email_getter.lookup(id) - if email is not None: - emails.add(email) + student_info = info_getter.lookup(id) + if student_info is not None: + info.add(student_info) else: print("WARNING: could not look up", id) else: print("WARNING: unrecognised ID format", id) - return emails - + return info def main(argv): """Go, go, go!""" @@ -228,7 +242,7 @@ def main(argv): id_file_csv = argv[1] assignment_page_url = argv[2] - emails = get_emails(mod_codes, id_file_csv) + emails = get_info(mod_codes, id_file_csv) with Browser() as browser: browser.login() -- GitLab