From e2d6c50f231f636fed362be37e7bf3319fc5d6b8 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Fri, 19 Mar 2021 11:05:22 +0100 Subject: Format - black: ishtar_common --- ishtar_common/data_importer.py | 1019 +++++++++++++++++++++++----------------- 1 file changed, 586 insertions(+), 433 deletions(-) (limited to 'ishtar_common/data_importer.py') diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index 108e52d7b..b777850c9 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -39,7 +39,7 @@ from django.utils.translation import ugettext_lazy as _ from ishtar_common.utils import debug_line_no, get_all_field_names, update_data -NEW_LINE_BREAK = '#####@@@#####' +NEW_LINE_BREAK = "#####@@@#####" RE_FILTER_CEDEX = re.compile("(.*) *(?: *CEDEX|cedex|Cedex|Cédex|cédex *\d*)") @@ -47,25 +47,42 @@ RE_FILTER_CEDEX = re.compile("(.*) *(?: *CEDEX|cedex|Cedex|Cédex|cédex *\d*)") def post_importer_action(func): def wrapper(self, context, value): return func(self, context, value) - wrapper.importer_trigger = 'post' + + wrapper.importer_trigger = "post" return wrapper def pre_importer_action(func): def wrapper(self, context, value): return func(self, context, value) - wrapper.importer_trigger = 'pre' + + wrapper.importer_trigger = "pre" return wrapper class ImportFormater(object): - def __init__(self, field_name, formater=None, required=True, through=None, - through_key=None, through_dict=None, - through_unicity_keys=None, duplicate_fields=None, regexp=None, - regexp_formater_args=None, force_value=None, - post_processing=False, concat=False, concat_str=False, - comment="", force_new=None, export_field_name=None, - value_format=None, label=""): + def __init__( + self, + field_name, + formater=None, + required=True, + through=None, + through_key=None, + through_dict=None, + through_unicity_keys=None, + duplicate_fields=None, + regexp=None, + regexp_formater_args=None, + force_value=None, + post_processing=False, + concat=False, + concat_str=False, + comment="", + force_new=None, + export_field_name=None, + value_format=None, + label="", + ): self.field_name = field_name if export_field_name: self.export_field_name = export_field_name @@ -117,28 +134,33 @@ class ImportFormater(object): def report_error(self, *args): return - def init(self, vals, output=None, choose_default=False, - import_instance=None, user=None): + def init( + self, vals, output=None, choose_default=False, import_instance=None, user=None + ): try: lst = iter(self.formater) except TypeError: lst = [self.formater] for formater in lst: if formater: - formater.check(vals, output, self.comment, - choose_default=choose_default, - import_instance=import_instance, - user=user) + formater.check( + vals, + output, + self.comment, + choose_default=choose_default, + import_instance=import_instance, + user=user, + ) def post_process(self, obj, context, value, owner=None): raise NotImplemented() class ImporterError(Exception): - STANDARD = 'S' - HEADER = 'H' + STANDARD = "S" + HEADER = "H" - def __init__(self, message, type='S'): + def __init__(self, message, type="S"): self.msg = message self.type = type @@ -148,13 +170,20 @@ class ImporterError(Exception): class Formater(object): def __init__(self, *args, **kwargs): - self.db_target = kwargs.get('db_target', None) + self.db_target = kwargs.get("db_target", None) def format(self, value): return value - def check(self, values, output=None, comment='', choose_default=False, - import_instance=None, user=None): + def check( + self, + values, + output=None, + comment="", + choose_default=False, + import_instance=None, + user=None, + ): return def init_db_target(self, user=None): @@ -163,17 +192,16 @@ class Formater(object): def _base_target_filter(self, user=None): # set for all users q_or = ( - Q(associated_import__isnull=True) & - Q(associated_user__isnull=True) & - Q(associated_group__isnull=True) + Q(associated_import__isnull=True) + & Q(associated_user__isnull=True) + & Q(associated_group__isnull=True) ) - if hasattr(self, 'import_instance') and self.import_instance: + if hasattr(self, "import_instance") and self.import_instance: # set for current import q_or = q_or | Q(associated_import=self.import_instance) if self.import_instance.associated_group: # set for associated group - q_or = q_or | Q( - associated_group=self.import_instance.associated_group) + q_or = q_or | Q(associated_group=self.import_instance.associated_group) if user: # set for current user q_or = q_or | Q(associated_user=user) @@ -192,17 +220,25 @@ class ChoiceChecker(object): def report_new(self, comment): if not self.new_keys: return - msg = "For \"%s\" these new associations have been made:\n" % comment - sys.stderr.write(msg.encode('utf-8')) + msg = 'For "%s" these new associations have been made:\n' % comment + sys.stderr.write(msg.encode("utf-8")) for k in self.new_keys: msg = '"%s";"%s"\n' % (k, self.new_keys[k]) - sys.stderr.write(msg.encode('utf-8')) + sys.stderr.write(msg.encode("utf-8")) class UnicodeFormater(Formater): - def __init__(self, max_length=None, clean=False, re_filter=None, - notnull=False, prefix='', db_target=None, - import_instance=None, many_split=None): + def __init__( + self, + max_length=None, + clean=False, + re_filter=None, + notnull=False, + prefix="", + db_target=None, + import_instance=None, + many_split=None, + ): self.max_length = max_length self.db_target = db_target self.clean = clean @@ -217,7 +253,7 @@ class UnicodeFormater(Formater): if type(value) != str: value = str(value.strip()) vals = [] - for v in value.split('\n'): + for v in value.split("\n"): v = v.strip() if v: vals.append(v) @@ -236,9 +272,12 @@ class UnicodeFormater(Formater): return if self.max_length and len(value) > self.max_length: raise ValueError( - _("\"%(value)s\" is too long. The max length is %(length)d " - "characters.") % {'value': value, - 'length': self.max_length}) + _( + '"%(value)s" is too long. The max length is %(length)d ' + "characters." + ) + % {"value": value, "length": self.max_length} + ) if self.notnull and not value: return if value: @@ -249,25 +288,23 @@ class UnicodeFormater(Formater): class BooleanFormater(Formater): def format(self, value): value = value.strip().upper() - if value in ('1', 'OUI', 'VRAI', 'YES', 'TRUE'): + if value in ("1", "OUI", "VRAI", "YES", "TRUE"): return True - if value in ('', '0', 'NON', 'FAUX', 'NO', 'FALSE'): + if value in ("", "0", "NON", "FAUX", "NO", "FALSE"): return False - raise ValueError(_("\"%(value)s\" not equal to yes or no") % { - 'value': value}) + raise ValueError(_('"%(value)s" not equal to yes or no') % {"value": value}) class FloatFormater(Formater): def format(self, value): - value = value.strip().replace(',', '.') - value = value.replace(' ', '') + value = value.strip().replace(",", ".") + value = value.replace(" ", "") if not value: return try: return float(value) except ValueError: - raise ValueError(_("\"%(value)s\" is not a float") % { - 'value': value}) + raise ValueError(_('"%(value)s" is not a float') % {"value": value}) class InseeFormater(Formater): @@ -276,11 +313,12 @@ class InseeFormater(Formater): The syntax "CodeINSEE-Year" is accepted (Ishtar trick) in order to manage old INSEE (year is the date of creation) """ - ERROR = _("\"{value}\" is not an appropriate INSEE code") + + ERROR = _('"{value}" is not an appropriate INSEE code') def format(self, value): value = value.strip() - exp = value.split('-') + exp = value.split("-") code = exp[0] try: int(code) @@ -293,7 +331,7 @@ class InseeFormater(Formater): elif len(exp) == 1: return code try: - datetime.datetime.strptime(exp[1], '%Y') + datetime.datetime.strptime(exp[1], "%Y") except ValueError: raise ValueError(str(self.ERROR).format(value)) return code + "-" + exp[1] @@ -308,8 +346,7 @@ class YearFormater(Formater): value = int(value) assert value > 0 and value < (datetime.date.today().year + 30) except (ValueError, AssertionError): - raise ValueError(_("\"%(value)s\" is not a valid date") % { - 'value': value}) + raise ValueError(_('"%(value)s" is not a valid date') % {"value": value}) return value @@ -322,28 +359,34 @@ class YearNoFuturFormater(Formater): value = int(value) assert value > 0 and value < datetime.date.today().year except (ValueError, AssertionError): - raise ValueError(_("\"%(value)s\" is not a valid date") % { - 'value': value}) + raise ValueError(_('"%(value)s" is not a valid date') % {"value": value}) return value class IntegerFormater(Formater): def format(self, value): value = value.strip() - value = value.replace(' ', '') + value = value.replace(" ", "") if not value: return try: return int(value) except ValueError: - raise ValueError(_("\"%(value)s\" is not an integer") % { - 'value': value}) + raise ValueError(_('"%(value)s" is not an integer') % {"value": value}) class StrChoiceFormater(Formater, ChoiceChecker): - def __init__(self, choices, strict=False, equiv_dict=None, model=None, - cli=False, many_split='', db_target=None, - import_instance=None): + def __init__( + self, + choices, + strict=False, + equiv_dict=None, + model=None, + cli=False, + many_split="", + db_target=None, + import_instance=None, + ): if not equiv_dict: equiv_dict = {} self.choices = list(choices) @@ -395,23 +438,30 @@ class StrChoiceFormater(Formater, ChoiceChecker): def prepare(self, value): return str(value).strip() - def _get_choices(self, comment=''): + def _get_choices(self, comment=""): msgstr = comment + " - " - msgstr += str(_("Choice for \"%s\" is not available. " - "Which one is relevant?\n")) + msgstr += str( + _('Choice for "%s" is not available. ' "Which one is relevant?\n") + ) idx = -1 for idx, choice in enumerate(self.choices): msgstr += "%d. %s\n" % (idx + 1, choice[1]) idx += 2 if self.create: - msgstr += str(_("%d. None of the above - create new")) % idx \ - + "\n" + msgstr += str(_("%d. None of the above - create new")) % idx + "\n" idx += 1 msgstr += str(_("%d. None of the above - skip")) % idx + "\n" return msgstr, idx - def check(self, values, output=None, comment='', choose_default=False, - import_instance=None, user=None): + def check( + self, + values, + output=None, + comment="", + choose_default=False, + import_instance=None, + user=None, + ): self.init_db_target(user) """ @@ -433,7 +483,7 @@ class StrChoiceFormater(Formater, ChoiceChecker): except IntegrityError: pass """ - if (not output or output == 'silent') and not choose_default: + if (not output or output == "silent") and not choose_default: return if self.many_split: new_values = [] @@ -446,7 +496,7 @@ class StrChoiceFormater(Formater, ChoiceChecker): value = self.prepare(value) if value in self.equiv_dict: continue - if output != 'cli' and not choose_default: + if output != "cli" and not choose_default: self.missings.add(value) continue msgstr, idx = self._get_choices(comment) @@ -455,7 +505,7 @@ class StrChoiceFormater(Formater, ChoiceChecker): res = 1 while res not in range(1, idx + 1): msg = msgstr % value - sys.stdout.write(msg.encode('utf-8')) + sys.stdout.write(msg.encode("utf-8")) sys.stdout.write("\n>>> ") res = input() try: @@ -472,14 +522,16 @@ class StrChoiceFormater(Formater, ChoiceChecker): self.new_keys[value] = v elif self.create and res == len(self.choices): self.equiv_dict[value] = self.new(base_value) - self.choices.append((self.equiv_dict[value].pk, - str(self.equiv_dict[value]))) + self.choices.append( + (self.equiv_dict[value].pk, str(self.equiv_dict[value])) + ) self.new_keys[value] = str(self.equiv_dict[value]) else: self.equiv_dict[value] = None if self.equiv_dict[value] and self.db_target: from ishtar_common.models import TargetKey - q = {'target': self.db_target, 'key': value} + + q = {"target": self.db_target, "key": value} query = TargetKey.objects.filter(**q) query = query.filter(self._base_target_filter(user)) if query.count(): @@ -488,29 +540,30 @@ class StrChoiceFormater(Formater, ChoiceChecker): target.is_set = True target.save() else: - q['associated_import'] = import_instance + q["associated_import"] = import_instance with transaction.atomic(): - q['value'] = self.equiv_dict[value] - q['is_set'] = True + q["value"] = self.equiv_dict[value] + q["is_set"] = True try: TargetKey.objects.create(**q) except IntegrityError: pass - if output == 'db' and self.db_target: + if output == "db" and self.db_target: from ishtar_common.models import TargetKey + for missing in self.missings: - q = {'target': self.db_target, 'key': missing} + q = {"target": self.db_target, "key": missing} query = TargetKey.objects.filter(**q) query = query.filter(self._base_target_filter(user)) if query.count(): continue with transaction.atomic(): - q['associated_import'] = import_instance + q["associated_import"] = import_instance try: TargetKey.objects.create(**q) except IntegrityError: pass - if output == 'cli': + if output == "cli": self.report_new(comment) def new(self, value): @@ -525,13 +578,20 @@ class StrChoiceFormater(Formater, ChoiceChecker): if not self.strict: value = slugify(value) if value in self.equiv_dict: - self.match_table[origin_value] = self.equiv_dict[value] or '' + self.match_table[origin_value] = self.equiv_dict[value] or "" return self.equiv_dict[value] class TypeFormater(StrChoiceFormater): - def __init__(self, model, cli=False, defaults=None, many_split=False, - db_target=None, import_instance=None): + def __init__( + self, + model, + cli=False, + defaults=None, + many_split=False, + db_target=None, + import_instance=None, + ): if not defaults: defaults = {} self.create = True @@ -559,20 +619,19 @@ class TypeFormater(StrChoiceFormater): def new(self, value): values = copy.copy(self.defaults) - values['label'] = value - values['txt_idx'] = slugify(value) - if 'order' in get_all_field_names(self.model): + values["label"] = value + values["txt_idx"] = slugify(value) + if "order" in get_all_field_names(self.model): order = 1 - q = self.model.objects.values('order').order_by('-order') + q = self.model.objects.values("order").order_by("-order") if q.count(): - order = q.all()[0]['order'] or 1 - values['order'] = order + order = q.all()[0]["order"] or 1 + values["order"] = order return self.model.objects.create(**values) class DateFormater(Formater): - def __init__(self, date_formats=None, db_target=None, - import_instance=None): + def __init__(self, date_formats=None, db_target=None, import_instance=None): if not date_formats: date_formats = ["%d/%m/%Y"] self.date_formats = date_formats @@ -590,8 +649,7 @@ class DateFormater(Formater): return datetime.datetime.strptime(value, date_format).date() except: continue - raise ValueError(_("\"%(value)s\" is not a valid date") % { - 'value': value}) + raise ValueError(_('"%(value)s" is not a valid date') % {"value": value}) class FileFormater(Formater): @@ -602,31 +660,38 @@ class FileFormater(Formater): if not value: return zp = zipfile.ZipFile(archive) - value = value.strip().replace('\\', '/') - items = value.replace('/', '_').split('.') - base_dir = settings.MEDIA_ROOT + 'imported' + value = value.strip().replace("\\", "/") + items = value.replace("/", "_").split(".") + base_dir = settings.MEDIA_ROOT + "imported" if not os.path.isdir(base_dir): os.mkdir(base_dir) - filename = base_dir + os.sep + \ - ".".join(items[:-1]) + '.' + items[-1] + filename = base_dir + os.sep + ".".join(items[:-1]) + "." + items[-1] try: - with open(filename, 'wb') as f: + with open(filename, "wb") as f: with zp.open(value) as z: f.write(z.read()) - f = open(filename, 'rb') + f = open(filename, "rb") my_file = File(f) # manually set the file size because of an issue with TempFile my_file.size = os.stat(filename).st_size return my_file except KeyError: - raise ValueError(_("\"%(value)s\" is not a valid path for the " - "given archive") % {'value': value}) + raise ValueError( + _('"%(value)s" is not a valid path for the ' "given archive") + % {"value": value} + ) class StrToBoolean(Formater, ChoiceChecker): - def __init__(self, choices=None, cli=False, strict=False, db_target=None, - import_instance=None): + def __init__( + self, + choices=None, + cli=False, + strict=False, + db_target=None, + import_instance=None, + ): if not choices: choices = {} self.dct = copy.copy(choices) @@ -656,14 +721,21 @@ class StrToBoolean(Formater, ChoiceChecker): value = slugify(value) return value - def check(self, values, output=None, comment='', choose_default=False, - import_instance=None, user=None): - if (not output or output == 'silent') and not choose_default: + def check( + self, + values, + output=None, + comment="", + choose_default=False, + import_instance=None, + user=None, + ): + if (not output or output == "silent") and not choose_default: return msgstr = comment + " - " - msgstr += str(_( - "Choice for \"%s\" is not available. " - "Which one is relevant?\n")) + msgstr += str( + _('Choice for "%s" is not available. ' "Which one is relevant?\n") + ) msgstr += "1. True\n" msgstr += "2. False\n" msgstr += "3. Empty\n" @@ -671,7 +743,7 @@ class StrToBoolean(Formater, ChoiceChecker): value = self.prepare(value) if value in self.dct: continue - if output != 'cli' and not choose_default: + if output != "cli" and not choose_default: self.missings.add(value) continue res = None @@ -679,7 +751,7 @@ class StrToBoolean(Formater, ChoiceChecker): res = 1 while res not in range(1, 4): msg = msgstr % value - sys.stdout.write(msg.encode('utf-8')) + sys.stdout.write(msg.encode("utf-8")) sys.stdout.write("\n>>> ") res = input() try: @@ -693,17 +765,21 @@ class StrToBoolean(Formater, ChoiceChecker): else: self.dct[value] = None self.new_keys[value] = str(self.dct[value]) - if output == 'db' and self.db_target: + if output == "db" and self.db_target: from ishtar_common.models import TargetKey + for missing in self.missings: try: - q = {'target': self.db_target, 'key': missing, - 'associated_import': import_instance} + q = { + "target": self.db_target, + "key": missing, + "associated_import": import_instance, + } if not TargetKey.objects.filter(**q).count(): TargetKey.objects.create(**q) except IntegrityError: pass - if output == 'cli': + if output == "cli": self.report_new(comment) def format(self, value): @@ -714,11 +790,12 @@ class StrToBoolean(Formater, ChoiceChecker): self.match_table[origin_value] = _(val) return self.dct[value] + logger = logging.getLogger(__name__) def get_object_from_path(obj, path): - for k in path.split('__')[:-1]: + for k in path.split("__")[:-1]: if not hasattr(obj, k): return obj = getattr(obj, k) @@ -726,8 +803,8 @@ def get_object_from_path(obj, path): class Importer(object): - SLUG = '' - NAME = '' + SLUG = "" + NAME = "" DESC = "" LINE_FORMAT = [] OBJECT_CLS = None @@ -737,22 +814,27 @@ class Importer(object): EXTRA_DEFAULTS = {} DEFAULTS = {} ERRORS = { - 'header_check': _( + "header_check": _( "The given file is not correct. Check the file " "format. If you use a CSV file: check that column separator " "and encoding are similar to the ones used by the reference " - "file."), - 'too_many_cols': _("Too many cols (%(user_col)d) when " - "maximum is %(ref_col)d"), - 'no_data': _("No data provided"), - 'value_required': _("Value is required"), - 'not_enough_cols': _("At least %d columns must be filled"), - 'regex_not_match': _("The regexp doesn't match."), - 'improperly_configured': _( + "file." + ), + "too_many_cols": _( + "Too many cols (%(user_col)d) when " "maximum is %(ref_col)d" + ), + "no_data": _("No data provided"), + "value_required": _("Value is required"), + "not_enough_cols": _("At least %d columns must be filled"), + "regex_not_match": _("The regexp doesn't match."), + "improperly_configured": _( "Forced creation is set for model {} but this model is not in the " - "list of models allowed to be created."), - 'does_not_exist_in_db': _("{} with values {} doesn't exist in the " - "database. Create it first or fix your source file."), + "list of models allowed to be created." + ), + "does_not_exist_in_db": _( + "{} with values {} doesn't exist in the " + "database. Create it first or fix your source file." + ), } def _create_models(self, force=False): @@ -761,6 +843,7 @@ class Importer(object): Not useful anymore? """ from ishtar_common import models + q = models.ImporterType.objects.filter(slug=self.SLUG) if not force and (not self.SLUG or q.count()): return @@ -768,45 +851,48 @@ class Importer(object): q.all()[0].delete() name = self.NAME if self.NAME else self.SLUG - model_name = self.OBJECT_CLS.__module__ + '.' + \ - self.OBJECT_CLS.__name__ + model_name = self.OBJECT_CLS.__module__ + "." + self.OBJECT_CLS.__name__ model_cls, c = models.ImporterModel.object.get_or_create( - klass=model_name, default={'name': self.OBJECT_CLS.__name__} + klass=model_name, default={"name": self.OBJECT_CLS.__name__} ) - unicity_keys = '' + unicity_keys = "" if self.UNICITY_KEYS: unicity_keys = ";".join(self.UNICITY_KEYS) importer = models.ImporterType.objects.create( - slug=self.SLUG, name=name, description=self.DESC, - associated_models=model_cls, unicity_keys=unicity_keys) + slug=self.SLUG, + name=name, + description=self.DESC, + associated_models=model_cls, + unicity_keys=unicity_keys, + ) for default in self.DEFAULTS: values = self.DEFAULTS[default] imp_default = models.ImporterDefault.objects.create( - importer_type=importer, - target='__'.join(default)) + importer_type=importer, target="__".join(default) + ) for key in values: - if key in ('history_modifier',): + if key in ("history_modifier",): continue value = values[key] - if hasattr(value, 'txt_idx') and value.txt_idx: + if hasattr(value, "txt_idx") and value.txt_idx: value = value.txt_idx - elif hasattr(value, 'pk') and value.pk: + elif hasattr(value, "pk") and value.pk: value = value.pk if callable(value): value = value() models.ImporterDefaultValues.objects.create( - default_target=imp_default, - target=key, - value=value) + default_target=imp_default, target=key, value=value + ) for idx, line in enumerate(self.line_format): idx += 1 if not line: continue column = models.ImporterColumn.objects.create( - importer_type=importer, col_number=idx) + importer_type=importer, col_number=idx + ) targets = line.field_name if type(targets) not in (list, tuple): targets = [targets] @@ -817,64 +903,73 @@ class Importer(object): formater = formaters[idx_target] formater_name = formater.__class__.__name__ if formater_name not in models.IMPORTER_TYPES_DCT: - formater_name = 'UnknowType' - options = '' - if formater_name == 'TypeFormater': - options = formater.model.__module__ + '.' + \ - formater.model.__name__ - elif formater_name == 'UnicodeFormater': - options = str(formater.max_length or '') - elif formater_name == 'DateFormater': + formater_name = "UnknowType" + options = "" + if formater_name == "TypeFormater": + options = formater.model.__module__ + "." + formater.model.__name__ + elif formater_name == "UnicodeFormater": + options = str(formater.max_length or "") + elif formater_name == "DateFormater": options = formater.date_formats[0] - formater_model, created = \ - models.FormaterType.objects.get_or_create( - formater_type=formater_name, options=options.strip(), - many_split=getattr(formater, 'many_split', None) or '') + formater_model, created = models.FormaterType.objects.get_or_create( + formater_type=formater_name, + options=options.strip(), + many_split=getattr(formater, "many_split", None) or "", + ) regexp_filter = None - if getattr(formater, 'regexp', None): - regexp_filter, created = \ - models.Regexp.objects.get_or_create( - regexp=formater.regex, - defaults={'name': "Default name"}) + if getattr(formater, "regexp", None): + regexp_filter, created = models.Regexp.objects.get_or_create( + regexp=formater.regex, defaults={"name": "Default name"} + ) models.ImportTarget.objects.get_or_create( - column=column, target=target, formater_type=formater_model, - force_new=getattr(formater, 'force_new', False), - concat=getattr(formater, 'concat', False), - concat_str=getattr(formater, 'concat_str', ''), + column=column, + target=target, + formater_type=formater_model, + force_new=getattr(formater, "force_new", False), + concat=getattr(formater, "concat", False), + concat_str=getattr(formater, "concat_str", ""), regexp_filter=regexp_filter, - comment=line.comment) + comment=line.comment, + ) return True def _get_improperly_conf_error(self, model): from ishtar_common.models import ImporterModel + cls_name = model.__module__ + "." + model.__name__ q = ImporterModel.objects.filter(klass=cls_name) if q.count(): cls_name = q.all()[0].name - return ImporterError( - str(self.ERRORS['improperly_configured']).format(cls_name)) + return ImporterError(str(self.ERRORS["improperly_configured"]).format(cls_name)) def _get_does_not_exist_in_db_error(self, model, data): from ishtar_common.models import ImporterModel + cls_name = model.__module__ + "." + model.__name__ q = ImporterModel.objects.filter(klass=cls_name) if q.count(): cls_name = q.all()[0].name - values = ", ".join( - ["{}: {}".format(k, data[k]) for k in data] - ) + values = ", ".join(["{}: {}".format(k, data[k]) for k in data]) raise ImporterError( - str(self.ERRORS['does_not_exist_in_db']).format(cls_name, values)) + str(self.ERRORS["does_not_exist_in_db"]).format(cls_name, values) + ) - def __init__(self, skip_lines=0, reference_header=None, - check_col_num=False, test=False, history_modifier=None, - output='silent', import_instance=None, - conservative_import=False): + def __init__( + self, + skip_lines=0, + reference_header=None, + check_col_num=False, + test=False, + history_modifier=None, + output="silent", + import_instance=None, + conservative_import=False, + ): """ - * skip_line must be set if the data provided has got headers lines. - * a reference_header can be provided to perform a data compliance - check. It can be useful to warn about bad parsing. - * test doesn't write in the database + * skip_line must be set if the data provided has got headers lines. + * a reference_header can be provided to perform a data compliance + check. It can be useful to warn about bad parsing. + * test doesn't write in the database """ self.skip_lines = skip_lines self.reference_header = reference_header @@ -918,14 +1013,15 @@ class Importer(object): self.history_modifier = self.import_instance.user.user_ptr else: # import made by the CLI: get the first admin - self.history_modifier = User.objects.filter( - is_superuser=True).order_by('pk')[0] + self.history_modifier = User.objects.filter(is_superuser=True).order_by( + "pk" + )[0] def post_processing(self, idx_line, item): # force django based post-processing for the item item = item.__class__.objects.get(pk=item.pk) item.save() - if hasattr(item, 'RELATED_POST_PROCESS'): + if hasattr(item, "RELATED_POST_PROCESS"): for related_key in item.RELATED_POST_PROCESS: for related in getattr(item, related_key).all(): related.save() @@ -937,8 +1033,7 @@ class Importer(object): self.errors.append((idx_line, None, msg)) return item - def initialize(self, table, output='silent', choose_default=False, - user=None): + def initialize(self, table, output="silent", choose_default=False, user=None): """ copy vals in columns and initialize formaters * output: @@ -948,7 +1043,7 @@ class Importer(object): (further exploitation by web interface) - user: associated user """ - assert output in ('silent', 'cli', 'db') + assert output in ("silent", "cli", "db") vals = [] for idx_line, line in enumerate(table): if self.skip_lines > idx_line: @@ -969,22 +1064,34 @@ class Importer(object): db_targets = [] for field_name in field_names: db_targets.append( - self.DB_TARGETS["{}-{}".format( - idx + 1, field_name)]) + self.DB_TARGETS["{}-{}".format(idx + 1, field_name)] + ) formater.reinit_db_target(db_targets, user=user) - formater.init(vals[idx], output, choose_default=choose_default, - import_instance=self.import_instance, - user=user) + formater.init( + vals[idx], + output, + choose_default=choose_default, + import_instance=self.import_instance, + user=user, + ) def get_formaters(self): return self.line_format - def importation(self, table, initialize=True, choose_default=False, - user=None, line_to_process=None, simulate=False): + def importation( + self, + table, + initialize=True, + choose_default=False, + user=None, + line_to_process=None, + simulate=False, + ): if initialize: - self.initialize(table, self.output, - choose_default=choose_default, user=user) + self.initialize( + table, self.output, choose_default=choose_default, user=user + ) self.simulate = simulate return self._importation(table, line_to_process=line_to_process) @@ -996,13 +1103,14 @@ class Importer(object): return self.DB_TARGETS = {} from ishtar_common.models import ImporterColumn, ImportTarget + for idx, line in enumerate(self.line_format): idx += 1 if not line: continue col = ImporterColumn.objects.get( - importer_type=self.import_instance.importer_type, - col_number=idx) + importer_type=self.import_instance.importer_type, col_number=idx + ) formater = line.formater targets = line.field_name if type(formater) not in (list, tuple): @@ -1012,18 +1120,26 @@ class Importer(object): tg = target if type(target) == list and type(target[0]) == list: tg = target[0] - self.DB_TARGETS["{}-{}".format(idx, tg)] = \ - ImportTarget.objects.get(column=col, target=tg) + self.DB_TARGETS["{}-{}".format(idx, tg)] = ImportTarget.objects.get( + column=col, target=tg + ) @classmethod def _field_name_to_data_dict( - cls, field_name, value, data, force_value=False, concat=False, - concat_str="", force_new=False): + cls, + field_name, + value, + data, + force_value=False, + concat=False, + concat_str="", + force_new=False, + ): field_names = field_name if type(field_names) not in (list, tuple): field_names = [field_name] for field_name in field_names: - keys = field_name.split('__') + keys = field_name.split("__") current_data = data for idx, key in enumerate(keys): if idx == (len(keys) - 1): # last @@ -1040,23 +1156,26 @@ class Importer(object): if not value: continue current_data[key] = ( - current_data[key] + (concat_str or "")) \ - if current_data[key] else "" + (current_data[key] + (concat_str or "")) + if current_data[key] + else "" + ) current_data[key] += value elif force_value and value: - if concat_str and key in current_data \ - and current_data[key]: - current_data[key] = str(current_data[key]) + \ - concat_str + str(value) + if concat_str and key in current_data and current_data[key]: + current_data[key] = ( + str(current_data[key]) + concat_str + str(value) + ) else: current_data[key] = value elif key not in current_data or not current_data[key]: current_data[key] = value elif concat_str: - current_data[key] = str(current_data[key]) +\ - concat_str + str(value) + current_data[key] = ( + str(current_data[key]) + concat_str + str(value) + ) if force_new: - current_data['__force_new'] = True + current_data["__force_new"] = True elif key not in current_data: current_data[key] = {} current_data = current_data[key] @@ -1066,10 +1185,12 @@ class Importer(object): self.match_table = {} table = list(table) if not table or not table[0]: - raise ImporterError(self.ERRORS['no_data'], ImporterError.HEADER) + raise ImporterError(self.ERRORS["no_data"], ImporterError.HEADER) if self.check_col_num and len(table[0]) > len(self.line_format): - raise ImporterError(self.ERRORS['too_many_cols'] % { - 'user_col': len(table[0]), 'ref_col': len(self.line_format)}) + raise ImporterError( + self.ERRORS["too_many_cols"] + % {"user_col": len(table[0]), "ref_col": len(self.line_format)} + ) self.errors = [] self.validity = [] self.number_imported = 0 @@ -1083,15 +1204,16 @@ class Importer(object): # min col number to be filled self.min_col_number = len(self.line_format) - idx_last_col # check the conformity with the reference header - if self.reference_header and \ - self.skip_lines and \ - self.reference_header != table[0]: - raise ImporterError(self.ERRORS['header_check'], - type=ImporterError.HEADER) + if ( + self.reference_header + and self.skip_lines + and self.reference_header != table[0] + ): + raise ImporterError(self.ERRORS["header_check"], type=ImporterError.HEADER) self.now = datetime.datetime.now() start = datetime.datetime.now() total = len(table) - if self.output == 'cli': + if self.output == "cli": sys.stdout.write("\n") results = [] for idx_line, line in enumerate(table): @@ -1101,7 +1223,7 @@ class Importer(object): continue if idx_line > line_to_process: return results - if self.output == 'cli': + if self.output == "cli": left = None if idx_line > 10: ellapsed = datetime.datetime.now() - start @@ -1111,7 +1233,7 @@ class Importer(object): txt = "\r* %d/%d" % (idx_line + 1, total) if left: txt += " (%d seconds left)" % left - sys.stdout.write(txt.encode('utf-8')) + sys.stdout.write(txt.encode("utf-8")) sys.stdout.flush() try: results.append(self._line_processing(idx_line, line)) @@ -1132,8 +1254,11 @@ class Importer(object): if not line: self.validity.append([]) return - if not self.simulate and self.import_instance and \ - not self.import_instance.has_changes(idx_line): + if ( + not self.simulate + and self.import_instance + and not self.import_instance.has_changes(idx_line) + ): self.validity.append(line) return @@ -1148,8 +1273,7 @@ class Importer(object): self.current_csv_line = line n = datetime.datetime.now() - logger.debug('%s - Processing line %d' % (str(n - self.now), - idx_line)) + logger.debug("%s - Processing line %d" % (str(n - self.now), idx_line)) self.now = n n2 = n self.c_errors = False @@ -1163,19 +1287,23 @@ class Importer(object): self.validity.append(c_row) if not self.c_errors and (idx_col + 1) < self.min_col_number: self.c_errors = True - self.errors.append(( - idx_line + 1, idx_col + 1, - self.ERRORS['not_enough_cols'] % self.min_col_number)) + self.errors.append( + ( + idx_line + 1, + idx_col + 1, + self.ERRORS["not_enough_cols"] % self.min_col_number, + ) + ) if self.c_errors: return n = datetime.datetime.now() - logger.debug('* %s - Cols read' % (str(n - n2))) + logger.debug("* %s - Cols read" % (str(n - n2))) n2 = n if self.test: return # manage unicity of items (mainly for updates) - if 'history_modifier' in get_all_field_names(self.OBJECT_CLS): - data['history_modifier'] = self.history_modifier + if "history_modifier" in get_all_field_names(self.OBJECT_CLS): + data["history_modifier"] = self.history_modifier self.new_objects, self.updated_objects = [], [] self.ambiguous_objects, self.not_find_objects = [], [] @@ -1189,8 +1317,7 @@ class Importer(object): if self.import_instance: self.import_instance.add_imported_line(self.idx_line) - if self.import_instance and hasattr(obj, 'imports') \ - and created: + if self.import_instance and hasattr(obj, "imports") and created: obj.imports.add(self.import_instance) if created: @@ -1198,17 +1325,18 @@ class Importer(object): else: self.number_updated += 1 - if not created and 'defaults' in data: - for k in data['defaults']: - setattr(obj, k, data['defaults'][k]) + if not created and "defaults" in data: + for k in data["defaults"]: + setattr(obj, k, data["defaults"][k]) obj.save() n = datetime.datetime.now() - logger.debug('* %s - Item saved' % (str(n - n2))) + logger.debug("* %s - Item saved" % (str(n - n2))) n2 = n for formater, value in self._throughs: n = datetime.datetime.now() - logger.debug('* %s - Processing formater %s' % (str(n - n2), - formater.field_name)) + logger.debug( + "* %s - Processing formater %s" % (str(n - n2), formater.field_name) + ) n2 = n data = {} if formater.through_dict: @@ -1218,38 +1346,41 @@ class Importer(object): data[formater.field_name] = value through_cls = formater.through if formater.through_unicity_keys: - data['defaults'] = {} + data["defaults"] = {} for k in list(data.keys()): - if k not in formater.through_unicity_keys \ - and k != 'defaults': - data['defaults'][k] = data.pop(k) + if k not in formater.through_unicity_keys and k != "defaults": + data["defaults"][k] = data.pop(k) created = False - if '__force_new' in data: - if self.MODEL_CREATION_LIMIT and \ - through_cls not in self.MODEL_CREATION_LIMIT: + if "__force_new" in data: + if ( + self.MODEL_CREATION_LIMIT + and through_cls not in self.MODEL_CREATION_LIMIT + ): raise self._get_improperly_conf_error(through_cls) - created = data.pop('__force_new') + created = data.pop("__force_new") t_obj = through_cls.objects.create(**data) else: - if not self.MODEL_CREATION_LIMIT or \ - through_cls in self.MODEL_CREATION_LIMIT: + if ( + not self.MODEL_CREATION_LIMIT + or through_cls in self.MODEL_CREATION_LIMIT + ): t_obj, created = through_cls.objects.get_or_create(**data) else: get_data = data.copy() - if 'defaults' in get_data: - get_data.pop('defaults') + if "defaults" in get_data: + get_data.pop("defaults") try: t_obj = through_cls.objects.get(**get_data) except through_cls.DoesNotExist: raise self._get_does_not_exist_in_db_error( - through_cls, get_data) - if not created and 'defaults' in data: + through_cls, get_data + ) + if not created and "defaults" in data: t_obj = t_obj.__class__.objects.get(pk=t_obj.pk) - for k in data['defaults']: - setattr(t_obj, k, data['defaults'][k]) + for k in data["defaults"]: + setattr(t_obj, k, data["defaults"][k]) t_obj.save() - if self.import_instance and hasattr(t_obj, 'imports') \ - and created: + if self.import_instance and hasattr(t_obj, "imports") and created: t_obj.imports.add(self.import_instance) if not obj: return data @@ -1270,35 +1401,37 @@ class Importer(object): self._post_processing.append((formater, val)) if not formater or not formater.field_name: - c_row.append(_('Not imported')) + c_row.append(_("Not imported")) return if formater.regexp: # multiline regexp is a mess... - val = val.replace('\n', NEW_LINE_BREAK) + val = val.replace("\n", NEW_LINE_BREAK) match = formater.regexp.match(val) if not match: if formater.required: self.errors.append( - (idx_line + 1, idx_col + 1, - self.ERRORS['value_required'])) + (idx_line + 1, idx_col + 1, self.ERRORS["value_required"]) + ) self.c_errors = True elif not val.strip(): c_row.append("") return - val = val.replace(NEW_LINE_BREAK, '\n') + val = val.replace(NEW_LINE_BREAK, "\n") self.errors.append( - (idx_line + 1, idx_col + 1, - str(self.ERRORS['regex_not_match']) + val)) + ( + idx_line + 1, + idx_col + 1, + str(self.ERRORS["regex_not_match"]) + val, + ) + ) c_row.append("") return val_group = [] for g in formater.regexp.findall(val): if isinstance(g, (tuple, list)): g = "".join(g) - val_group.append( - g.replace(NEW_LINE_BREAK, '\n') if g else '' - ) + val_group.append(g.replace(NEW_LINE_BREAK, "\n") if g else "") val = "".join(val_group) field_names = formater.field_name @@ -1314,7 +1447,7 @@ class Importer(object): func = getattr(self, func) values = [val] - many_values = getattr(func, 'many_split', None) + many_values = getattr(func, "many_split", None) if many_values: values = re.split(func.many_split, values[0]) # filter empty entries on m2m such as "my-value & " @@ -1341,8 +1474,8 @@ class Importer(object): if self.DB_TARGETS: formater.import_instance = self.import_instance formater.reinit_db_target( - self.DB_TARGETS["{}-{}".format(idx_col + 1, field_name)], - idx_fields) + self.DB_TARGETS["{}-{}".format(idx_col + 1, field_name)], idx_fields + ) for idx, v in enumerate(values): try: """ @@ -1354,7 +1487,7 @@ class Importer(object): value = func.format(*args) else: """ - if getattr(func, 'need_archive', False): + if getattr(func, "need_archive", False): value = func.format(v, archive=self.archive) else: value = func.format(v) @@ -1362,7 +1495,7 @@ class Importer(object): if formater.required: self.c_errors = True self.errors.append((idx_line + 1, idx_col + 1, str(e))) - c_values.append('') + c_values.append("") return if formater.value_format and value is not None and value != "": value = formater.value_format.format(value) @@ -1371,7 +1504,7 @@ class Importer(object): # later self.to_be_close.append(value) formated_values.append(value) - if hasattr(func, 'match_table'): + if hasattr(func, "match_table"): if field_name not in self.match_table: self.match_table[field_name] = {} self.match_table[field_name].update(func.match_table) @@ -1390,8 +1523,9 @@ class Importer(object): c_values.append(" ; ".join([str(v) for v in printed_values])) if value is None and formater.required: self.c_errors = True - self.errors.append((idx_line + 1, idx_col + 1, - self.ERRORS['value_required'])) + self.errors.append( + (idx_line + 1, idx_col + 1, self.ERRORS["value_required"]) + ) return field_names = [field_name] @@ -1402,11 +1536,11 @@ class Importer(object): # duplicate fields are only for the first occurrence for duplicate_field in formater.duplicate_fields: if type(duplicate_field[0]) in (list, tuple): - duplicate_field, force_new, concat, conc_str = \ - duplicate_field[0] + duplicate_field, force_new, concat, conc_str = duplicate_field[ + 0 + ] else: - duplicate_field, force_new, concat, conc_str = \ - duplicate_field + duplicate_field, force_new, concat, conc_str = duplicate_field field_names += [duplicate_field] force_news += [force_new] concats += [concat] @@ -1417,13 +1551,17 @@ class Importer(object): else: for idx, f_name in enumerate(field_names): self._field_name_to_data_dict( - f_name, value, data, formater.force_value, - force_new=force_news[idx], concat=concats[idx], - concat_str=concat_str[idx]) + f_name, + value, + data, + formater.force_value, + force_new=force_news[idx], + concat=concats[idx], + concat_str=concat_str[idx], + ) c_row.append(" ; ".join([v for v in c_values])) - def _get_field_m2m(self, attribute, data, c_path, new_created, - field_object): + def _get_field_m2m(self, attribute, data, c_path, new_created, field_object): """ Manage and m2m field from raw data @@ -1439,13 +1577,13 @@ class Importer(object): many_values = data.pop(attribute) model = None - if hasattr(field_object, 'rel'): + if hasattr(field_object, "rel"): model = field_object.rel.to - elif hasattr(field_object, 'related_model'): + elif hasattr(field_object, "related_model"): model = field_object.related_model - elif hasattr(field_object, 'to'): + elif hasattr(field_object, "to"): model = field_object.to - elif hasattr(field_object, 'model'): + elif hasattr(field_object, "model"): model = field_object.model if type(many_values) not in (list, tuple): many_values = [many_values] @@ -1498,34 +1636,31 @@ class Importer(object): field_names = get_all_field_names(model) for v in vals: - if 'history_modifier' in field_names: - if 'defaults' not in v: - v['defaults'] = {} - v['defaults']['history_modifier'] = \ - self.history_modifier + if "history_modifier" in field_names: + if "defaults" not in v: + v["defaults"] = {} + v["defaults"]["history_modifier"] = self.history_modifier m2m_m2ms = [] c_c_path = c_path[:] for k in list(v.keys()): if k not in field_names: continue - self.get_field(model, k, v, m2m_m2ms, c_c_path, - new_created) - if '__force_new' in v: - created = v.pop('__force_new') - key = ";".join(["{}-{}".format(k, v[k]) - for k in sorted(v.keys())]) + self.get_field(model, k, v, m2m_m2ms, c_c_path, new_created) + if "__force_new" in v: + created = v.pop("__force_new") + key = ";".join(["{}-{}".format(k, v[k]) for k in sorted(v.keys())]) # only one forced creation - if attribute in new_created \ - and key in new_created[attribute]: + if attribute in new_created and key in new_created[attribute]: continue if attribute not in new_created: new_created[attribute] = [] new_created[attribute].append(key) - has_values = bool([1 for k in v - if v[k] and k != "defaults"]) + has_values = bool([1 for k in v if v[k] and k != "defaults"]) if has_values: - if self.MODEL_CREATION_LIMIT and \ - model not in self.MODEL_CREATION_LIMIT: + if ( + self.MODEL_CREATION_LIMIT + and model not in self.MODEL_CREATION_LIMIT + ): raise self._get_improperly_conf_error(model) if "defaults" in v: default_values = v.pop("defaults") @@ -1536,7 +1671,7 @@ class Importer(object): else: continue else: - v['defaults'] = v.get('defaults', {}) + v["defaults"] = v.get("defaults", {}) extra_fields = {} # "File" type is a temp object and can be different # for the same filename - it must be treated @@ -1544,33 +1679,32 @@ class Importer(object): for field in model._meta.fields: k = field.name # attr_class is a FileField attribute - if hasattr(field, 'attr_class') and k in v: + if hasattr(field, "attr_class") and k in v: extra_fields[k] = v.pop(k) created = False - if not self.MODEL_CREATION_LIMIT or \ - model in self.MODEL_CREATION_LIMIT: + if ( + not self.MODEL_CREATION_LIMIT + or model in self.MODEL_CREATION_LIMIT + ): try: - v, created = model.objects.get_or_create( - **v) + v, created = model.objects.get_or_create(**v) except FieldError as e: raise ImporterError( - str( - _("Importer configuration error: " - "\"{}\".")).format(e)) + str(_("Importer configuration error: " '"{}".')).format( + e + ) + ) except Exception as e: - msg = str( - _("Import error: {} - \"{}\".") - ).format(model, e) + msg = str(_('Import error: {} - "{}".')).format(model, e) raise ImporterError(msg) else: get_v = v.copy() - if 'defaults' in get_v: - get_v.pop('defaults') + if "defaults" in get_v: + get_v.pop("defaults") try: v = model.objects.get(**get_v) except model.DoesNotExist: - raise self._get_does_not_exist_in_db_error( - model, get_v) + raise self._get_does_not_exist_in_db_error(model, get_v) changed = False for k in extra_fields.keys(): if extra_fields[k]: @@ -1583,8 +1717,7 @@ class Importer(object): objs = [objs] for obj in objs: getattr(v, att).add(obj) - if self.import_instance \ - and hasattr(v, 'imports') and created: + if self.import_instance and hasattr(v, "imports") and created: v.imports.add(self.import_instance) m2ms.append((attribute, v)) return m2ms @@ -1600,15 +1733,16 @@ class Importer(object): :return: None """ func = getattr(cls, attribute) - if func.importer_trigger == 'pre': + if func.importer_trigger == "pre": func(data, data[attribute]) - elif func.importer_trigger == 'post': - self._item_post_processing.append([attribute, data, - data[attribute]]) + elif func.importer_trigger == "post": + self._item_post_processing.append([attribute, data, data[attribute]]) else: - logger.warning("Unknow importer_trigger '{}' for '{}'".format( - func.importer_trigger, attribute - )) + logger.warning( + "Unknow importer_trigger '{}' for '{}'".format( + func.importer_trigger, attribute + ) + ) data.pop(attribute) def get_field(self, cls, attribute, data, m2ms, c_path, new_created): @@ -1624,53 +1758,62 @@ class Importer(object): multiple creation :return: None """ - if hasattr(cls, attribute) and \ - getattr(getattr(cls, attribute), 'importer_trigger', None): + if hasattr(cls, attribute) and getattr( + getattr(cls, attribute), "importer_trigger", None + ): # importer trigger self._set_importer_trigger(cls, attribute, data) return - if attribute == 'data': # json field + if attribute == "data": # json field # no need to do anything return - if attribute == 'get_default': + if attribute == "get_default": # force evaluation of default value for this field return try: field_object = cls._meta.get_field(attribute) except FieldDoesNotExist: - raise ImporterError(str( - _("Importer configuration error: field \"{}\" does not exist " - "for {}.")).format(attribute, cls._meta.verbose_name)) + raise ImporterError( + str( + _( + 'Importer configuration error: field "{}" does not exist ' + "for {}." + ) + ).format(attribute, cls._meta.verbose_name) + ) if field_object.many_to_many: try: - m2ms += self._get_field_m2m(attribute, data, c_path, - new_created, field_object) + m2ms += self._get_field_m2m( + attribute, data, c_path, new_created, field_object + ) except Exception as e: self.errors.append((self.idx_line, None, str(e))) return - if not hasattr(field_object, 'rel') or not field_object.rel: + if not hasattr(field_object, "rel") or not field_object.rel: return if type(data[attribute]) == list: # extract the first item from list # be careful if the list has more than one item this is arbitrary if len(data[attribute]) > 1: logger.warning( - 'Import {}: {} has many when only one is expected. Get ' - 'the first one but it is not OK!'.format( - self.import_instance, attribute)) + "Import {}: {} has many when only one is expected. Get " + "the first one but it is not OK!".format( + self.import_instance, attribute + ) + ) data[attribute] = data[attribute][0] return if not isinstance(data[attribute], dict): # we treat only dict formated values return # put history_modifier for every created item - if 'history_modifier' in get_all_field_names(field_object.rel.to): - data[attribute]['history_modifier'] = \ - self.history_modifier + if "history_modifier" in get_all_field_names(field_object.rel.to): + data[attribute]["history_modifier"] = self.history_modifier try: c_path.append(attribute) data[attribute], created = self.get_object( - field_object.rel.to, data[attribute].copy(), c_path) + field_object.rel.to, data[attribute].copy(), c_path + ) except ImporterError as msg: self.errors.append((self.idx_line, None, msg)) data[attribute] = None @@ -1684,8 +1827,8 @@ class Importer(object): return data, False is_empty = not bool( - [k for k in data if k not in ('history_modifier', 'defaults') - and data[k]]) + [k for k in data if k not in ("history_modifier", "defaults") and data[k]] + ) if is_empty: # if no value, no creation return None, False @@ -1704,29 +1847,29 @@ class Importer(object): continue if not data[attribute]: if hasattr(cls, attribute) and getattr( - getattr(cls, attribute), 'importer_trigger', None): + getattr(cls, attribute), "importer_trigger", None + ): data.pop(attribute) continue field_object = cls._meta.get_field(attribute) if field_object.many_to_many: data.pop(attribute) continue - if attribute != '__force_new': - self.get_field(cls, attribute, data, m2ms, c_c_path, - new_created) + if attribute != "__force_new": + self.get_field(cls, attribute, data, m2ms, c_c_path, new_created) except (ValueError, IntegrityError, FieldDoesNotExist) as e: try: message = str(e) except (UnicodeDecodeError, UnicodeDecodeError): - message = '' + message = "" try: data = str(data) except UnicodeDecodeError: - data = '' + data = "" raise ImporterError( "Erreur d'import %s %s, contexte : %s, erreur : %s" - % (str(cls), str("__".join(path)), - str(data), message)) + % (str(cls), str("__".join(path)), str(data), message) + ) # image field is not serialized image, associated_file = None, None @@ -1744,7 +1887,7 @@ class Importer(object): for k in list(create_dict.keys()): # filter unnecessary default values but not the json field - if isinstance(create_dict[k], dict) and k != 'data': + if isinstance(create_dict[k], dict) and k != "data": if self.simulate: create_dict[k] = _("* created *") else: @@ -1763,10 +1906,8 @@ class Importer(object): if (k not in data or not data[k]) and self._defaults[path][k]: defaults[k] = self._defaults[path][k] - if 'history_modifier' in create_dict: - defaults.update({ - 'history_modifier': create_dict.pop('history_modifier') - }) + if "history_modifier" in create_dict: + defaults.update({"history_modifier": create_dict.pop("history_modifier")}) created = False post_save_keys = [] @@ -1782,14 +1923,16 @@ class Importer(object): if getattr(dct[key], "post_save", True): dct.pop(key) post_save_keys.append(key) - if '__force_new' in dct: - created = dct.pop('__force_new') + if "__force_new" in dct: + created = dct.pop("__force_new") if not [k for k in dct if dct[k] is not None]: return None, created new_dct = defaults.copy() new_dct.update(dct) - if self.MODEL_CREATION_LIMIT and \ - cls not in self.MODEL_CREATION_LIMIT: + if ( + self.MODEL_CREATION_LIMIT + and cls not in self.MODEL_CREATION_LIMIT + ): raise self._get_improperly_conf_error(cls) if not self.simulate: obj = cls.objects.create(**new_dct) @@ -1799,8 +1942,7 @@ class Importer(object): # manage UNICITY_KEYS - only level 1 if not path and self.UNICITY_KEYS: for k in list(dct.keys()): - if k not in self.UNICITY_KEYS \ - and k != 'defaults': + if k not in self.UNICITY_KEYS and k != "defaults": if dct[k]: defaults[k] = dct.pop(k) else: @@ -1815,68 +1957,72 @@ class Importer(object): if self.simulate: q = cls.objects.filter(**dct) if not q.count(): - if self.MODEL_CREATION_LIMIT and \ - cls not in self.MODEL_CREATION_LIMIT: - self.not_find_objects.append( - (path, cls, dct) - ) + if ( + self.MODEL_CREATION_LIMIT + and cls not in self.MODEL_CREATION_LIMIT + ): + self.not_find_objects.append((path, cls, dct)) return _("* match not find *"), False dct.update(defaults) self.new_objects.append([path, cls, dct]) created = True elif q.count() > 1: - self.ambiguous_objects.append( - (path, list(q.all()), dct) - ) + self.ambiguous_objects.append((path, list(q.all()), dct)) if q.count() > 10: - return _("* the query match more than 10 " - "results*"), False + return ( + _("* the query match more than 10 " "results*"), + False, + ) else: - return str(_(" or ")).join( - [str(item) for item in q.all()] - ), False + return ( + str(_(" or ")).join( + [str(item) for item in q.all()] + ), + False, + ) else: - self.updated_objects.append( - [path, q.all()[0], dct, {}]) - dct['defaults'] = defaults.copy() + self.updated_objects.append([path, q.all()[0], dct, {}]) + dct["defaults"] = defaults.copy() else: if not dct and not defaults: obj = None else: - if not self.MODEL_CREATION_LIMIT or \ - cls in self.MODEL_CREATION_LIMIT: - dct['defaults'] = defaults.copy() + if ( + not self.MODEL_CREATION_LIMIT + or cls in self.MODEL_CREATION_LIMIT + ): + dct["defaults"] = defaults.copy() obj, created = cls.objects.get_or_create(**dct) else: try: obj = cls.objects.get(**dct) - dct['defaults'] = defaults.copy() + dct["defaults"] = defaults.copy() except cls.DoesNotExist: - raise self._get_does_not_exist_in_db_error( - cls, dct) + raise self._get_does_not_exist_in_db_error(cls, dct) if not created and not path and self.UNICITY_KEYS: updated_dct = {} if self.conservative_import: - for k in dct['defaults']: - new_val = dct['defaults'][k] - if new_val is None or new_val == '': + for k in dct["defaults"]: + new_val = dct["defaults"][k] + if new_val is None or new_val == "": continue val = getattr(obj, k) - if val is None or val == '': + if val is None or val == "": updated_dct[k] = new_val - elif k in self.concats \ - and type(val) == str \ - and type(new_val) == str: + elif ( + k in self.concats + and type(val) == str + and type(new_val) == str + ): updated_dct[k] = val + "\n" + new_val else: - for k in dct['defaults']: - new_val = dct['defaults'][k] - if new_val is None or new_val == '': + for k in dct["defaults"]: + new_val = dct["defaults"][k] + if new_val is None or new_val == "": continue - if obj and k == 'data': - updated_dct[k] = update_data(obj.data, - new_val) + if obj and k == "data": + updated_dct[k] = update_data(obj.data, new_val) else: updated_dct[k] = new_val if updated_dct: @@ -1886,16 +2032,19 @@ class Importer(object): for k in updated_dct: setattr(obj, k, updated_dct[k]) obj.save() - if not self.simulate and self.import_instance and \ - hasattr(obj, 'imports') and created: + if ( + not self.simulate + and self.import_instance + and hasattr(obj, "imports") + and created + ): obj.imports.add(self.import_instance) - except (ValueError, IntegrityError, DatabaseError, - GEOSException) as e: + except (ValueError, IntegrityError, DatabaseError, GEOSException) as e: raise IntegrityError(str(e)) except cls.MultipleObjectsReturned as e: created = False - if 'defaults' in dct: - dct.pop('defaults') + if "defaults" in dct: + dct.pop("defaults") raise IntegrityError(str(e)) # obj = cls.objects.filter(**dct).all()[0] for key in post_save_keys: @@ -1914,13 +2063,15 @@ class Importer(object): for v in values: related_model = getattr(obj, attr) # an intermediary model is used - if hasattr(related_model, 'through') and \ - not related_model.through._meta.auto_created: + if ( + hasattr(related_model, "through") + and not related_model.through._meta.auto_created + ): # try to create it with default attributes inter_model = related_model.through target_name, item_name = None, None for field in inter_model._meta.get_fields(): - rel_model = getattr(field, 'related_model', None) + rel_model = getattr(field, "related_model", None) # assume that the first found is correct... if rel_model == v.__class__: target_name = field.name @@ -1948,8 +2099,7 @@ class Importer(object): current_data[item] = {} current_data = current_data[item] for key, value in m2ms: - if not isinstance(value, list) and \ - not isinstance(value, tuple): + if not isinstance(value, list) and not isinstance(value, tuple): value = [value] current_data[key] = value @@ -1957,34 +2107,36 @@ class Importer(object): return dct, True else: # defaults are not presented as matching data - dct.pop('defaults') + dct.pop("defaults") return self.updated_objects[-1][1], False if m2ms: # force post save script obj.save() - if hasattr(obj, 'fix'): + if hasattr(obj, "fix"): # post save/m2m specific fix obj.fix() except IntegrityError as e: try: message = str(e) except (UnicodeDecodeError, UnicodeDecodeError): - message = '' + message = "" try: data = str(data) except UnicodeDecodeError: - data = '' + data = "" raise ImporterError( "Erreur d'import %s %s, contexte : %s, erreur : %s" - % (str(cls), str("__".join(path)), - str(data), message)) + % (str(cls), str("__".join(path)), str(data), message) + ) return obj, created def _format_csv_line(self, values, empty="-"): - return '"' + '","'.join( - [(v and str(v).replace('"', '""')) or empty - for v in values]) + '"' + return ( + '"' + + '","'.join([(v and str(v).replace('"', '""')) or empty for v in values]) + + '"' + ) def _get_csv(self, rows, header=None, empty="-"): if not rows: @@ -1999,14 +2151,13 @@ class Importer(object): return "\n".join(csv_v) def get_csv_errors(self): - return self._get_csv( - self.errors, header=[_("line"), _("col"), _("error")]) + return self._get_csv(self.errors, header=[_("line"), _("col"), _("error")]) def get_csv_result(self): return self._get_csv(self.validity) def get_csv_matches(self): - header = [_('field'), _('source'), _('result')] + header = [_("field"), _("source"), _("result")] values = [] for field in self.match_table: for source in self.match_table[field]: @@ -2022,10 +2173,12 @@ class Importer(object): return if value not in choices_dct.values(): raise ValueError( - _("\"%(value)s\" not in %(values)s") % { - 'value': value, - 'values': ", ".join( - [val for val in choices_dct.values()]) - }) + _('"%(value)s" not in %(values)s') + % { + "value": value, + "values": ", ".join([val for val in choices_dct.values()]), + } + ) return value + return function -- cgit v1.2.3