diff options
Diffstat (limited to 'ishtar_common')
31 files changed, 2927 insertions, 262 deletions
diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index 8184f2117..98e681635 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -63,14 +63,15 @@ class ImportFormater(object): def report_error(self, *args): return - def init(self, vals, output=None): + def init(self, vals, output=None, choose_default=False): try: lst = iter(self.formater) except TypeError: lst = [self.formater] for formater in lst: if formater: - formater.check(vals, output) + formater.check(vals, output, self.comment, + choose_default=choose_default) def post_process(self, obj, context, value, owner=None): raise NotImplemented() @@ -92,9 +93,19 @@ class Formater(object): def format(self, value): return value - def check(self, values, output=None): + def check(self, values, output=None, comment='', choose_default=False): return +class ChoiceChecker(object): + def report_new(self, comment): + if not self.new_keys: + return + msg = u"For \"%s\" these new associations have been made:\n" % comment + sys.stderr.write(msg.encode('utf-8')) + for k in self.new_keys: + msg = u'"%s";"%s"\n' % (k, self.new_keys[k]) + sys.stderr.write(msg.encode('utf-8')) + class UnicodeFormater(Formater): def __init__(self, max_length, clean=False, re_filter=None, notnull=False, db_target=None): @@ -183,7 +194,7 @@ class IntegerFormater(Formater): raise ValueError(_(u"\"%(value)s\" is not an integer") % { 'value':value}) -class StrChoiceFormater(Formater): +class StrChoiceFormater(Formater, ChoiceChecker): def __init__(self, choices, strict=False, equiv_dict={}, model=None, cli=False, many_split='', db_target=None): self.choices = list(choices) @@ -194,6 +205,7 @@ class StrChoiceFormater(Formater): self.db_target = db_target self.create = False self.missings = set() + self.new_keys = {} self.many_split = many_split for key, value in self.choices: value = unicode(value) @@ -219,8 +231,9 @@ class StrChoiceFormater(Formater): def prepare(self, value): return unicode(value).strip() - def _get_choices(self): - msgstr = unicode(_(u"Choice for \"%s\" is not available. "\ + def _get_choices(self, comment=''): + msgstr = comment + u" - " + msgstr += unicode(_(u"Choice for \"%s\" is not available. "\ u"Which one is relevant?\n")) idx = -1 for idx, choice in enumerate(self.choices): @@ -233,8 +246,8 @@ class StrChoiceFormater(Formater): msgstr += unicode(_(u"%d. None of the above - skip")) % idx + u"\n" return msgstr, idx - def check(self, values, output=None): - if not output or output == 'silent': + def check(self, values, output=None, comment='', choose_default=False): + if (not output or output == 'silent') and not choose_default: return if self.many_split: new_values = [] @@ -247,14 +260,18 @@ class StrChoiceFormater(Formater): value = self.prepare(value) if value in self.equiv_dict: continue - if output != 'cli': + if output != 'cli' and not choose_default: self.missings.add(value) continue - msgstr, idx = self._get_choices() + msgstr, idx = self._get_choices(comment) res = None + if choose_default: + res = 1 while res not in range(1, idx+1): - sys.stdout.write(msgstr % value) - res = raw_input(">>> ") + msg = msgstr % value + sys.stdout.write(msg.encode('utf-8')) + sys.stdout.write("\n>>> ") + res = raw_input() try: res = int(res) except ValueError: @@ -266,10 +283,12 @@ class StrChoiceFormater(Formater): v = self.model.objects.get(pk=v) self.equiv_dict[value] = v self.add_key(v, value) + 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, unicode(self.equiv_dict[value]))) + self.new_keys[value] = unicode(self.equiv_dict[value]) else: self.equiv_dict[value] = None if output == 'db' and self.db_target: @@ -283,6 +302,8 @@ class StrChoiceFormater(Formater): TargetKey.objects.create(**q) except IntegrityError: pass + if output == 'cli': + self.report_new(comment) def new(self, value): return @@ -308,6 +329,7 @@ class TypeFormater(StrChoiceFormater): self.db_target = db_target self.missings = set() self.equiv_dict, self.choices = {}, [] + self.new_keys = {} for item in model.objects.all(): self.choices.append((item.pk, unicode(item))) for key in item.get_keys(): @@ -332,21 +354,25 @@ class TypeFormater(StrChoiceFormater): return self.model.objects.create(**values) class DateFormater(Formater): - def __init__(self, date_format="%d/%m/%Y", db_target=None): - self.date_format = date_format + def __init__(self, date_formats=["%d/%m/%Y"], db_target=None): + self.date_formats = date_formats + if type(date_formats) not in (list, tuple): + self.date_formats = [self.date_formats] self.db_target = db_target def format(self, value): value = value.strip() if not value: return - try: - return datetime.datetime.strptime(value, self.date_format).date() - except: - raise ValueError(_(u"\"%(value)s\" is not a valid date") % { + for date_format in self.date_formats: + try: + return datetime.datetime.strptime(value, date_format).date() + except: + continue + raise ValueError(_(u"\"%(value)s\" is not a valid date") % { 'value':value}) -class StrToBoolean(Formater): +class StrToBoolean(Formater, ChoiceChecker): def __init__(self, choices={}, cli=False, strict=False, db_target=None): self.dct = copy.copy(choices) self.cli = cli @@ -367,6 +393,7 @@ class StrToBoolean(Formater): else: v = None self.dct[value] = v + self.new_keys = {} def prepare(self, value): value = unicode(value).strip() @@ -374,10 +401,11 @@ class StrToBoolean(Formater): value = slugify(value) return value - def check(self, values, output=None): - if not output or output == 'silent': + def check(self, values, output=None, comment='', choose_default=False): + if (not output or output == 'silent') and not choose_default: return - msgstr = unicode(_(u"Choice for \"%s\" is not available. "\ + msgstr = comment + u" - " + msgstr += unicode(_(u"Choice for \"%s\" is not available. "\ u"Which one is relevant?\n")) msgstr += u"1. True\n" msgstr += u"2. False\n" @@ -386,13 +414,17 @@ class StrToBoolean(Formater): value = self.prepare(value) if value in self.dct: continue - if not self.cli: + if output != 'cli' and not choose_default: self.missings.add(value) continue res = None + if choose_default: + res = 1 while res not in range(1, 4): - sys.stdout.write(msgstr % value) - res = raw_input(">>> ") + msg = msgstr % value + sys.stdout.write(msg.encode('utf-8')) + sys.stdout.write("\n>>> ") + res = raw_input() try: res = int(res) except ValueError: @@ -411,6 +443,9 @@ class StrToBoolean(Formater): models.TargetKey.objects.create(**q) except IntegrityError: pass + self.new_keys[value] = unicode(self.dct[value]) + if output == 'cli': + self.report_new(comment) def format(self, value): value = self.prepare(value) @@ -419,13 +454,22 @@ class StrToBoolean(Formater): logger = logging.getLogger(__name__) +def get_object_from_path(obj, path): + for k in path.split('__')[:-1]: + if not hasattr(obj, k): + return + obj = getattr(obj, k) + return obj + class Importer(object): DESC = "" LINE_FORMAT = [] OBJECT_CLS = None IMPORTED_LINE_FIELD = None UNICITY_KEYS = [] + EXTRA_DEFAULTS = {} DEFAULTS = {} + STR_CUT = {} ERRORS = { 'header_check':_(u"The given file is not correct. Check the file " u"format. If you use a CSV file: check that column separator " @@ -459,6 +503,12 @@ class Importer(object): self.line_format = copy.copy(self.LINE_FORMAT) self.import_instance = import_instance self._defaults = self.DEFAULTS.copy() + # EXTRA_DEFAULTS are for multiple inheritance + if self.EXTRA_DEFAULTS: + for k in self.EXTRA_DEFAULTS: + if k not in self._defaults: + self._defaults[k] = {} + self._defaults[k].update(self.EXTRA_DEFAULTS[k]) self.history_modifier = history_modifier self.output = output if not self.history_modifier: @@ -469,7 +519,10 @@ class Importer(object): self.history_modifier = User.objects.filter( is_superuser=True).order_by('pk')[0] - def initialize(self, table, output='silent'): + def post_processing(self, item, data): + return item + + def initialize(self, table, output='silent', choose_default=False): """ copy vals in columns and initialize formaters * output: @@ -491,11 +544,11 @@ class Importer(object): vals[idx_col].append(val) for idx, formater in enumerate(self.line_format): if formater and idx < len(vals): - formater.init(vals[idx], output) + formater.init(vals[idx], output, choose_default=choose_default) - def importation(self, table, initialize=True): + def importation(self, table, initialize=True, choose_default=False): if initialize: - self.initialize(table, self.output) + self.initialize(table, self.output, choose_default=choose_default) self._importation(table) @classmethod @@ -560,10 +613,10 @@ class Importer(object): time_by_item = ellapsed/idx_line if time_by_item: left = ((total - idx_line)*time_by_item).seconds - txt = "\r* %d/%d" % (idx_line+1, total) + txt = u"\r* %d/%d" % (idx_line+1, total) if left: - txt += " (%d seconds left)" % left - sys.stdout.write(txt) + txt += u" (%d seconds left)" % left + sys.stdout.write(txt.encode('utf-8')) sys.stdout.flush() try: self._line_processing(idx_line, line) @@ -623,8 +676,8 @@ class Importer(object): if 'history_modifier' in \ self.OBJECT_CLS._meta.get_all_field_names(): data['history_modifier'] = self.history_modifier - obj, created = self.get_object(self.OBJECT_CLS, data) + obj, created = self.get_object(self.OBJECT_CLS, data) if self.import_instance and hasattr(obj, 'imports'): obj.imports.add(self.import_instance) @@ -669,6 +722,8 @@ class Importer(object): for formater, val in self._post_processing: formater.post_process(obj, data, val, owner=self.history_modifier) + obj = self.post_processing(obj, data) + def _row_processing(self, c_row, idx_col, idx_line, val, data): if idx_col >= len(self.line_format): return @@ -804,12 +859,19 @@ class Importer(object): if self.import_instance and hasattr(val, 'imports'): val.imports.add(self.import_instance) m2ms.append((attribute, val)) + # default values path = tuple(path) if path in self._defaults: for k in self._defaults[path]: if k not in data or not data[k]: data[k] = self._defaults[path][k] + # pre treatment + if path in self.STR_CUT: + for k in self.STR_CUT[path]: + if k in data and data[k]: + data[k] = unicode(data[k])[:self.STR_CUT[path][k]] + # filter default values create_dict = copy.deepcopy(data) for k in create_dict.keys(): @@ -827,7 +889,7 @@ class Importer(object): obj.imports.add(self.import_instance) except IntegrityError as e: raise IntegrityError(e.message) - except: + except cls.MultipleObjectsReturned: created = False obj = cls.objects.filter(**create_dict).all()[0] for attr, value in m2ms: @@ -837,8 +899,17 @@ class Importer(object): for v in values: getattr(obj, attr).add(v) except IntegrityError as e: - raise ImporterError("Erreur d'import %s, contexte : %s, erreur : %s" \ - % (unicode(cls), unicode(data), e.message.decode('utf-8'))) + message = e.message + try: + message = unicode(e.message.decode('utf-8')) + except (UnicodeDecodeError, UnicodeDecodeError): + message = '' + try: + data = unicode(data) + except UnicodeDecodeError: + data = '' + raise ImporterError(u"Erreur d'import %s, contexte : %s, erreur : %s" \ + % (unicode(cls), data, message)) return obj, created return data diff --git a/ishtar_common/fixtures/initial_dpts-fr.json b/ishtar_common/fixtures/initial_dpts-fr.json new file mode 100644 index 000000000..721a96a8a --- /dev/null +++ b/ishtar_common/fixtures/initial_dpts-fr.json @@ -0,0 +1,1127 @@ +[ + { + "pk": 28, + "model": "ishtar_common.state", + "fields": { + "number": "1", + "label": "Guadeloupe" + } + }, + { + "pk": 33, + "model": "ishtar_common.state", + "fields": { + "number": "11", + "label": "\u00cele-de-France" + } + }, + { + "pk": 29, + "model": "ishtar_common.state", + "fields": { + "number": "2", + "label": "Martinique" + } + }, + { + "pk": 34, + "model": "ishtar_common.state", + "fields": { + "number": "21", + "label": "Champagne-Ardenne" + } + }, + { + "pk": 35, + "model": "ishtar_common.state", + "fields": { + "number": "22", + "label": "Picardie" + } + }, + { + "pk": 36, + "model": "ishtar_common.state", + "fields": { + "number": "23", + "label": "Haute-Normandie" + } + }, + { + "pk": 37, + "model": "ishtar_common.state", + "fields": { + "number": "24", + "label": "Centre" + } + }, + { + "pk": 38, + "model": "ishtar_common.state", + "fields": { + "number": "25", + "label": "Basse-Normandie" + } + }, + { + "pk": 39, + "model": "ishtar_common.state", + "fields": { + "number": "26", + "label": "Bourgogne" + } + }, + { + "pk": 30, + "model": "ishtar_common.state", + "fields": { + "number": "3", + "label": "Guyane" + } + }, + { + "pk": 40, + "model": "ishtar_common.state", + "fields": { + "number": "31", + "label": "Nord-Pas-de-Calais" + } + }, + { + "pk": 31, + "model": "ishtar_common.state", + "fields": { + "number": "4", + "label": "La R\u00e9union" + } + }, + { + "pk": 41, + "model": "ishtar_common.state", + "fields": { + "number": "41", + "label": "Lorraine" + } + }, + { + "pk": 42, + "model": "ishtar_common.state", + "fields": { + "number": "42", + "label": "Alsace" + } + }, + { + "pk": 43, + "model": "ishtar_common.state", + "fields": { + "number": "43", + "label": "Franche-Comt\u00e9" + } + }, + { + "pk": 44, + "model": "ishtar_common.state", + "fields": { + "number": "52", + "label": "Pays de la Loire" + } + }, + { + "pk": 45, + "model": "ishtar_common.state", + "fields": { + "number": "53", + "label": "Bretagne" + } + }, + { + "pk": 46, + "model": "ishtar_common.state", + "fields": { + "number": "54", + "label": "Poitou-Charentes" + } + }, + { + "pk": 32, + "model": "ishtar_common.state", + "fields": { + "number": "6", + "label": "Mayotte" + } + }, + { + "pk": 47, + "model": "ishtar_common.state", + "fields": { + "number": "72", + "label": "Aquitaine" + } + }, + { + "pk": 48, + "model": "ishtar_common.state", + "fields": { + "number": "73", + "label": "Midi-Pyr\u00e9n\u00e9es" + } + }, + { + "pk": 49, + "model": "ishtar_common.state", + "fields": { + "number": "74", + "label": "Limousin" + } + }, + { + "pk": 50, + "model": "ishtar_common.state", + "fields": { + "number": "82", + "label": "Rh\u00f4ne-Alpes" + } + }, + { + "pk": 51, + "model": "ishtar_common.state", + "fields": { + "number": "83", + "label": "Auvergne" + } + }, + { + "pk": 52, + "model": "ishtar_common.state", + "fields": { + "number": "91", + "label": "Languedoc-Roussillon" + } + }, + { + "pk": 53, + "model": "ishtar_common.state", + "fields": { + "number": "93", + "label": "Provence-Alpes-C\u00f4te d'Azur" + } + }, + { + "pk": 54, + "model": "ishtar_common.state", + "fields": { + "number": "94", + "label": "Corse" + } + }, + { + "pk": 1, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "01", + "label": "Ain" + } + }, + { + "pk": 2, + "model": "ishtar_common.department", + "fields": { + "state": 35, + "number": "02", + "label": "Aisne" + } + }, + { + "pk": 3, + "model": "ishtar_common.department", + "fields": { + "state": 51, + "number": "03", + "label": "Allier" + } + }, + { + "pk": 4, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "04", + "label": "Alpes-de-Haute-Provence" + } + }, + { + "pk": 5, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "05", + "label": "Hautes-Alpes" + } + }, + { + "pk": 6, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "06", + "label": "Alpes-Maritimes" + } + }, + { + "pk": 7, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "07", + "label": "Ard\u00e8che" + } + }, + { + "pk": 8, + "model": "ishtar_common.department", + "fields": { + "state": 34, + "number": "08", + "label": "Ardennes" + } + }, + { + "pk": 9, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "09", + "label": "Ari\u00e8ge" + } + }, + { + "pk": 10, + "model": "ishtar_common.department", + "fields": { + "state": 34, + "number": "10", + "label": "Aube" + } + }, + { + "pk": 11, + "model": "ishtar_common.department", + "fields": { + "state": 52, + "number": "11", + "label": "Aude" + } + }, + { + "pk": 12, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "12", + "label": "Aveyron" + } + }, + { + "pk": 13, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "13", + "label": "Bouches-du-Rh\u00f4ne" + } + }, + { + "pk": 14, + "model": "ishtar_common.department", + "fields": { + "state": 38, + "number": "14", + "label": "Calvados" + } + }, + { + "pk": 15, + "model": "ishtar_common.department", + "fields": { + "state": 51, + "number": "15", + "label": "Cantal" + } + }, + { + "pk": 16, + "model": "ishtar_common.department", + "fields": { + "state": 46, + "number": "16", + "label": "Charente" + } + }, + { + "pk": 17, + "model": "ishtar_common.department", + "fields": { + "state": 46, + "number": "17", + "label": "Charente-Maritime" + } + }, + { + "pk": 18, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "18", + "label": "Cher" + } + }, + { + "pk": 19, + "model": "ishtar_common.department", + "fields": { + "state": 49, + "number": "19", + "label": "Corr\u00e8ze" + } + }, + { + "pk": 22, + "model": "ishtar_common.department", + "fields": { + "state": 39, + "number": "21", + "label": "C\u00f4te-d'Or" + } + }, + { + "pk": 23, + "model": "ishtar_common.department", + "fields": { + "state": 45, + "number": "22", + "label": "C\u00f4tes-d'Armor" + } + }, + { + "pk": 24, + "model": "ishtar_common.department", + "fields": { + "state": 49, + "number": "23", + "label": "Creuse" + } + }, + { + "pk": 25, + "model": "ishtar_common.department", + "fields": { + "state": 47, + "number": "24", + "label": "Dordogne" + } + }, + { + "pk": 26, + "model": "ishtar_common.department", + "fields": { + "state": 43, + "number": "25", + "label": "Doubs" + } + }, + { + "pk": 27, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "26", + "label": "Dr\u00f4me" + } + }, + { + "pk": 28, + "model": "ishtar_common.department", + "fields": { + "state": 36, + "number": "27", + "label": "Eure" + } + }, + { + "pk": 29, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "28", + "label": "Eure-et-Loir" + } + }, + { + "pk": 30, + "model": "ishtar_common.department", + "fields": { + "state": 45, + "number": "29", + "label": "Finist\u00e8re" + } + }, + { + "pk": 20, + "model": "ishtar_common.department", + "fields": { + "state": 54, + "number": "2A", + "label": "Corse-du-Sud" + } + }, + { + "pk": 21, + "model": "ishtar_common.department", + "fields": { + "state": 54, + "number": "2B", + "label": "Haute-Corse" + } + }, + { + "pk": 31, + "model": "ishtar_common.department", + "fields": { + "state": 52, + "number": "30", + "label": "Gard" + } + }, + { + "pk": 32, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "31", + "label": "Haute-Garonne" + } + }, + { + "pk": 33, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "32", + "label": "Gers" + } + }, + { + "pk": 34, + "model": "ishtar_common.department", + "fields": { + "state": 47, + "number": "33", + "label": "Gironde" + } + }, + { + "pk": 35, + "model": "ishtar_common.department", + "fields": { + "state": 52, + "number": "34", + "label": "H\u00e9rault" + } + }, + { + "pk": 36, + "model": "ishtar_common.department", + "fields": { + "state": 45, + "number": "35", + "label": "Ille-et-Vilaine" + } + }, + { + "pk": 37, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "36", + "label": "Indre" + } + }, + { + "pk": 38, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "37", + "label": "Indre-et-Loire" + } + }, + { + "pk": 39, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "38", + "label": "Is\u00e8re" + } + }, + { + "pk": 40, + "model": "ishtar_common.department", + "fields": { + "state": 43, + "number": "39", + "label": "Jura" + } + }, + { + "pk": 41, + "model": "ishtar_common.department", + "fields": { + "state": 47, + "number": "40", + "label": "Landes" + } + }, + { + "pk": 42, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "41", + "label": "Loir-et-Cher" + } + }, + { + "pk": 43, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "42", + "label": "Loire" + } + }, + { + "pk": 44, + "model": "ishtar_common.department", + "fields": { + "state": 51, + "number": "43", + "label": "Haute-Loire" + } + }, + { + "pk": 45, + "model": "ishtar_common.department", + "fields": { + "state": 44, + "number": "44", + "label": "Loire-Atlantique" + } + }, + { + "pk": 46, + "model": "ishtar_common.department", + "fields": { + "state": 37, + "number": "45", + "label": "Loiret" + } + }, + { + "pk": 47, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "46", + "label": "Lot" + } + }, + { + "pk": 48, + "model": "ishtar_common.department", + "fields": { + "state": 47, + "number": "47", + "label": "Lot-et-Garonne" + } + }, + { + "pk": 49, + "model": "ishtar_common.department", + "fields": { + "state": 52, + "number": "48", + "label": "Loz\u00e8re" + } + }, + { + "pk": 50, + "model": "ishtar_common.department", + "fields": { + "state": 44, + "number": "49", + "label": "Maine-et-Loire" + } + }, + { + "pk": 51, + "model": "ishtar_common.department", + "fields": { + "state": 38, + "number": "50", + "label": "Manche" + } + }, + { + "pk": 52, + "model": "ishtar_common.department", + "fields": { + "state": 34, + "number": "51", + "label": "Marne" + } + }, + { + "pk": 53, + "model": "ishtar_common.department", + "fields": { + "state": 34, + "number": "52", + "label": "Haute-Marne" + } + }, + { + "pk": 54, + "model": "ishtar_common.department", + "fields": { + "state": 44, + "number": "53", + "label": "Mayenne" + } + }, + { + "pk": 55, + "model": "ishtar_common.department", + "fields": { + "state": 41, + "number": "54", + "label": "Meurthe-et-Moselle" + } + }, + { + "pk": 56, + "model": "ishtar_common.department", + "fields": { + "state": 41, + "number": "55", + "label": "Meuse" + } + }, + { + "pk": 57, + "model": "ishtar_common.department", + "fields": { + "state": 45, + "number": "56", + "label": "Morbihan" + } + }, + { + "pk": 58, + "model": "ishtar_common.department", + "fields": { + "state": 41, + "number": "57", + "label": "Moselle" + } + }, + { + "pk": 59, + "model": "ishtar_common.department", + "fields": { + "state": 39, + "number": "58", + "label": "Ni\u00e8vre" + } + }, + { + "pk": 60, + "model": "ishtar_common.department", + "fields": { + "state": 40, + "number": "59", + "label": "Nord" + } + }, + { + "pk": 61, + "model": "ishtar_common.department", + "fields": { + "state": 35, + "number": "60", + "label": "Oise" + } + }, + { + "pk": 62, + "model": "ishtar_common.department", + "fields": { + "state": 38, + "number": "61", + "label": "Orne" + } + }, + { + "pk": 63, + "model": "ishtar_common.department", + "fields": { + "state": 40, + "number": "62", + "label": "Pas-de-Calais" + } + }, + { + "pk": 64, + "model": "ishtar_common.department", + "fields": { + "state": 51, + "number": "63", + "label": "Puy-de-D\u00f4me" + } + }, + { + "pk": 65, + "model": "ishtar_common.department", + "fields": { + "state": 47, + "number": "64", + "label": "Pyr\u00e9n\u00e9es-Atlantiques" + } + }, + { + "pk": 66, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "65", + "label": "Hautes-Pyr\u00e9n\u00e9es" + } + }, + { + "pk": 67, + "model": "ishtar_common.department", + "fields": { + "state": 52, + "number": "66", + "label": "Pyr\u00e9n\u00e9es-Orientales" + } + }, + { + "pk": 68, + "model": "ishtar_common.department", + "fields": { + "state": 42, + "number": "67", + "label": "Bas-Rhin" + } + }, + { + "pk": 69, + "model": "ishtar_common.department", + "fields": { + "state": 42, + "number": "68", + "label": "Haut-Rhin" + } + }, + { + "pk": 70, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "69", + "label": "Rh\u00f4ne" + } + }, + { + "pk": 71, + "model": "ishtar_common.department", + "fields": { + "state": 43, + "number": "70", + "label": "Haute-Sa\u00f4ne" + } + }, + { + "pk": 72, + "model": "ishtar_common.department", + "fields": { + "state": 39, + "number": "71", + "label": "Sa\u00f4ne-et-Loire" + } + }, + { + "pk": 73, + "model": "ishtar_common.department", + "fields": { + "state": 44, + "number": "72", + "label": "Sarthe" + } + }, + { + "pk": 74, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "73", + "label": "Savoie" + } + }, + { + "pk": 75, + "model": "ishtar_common.department", + "fields": { + "state": 50, + "number": "74", + "label": "Haute-Savoie" + } + }, + { + "pk": 76, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "75", + "label": "Paris" + } + }, + { + "pk": 77, + "model": "ishtar_common.department", + "fields": { + "state": 36, + "number": "76", + "label": "Seine-Maritime" + } + }, + { + "pk": 78, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "77", + "label": "Seine-et-Marne" + } + }, + { + "pk": 79, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "78", + "label": "Yvelines" + } + }, + { + "pk": 80, + "model": "ishtar_common.department", + "fields": { + "state": 46, + "number": "79", + "label": "Deux-S\u00e8vres" + } + }, + { + "pk": 81, + "model": "ishtar_common.department", + "fields": { + "state": 35, + "number": "80", + "label": "Somme" + } + }, + { + "pk": 82, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "81", + "label": "Tarn" + } + }, + { + "pk": 83, + "model": "ishtar_common.department", + "fields": { + "state": 48, + "number": "82", + "label": "Tarn-et-Garonne" + } + }, + { + "pk": 84, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "83", + "label": "Var" + } + }, + { + "pk": 85, + "model": "ishtar_common.department", + "fields": { + "state": 53, + "number": "84", + "label": "Vaucluse" + } + }, + { + "pk": 86, + "model": "ishtar_common.department", + "fields": { + "state": 44, + "number": "85", + "label": "Vend\u00e9e" + } + }, + { + "pk": 87, + "model": "ishtar_common.department", + "fields": { + "state": 46, + "number": "86", + "label": "Vienne" + } + }, + { + "pk": 88, + "model": "ishtar_common.department", + "fields": { + "state": 49, + "number": "87", + "label": "Haute-Vienne" + } + }, + { + "pk": 89, + "model": "ishtar_common.department", + "fields": { + "state": 41, + "number": "88", + "label": "Vosges" + } + }, + { + "pk": 90, + "model": "ishtar_common.department", + "fields": { + "state": 39, + "number": "89", + "label": "Yonne" + } + }, + { + "pk": 91, + "model": "ishtar_common.department", + "fields": { + "state": 43, + "number": "90", + "label": "Territoire de Belfort" + } + }, + { + "pk": 92, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "91", + "label": "Essonne" + } + }, + { + "pk": 93, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "92", + "label": "Hauts-de-Seine" + } + }, + { + "pk": 94, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "93", + "label": "Seine-Saint-Denis" + } + }, + { + "pk": 95, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "94", + "label": "Val-de-Marne" + } + }, + { + "pk": 96, + "model": "ishtar_common.department", + "fields": { + "state": 33, + "number": "95", + "label": "Val-d'Oise" + } + }, + { + "pk": 97, + "model": "ishtar_common.department", + "fields": { + "state": 28, + "number": "971", + "label": "Guadeloupe" + } + }, + { + "pk": 98, + "model": "ishtar_common.department", + "fields": { + "state": 29, + "number": "972", + "label": "Martinique" + } + }, + { + "pk": 99, + "model": "ishtar_common.department", + "fields": { + "state": 30, + "number": "973", + "label": "Guyane" + } + }, + { + "pk": 100, + "model": "ishtar_common.department", + "fields": { + "state": 31, + "number": "974", + "label": "La R\u00e9union" + } + }, + { + "pk": 101, + "model": "ishtar_common.department", + "fields": { + "state": 32, + "number": "976", + "label": "Mayotte" + } + } +]
\ No newline at end of file diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 88a3306f4..e6f21ae5b 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2010-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2010-2015 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index d8740b68c..23d7126c9 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -54,6 +54,14 @@ def get_town_field(label=_(u"Town"), required=True): validators=[models.valid_id(models.Town)], label=label, help_text=mark_safe(help_text), required=required) +def get_advanced_town_field(label=_(u"Town"), required=True): + # !FIXME hard_link, reverse_lazy doen't seem to work with formsets + return forms.IntegerField( + widget=widgets.JQueryTown("/" + settings.URL_PATH + \ + 'autocomplete-advanced-town'), + validators=[models.valid_id(models.Town)], label=label, + required=required) + def get_person_field(label=_(u"Person"), required=True, person_types=[]): # !FIXME hard_link, reverse_lazy doen't seem to work with formsets widget = None @@ -191,6 +199,13 @@ class OrganizationFormSelection(forms.Form): OrganizationSelect, models.Organization), validators=[models.valid_id(models.Organization)]) +class BaseOrganizationForm(forms.ModelForm): + form_prefix = "orga" + class Meta: + model = models.Organization + fields = ['name', 'organization_type', 'address', 'address_complement', + 'town', 'postal_code',] + class PersonSelect(TableSelect): name = forms.CharField(label=_(u"Name"), max_length=30) surname = forms.CharField(label=_(u"Surname"), max_length=20) @@ -242,6 +257,39 @@ class SimplePersonForm(NewItemForm): associated_model=models.Organization, new=True), validators=[models.valid_id(models.Organization)], required=False) +class BasePersonForm(forms.ModelForm): + class Meta: + model = models.Person + fields = ['title', 'name', 'surname', 'address', 'address_complement', + 'town', 'postal_code'] + +class BaseOrganizationPersonForm(forms.ModelForm): + class Meta: + model = models.Person + fields = ['attached_to', 'title', 'name', 'surname',] + widgets = {'attached_to':widgets.JQueryPersonOrganization( + reverse_lazy('autocomplete-organization'), + reverse_lazy('organization_create'), + model=models.Organization, + attrs={'hidden':True}, + new=True), + } + + def __init__(self, *args, **kwargs): + super(BaseOrganizationPersonForm, self).__init__(*args, **kwargs) + + def save(self, *args, **kwargs): + person = super(BaseOrganizationPersonForm, self).save(*args, **kwargs) + instance = person.attached_to + form = BaseOrganizationForm(self.data, instance=instance, + prefix=BaseOrganizationForm.form_prefix) + if form.is_valid(): + orga = form.save() + if not person.attached_to: + person.attached_to = orga + person.save() + return person + class PersonForm(SimplePersonForm): person_types = forms.MultipleChoiceField(label=_("Person type"), choices=[], widget=forms.CheckboxSelectMultiple) diff --git a/ishtar_common/ishtar_menu.py b/ishtar_common/ishtar_menu.py index fdfe60448..a26234265 100644 --- a/ishtar_common/ishtar_menu.py +++ b/ishtar_common/ishtar_menu.py @@ -39,6 +39,9 @@ MENU_SECTIONS = [ childs=[ SectionItem('person', _(u"Person"), childs=[ + MenuItem('person_search', _(u"Search"), + model=models.Person, + access_controls=['add_person', 'add_own_person']), MenuItem('person_creation', _(u"Creation"), model=models.Person, access_controls=['add_person', 'add_own_person']), @@ -54,6 +57,10 @@ MENU_SECTIONS = [ ]), SectionItem('organization', _(u"Organization"), childs=[ + MenuItem('organization_search', _(u"Search"), + model=models.Organization, + access_controls=['add_organization', + 'add_own_organization']), MenuItem('organization_creation', _(u"Creation"), model=models.Organization, access_controls=['add_organization', diff --git a/ishtar_common/locale/fr/LC_MESSAGES/django.po b/ishtar_common/locale/fr/LC_MESSAGES/django.po index 2a957a357..f9926d744 100644 --- a/ishtar_common/locale/fr/LC_MESSAGES/django.po +++ b/ishtar_common/locale/fr/LC_MESSAGES/django.po @@ -1,15 +1,15 @@ # Ishtar po translation. -# Copyright (C) 2010-2015 +# Copyright (C) 2010-2013 # This file is distributed under the same license as the Ishtar package. -# Étienne Loks <etienne.loks at peacefrogs net>, 2010-2015. +# Étienne Loks <etienne.loks at peacefrogs net>, 2010-2013. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-25 01:39+0100\n" -"PO-Revision-Date: 2015-01-25\n" +"POT-Creation-Date: 2014-12-31 12:21+0100\n" +"PO-Revision-Date: 2010-12-09\n" "Last-Translator: Étienne Loks <etienne.loks at peacefrogs net>\n" "Language-Team: \n" "Language: \n" @@ -47,37 +47,38 @@ msgstr "Mobilier" #: data_importer.py:117 #, python-format msgid "\"%(value)s\" is too long. The max length is %(length)d characters." -msgstr "\"%(value)s\" est trop long. La longueur maximum est de %(length)d caractères." +msgstr "" +"\"%(value)s\" est trop long. La longeur maximale est de %(length)d caractères." #: data_importer.py:129 #, python-format msgid "\"%(value)s\" not equal to yes or no" -msgstr "\"%(value)s\" diffère de oui ou non" +msgstr "\"%(value)s\" n'est pas égal à oui ou non" #: data_importer.py:140 #, python-format msgid "\"%(value)s\" is not a float" -msgstr "\"%(value)s\" n'est pas un flottant" +msgstr "\"%(value)s\" n'est pas un chiffre à virgule" #: data_importer.py:152 data_importer.py:164 data_importer.py:248 #, python-format msgid "\"%(value)s\" is not a valid date" -msgstr "\"%(value)s\" n'est pas une date valide" +msgstr "\"%(value)s\" n'est pas une date valide" #: data_importer.py:175 #, python-format msgid "\"%(value)s\" is not an integer" -msgstr "\"%(value)s\" n'est pas un entier" +msgstr "\"%(value)s\" n'est pas un entier" #: data_importer.py:203 #, python-format msgid "Choice for \"%s\" is not available. Which one is relevant?\n" -msgstr "Le choix pour \"%s\" n'est pas disponible. Lequel est pertinent ?\n" +msgstr "Le choix pour \"%s\" n'est pas disponible. Lequel est pertinent ?\n" #: data_importer.py:207 #, python-format msgid "%d. None of the above" -msgstr "%d. Aucun de ceux-là " +msgstr "%d. Aucun des choix" #: data_importer.py:260 msgid "" @@ -85,22 +86,22 @@ msgid "" "check that column separator and encoding are similar to the ones used by the " "reference file." msgstr "" -"Le fichier fourni n'est pas correct. Vérifiez le format du fichier. " -"Si vous utilisez un fichier CSV : vérifiez que le séparateur de colonne et " -"l'encodage sont similaires à ceux du fichier de référence." +"Le fichier fourni n'est pas correct. Vérifiez le format du fichier. Si vous " +"utilisez un fichier CSV : vérifiez que les séparateurs de colonne et le " +"codage des caractères correspond au fichier de référence." #: data_importer.py:264 #, python-format msgid "Too many cols (%(user_col)d) when maximum is %(ref_col)d" -msgstr "Trop de colonnes (%(user_col)d). Le maximum est %(ref_col)d" +msgstr "Trop de colonnes (%(user_col)d) quand le maximum est %(ref_col)d" #: data_importer.py:266 msgid "No data provided" -msgstr "Aucune donnée fournie" +msgstr "Pas de donnée fourni" #: data_importer.py:267 msgid "Value is required" -msgstr "Valeur requise" +msgstr "Une valeur est attendue" #: data_importer.py:268 #, python-format @@ -109,7 +110,7 @@ msgstr "Au moins %d colonnes doivent être remplies" #: data_importer.py:269 msgid "The regexp doesn't match." -msgstr "L'expression régulière ne fonctionne pas" +msgstr "L'espression régulière ne correspond pas." #: data_importer.py:857 #, python-format @@ -120,13 +121,13 @@ msgstr "\"%(value)s\" n'est pas dans %(values)s" msgid "Enter a valid name consisting of letters, spaces and hyphens." msgstr "Entrez un nom correct composé de lettres, espaces et tirets." -#: forms.py:76 forms_common.py:283 +#: forms.py:76 forms_common.py:331 msgid "Confirm" msgstr "Confirmation" #: forms.py:80 msgid "Are you sure you want to delete?" -msgstr "Êtes-vous sûr de vouloir supprimer ?" +msgstr "Êtes vous sûr de vouloir supprimer ?" #: forms.py:88 msgid "There are identical items." @@ -134,7 +135,7 @@ msgstr "Il y a des éléments identiques." #: forms.py:122 forms.py:123 msgid "Closing date" -msgstr "Date de clôture" +msgstr "Date de clotûre" #: forms.py:127 msgid "You should select an item." @@ -148,8 +149,9 @@ msgstr "Ajouter un nouvel élément" msgid "Template" msgstr "Patron" -#: forms_common.py:43 forms_common.py:105 forms_common.py:186 -#: forms_common.py:191 models.py:829 models.py:1204 +#: forms_common.py:43 forms_common.py:57 forms_common.py:113 +#: forms_common.py:201 forms_common.py:206 models.py:842 models.py:1223 +#: templates/blocks/JQueryAdvancedTown.html:19 #: templates/ishtar/sheet_organization.html:17 #: templates/ishtar/sheet_person.html:20 templates/ishtar/sheet_person.html:30 msgid "Town" @@ -167,74 +169,74 @@ msgstr "" "<p>Tapez le nom, le numéro de département et - ou le code postal de la " "commune que vous voulez sélectionner. La recherche n'est pas sensible à la " "casse.</p>\n" -"<p>Seuls les vingt premiers résultats sont affichés mais en plus du nom, " +"<p>Seuls les vingt premiers résultats sont affichés mais en plus du nom " "préciser le numéro de département est généralement suffisant pour obtenir le " "résultat souhaité.</p>\n" "<p class='example'>Par exemple tapez « saint denis 93 » pour obtenir la " "commune Saint-Denis dans le département français de Seine-Saint-Denis.</p>" -#: forms_common.py:57 forms_common.py:471 ishtar_menu.py:40 models.py:1005 -#: models.py:1089 models.py:1129 templates/ishtar/sheet_person.html:6 +#: forms_common.py:65 forms_common.py:519 ishtar_menu.py:40 models.py:1018 +#: models.py:1108 models.py:1148 templates/ishtar/sheet_person.html:6 msgid "Person" msgstr "Individu" -#: forms_common.py:93 forms_common.py:153 ishtar_menu.py:55 models.py:945 +#: forms_common.py:101 forms_common.py:168 ishtar_menu.py:55 models.py:958 #: templates/ishtar/sheet_organization.html:6 msgid "Organization" msgstr "Organisation" -#: forms_common.py:95 forms_common.py:131 forms_common.py:149 -#: forms_common.py:178 models.py:779 models.py:911 models.py:940 models.py:995 -#: models.py:1190 templates/ishtar/sheet_organization.html:12 +#: forms_common.py:103 forms_common.py:139 forms_common.py:164 +#: forms_common.py:193 models.py:779 models.py:924 models.py:953 +#: models.py:1008 models.py:1209 templates/ishtar/sheet_organization.html:12 #: templates/ishtar/sheet_organization.html:25 #: templates/ishtar/sheet_person.html:12 templates/ishtar/sheet_person.html:26 msgid "Name" msgstr "Nom" -#: forms_common.py:97 models.py:893 +#: forms_common.py:105 models.py:906 msgid "Organization type" msgstr "Type d'organisation" -#: forms_common.py:99 forms_common.py:180 models.py:824 +#: forms_common.py:107 forms_common.py:195 models.py:837 #: templates/ishtar/sheet_organization.html:14 #: templates/ishtar/sheet_person.html:17 templates/ishtar/sheet_person.html:27 msgid "Address" msgstr "Adresse" -#: forms_common.py:101 forms_common.py:182 models.py:825 +#: forms_common.py:109 forms_common.py:197 models.py:838 #: templates/ishtar/sheet_organization.html:15 #: templates/ishtar/sheet_person.html:18 templates/ishtar/sheet_person.html:28 msgid "Address complement" msgstr "Complément d'adresse" -#: forms_common.py:103 forms_common.py:184 models.py:827 +#: forms_common.py:111 forms_common.py:199 models.py:840 #: templates/ishtar/sheet_organization.html:16 #: templates/ishtar/sheet_person.html:19 templates/ishtar/sheet_person.html:29 msgid "Postal code" msgstr "Code postal" -#: forms_common.py:106 forms_common.py:187 models.py:830 +#: forms_common.py:114 forms_common.py:202 models.py:843 msgid "Country" msgstr "Pays" -#: forms_common.py:108 forms_common.py:151 forms_common.py:189 -#: forms_common.py:241 models.py:835 templates/ishtar/sheet_person.html:15 +#: forms_common.py:116 forms_common.py:166 forms_common.py:204 +#: forms_common.py:289 models.py:848 templates/ishtar/sheet_person.html:15 msgid "Email" msgstr "Courriel" -#: forms_common.py:109 forms_common.py:190 models.py:832 +#: forms_common.py:117 forms_common.py:205 models.py:845 #: templates/ishtar/sheet_organization.html:18 #: templates/ishtar/sheet_person.html:21 templates/ishtar/sheet_person.html:31 msgid "Phone" msgstr "Téléphone" -#: forms_common.py:110 models.py:833 +#: forms_common.py:118 models.py:846 #: templates/ishtar/sheet_organization.html:19 #: templates/ishtar/sheet_person.html:22 templates/ishtar/sheet_person.html:32 msgid "Mobile phone" msgstr "Téléphone portable" -#: forms_common.py:132 forms_common.py:152 models.py:942 models.py:1153 +#: forms_common.py:140 forms_common.py:167 models.py:955 models.py:1172 #: templates/sheet_ope.html:85 templates/sheet_ope.html.py:105 #: templates/sheet_ope.html:126 templates/ishtar/sheet_organization.html:27 #: templates/ishtar/sheet_person.html:42 templates/ishtar/sheet_person.html:95 @@ -242,121 +244,121 @@ msgstr "Téléphone portable" msgid "Type" msgstr "Type" -#: forms_common.py:140 +#: forms_common.py:148 msgid "Organization search" msgstr "Recherche d'organisations" -#: forms_common.py:150 forms_common.py:176 models.py:993 +#: forms_common.py:165 forms_common.py:191 models.py:1006 #: templates/ishtar/sheet_organization.html:26 #: templates/ishtar/sheet_person.html:13 msgid "Surname" msgstr "Prénom" -#: forms_common.py:164 +#: forms_common.py:179 msgid "Person search" msgstr "Recherche d'individus" -#: forms_common.py:173 +#: forms_common.py:188 msgid "Identity" msgstr "Identité" -#: forms_common.py:175 forms_common.py:428 models.py:992 models.py:1152 +#: forms_common.py:190 forms_common.py:476 models.py:1005 models.py:1171 #: templates/sheet_ope.html:104 templates/ishtar/sheet_person.html:94 #: templates/ishtar/blocks/window_tables/documents.html:5 msgid "Title" msgstr "Titre" -#: forms_common.py:193 +#: forms_common.py:208 msgid "Current organization" msgstr "Organisation actuelle" -#: forms_common.py:200 forms_common.py:223 forms_common.py:226 models.py:978 +#: forms_common.py:248 forms_common.py:271 forms_common.py:274 models.py:991 msgid "Person type" msgstr "Type d'individu" -#: forms_common.py:236 forms_common.py:240 +#: forms_common.py:284 forms_common.py:288 msgid "Account" msgstr "Compte" -#: forms_common.py:243 wizards.py:938 +#: forms_common.py:291 wizards.py:941 msgid "New password" msgstr "Nouveau mot de passe" -#: forms_common.py:247 +#: forms_common.py:295 msgid "New password (confirmation)" msgstr "Nouveau mot de passe (confirmation)" -#: forms_common.py:265 +#: forms_common.py:313 msgid "Your password and confirmation password do not match." msgstr "La vérification du mot de passe a échoué." -#: forms_common.py:270 +#: forms_common.py:318 msgid "You must provide a correct password." msgstr "Vous devez fournir un mot de passe correct." -#: forms_common.py:278 +#: forms_common.py:326 msgid "This username already exists." msgstr "Ce nom d'utilisateur existe déjà ." -#: forms_common.py:284 +#: forms_common.py:332 msgid "Send the new password by email?" msgstr "Envoyer le nouveau mot de passe par courriel ?" -#: forms_common.py:292 forms_common.py:304 models.py:1205 +#: forms_common.py:340 forms_common.py:352 models.py:1224 #: templates/ishtar/sheet_person.html:72 msgid "Towns" msgstr "Communes" -#: forms_common.py:301 +#: forms_common.py:349 msgid "There are identical towns." msgstr "Il y a des communes identiques." -#: forms_common.py:380 +#: forms_common.py:428 msgid "Only one choice can be checked." -msgstr "Seul un choix peut être coché." +msgstr "Seul un choix peut être sélectionné." -#: forms_common.py:426 +#: forms_common.py:474 msgid "Documentation informations" msgstr "Information sur le document" -#: forms_common.py:430 forms_common.py:449 models.py:1148 +#: forms_common.py:478 forms_common.py:497 models.py:1167 msgid "Source type" msgstr "Type de source" -#: forms_common.py:432 models.py:1157 +#: forms_common.py:480 models.py:1176 msgid "Numerical ressource (web address)" msgstr "Ressource numérique (adresse web)" -#: forms_common.py:433 models.py:1159 +#: forms_common.py:481 models.py:1178 msgid "Receipt date" msgstr "Date de réception" -#: forms_common.py:435 models.py:925 models.py:1161 +#: forms_common.py:483 models.py:938 models.py:1180 msgid "Creation date" msgstr "Date de création" -#: forms_common.py:446 forms_common.py:465 forms_common.py:496 models.py:1134 +#: forms_common.py:494 forms_common.py:513 forms_common.py:544 models.py:1153 #: templates/ishtar/wizard/wizard_person_deletion.html:124 msgid "Author" msgstr "Auteur" -#: forms_common.py:458 +#: forms_common.py:506 msgid "Would you like to delete this documentation?" -msgstr "Voulez-vous supprimer ce document ?" +msgstr "Voulez vous supprimer ce document ?" -#: forms_common.py:472 models.py:1125 models.py:1131 +#: forms_common.py:520 models.py:1144 models.py:1150 msgid "Author type" msgstr "Type d'auteur" -#: forms_common.py:490 +#: forms_common.py:538 msgid "Author selection" msgstr "Sélection d'auteur" -#: forms_common.py:502 +#: forms_common.py:550 msgid "There are identical authors." msgstr "Il y a des auteurs identiques." -#: forms_common.py:506 models.py:1135 models.py:1154 +#: forms_common.py:554 models.py:1154 models.py:1173 #: templates/sheet_ope.html:106 #: templates/ishtar/blocks/window_tables/documents.html:7 msgid "Authors" @@ -366,7 +368,7 @@ msgstr "Auteurs" msgid "Administration" msgstr "Administration" -#: ishtar_menu.py:30 views.py:122 +#: ishtar_menu.py:30 views.py:126 msgid "Account management" msgstr "Gestion des comptes" @@ -388,11 +390,11 @@ msgstr "Modification" #: ishtar_menu.py:48 ishtar_menu.py:65 templates/ishtar/merge.html:5 msgid "Merge" -msgstr "Fusion" +msgstr "Rapprochement" -#: ishtar_menu.py:51 ishtar_menu.py:68 widgets.py:81 +#: ishtar_menu.py:51 ishtar_menu.py:68 widgets.py:82 msgid "Delete" -msgstr "Suppression" +msgstr "Supprimer" #: models.py:148 msgid "Not a valid item." @@ -406,7 +408,7 @@ msgstr "Un élément sélectionné n'est pas valide." msgid "This item already exist." msgstr "Cet élément existe déjà ." -#: models.py:222 models.py:812 +#: models.py:222 models.py:812 models.py:823 msgid "Label" msgstr "Libellé" @@ -450,7 +452,7 @@ msgstr "Variables globales" msgid "Total" msgstr "Total" -#: models.py:691 models.py:813 +#: models.py:691 models.py:813 models.py:824 #: templates/ishtar/dashboards/dashboard_main_detail.html:135 #: templates/ishtar/dashboards/dashboard_main_detail_users.html:26 msgid "Number" @@ -472,209 +474,208 @@ msgstr "Patron de document" msgid "Document templates" msgstr "Patrons de documents" -#: models.py:816 +#: models.py:816 models.py:825 models.py:937 +msgid "State" +msgstr "Région" + +#: models.py:829 templates/blocks/JQueryAdvancedTown.html:12 msgid "Department" msgstr "Département" -#: models.py:817 +#: models.py:830 msgid "Departments" msgstr "Départements" -#: models.py:842 +#: models.py:855 msgid "Merge key" -msgstr "Clé de fusion" +msgstr "Clé de rapprochement" -#: models.py:894 +#: models.py:907 msgid "Organization types" msgstr "Types d'organisation" -#: models.py:897 templates/ishtar/dashboards/dashboard_main_detail.html:61 +#: models.py:910 templates/ishtar/dashboards/dashboard_main_detail.html:61 msgid "Created" msgstr "Créé" -#: models.py:898 +#: models.py:911 msgid "Analyse in progress" msgstr "Analyse en cours" -#: models.py:899 +#: models.py:912 msgid "Analysed" -msgstr "Analysé" +msgstr "Analisé" -#: models.py:900 +#: models.py:913 msgid "Import pending" msgstr "Import en attente" -#: models.py:901 +#: models.py:914 msgid "Import in progress" msgstr "Import en cours" -#: models.py:902 +#: models.py:915 msgid "Finished" -msgstr "Terminé" +msgstr "Fini" -#: models.py:906 -#, fuzzy +#: models.py:919 msgid "Archaeological files - SRA Pays de la Loire" -msgstr "Dossiers archéologiques" +msgstr "Dossiers archéologiques - SRA Pays de la Loire" -#: models.py:907 +#: models.py:920 msgid "Custom" -msgstr "" +msgstr "Personnalisé" -#: models.py:914 +#: models.py:927 msgid "Imported file" msgstr "Fichier importé" -#: models.py:916 +#: models.py:929 msgid "Error file" -msgstr "Fichier erreur" +msgstr "Fichier d'erreur" -#: models.py:919 +#: models.py:932 msgid "Result file" -msgstr "Fichier résultant" +msgstr "Fichier de résultat" -#: models.py:922 +#: models.py:935 msgid "Importer type" -msgstr "" +msgstr "Type d'importeur" -#: models.py:924 -msgid "State" -msgstr "État" - -#: models.py:927 +#: models.py:940 msgid "End date" msgstr "Date de fin" -#: models.py:929 +#: models.py:942 msgid "Seconds remaining" msgstr "Secondes restantes" -#: models.py:932 +#: models.py:945 msgid "Import" msgstr "Import" -#: models.py:933 +#: models.py:946 msgid "Imports" msgstr "Imports" -#: models.py:946 +#: models.py:959 msgid "Organizations" msgstr "Organisations" -#: models.py:948 +#: models.py:961 msgid "Can view all Organization" msgstr "Peut voir toutes les Organisations" -#: models.py:949 +#: models.py:962 msgid "Can view own Organization" msgstr "Peut voir sa propre Organisation" -#: models.py:950 +#: models.py:963 msgid "Can add own Organization" msgstr "Peut ajouter sa propre Organisation" -#: models.py:951 +#: models.py:964 msgid "Can change own Organization" msgstr "Peut changer sa propre Organisation" -#: models.py:952 +#: models.py:965 msgid "Can delete own Organization" msgstr "Peut supprimer sa propre Organisation" -#: models.py:975 +#: models.py:988 msgid "Groups" msgstr "Groupes" -#: models.py:979 +#: models.py:992 msgid "Person types" msgstr "Types d'individu" -#: models.py:984 +#: models.py:997 msgid "Mr" msgstr "M." -#: models.py:985 +#: models.py:998 msgid "Miss" msgstr "Mlle" -#: models.py:986 +#: models.py:999 msgid "Mr and Miss" msgstr "M. et Mme" -#: models.py:987 +#: models.py:1000 msgid "Mrs" msgstr "Mme" -#: models.py:988 +#: models.py:1001 msgid "Doctor" msgstr "Dr." -#: models.py:997 +#: models.py:1010 msgid "Raw name" -msgstr "" +msgstr "Nom brut" -#: models.py:999 models.py:1036 +#: models.py:1012 models.py:1052 msgid "Types" msgstr "Types" -#: models.py:1002 +#: models.py:1015 msgid "Is attached to" msgstr "Est rattaché à " -#: models.py:1006 +#: models.py:1019 msgid "Persons" msgstr "Individus" -#: models.py:1008 +#: models.py:1021 msgid "Can view all Person" msgstr "Peut voir toutes les Personnes" -#: models.py:1009 +#: models.py:1022 msgid "Can view own Person" msgstr "Peut voir sa propre Personne" -#: models.py:1010 +#: models.py:1023 msgid "Can add own Person" msgstr "Peut ajouter sa propre Personne" -#: models.py:1011 +#: models.py:1024 msgid "Can change own Person" msgstr "Peut changer sa propre Personne" -#: models.py:1012 +#: models.py:1025 msgid "Can delete own Person" msgstr "Peut supprimer sa propre Personne" -#: models.py:1093 +#: models.py:1112 msgid "Ishtar user" msgstr "Utilisateur d'Ishtar" -#: models.py:1094 +#: models.py:1113 msgid "Ishtar users" msgstr "Utilisateurs d'Ishtar" -#: models.py:1126 +#: models.py:1145 msgid "Author types" msgstr "Types d'auteur" -#: models.py:1149 +#: models.py:1168 msgid "Source types" msgstr "Types de source" -#: models.py:1162 templates/ishtar/sheet_person.html:40 +#: models.py:1181 templates/ishtar/sheet_person.html:40 #: templates/ishtar/sheet_person.html:67 msgid "Ref." msgstr "Réf." -#: models.py:1164 +#: models.py:1183 msgid "Internal reference" msgstr "Référence interne" -#: models.py:1191 +#: models.py:1210 msgid "Surface (m²)" -msgstr "Surface (m²)" +msgstr "Area (m²)" -#: models.py:1192 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107 +#: models.py:1211 templates/sheet_ope.html:46 templates/sheet_ope.html.py:107 msgid "Localisation" msgstr "Localisation" @@ -682,90 +683,98 @@ msgstr "Localisation" msgid " (...)" msgstr " (...)" -#: views.py:82 +#: views.py:86 msgid "New person" msgstr "Nouvelle personne" -#: views.py:90 +#: views.py:94 msgid "Person modification" msgstr "Modification d'une personne" -#: views.py:96 +#: views.py:100 msgid "Person deletion" -msgstr "Suppression de personne" +msgstr "Suppresion de personne" -#: views.py:102 +#: views.py:106 msgid "New organization" msgstr "Nouvelle organisation" -#: views.py:109 +#: views.py:113 msgid "Organization modification" msgstr "Modification d'une organisation" -#: views.py:115 +#: views.py:119 msgid "Organization deletion" -msgstr "Suppression d'une organisation" +msgstr "Suppresion d'une organisation" -#: views.py:212 +#: views.py:255 msgid "True" msgstr "Oui" -#: views.py:214 +#: views.py:257 msgid "False" msgstr "Non" -#: views.py:437 templates/base.html:75 +#: views.py:480 templates/base.html:75 #: templates/ishtar/sheet_organization.html:35 #: templates/ishtar/sheet_person.html:57 templates/ishtar/sheet_person.html:83 msgid "Details" msgstr "Détails" -#: views.py:647 views.py:694 +#: views.py:693 views.py:740 msgid "Operation not permitted." -msgstr "Opération non permise." +msgstr "Opération non permise" -#: views.py:649 +#: views.py:695 #, python-format msgid "New %s" msgstr "Nouveau %s" -#: views.py:711 views.py:767 +#: views.py:757 views.py:813 msgid "Archaeological files" msgstr "Dossiers archéologiques" -#: views.py:712 views.py:771 +#: views.py:758 views.py:817 msgid "Operations" msgstr "Opérations" -#: views.py:714 views.py:776 +#: views.py:760 views.py:822 msgid "Context records" msgstr "Unité d'Enregistrement" -#: views.py:716 views.py:781 +#: views.py:762 views.py:827 msgid "Finds" msgstr "Mobilier" -#: widgets.py:326 +#: views.py:954 views.py:969 +msgid "Corporation manager" +msgstr "Représentant de la personne morale" + +#: widgets.py:196 widgets.py:300 widgets.py:415 +msgid "Search..." +msgstr "Recherche..." + +#: widgets.py:526 msgid "No results" msgstr "Pas de résultats" -#: widgets.py:327 +#: widgets.py:527 msgid "Loading..." msgstr "Chargement..." -#: widgets.py:328 +#: widgets.py:528 msgid "Remove" msgstr "Enlever" -#: wizards.py:227 +#: wizards.py:230 msgid "Yes" msgstr "Oui" -#: wizards.py:229 +#: wizards.py:232 msgid "No" msgstr "Non" -#: wizards.py:992 +#: wizards.py:995 #, python-format msgid "[%(app_name)s] Account creation/modification" msgstr "[%(app_name)s] Ajout - modification du compte" @@ -907,7 +916,7 @@ msgstr "Opération fermée" #: templates/sheet_ope.html:22 msgid "Closing date:" -msgstr "Date de clôture :" +msgstr "Date de clotûre :" #: templates/sheet_ope.html:22 msgid "by" @@ -1078,7 +1087,7 @@ msgstr "Description" #: templates/sheet_ope.html:142 msgid "No recording unit associated to this operation" -msgstr "Pas d'Unité d'Enregistrement associée à ce dossier" +msgstr "Pas d'Unité d'Enregistrement associée à ce dosser" #: templates/window.html:37 templates/blocks/JQueryJqGrid.html:26 msgid "Add" @@ -1088,6 +1097,15 @@ msgstr "Ajout" msgid "Ishtar administration" msgstr "Administration d'Ishtar" +#: templates/blocks/JQueryAdvancedTown.html:3 +msgctxt "Région" +msgid "State" +msgstr "Région" + +#: templates/blocks/JQueryAdvancedTown.html:29 +msgid "Choose a state first" +msgstr "Choisissez une région" + #: templates/blocks/JQueryJqGrid.html:4 msgid "Search" msgstr "Recherche" @@ -1132,31 +1150,60 @@ msgstr "Valider" #: templates/ishtar/merge.html:7 msgid "Every operation on this form is irreversible" -msgstr "" +msgstr "Chaque opération sur ce formulaire est irréversible" #: templates/ishtar/merge.html:9 msgid "Page " -msgstr "" +msgstr "Page " #: templates/ishtar/merge.html:17 msgid "Item A" -msgstr "" +msgstr "Élément A" #: templates/ishtar/merge.html:18 msgid "Item B" -msgstr "" +msgstr "Élément B" #: templates/ishtar/merge.html:19 msgid "B is a duplicate of A" -msgstr "" +msgstr "B doublonne A" #: templates/ishtar/merge.html:20 msgid "A is a duplicate of B" -msgstr "" +msgstr "A doublonne B" #: templates/ishtar/merge.html:21 msgid "Is not duplicate" -msgstr "" +msgstr "N'est pas un doublon" + +#: templates/ishtar/organization_person_form.html:9 +#: templates/ishtar/person_form.html:9 +msgid "Identification" +msgstr "Identification" + +#: templates/ishtar/organization_person_form.html:32 +#: templates/ishtar/person_form.html:43 +msgid "Modify" +msgstr "Modifier" + +#: templates/ishtar/organization_person_form.html:33 +#: templates/ishtar/person_form.html:44 +msgid "New" +msgstr "Nouveau" + +#: templates/ishtar/organization_person_form.html:34 +#: templates/ishtar/person_form.html:45 +msgid "Save" +msgstr "Enregistrer" + +#: templates/ishtar/organization_person_form.html:35 +#: templates/ishtar/person_form.html:46 +msgid "Cancel" +msgstr "Annuler" + +#: templates/ishtar/person_form.html:24 +msgid "Contact informations" +msgstr "Coordonnées" #: templates/ishtar/sheet.html:21 msgid "Previous" @@ -1185,7 +1232,7 @@ msgstr "Personnes au sein de l'organisation" #: templates/ishtar/sheet_organization.html:38 msgid "No person in this organization" -msgstr "Pas de personne au sein de cette organisation" +msgstr "Pas de personne au sein de l'organisation" #: templates/ishtar/sheet_person.html:16 msgid "Type(s)" @@ -1345,7 +1392,7 @@ msgstr "Vous avez entré les informations suivantes :" #: templates/ishtar/wizard/confirm_wizard.html:39 msgid "Would you like to save them?" -msgstr "Voulez-vous sauver ces informations ?" +msgstr "Voulez vous enregistrer ces informations ?" #: templates/ishtar/wizard/default_wizard.html:34 #: templates/ishtar/wizard/parcels_wizard.html:24 @@ -1363,8 +1410,8 @@ msgstr "Valider et confirmer" msgid "" "The form has changed if you don't validate it all your changes will be lost." msgstr "" -"Le formulaire a changé. Si vous ne le validez pas, tous vos changements seront " -"perdus." +"Le formulaire a changé. Si vous ne le validez pas tout vos changements vont " +"être perdus." #: templates/ishtar/wizard/parcels_wizard.html:20 msgid "all" @@ -1380,7 +1427,7 @@ msgstr "Pas de commune dans le dossier associé." #: templates/ishtar/wizard/wizard_closing_done.html:4 msgid "Item successfully closed" -msgstr "Élément clos avec succès" +msgstr "Élément clôt avec succès" #: templates/ishtar/wizard/wizard_delete_done.html:4 msgid "Item successfully deleted" @@ -1497,11 +1544,11 @@ msgstr "Possède des parcelles" #: templates/registration/activate.html:8 msgid "Account successfully activated" -msgstr "Compte créé avec succès" +msgstr "Compte crée avec succès" #: templates/registration/activate.html:14 msgid "Account activation failed" -msgstr "La création du compte a échoué" +msgstr "La création du compte a échouée" #: templates/registration/activation_complete.html:7 msgid "You may now login with your username and password." @@ -1579,7 +1626,7 @@ msgstr "" #: templates/registration/password_reset_email.html:2 #, python-format msgid "Reset password at %(site_name)s" -msgstr "Remise à zéro du mot de passe pour %(site_name)s" +msgstr "Remise à réro du mot de passe pour %(site_name)s" #: templates/registration/password_reset_form.html:7 msgid "Reset password" @@ -1588,7 +1635,7 @@ msgstr "Réinitialisation du mot de passe" #: templates/registration/registration_complete.html:6 msgid "You are now registered. Activation email sent." msgstr "" -"Vous êtes maintenant enregistré. Un courriel d'activation de votre compte " +"Vous être maintenant enregistré. Un courriel d'activation de votre compte " "vous a été envoyé." #~ msgid "Wizard" @@ -1663,9 +1710,6 @@ msgstr "" #~ msgid "Activity:" #~ msgstr "Activité :" -#~ msgid "Identification:" -#~ msgstr "Identification :" - #~ msgid "Interpretation:" #~ msgstr "Interpretation :" diff --git a/ishtar_common/management/commands/update_towns.py b/ishtar_common/management/commands/update_towns.py new file mode 100644 index 000000000..fa201b677 --- /dev/null +++ b/ishtar_common/management/commands/update_towns.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import csv +import datetime, time + +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError + +from ishtar_common import tasks + +class Command(BaseCommand): + help = "Update department for french towns" + + def handle(self, *args, **options): + self.stdout.write("* Loading towns\n") + self.stdout.flush() + created, updated = tasks.update_towns() + self.stdout.write("%d towns created, %s towns updated\n\n" % (created, + updated)) + self.stdout.flush() + diff --git a/ishtar_common/migrations/0030_auto__add_state__chg_field_sourcetype_txt_idx__chg_field_authortype_tx.py b/ishtar_common/migrations/0030_auto__add_state__chg_field_sourcetype_txt_idx__chg_field_authortype_tx.py new file mode 100644 index 000000000..3f7a2e498 --- /dev/null +++ b/ishtar_common/migrations/0030_auto__add_state__chg_field_sourcetype_txt_idx__chg_field_authortype_tx.py @@ -0,0 +1,371 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'State' + db.create_table('ishtar_common_state', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('label', self.gf('django.db.models.fields.CharField')(max_length=30)), + ('number', self.gf('django.db.models.fields.CharField')(unique=True, max_length=3)), + )) + db.send_create_signal('ishtar_common', ['State']) + + + # Changing field 'SourceType.txt_idx' + db.alter_column('ishtar_common_sourcetype', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + + # Changing field 'AuthorType.txt_idx' + db.alter_column('ishtar_common_authortype', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + + # Changing field 'PersonType.txt_idx' + db.alter_column('ishtar_common_persontype', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + # Adding field 'Department.state' + db.add_column('ishtar_common_department', 'state', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ishtar_common.State'], null=True, blank=True), + keep_default=False) + + + # Changing field 'OrganizationType.txt_idx' + db.alter_column('ishtar_common_organizationtype', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + + # Changing field 'SupportType.txt_idx' + db.alter_column('ishtar_common_supporttype', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + + # Changing field 'Format.txt_idx' + db.alter_column('ishtar_common_format', 'txt_idx', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)) + + def backwards(self, orm): + # Deleting model 'State' + db.delete_table('ishtar_common_state') + + + # Changing field 'SourceType.txt_idx' + db.alter_column('ishtar_common_sourcetype', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + + # Changing field 'AuthorType.txt_idx' + db.alter_column('ishtar_common_authortype', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + + # Changing field 'PersonType.txt_idx' + db.alter_column('ishtar_common_persontype', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + # Deleting field 'Department.state' + db.delete_column('ishtar_common_department', 'state_id') + + + # Changing field 'OrganizationType.txt_idx' + db.alter_column('ishtar_common_organizationtype', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + + # Changing field 'SupportType.txt_idx' + db.alter_column('ishtar_common_supporttype', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + + # Changing field 'Format.txt_idx' + db.alter_column('ishtar_common_format', 'txt_idx', self.gf('django.db.models.fields.CharField')(max_length=30, unique=True)) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'ishtar_common.arrondissement': { + 'Meta': {'object_name': 'Arrondissement'}, + 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) + }, + 'ishtar_common.author': { + 'Meta': {'object_name': 'Author'}, + 'author_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.AuthorType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'author'", 'to': "orm['ishtar_common.Person']"}) + }, + 'ishtar_common.authortype': { + 'Meta': {'object_name': 'AuthorType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.canton': { + 'Meta': {'object_name': 'Canton'}, + 'arrondissement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Arrondissement']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) + }, + 'ishtar_common.department': { + 'Meta': {'ordering': "['number']", 'object_name': 'Department'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.State']", 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.documenttemplate': { + 'Meta': {'ordering': "['associated_object_name', 'name']", 'object_name': 'DocumentTemplate'}, + 'associated_object_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'template': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + }, + 'ishtar_common.format': { + 'Meta': {'object_name': 'Format'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.formatertype': { + 'Meta': {'unique_together': "(('formater_type', 'options', 'many_split'),)", 'object_name': 'FormaterType'}, + 'formater_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'many_split': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'options': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.globalvar': { + 'Meta': {'ordering': "['slug']", 'object_name': 'GlobalVar'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.historicalorganization': { + 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalOrganization'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'history_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'history_modifier_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'history_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'history_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'organization_type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.import': { + 'Meta': {'object_name': 'Import'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), + 'end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'error_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imported_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.ImporterType']"}), + 'result_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'seconds_remaining': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'skip_lines': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'C'", 'max_length': '2'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.IshtarUser']"}) + }, + 'ishtar_common.importercolumn': { + 'Meta': {'object_name': 'ImporterColumn'}, + 'col_number': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'columns'", 'to': "orm['ishtar_common.ImporterType']"}), + 'regexp_pre_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), + 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'ishtar_common.importerdefault': { + 'Meta': {'object_name': 'ImporterDefault'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'defaults'", 'to': "orm['ishtar_common.ImporterType']"}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.importerdefaultvalues': { + 'Meta': {'object_name': 'ImporterDefaultValues'}, + 'default_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'default_values'", 'to': "orm['ishtar_common.ImporterDefault']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.importerduplicatefield': { + 'Meta': {'object_name': 'ImporterDuplicateField'}, + 'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'duplicate_fields'", 'to': "orm['ishtar_common.ImporterColumn']"}), + 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'ishtar_common.importertype': { + 'Meta': {'object_name': 'ImporterType'}, + 'associated_models': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ishtar_common.IshtarUser']", 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.importtarget': { + 'Meta': {'object_name': 'ImportTarget'}, + 'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'targets'", 'to': "orm['ishtar_common.ImporterColumn']"}), + 'formater_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.FormaterType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'regexp_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.ishtaruser': { + 'Meta': {'object_name': 'IshtarUser', '_ormbases': ['auth.User']}, + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ishtaruser'", 'unique': 'True', 'to': "orm['ishtar_common.Person']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'ishtar_common.itemkey': { + 'Meta': {'object_name': 'ItemKey'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Import']", 'null': 'True', 'blank': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}) + }, + 'ishtar_common.organization': { + 'Meta': {'object_name': 'Organization'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imports': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'imported_ishtar_common_organization'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['ishtar_common.Import']"}), + 'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), + 'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'organization_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.OrganizationType']"}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.organizationtype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'OrganizationType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'attached_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'members'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['ishtar_common.Organization']"}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imports': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'imported_ishtar_common_person'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['ishtar_common.Import']"}), + 'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), + 'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'person_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ishtar_common.PersonType']", 'symmetrical': 'False'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'raw_name': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'surname': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.persontype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'PersonType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.regexp': { + 'Meta': {'object_name': 'Regexp'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'regexp': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.sourcetype': { + 'Meta': {'object_name': 'SourceType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.state': { + 'Meta': {'ordering': "['number']", 'object_name': 'State'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}) + }, + 'ishtar_common.supporttype': { + 'Meta': {'object_name': 'SupportType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'ishtar_common.targetkey': { + 'Meta': {'unique_together': "(('target', 'value'),)", 'object_name': 'TargetKey'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_set': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'keys'", 'to': "orm['ishtar_common.ImportTarget']"}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.town': { + 'Meta': {'ordering': "['numero_insee']", 'object_name': 'Town'}, + 'canton': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Canton']", 'null': 'True', 'blank': 'True'}), + 'center': ('django.contrib.gis.db.models.fields.PointField', [], {'srid': '27572', 'null': 'True', 'blank': 'True'}), + 'departement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'numero_insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '6'}), + 'surface': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['ishtar_common']
\ No newline at end of file diff --git a/ishtar_common/models.py b/ishtar_common/models.py index e79eb4253..2ed6b28c5 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -238,7 +238,7 @@ class GeneralType(models.Model): """ label = models.CharField(_(u"Label"), max_length=100) txt_idx = models.CharField(_(u"Textual ID"), - validators=[validate_slug], max_length=30, unique=True) + validators=[validate_slug], max_length=100, unique=True) comment = models.TextField(_(u"Comment"), blank=True, null=True) available = models.BooleanField(_(u"Available"), default=True) HELP_TEXT = u"" @@ -365,7 +365,7 @@ class GeneralType(models.Model): self.label = u" ".join(u" ".join(self.txt_idx.split('-') ).split('_')).title() if not self.txt_idx: - self.txt_idx = slugify(self.label) + self.txt_idx = slugify(self.label)[:100] # clean old keys if self.pk: @@ -880,9 +880,22 @@ class DocumentTemplate(models.Model): missing = ooo_replace(self.template, output_name, values) return output_name +class State(models.Model): + label = models.CharField(_(u"Label"), max_length=30) + number = models.CharField(_(u"Number"), unique=True, max_length=3) + + class Meta: + verbose_name = _(u"State") + ordering = ['number'] + + def __unicode__(self): + return self.label + class Department(models.Model): label = models.CharField(_(u"Label"), max_length=30) number = models.CharField(_(u"Number"), unique=True, max_length=3) + state = models.ForeignKey('State', verbose_name=_(u"State"), blank=True, + null=True) class Meta: verbose_name = _(u"Department") @@ -890,7 +903,7 @@ class Department(models.Model): ordering = ['number'] def __unicode__(self): - return u"%s (%s)" % (self.label, self.number) + return self.label class Address(BaseHistorizedItem): address = models.TextField(_(u"Address"), null=True, blank=True) @@ -1544,10 +1557,13 @@ class Person(Address, Merge, OwnPerms, ValueGetter) : values = [unicode(getattr(self, attr)) for attr in ('surname', 'name') if getattr(self, attr)] - if not values: - values = [self.raw_name or ""] + if not values and self.raw_name: + values = [self.raw_name] if self.attached_to: - values.append(u"- " + unicode(self.attached_to)) + attached_to = unicode(self.attached_to) + if values: + values.append(u'-') + values.append(attached_to) return u" ".join(values) def get_values(self, prefix=''): @@ -1581,6 +1597,9 @@ class Person(Address, Merge, OwnPerms, ValueGetter) : self.merge_key += "-" + self.attached_to.merge_key self.merge_key = self.merge_key[:300] + def is_natural(self): + return not self.attached_to + def has_right(self, right_name): if '.' in right_name: right_name = right_name.split('.')[-1] @@ -1615,6 +1634,15 @@ class Person(Address, Merge, OwnPerms, ValueGetter) : if getattr(self, attr)] return slugify(u"-".join(values)) + def save(self, *args, **kwargs): + super(Person, self).save(*args, **kwargs) + if hasattr(self, 'responsible_town_planning_service'): + for fle in self.responsible_town_planning_service.all(): + fle.save() # force update of raw_town_planning_service + if hasattr(self, 'general_contractor'): + for fle in self.general_contractor.all(): + fle.save() # force update of raw_general_contractor + class IshtarUser(User): person = models.ForeignKey(Person, verbose_name=_(u"Person"), unique=True, related_name='ishtaruser') diff --git a/ishtar_common/ooo_replace.py b/ishtar_common/ooo_replace.py index 4c487dd17..54ecfced4 100644 --- a/ishtar_common/ooo_replace.py +++ b/ishtar_common/ooo_replace.py @@ -23,6 +23,18 @@ from cStringIO import StringIO from xml.etree.cElementTree import ElementTree, fromstring from django.conf import settings +from ooo_translation import ooo_translation + +def translate_context(context, locale): + if locale not in ooo_translation: + return context + new_context = {} + for k in context: + new_key = k + if k in ooo_translation[locale]: + new_key = ooo_translation[locale][k] + new_context[new_key] = context[k] + return new_context OOO_NS = "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}" @@ -135,13 +147,9 @@ def _custom_parsing(context, value, default_value=''): value = re.sub(sub_exp % (pre_tag, base_key), v, value) return value -def ooo_replace(infile, outfile, context, default_value=''): - inzip = ZipFile(infile, 'r', ZIP_DEFLATED) - outzip = ZipFile(outfile, 'w', ZIP_DEFLATED) +def _ooo_replace(content, context, missing_keys, default_value=''): # regular ooo parsing - content = ElementTree(fromstring(inzip.read('content.xml'))) - missing_keys = set() for xp in ('variable-set', 'variable-get'): for p in content.findall(".//"+OOO_NS+xp): name = p.get(OOO_NS+"name") @@ -167,12 +175,25 @@ def ooo_replace(infile, outfile, context, default_value=''): content.write(str_io) value = str_io.getvalue() value = _custom_parsing(context, value, default_value).encode('utf-8') + return value + +def ooo_replace(infile, outfile, context, default_value=''): + inzip = ZipFile(infile, 'r', ZIP_DEFLATED) + outzip = ZipFile(outfile, 'w', ZIP_DEFLATED) + + values = {} + missing_keys = set() + for xml_file in ('content.xml', 'styles.xml'): + content = ElementTree(fromstring(inzip.read(xml_file))) + values[xml_file] = _ooo_replace(content, context, missing_keys, + default_value) for f in inzip.infolist(): - if f.filename == 'content.xml': - outzip.writestr('content.xml', value) + if f.filename in values: + outzip.writestr(f.filename, values[f.filename]) else: outzip.writestr(f, inzip.read(f.filename)) + inzip.close() outzip.close() return missing_keys diff --git a/ishtar_common/ooo_translation.py b/ishtar_common/ooo_translation.py new file mode 100644 index 000000000..a93a916ae --- /dev/null +++ b/ishtar_common/ooo_translation.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (C) 2013-2015 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed 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 +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +from django.conf import settings +from django.utils import translation +from django.utils.translation import pgettext_lazy + +# [('study', pgettext_lazy('ooo key', u'study')),] +TRANSLATION_STRINGS = [] + +ooo_translation = {} +cur_language = translation.get_language() + +try: + for language, lbl in settings.LANGUAGES: + translation.activate(language) + ooo_translation[language] = {} + for k, v in TRANSLATION_STRINGS: + ooo_translation[language][k] = unicode(v) +finally: + translation.activate(cur_language) diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 5168cba6e..0505e21a5 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -3,7 +3,7 @@ background-color:#ff6e6e; } -a.add-button, a.remove, +a.remove, #progress-content{ background-color:#fff; } @@ -16,16 +16,19 @@ div.form { background-color: #922; } +a.add-button, #reset_wizards{ + background-color: #D14; +} + /* color */ +#window hr, #context_menu .red, a, a.remove { color:#D14; } -a.add-button{ - color:#61615C; -} - +a.add-button, +#reset_wizards, #window h1{ color:#fff; } @@ -49,6 +52,11 @@ div.form { border:0 solid #CCC; } +#window hr{ + border:0; + border-bottom:1px solid #D14; +} + /* shadows */ #progress-content, .sheet{ @@ -58,6 +66,7 @@ div.form { } /* radius */ +a.button, a.add-button, a.remove, .sheet, #progress-content, @@ -96,6 +105,10 @@ h3{ margin:1em 0 0.5em 0; } +h4{ + margin:10px 0; +} + select{ max-width:550px; } @@ -120,6 +133,15 @@ td{ text-align:left; } +div.selected{ + border:1px solid #922; +} + +textarea:disabled, +input[type="text"]:disabled{ + background-color: #eee; +} + button{ background-color:#f1f2f6; border:1px solid #AAA; @@ -197,8 +219,7 @@ div#header{ width:100%; text-align:left; font-size: 0.9em; - background-color: #f1f2f6; - border-bottom:1px solid #CCC; + background-color: #CCC; margin-bottom:10px; line-height:30px; padding:0 20px; @@ -218,6 +239,26 @@ div#logo{ background-repeat:no-repeat; } +div#validation-bar p{ + margin:0; +} + +div#validation-bar input{ + margin:0 30px; +} + +div#validation-bar.big{ + height:60px; +} + +a.button{ + padding:0.5em; +} + +#reset_wizards{ + color: white; +} + .display_details_inline, .display_details{ display:inline-block; @@ -348,7 +389,7 @@ div#main_menu > ul > li{ div#content{ clear:both; - margin:0 200px; + margin:0 200px 70px 200px; text-align:center; } @@ -843,6 +884,7 @@ td.submit_button{ a.add-button, a.remove{ padding:0 4px; + font-weight:bold; } a.remove{ @@ -897,6 +939,10 @@ a.remove{ width:60px; } +.small, .small input{ + width:60px; +} + #progress{ display:none; position:fixed; diff --git a/ishtar_common/tasks.py b/ishtar_common/tasks.py index a9db26087..a8db97bb1 100644 --- a/ishtar_common/tasks.py +++ b/ishtar_common/tasks.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2013-2014 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -17,13 +17,15 @@ # See the file COPYING for details. +import sys + from django.conf import settings from django.db.models import Q -from geodjangofla.models import Commune -from ishtar_common.models import Town +from ishtar_common.models import Town, Department def load_towns(): + from geodjangofla.models import Commune q = None for dpt_number in settings.ISHTAR_DPTS: query = Q(insee_com__istartswith=dpt_number) @@ -51,3 +53,30 @@ def load_towns(): setattr(town, k, defaults[k]) town.save() return nb, updated + +def update_towns(): + nb, updated = 0, 0 + dpts = dict([(dpt.number, dpt) for dpt in Department.objects.all()]) + q = Town.objects.filter(numero_insee__isnull=False) + total = q.count() + for idx, town in enumerate(q.all()): + sys.stdout.write('\rProcessing... %s/%d' % ( + str(idx+1).zfill(len(str(total))), total)) + if len(town.numero_insee) < 2: + continue + dpt_code = town.numero_insee[:2] + if dpt_code.startswith('9') and int(dpt_code) > 95: + dpt_code = town.numero_insee[:3] + if dpt_code not in dpts: + sys.stdout.write('Missing department with INSEE code: %s' % dpt_code) + continue + if town.departement == dpts[dpt_code]: + continue + if town.departement: + updated += 1 + else: + nb += 1 + town.departement = dpts[dpt_code] + town.save() + sys.stdout.write('\n') + return nb, updated diff --git a/ishtar_common/templates/base.html b/ishtar_common/templates/base.html index 1ebebd815..92e61493b 100644 --- a/ishtar_common/templates/base.html +++ b/ishtar_common/templates/base.html @@ -19,6 +19,7 @@ <link type="text/css" href="{{JQUERY_UI_URL}}css/smoothness/jquery-ui.css" rel="stylesheet" /> <link rel="stylesheet" href="{{STATIC_URL}}/js/prettyPhoto/css/prettyPhoto.css" /> <link rel="stylesheet" href="{{STATIC_URL}}/media/style.css" /> + {% include "blocks/extra_head.html" %} {% block extra_head %} {% endblock %} </head> diff --git a/ishtar_common/templates/blocks/JQueryAdvancedTown.html b/ishtar_common/templates/blocks/JQueryAdvancedTown.html new file mode 100644 index 000000000..78d2d7831 --- /dev/null +++ b/ishtar_common/templates/blocks/JQueryAdvancedTown.html @@ -0,0 +1,99 @@ +{% load i18n %}{% load url from future %}</td></tr> +<tr> + <td>{% trans "State" context "Région" %}</td> + <td> + <select id='current-state'> + <option value=''>--------</option>{% for state in states %} + <option value='{{state.number}}'{% if state.number == selected_state %}selected='selected'{% endif %}>{{state}}</option> + {% endfor %}</select> + </td> +</tr> +<tr> + <td>{% trans "Department" %}</td> + <td> + <select id='current-department'> + </select> + </td> +</tr> +<tr class='required'> + <th><label>{% trans "Town" %}</label></th> + <td><input{{attrs_select}}/> +<input type="hidden"{{attrs_hidden}}/> +<script type="text/javascript"><!--// + selected_department = "{{selected_department}}"; + var empty_select = "<option value=''>--------</option>"; + + function update_department_field(){ + var selected_state = $("#current-state").val(); + if (!selected_state){ + $("#current-department").html("<option value=''>"+"{% trans 'Choose a state first' %}"+"</option>"); + $("#current-department").prop('disabled', true); + $('#id_select_{{field_id}}').prop('disabled', true); + return; + } + $.ajax({ + url: "{% url 'department-by-state' %}" + selected_state, + type: 'get', + dataType: 'json', + success: function(data) { + var html = ""; + for (idx in data){ + dpt = data[idx]; + html += "<option value='" + dpt.number + "'"; + if (String(dpt.number) == String(selected_department)){ + html += " selected='selected'"; + } + html += ">" + dpt.value + "</option>"; + } + $("#current-department").html(html); + $("#current-department").prop('disabled', false); + update_search_town(); + } + }); + } + + function update_search_town(){ + selected_department = $("#current-department").val(); + if (selected_department){ + $("#id_select_{{field_id}}").autocomplete( "option", "source", {{source}}+selected_department); + $('#id_select_{{field_id}}').prop('disabled', false); + } else { + $('#id_select_{{field_id}}').prop('disabled', true); + } + } + + function empty_town(){ + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); + } + + $(function() { + update_department_field(); + + $("#current-state").change(function(){ + empty_town(); + update_department_field(); + }); + + $("#current-department").change(function(){ + empty_town(); + update_search_town(); + }); + + $("#id_select_{{field_id}}").autocomplete({ + source: {{source}}, + select: function( event, ui ) { + if(ui.item){ + $('#id_{{field_id}}').val(ui.item.id); + } else { + $('#id_{{field_id}}').val(null); + } + }, + minLength: 2{% if options %}, + {{options}} + {% endif %} + }); + + $('#id_select_{{field_id}}').live('click', empty_town); + +});//--></script> diff --git a/ishtar_common/templates/blocks/JQueryAdvancedTown.js b/ishtar_common/templates/blocks/JQueryAdvancedTown.js new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ishtar_common/templates/blocks/JQueryAdvancedTown.js @@ -0,0 +1 @@ + diff --git a/ishtar_common/templates/blocks/JQueryPersonOrganization.js b/ishtar_common/templates/blocks/JQueryPersonOrganization.js new file mode 100644 index 000000000..b13a2c28e --- /dev/null +++ b/ishtar_common/templates/blocks/JQueryPersonOrganization.js @@ -0,0 +1,50 @@ + +$("#id_select_{{field_id}}").autocomplete({ + source: {{source}}, + select: function( event, ui ) { + var url = {{edit_source}}; + if(ui.item){ + url = {{edit_source}}+ui.item.id; + $('#id_{{field_id}}').val(ui.item.id); + } else { + $('#id_{{field_id}}').val(null); + } + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); + }, + minLength: 2{% if options %}, + {{options}} + {% endif %} +}); + +$.get( {{edit_source}}{% if selected %}+'{{selected}}'{% endif %}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); +}); + +$('#id_select_{{field_id}}').live('click', function(){ + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); + $.get( {{edit_source}}, function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); +}); + +person_save_callback = function(item_id, lbl){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(lbl); + if (item_id){ + url = {{edit_source}}+item_id; + $('#id_{{field_id}}').val(item_id); + } + $("#id_select_{{field_id}}").trigger('autocompletechange'); + $.get(url , function( data ) { + $( "#div-{{field_id}}" ).html( data ); + }); +}; +person_new_callback = function(){ + var url = {{edit_source}}; + $('#id_{{field_id}}').val(null); + $('#id_select_{{field_id}}').val(null); +} diff --git a/ishtar_common/templates/blocks/PersonOrganization.html b/ishtar_common/templates/blocks/PersonOrganization.html new file mode 100644 index 000000000..6e7264c8e --- /dev/null +++ b/ishtar_common/templates/blocks/PersonOrganization.html @@ -0,0 +1,9 @@ +{% load i18n %}<input{{attrs_select}}/> +</table> + +<div id='div-{{name}}'></div> + +<input type="hidden"{{attrs_hidden}}/> +<script type="text/javascript"><!--// +{{js}} +//--></script> diff --git a/ishtar_common/templates/blocks/extra_head.html b/ishtar_common/templates/blocks/extra_head.html new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/ishtar_common/templates/blocks/extra_head.html @@ -0,0 +1,2 @@ + + diff --git a/ishtar_common/templates/ishtar/organization_form.html b/ishtar_common/templates/ishtar/organization_form.html new file mode 100644 index 000000000..207116c21 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_form.html @@ -0,0 +1,30 @@ +{% load i18n %}<table class='organization'> +<tr> + <th><label>{{form.name.label}}</label></th> + <td>{{form.name}}</td> +</tr> +</table> +{% for hidden in form.hidden_fields %}{{hidden}}{% endfor %} +<table class='organization-address'> +<tr> + <th><label>{{form.organization_type.label}}</label></th> + <td>{{form.organization_type}}</td> +</tr> +<tr> + <th><label>{{form.address.label}}</label></th> + <td>{{form.address}}</td> +</tr> +<tr> + <th><label>{{form.address_complement.label}}</label></th> + <td>{{form.address_complement}}</td> +</tr> +<tr> + <th><label>{{form.postal_code.label}}</label></th> + <td>{{form.postal_code}}</td> +</tr> +<tr> + <th><label>{{form.town.label}}</label></th> + <td>{{form.town}}</td> +</tr> +</table> + diff --git a/ishtar_common/templates/ishtar/organization_person_form.html b/ishtar_common/templates/ishtar/organization_person_form.html new file mode 100644 index 000000000..46f2cdc15 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_person_form.html @@ -0,0 +1,131 @@ +{% load i18n %}{% load url from future %} +<div id='orga-person-form'> +<form id='dyn-form-person' method='post'> +{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} +<input type='hidden' name='hidden_person_pk' id='hidden_person_pk' value='{{object.pk}}'/> +<input type='hidden' name='hidden_person_lbl' id='hidden_person_lbl' value="{{object}}"/> +<table class='organization'> +<caption>{% trans "Identification" %}</caption> +<tr> + <th> </th> + <td>{{form.attached_to}}</td> +</tr> +</table> + +<table class='person'> +<caption>{{relative_label}}</caption> +<tr> + <th><label>{{form.title.label}}</label></th> + <td>{{form.title}}</td> +</tr> +<tr> + <th><label>{{form.name.label}}</label></th> + <td>{{form.name}}</td> +</tr> +<tr> + <th><label>{{form.surname.label}}</label></th> + <td>{{form.surname}}</td> +</tr> +</table> +<div> +<input type='submit' id='btn-modify-person' value="{% trans "Modify"%}"/> +<input type='submit' id='btn-new-person' value="{% trans "New"%}"/> +<input type='submit' id='btn-save-person' value="{% trans "Save"%}"/> +<input type='submit' id='btn-cancel-person' value="{% trans "Cancel"%}"/> +</div> +</form> +</div> +<script type="text/javascript"><!--// + +function checkPendingRequest(todo) { + window.setTimeout(function () { + if ($.active > 0) { + window.setTimeout(checkPendingRequest(todo), 250); + } else { + todo(); + } + }, 250); +}; + +person_save_callback = function(){ + var item_id = $('#hidden_person_pk').val(); + + var url = edit_url; + $('#id_' + parent_id).val(null); + if (item_id){ + url = edit_url+item_id; + $('#id_'+parent_id).val(item_id); + } + $("#id_select_"+parent_id).trigger('autocompletechange'); + $.get(url , function( data ) { + $( "#div-"+parent_id ).html( data ); + var lbl = $('#hidden_person_lbl').val(); + $('#id_select_' + parent_id).val(lbl); + }); +}; + +person_new_callback = function(){ + var url = edit_url; + $('#id_'+parent_id).val(null); + $('#id_select_'+parent_id).val(null); +} +$(function() { + disable_person = function(){ + $("#orga-person-form :input[type='text'], #orga-person-form textarea, #orga-person-form select").prop("disabled", true); + $("#btn-save-person").hide(); + $("#btn-cancel-person").hide(); + $("#btn-modify-person").show(); + $("#btn-new-person").show(); + $("#orga-person-form").removeClass('selected'); + } + enable_person = function(){ + $("#orga-person-form :input[type='text'], #orga-person-form textarea, #orga-person-form select").prop("disabled", false); + $("#btn-save-person").show(); + $("#btn-cancel-person").show(); + $("#btn-modify-person").hide(); + $("#btn-new-person").hide(); + $("#orga-person-form").addClass('selected'); + } + + checkPendingRequest(disable_person); + + $("#btn-modify-person").on('click', function(){ + {% if object %}submit_url = "{% url 'organization_person_edit' object.pk %}"; + {% else %}submit_url = "{% url 'organization_person_create' %}";{% endif %} + enable_person(); + return false; + }); + $("#btn-new-person").on('click', function(){ + submit_url = "{% url 'organization_person_create' %}"; + $("#orga-person-form").find("input[type=text], textarea, input[type=hidden]").val(""); + enable_person(); + person_new_callback(); + return false; + }); + $("#btn-cancel-person").on('click', function(){ + person_save_callback(); + disable_person(); + return false; + }); + $("#btn-save-person").on('click', function(){ + var form = $("#dyn-form-person"); + $.ajax({ + url: submit_url, + type: 'post', + data: form.serialize(), + dataType: 'html', + success: function(data) { + $("#orga-person-form").parent().html(data); + person_save_callback(); + disable_person(); + return false; + } + }); + return false; + }); + + +}); +//--></script> + diff --git a/ishtar_common/templates/ishtar/person_form.html b/ishtar_common/templates/ishtar/person_form.html new file mode 100644 index 000000000..555aa1a5f --- /dev/null +++ b/ishtar_common/templates/ishtar/person_form.html @@ -0,0 +1,113 @@ +{% load i18n %}{% load url from future %} +<div id='person-form'> +<form id='dyn-form-person' method='post'> +{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} +<input type='hidden' name='hidden_person_pk' id='hidden_person_pk' value='{{object.pk}}'/> +<input type='hidden' name='hidden_person_lbl' id='hidden_person_lbl' value="{{object}}"/> +<table class='person'> +<caption>{% trans "Identification" %}</caption> +<tr> + <th><label>{{form.title.label}}</label></th> + <td>{{form.title}}</td> +</tr> +<tr> + <th><label>{{form.name.label}}</label></th> + <td>{{form.name}}</td> +</tr> +<tr> + <th><label>{{form.surname.label}}</label></th> + <td>{{form.surname}}</td> +</tr> +</table> +<table class='person-address'> +<caption>{% trans "Contact informations" %}</caption> +<tr> + <th><label>{{form.address.label}}</label></th> + <td>{{form.address}}</td> +</tr> +<tr> + <th><label>{{form.address_complement.label}}</label></th> + <td>{{form.address_complement}}</td> +</tr> +<tr> + <th><label>{{form.postal_code.label}}</label></th> + <td>{{form.postal_code}}</td> +</tr> +<tr> + <th><label>{{form.town.label}}</label></th> + <td>{{form.town}}</td> +</tr> +</table> +<div> +<input type='submit' id='btn-modify-person' value="{% trans "Modify"%}"/> +<input type='submit' id='btn-new-person' value="{% trans "New"%}"/> +<input type='submit' id='btn-save-person' value="{% trans "Save"%}"/> +<input type='submit' id='btn-cancel-person' value="{% trans "Cancel"%}"/> +</div> +</form> +</div> +<script type="text/javascript"><!--// +$(function() { + // person_save_callback and person_new_callback must have been defined + disable_person = function(){ + $("#person-form :input[type='text'], #person-form textarea, #person-form select").prop("disabled", true); + $("#btn-save-person").hide(); + $("#btn-cancel-person").hide(); + $("#btn-modify-person").show(); + $("#btn-new-person").show(); + $("#person-form").removeClass('selected'); + } + + enable_person = function(){ + $("#person-form :input[type='text'], #person-form textarea, #person-form select").prop("disabled", false); + $("#btn-save-person").show(); + $("#btn-cancel-person").show(); + $("#btn-modify-person").hide(); + $("#btn-new-person").hide(); + $("#person-form").addClass('selected'); + } + + disable_person(); + + var submit_url; + $("#btn-modify-person").on('click', function(){ + {% if object %}submit_url = "{% url 'person_edit' object.pk %}"; + {% else %}submit_url = "{% url 'person_create' %}";{% endif %} + enable_person(); + return false; + }); + $("#btn-new-person").on('click', function(){ + submit_url = "{% url 'person_create' %}"; + $("#person-form").find("input[type=text], textarea").val(""); + enable_person(); + person_new_callback(); + return false; + }); + $("#btn-cancel-person").on('click', function(){ + person_save_callback($('#hidden_person_pk').val(), + $('#hidden_person_lbl').val()); + disable_person(); + return false; + }); + $("#btn-save-person").on('click', function(){ + var form = $("#dyn-form-person"); + $.ajax({ + url: submit_url, + type: 'post', + data: form.serialize(), + dataType: 'html', + success: function(data) { + $("#person-form").parent().html(data); + person_save_callback($('#hidden_person_pk').val(), + $('#hidden_person_lbl').val()); + disable_person(); + return false; + } + }); + return false; + }); + + +}); +//--></script> diff --git a/ishtar_common/templates/ishtar/wizard/confirm_wizard.html b/ishtar_common/templates/ishtar/wizard/confirm_wizard.html index 0dd1f6f12..15bb9e9bd 100644 --- a/ishtar_common/templates/ishtar/wizard/confirm_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/confirm_wizard.html @@ -36,8 +36,10 @@ </table> {% endif %} {% block "extra_informations" %}{% endblock %} +<div id='validation-bar' class='big'> <p>{%if confirm_end_msg %}{{confirm_end_msg|safe}}{%else%}{% trans "Would you like to save them?" %}{%endif%}</p> <input type="submit" value="{% trans "Validate" %}"/> </div> +</div> </form> {% endblock %} diff --git a/ishtar_common/templates/ishtar/wizard/default_wizard.html b/ishtar_common/templates/ishtar/wizard/default_wizard.html index 3f2f3943e..a39037d10 100644 --- a/ishtar_common/templates/ishtar/wizard/default_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/default_wizard.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load i18n range table_form %} +{% load url from future %} {% block extra_head %} {{form.media}} {% endblock %} @@ -40,10 +41,9 @@ {% endif %} <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> {{ previous_fields|safe }} -<div id='validation-bar'> - <input type="submit" id="submit_form" name='validate' value="{% trans "Validate" %}"/> - {% if next_steps %}<input type="submit" id="submit_end_form" name='validate_and_end' value="{% trans "Validate and end" %}"/>{% endif %} -</div> +{% block "validation_bar" %} +{% include 'ishtar/wizard/validation_bar.html' %} +{% endblock %} </div> </form> {% endblock %} diff --git a/ishtar_common/templates/ishtar/wizard/parcels_wizard.html b/ishtar_common/templates/ishtar/wizard/parcels_wizard.html index b550cf6c4..ce2bc9780 100644 --- a/ishtar_common/templates/ishtar/wizard/parcels_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/parcels_wizard.html @@ -9,7 +9,6 @@ {{ wizard.form.media }} {{ wizard.management_form }} {{ wizard.form.management_form }} -<div class='top_button'><input type="submit" id="submit_form" value="{% trans "Validate" %}"/></div> {%if wizard.form.non_form_errors%} <table class='formset'> <tr class='error'><th colspan='2'>{{wizard.form.non_form_errors}}</th></tr> @@ -24,8 +23,9 @@ <p><button name="formset_modify" value="{{wizard.steps.current}}">{% trans "Add/Modify" %}</button></p> <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> {{ previous_fields|safe }} -<input type="submit" id="submit_form" name='validate' value="{% trans "Validate" %}"/> -{% if next_steps %}<input type="submit" id="submit_end_form" name='validate_and_end' value="{% trans "Validate and end" %}"/>{% endif %} +{% block "validation_bar" %} +{% include 'ishtar/wizard/validation_bar.html' %} +{% endblock %} </div> </form> {% endblock %} diff --git a/ishtar_common/templates/ishtar/wizard/towns_wizard.html b/ishtar_common/templates/ishtar/wizard/towns_wizard.html index 8e0b3551f..4f9ff6c5f 100644 --- a/ishtar_common/templates/ishtar/wizard/towns_wizard.html +++ b/ishtar_common/templates/ishtar/wizard/towns_wizard.html @@ -9,7 +9,6 @@ <div class='form'> {% if TOWNS %} {% if wizard.form.forms %} -<div class='top_button'><input type="submit" id="submit_form" value="{% trans "Validate" %}"/></div> <table class='formset'> {{ wizard.management_form }} {%if wizard.form.non_form_errors%}<tr class='error'><th colspan='2'>{{wizard.form.non_form_errors}}</th></tr>{%endif%} @@ -29,7 +28,9 @@ {% endif %} <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> {{ previous_fields|safe }} -<input type="submit" id="submit_form" value="{% trans "Validate" %}"/> +{% block "validation_bar" %} +{% include 'ishtar/wizard/validation_bar.html' %} +{% endblock %} </div> </form> {% endblock %} diff --git a/ishtar_common/templates/ishtar/wizard/validation_bar.html b/ishtar_common/templates/ishtar/wizard/validation_bar.html new file mode 100644 index 000000000..09907af67 --- /dev/null +++ b/ishtar_common/templates/ishtar/wizard/validation_bar.html @@ -0,0 +1,7 @@ +{% load i18n %} +{% load url from future %} +<div id='validation-bar'> + <input type="submit" id="submit_form" name='validate' value="{% trans "Validate" %}"/> + {% if next_steps %}<input type="submit" id="submit_end_form" name='validate_and_end' value="{% trans "Validate and end" %}"/>{% endif %} + <a href='{% url 'reset_wizards' %}' id="reset_wizards" class='button'>{% trans "Cancel" %}</a> +</div> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 97fe4f81a..13c19096e 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -33,12 +33,20 @@ urlpatterns = patterns('', # internationalization url(r'^i18n/', include('django.conf.urls.i18n')), # General + url(r'person_search/(?P<step>.+)?$', + views.person_search_wizard, name='person_search'), url(r'person_creation/(?P<step>.+)?$', views.person_creation_wizard, name='person_creation'), url(r'person_modification/(?P<step>.+)?$', views.person_modification_wizard, name='person_modification'), url(r'person_deletion/(?P<step>.+)?$', views.person_deletion_wizard, name='person_deletion'), + url(r'^person-edit/$', + views.PersonCreate.as_view(), name='person_create'), + url(r'^person-edit/(?P<pk>\d+)$', + views.PersonEdit.as_view(), name='person_edit'), + url(r'organization_search/(?P<step>.+)?$', + views.organization_search_wizard, name='organization_search'), url(r'organization_creation/(?P<step>.+)?$', views.organization_creation_wizard, name='organization_creation'), url(r'organization_modification/(?P<step>.+)?$', @@ -46,6 +54,16 @@ urlpatterns = patterns('', name='organization_modification'), url(r'organization_deletion/(?P<step>.+)?$', views.organization_deletion_wizard, name='organization_deletion'), + url(r'organization-edit/$', + views.OrganizationCreate.as_view(), name='organization_create'), + url(r'organization-edit/(?P<pk>\d+)$', + views.OrganizationEdit.as_view(), name='organization_edit'), + url(r'organization-person-edit/$', + views.OrganizationPersonCreate.as_view(), + name='organization_person_create'), + url(r'organization-person-edit/(?P<pk>\d+)$', + views.OrganizationPersonEdit.as_view(), + name='organization_person_edit'), url(r'account_management/(?P<step>.+)?$', views.account_management_wizard, name='account_management'), url(r'import/$', views.NewImportView.as_view(), name='new_import'), @@ -84,8 +102,12 @@ urlpatterns += patterns('ishtar_common.views', name='get-person'), url(r'show-person(?:/(?P<pk>.+))?/(?P<type>.+)?$', 'show_person', name='show-person'), + url(r'department-by-state/(?P<state_id>.+)?$', 'department_by_state', + name='department-by-state'), url(r'autocomplete-town/?$', 'autocomplete_town', name='autocomplete-town'), + url(r'autocomplete-advanced-town/(?P<department_id>[0-9]+[ABab]?)?$', + 'autocomplete_advanced_town', name='autocomplete-advanced-town'), url(r'autocomplete-department/?$', 'autocomplete_department', name='autocomplete-department'), url(r'new-author/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$', @@ -105,6 +127,7 @@ urlpatterns += patterns('ishtar_common.views', url(r'person_merge/(?:(?P<page>\d+)/)?$', 'person_merge', name='person_merge'), url(r'organization_merge/(?:(?P<page>\d+)/)?$', 'organization_merge', name='organization_merge'), + url(r'reset/$', 'reset_wizards', name='reset_wizards'), url(r'(?P<action_slug>' + actions + r')/$', 'action', name='action'), ) diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 1b27bcf43..519332222 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -44,7 +44,7 @@ from django.shortcuts import render_to_response, redirect from django.template import RequestContext, loader from django.utils.decorators import method_decorator from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView +from django.views.generic import ListView, UpdateView, CreateView from django.views.generic.edit import CreateView, DeleteView from xhtml2odt import xhtml2odt @@ -75,6 +75,11 @@ def index(request): return render_to_response('index.html', dct, context_instance=RequestContext(request)) +person_search_wizard = wizards.SearchWizard.as_view([ + ('general-person_search', forms.PersonFormSelection)], + label=_(u"Person search"), + url_name='person_search',) + person_creation_wizard = wizards.PersonWizard.as_view([ ('identity-person_creation', forms.SimplePersonForm), ('person_type-person_creation', forms.PersonTypeForm), @@ -96,6 +101,11 @@ person_deletion_wizard = wizards.PersonDeletionWizard.as_view([ label=_(u"Person deletion"), url_name='person_deletion',) +organization_search_wizard = wizards.SearchWizard.as_view([ + ('general-organization_search', forms.OrganizationFormSelection)], + label=_(u"Organization search"), + url_name='organization_search',) + organization_creation_wizard = wizards.OrganizationWizard.as_view([ ('identity-organization_creation', forms.OrganizationForm), ('final-organization_creation', FinalForm)], @@ -202,6 +212,45 @@ def autocomplete_town(request): for town in towns]) return HttpResponse(data, mimetype='text/plain') +def autocomplete_advanced_town(request, department_id=None, state_id=None): + if not request.GET.get('term'): + return HttpResponse(mimetype='text/plain') + q = request.GET.get('term') + q = unicodedata.normalize("NFKD", q).encode('ascii','ignore') + query = Q() + for q in q.split(' '): + extra = Q(name__icontains=q) + if settings.COUNTRY == 'fr': + extra = extra | Q(numero_insee__istartswith=q) + if not department_id: + extra = extra | Q(departement__label__istartswith=q) + query = query & extra + if department_id: + query = query & Q(departement__number__iexact=department_id) + if state_id: + query = query & Q(departement__state__number__iexact=state_id) + limit = 20 + towns = models.Town.objects.filter(query)[:limit] + result = [] + for town in towns: + val = town.name + if hasattr(town, 'numero_insee'): + val += " (%s)" % town.numero_insee + result.append({'id':town.pk, 'value':val}) + data = json.dumps(result) + return HttpResponse(data, mimetype='text/plain') + +def department_by_state(request, state_id=''): + if not state_id: + data = [] + else: + departments = models.Department.objects.filter(state__number=state_id) + data = json.dumps([{'id':department.pk, 'number':department.number, + 'value':unicode(department)} + for department in departments]) + return HttpResponse(data, mimetype='text/plain') + + from types import NoneType def format_val(val): @@ -480,7 +529,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[], return func -def show_item(model, name): +def show_item(model, name, extra_dct=None): def func(request, pk, **dct): try: item = model.objects.get(pk=pk) @@ -511,6 +560,9 @@ def show_item(model, name): if len(historized) > 1: dct['previous'] = historized[1].history_date dct['item'], dct['item_name'] = item, name + # add context + if extra_dct: + dct.update(extra_dct(request, item)) context_instance = RequestContext(request) context_instance.update(dct) n = datetime.datetime.now() @@ -790,6 +842,20 @@ def dashboard_main_detail(request, item_name): return render_to_response('ishtar/dashboards/dashboard_main_detail.html', dct, context_instance=RequestContext(request)) +def reset_wizards(request): + # dynamicaly execute each reset_wizards of each ishtar app + for app in settings.INSTALLED_APPS: + if app == 'ishtar_common': + # no need for infinite recursion + continue + try: + module = __import__(app) + except ImportError: + continue + if hasattr(module, 'views') and hasattr(module.views, 'reset_wizards'): + module.views.reset_wizards(request) + return redirect(reverse('start')) + ITEM_PER_PAGE = 20 def merge_action(model, form, key): def merge(request, page=1): @@ -943,3 +1009,72 @@ class ImportDeleteView(IshtarMixin, LoginRequiredMixin, DeleteView): def get_success_url(self): return reverse('current_imports') + +class PersonCreate(LoginRequiredMixin, CreateView): + model = models.Person + form_class = forms.BasePersonForm + template_name = 'ishtar/person_form.html' + + def get_success_url(self): + return reverse('person_edit', args=[self.object.pk]) + +class PersonEdit(LoginRequiredMixin, UpdateView): + model = models.Person + form_class = forms.BasePersonForm + template_name = 'ishtar/person_form.html' + + def get_success_url(self): + return reverse('person_edit', args=[self.object.pk]) + +class OrganizationCreate(LoginRequiredMixin, CreateView): + model = models.Organization + form_class = forms.BaseOrganizationForm + template_name = 'ishtar/organization_form.html' + form_prefix = "orga" + + def get_form_kwargs(self): + kwargs = super(OrganizationCreate, self).get_form_kwargs() + if hasattr(self.form_class, 'form_prefix'): + kwargs.update({'prefix': self.form_class.form_prefix}) + return kwargs + +class OrganizationEdit(LoginRequiredMixin, UpdateView): + model = models.Organization + form_class = forms.BaseOrganizationForm + template_name = 'ishtar/organization_form.html' + + def get_form_kwargs(self): + kwargs = super(OrganizationEdit, self).get_form_kwargs() + if hasattr(self.form_class, 'form_prefix'): + kwargs.update({'prefix': self.form_class.form_prefix}) + return kwargs + +class OrganizationPersonCreate(LoginRequiredMixin, CreateView): + model = models.Person + form_class = forms.BaseOrganizationPersonForm + template_name = 'ishtar/organization_person_form.html' + relative_label = _("Corporation manager") + + def get_context_data(self, *args, **kwargs): + data = super(OrganizationPersonCreate, self).get_context_data(*args, + **kwargs) + data['relative_label'] = self.relative_label + return data + + def get_success_url(self): + return reverse('organization_person_edit', args=[self.object.pk]) + +class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): + model = models.Person + form_class = forms.BaseOrganizationPersonForm + template_name = 'ishtar/organization_person_form.html' + relative_label = _("Corporation manager") + + def get_context_data(self, *args, **kwargs): + data = super(OrganizationPersonEdit, self).get_context_data(*args, + **kwargs) + data['relative_label'] = self.relative_label + return data + + def get_success_url(self): + return reverse('organization_person_edit', args=[self.object.pk]) diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index 0ba4061a8..ba7e61e46 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -26,6 +26,7 @@ from django.db.models import fields from django.forms import ClearableFileInput from django.forms.widgets import flatatt from django.template import Context, loader +from django.template.defaultfilters import slugify from django.utils.encoding import smart_unicode from django.utils.functional import lazy from django.utils.html import escape @@ -33,7 +34,7 @@ from django.utils.safestring import mark_safe from django.utils.simplejson import JSONEncoder from django.utils.translation import ugettext_lazy as _ -import models +from ishtar_common import models reverse_lazy = lazy(reverse, unicode) @@ -203,7 +204,7 @@ class JQueryAutoComplete(forms.TextInput): except: raise ValueError('source type is not valid') dct = {'source':mark_safe(source), - 'field_id':field_id} + 'field_id':field_id,} if self.options: dct['options'] = mark_safe('%s' % self.options) @@ -218,6 +219,7 @@ class JQueryAutoComplete(forms.TextInput): def render(self, name, value=None, attrs=None): attrs_hidden = self.build_attrs(attrs, name=name) attrs_select = self.build_attrs(attrs) + attrs_select['placeholder'] = _(u"Search...") if value: hiddens = [] selects = [] @@ -281,6 +283,204 @@ class JQueryAutoComplete(forms.TextInput): } return html +class JQueryTown(forms.TextInput): + """ + Town fields whith state and department pre-selections + """ + + def __init__(self, source, options={}, + attrs={}, new=False, limit={}): + self.options = None + self.attrs = {} + self.source = source + if len(options) > 0: + self.options = JSONEncoder().encode(options) + self.attrs.update(attrs) + self.new = new + self.limit = limit + + @classmethod + def encode_source(cls, source): + encoded_src = '' + if isinstance(source, list): + encoded_src = JSONEncoder().encode(source) + elif isinstance(source, str) \ + or isinstance(source, unicode): + src = escape(source) + if not src.endswith('/'): + src += "/" + encoded_src = "'%s'" % src + else: + try: + src = unicode(source) + if not src.endswith('/'): + src += "/" + encoded_src = "'%s'" % src + except: + raise ValueError('source type is not valid') + return encoded_src + + def render(self, name, value=None, attrs=None): + attrs_hidden = self.build_attrs(attrs, name=name) + attrs_select = self.build_attrs(attrs) + attrs_select['placeholder'] = _(u"Search...") + selected = '' + selected_state = '' + selected_department = '' + if value: + hiddens = [] + selects = [] + if type(value) not in (list, tuple): + values = unicode(escape(smart_unicode(value))) + values = values.replace('[', '').replace(']', '') + values = values.split(',') + else: + values = [] + for v in value: + values += v.split(',') + for v in values: + if not v: + continue + hiddens.append(v) + selects.append(v) + try: + item = models.Town.objects.get(pk=v) + selects[-1] = unicode(item) + if item.departement: + selected_department = item.departement.number + if item.departement.state: + selected_state = item.departement.state.number + selected = item.pk + except (models.Town.DoesNotExist, ValueError): + selects.pop() + hiddens.pop() + if hiddens and selects: + attrs_hidden['value'] = hiddens[0] + attrs_select['value'] = selects[0] + if not self.attrs.has_key('id'): + attrs_hidden['id'] = 'id_%s' % name + attrs_select['id'] = 'id_select_%s' % name + if 'class' not in attrs_select: + attrs_select['class'] = 'autocomplete' + + source = self.encode_source(self.source) + dct = {'source':mark_safe(source), + 'selected':selected, + 'safe_field_id':slugify(name).replace('-', '_'), + 'field_id':name} + if self.options: + dct['options'] = mark_safe('%s' % self.options) + + dct.update({'attrs_select':mark_safe(flatatt(attrs_select)), + 'attrs_hidden':mark_safe(flatatt(attrs_hidden)), + 'name':name, + 'states':models.State.objects.all().order_by('label'), + 'selected_department':selected_department, + 'selected_state':selected_state + }) + html = loader.get_template('blocks/JQueryAdvancedTown.html').render( + Context(dct)) + return html + +class JQueryPersonOrganization(forms.TextInput): + """ + Complex widget which manage: + * link between person and organization + * display addresses of the person and of the organization + * create new person and new organization + """ + + def __init__(self, source, edit_source, model, options={}, + attrs={}, new=False, limit={}, + html_template = 'blocks/PersonOrganization.html', + js_template='blocks/JQueryPersonOrganization.js'): + self.options = None + self.attrs = {} + self.model = model + self.source = source + self.edit_source = edit_source + if len(options) > 0: + self.options = JSONEncoder().encode(options) + self.attrs.update(attrs) + self.new = new + self.limit = limit + self.js_template = js_template + self.html_template = html_template + + @classmethod + def encode_source(cls, source): + encoded_src = '' + if isinstance(source, list): + encoded_src = JSONEncoder().encode(source) + elif isinstance(source, str) \ + or isinstance(source, unicode): + encoded_src = "'%s'" % escape(source) + else: + try: + encoded_src = "'" + unicode(source) + "'" + except: + raise ValueError('source type is not valid') + return encoded_src + + def render_js(self, field_id, selected=''): + source = self.encode_source(self.source) + edit_source = self.encode_source(self.edit_source) + dct = {'source':mark_safe(source), + 'edit_source':mark_safe(edit_source), + 'selected':selected, + 'safe_field_id':slugify(field_id).replace('-', '_'), + 'field_id':field_id} + if self.options: + dct['options'] = mark_safe('%s' % self.options) + js = loader.get_template(self.js_template).render(Context(dct)) + return js + + def render(self, name, value=None, attrs=None): + attrs_hidden = self.build_attrs(attrs, name=name) + attrs_select = self.build_attrs(attrs) + attrs_select['placeholder'] = _(u"Search...") + selected = '' + if value: + hiddens = [] + selects = [] + if type(value) not in (list, tuple): + values = unicode(escape(smart_unicode(value))) + values = values.replace('[', '').replace(']', '') + values = values.split(',') + else: + values = [] + for v in value: + values += v.split(',') + for v in values: + if not v: + continue + hiddens.append(v) + selects.append(v) + if self.model: + try: + item = self.model.objects.get(pk=v) + selects[-1] = unicode(item) + selected = item.pk + except (self.model.DoesNotExist, ValueError): + selects.pop() + hiddens.pop() + if hiddens and selects: + attrs_hidden['value'] = hiddens[0] + attrs_select['value'] = selects[0] + if not self.attrs.has_key('id'): + attrs_hidden['id'] = 'id_%s' % name + attrs_select['id'] = 'id_select_%s' % name + if 'class' not in attrs_select: + attrs_select['class'] = 'autocomplete' + new = '' + dct = {'attrs_select':mark_safe(flatatt(attrs_select)), + 'attrs_hidden':mark_safe(flatatt(attrs_hidden)), + 'name':name, + 'js':self.render_js(name, selected), + 'new':mark_safe(new)} + html = loader.get_template(self.html_template).render(Context(dct)) + return html + class JQueryJqGrid(forms.RadioSelect): COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" class Media: diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index 190a7fc86..1e515d13e 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -95,8 +95,11 @@ class Wizard(NamedUrlWizardView): def get_template_names(self): templates = ['ishtar/wizard/default_wizard.html'] current_step = self.steps.current - if current_step in self.wizard_templates: - templates = [self.wizard_templates[current_step]] + templates + wizard_templates = dict([ + (key % {'url_name':self.url_name}, self.wizard_templates[key]) + for key in self.wizard_templates]) + if current_step in wizard_templates: + templates = [wizard_templates[current_step]] + templates elif current_step == self.steps.last: templates = ['ishtar/wizard/confirm_wizard.html'] + templates return templates @@ -617,6 +620,13 @@ class Wizard(NamedUrlWizardView): return key in request.session[storage.prefix]['step_data'][form_key] @classmethod + def session_reset(cls, request, url_name): + prefix = url_name + normalize_name(cls.__name__) + storage = get_storage(cls.storage_name, prefix, request, + getattr(cls, 'file_storage', None)) + storage.reset() + + @classmethod def session_set_value(cls, request, form_key, key, value, reset=False): prefix = form_key.split('-')[1] + normalize_name(cls.__name__) storage = get_storage(cls.storage_name, prefix, request, |