Source code for ucsschool.importer.frontend.parse_user_import_cmdline

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Univention UCS@school
# Copyright 2016-2021 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.

"""
Default command line frontend for import.
"""

import os
from argparse import ArgumentParser
from typing import Any, Dict

from six import string_types


[docs]class ParseUserImportCmdline(object): """ Setup a command line frontend. """ def __init__(self): """ Setup the parser. Override to add more arguments or change the defaults. """ self.args = None # TODO: read defaults from user_import_defaults.json self.defaults = dict( dry_run=False, logfile=None, no_delete=False, school=None, source_uid=None, user_role=None, verbose=False, ) self.parser = ArgumentParser(description="UCS@school import tool") self.parser.add_argument( "-c", "--conffile", help="Configuration file to use (see /usr/share/doc/ucs-school-import for an explanation on " "configuration file stacking).", ) self.parser.add_argument( "-i", "--infile", dest="infile", help="CSV file with users to import (shortcut for --set input:filename=...).", ) self.parser.add_argument( "-l", "--logfile", help="Write to additional logfile (shortcut for --set logfile=...)." ) self.parser.add_argument( "--set", dest="settings", metavar="KEY=VALUE", nargs="*", help="Overwrite setting(s) from the configuration file. Use ':' in key to set nested values " "(e.g. 'scheme:email=...').", ) self.parser.add_argument( "-m", "--no-delete", dest="no_delete", action="store_true", help="Do not delete user objects if actions are automatically determined. User objects in " "input data are only added/modified. Please note: if user objects in input data are " "explicitly marked for deletion (__action=D), the objects will be still deleted! " "(shortcut for --set no_delete=...) [default: %(default)s].", ) self.parser.add_argument( "-n", "--dry-run", dest="dry_run", action="store_true", help="Dry-run: don't actually commit changes to LDAP (shortcut for --set dry_run=...) " "[default: %(default)s].", ) self.parser.add_argument( "--source_uid", help="The ID of the source database (shortcut for --set source_uid=...) [mandatory either " "here or in the configuration file].", ) self.parser.add_argument( "-s", "--school", help="Name of school. Set only, if the source data does not contain the name of the school " "and all users are from one school (shortcut for --set school=...) [default: " "%(default)s].", ) self.parser.add_argument( "-u", "--user_role", help="Set this, if the source data contains users with only one role " "<student|staff|teacher|teacher_and_staff> (shortcut for --set user_role=...) " "[default: %(default)s].", ) self.parser.add_argument( "-v", "--verbose", action="store_true", help="Enable debugging output on the console [default: %(default)s].", ) self.parser.set_defaults(**self.defaults)
[docs] def parse_cmdline(self): """ Parse the command line. :return: the object with the parsed arguments assigned to attributes :rtype: argparse.Namespace """ self.args = self.parser.parse_args() if ( hasattr(self.args, "user_role") and self.args.user_role and self.args.user_role not in ["none", "student", "staff", "teacher", "teacher_and_staff"] ): self.parser.error( "Invalid user role. Must be one of none, student, staff, teacher, teacher_and_staff." ) if hasattr(self.args, "user_role") and self.args.user_role == "none": self.args.user_role = None settings = {} if hasattr(self.args, "infile") and self.args.infile: if not os.access(self.args.infile, os.R_OK): self.parser.error("Cannot read input data file '{}'.".format(self.args.infile)) settings["input"] = {"filename": self.args.infile} if hasattr(self.args, "settings") and self.args.settings: for setting in self.args.settings: try: k, v = setting.split("=") except ValueError: self.parser.error("Invalid setting '{}'.".format(setting)) start, symb, end = k.rpartition(":") # try to convert to correct type if v.lower() == "true": v = True elif v.lower() == "false": v = False else: try: v = int(v) except ValueError: pass # support nested settings while symb: k = start v = {end: v} start, symb, end = start.rpartition(":") settings[k] = v self.args.settings = self.apply_quirks(settings) # only set shortcuts if they were set by the user for k, v in self.defaults.items(): if getattr(self.args, k) != v: self.args.settings[k] = getattr(self.args, k) return self.args
[docs] def apply_quirks(self, settings): # type: (Dict[str, Any]) -> Dict[str, Any] """ Apply modifications that cannot be done automatically. """ # A default for config["disabled_checks"] does not exist in any # official config file, thus setting the value type in # ReadOnlyDict._recursive_typed_update() will not work. Converting the # string from the cmdline to a list here. disabled_checks = settings.get("disabled_checks") if isinstance(disabled_checks, string_types): settings["disabled_checks"] = [s.strip() for s in disabled_checks.split(",")] return settings