autograder.cli.users.upsert-file

Upsert server users from a TSV file.

usage: python3 -m autograder.cli.users.upsert-file [-h] [--version]
                                                   [--server SERVER]
                                                   [--user USER] [--pass PASS]
                                                   [--dry-run]
                                                   [--skip-inserts]
                                                   [--skip-updates]
                                                   [--send-emails]
                                                   [--testing-mode]
                                                   PATH

Upsert server users from a TSV file.

positional arguments:
    PATH             Path to a TSV file where each line contains up to seven
                     columns: [email, pass, name, role, course, course-role,
                     lms-id]. Only the email is required. Leading and trailing
                     whitespace is stripped from all fields, including pass.
                     If pass is empty, a password will be randomly generated
                     and emailed to the user.

options:
    -h, --help       show this help message and exit
    --version        show program's version number and exit

command-specific options:
    --server SERVER  The URL of the autograder server to communicate with.
    --user USER      The email of the user making this request.
    --pass PASS      The password of the user making this request.
    --dry-run        Do not commit/finalize the operation, just do all the
                     steps and state what the result would look like.
    --skip-inserts   Skip insert operations.
    --skip-updates   Skip update operations.
    --send-emails    Send any relevant emails to users affected by this
                     operation (e.g., a user being enrolled in a course).

testing options:
    --testing-mode   Run as if a test is being run (default: False).
  1# pylint: disable=invalid-name
  2
  3"""
  4Upsert server users from a TSV file.
  5"""
  6
  7import argparse
  8import sys
  9import typing
 10
 11import edq.util.hash
 12
 13import autograder.api.config
 14import autograder.model.user
 15import autograder.api.users.upsert
 16import autograder.cli.parser
 17import autograder.util.load
 18
 19API_PARAMS: typing.List[autograder.api.config.APIParam] = [
 20    autograder.api.config.PARAM_SERVER,
 21    autograder.api.config.PARAM_USER_EMAIL,
 22    autograder.api.config.PARAM_USER_PASS,
 23
 24    autograder.api.config.PARAM_DRY_RUN,
 25    autograder.api.config.PARAM_SKIP_INSERTS,
 26    autograder.api.config.PARAM_SKIP_UPDATES,
 27
 28    autograder.api.config.PARAM_SEND_EMAILS,
 29
 30    autograder.api.config.PARAM_RAW_SERVER_USERS,
 31]
 32
 33def run_cli(args: argparse.Namespace) -> int:
 34    """ Run the CLI. """
 35
 36    config = args._config_info.application_config
 37    config.raw_server_users = _load_users(args.path)
 38
 39    result = autograder.api.users.upsert.send(config, exit_on_error = True)
 40    print(edq.util.json.dumps(result, indent = 4))
 41
 42    return 0
 43
 44def _load_users(path: str) -> typing.List[typing.Dict[str, str]]:
 45    """ Load raw user entries from a TSV file. """
 46
 47    users = []
 48
 49    rows = autograder.util.load.load_tsv(path, 7)
 50    for (lineno, row) in enumerate(rows):
 51        email = row.pop(0)
 52
 53        password = ''
 54        if (len(row) > 0):
 55            password = row.pop(0)
 56            if (password != ''):
 57                password = edq.util.hash.sha256_hex(password)
 58
 59        name = ''
 60        if (len(row) > 0):
 61            name = row.pop(0)
 62
 63        role = 'user'
 64        if (len(row) > 0):
 65            role = row.pop(0)
 66            role = role.lower()
 67
 68        if (not autograder.model.user.ServerRole.has_value(role)):
 69            raise ValueError(f"File ('{path}') line ({lineno}) has an invalid role '{role}'.")
 70
 71        course = ''
 72        if (len(row) > 0):
 73            course = row.pop(0)
 74
 75        course_role = 'unknown'
 76        if (len(row) > 0):
 77            course_role = row.pop(0)
 78            course_role = course_role.lower()
 79
 80        if (not autograder.model.user.CourseRole.has_value(course_role)):
 81            raise ValueError(f"File ('{path}') line ({lineno}) has an invalid course role '{course_role}'.")
 82
 83        course_lms_id = ''
 84        if (len(row) > 0):
 85            course_lms_id = row.pop(0)
 86
 87        users.append({
 88            'email': email,
 89            'pass': password,
 90            'name': name,
 91            'role': role,
 92            'course': course,
 93            'course-role': course_role,
 94            'course-lms-id': course_lms_id,
 95        })
 96
 97    return users
 98
 99def main() -> int:
100    """ Get a parser, parse the args, and call run. """
101
102    return run_cli(_get_parser().parse_args())
103
104def _get_parser() -> argparse.ArgumentParser:
105    """ Get a parser for this operation. """
106
107    parser = autograder.cli.parser.get_parser(
108        __doc__.strip(),
109        API_PARAMS)
110
111    parser.add_argument('path', metavar = 'PATH',
112        action = 'store', type = str,
113        help = 'Path to a TSV file where each line contains up to seven columns:'
114                + ' [email, pass, name, role, course, course-role, lms-id].'
115                + ' Only the email is required. Leading and trailing whitespace is stripped'
116                + ' from all fields, including pass. If pass is empty, a password will be'
117                + ' randomly generated and emailed to the user.')
118
119    return parser
120
121if (__name__ == '__main__'):
122    sys.exit(main())
def run_cli(args: argparse.Namespace) -> int:
34def run_cli(args: argparse.Namespace) -> int:
35    """ Run the CLI. """
36
37    config = args._config_info.application_config
38    config.raw_server_users = _load_users(args.path)
39
40    result = autograder.api.users.upsert.send(config, exit_on_error = True)
41    print(edq.util.json.dumps(result, indent = 4))
42
43    return 0

Run the CLI.

def main() -> int:
100def main() -> int:
101    """ Get a parser, parse the args, and call run. """
102
103    return run_cli(_get_parser().parse_args())

Get a parser, parse the args, and call run.