summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/data_importer.py143
-rw-r--r--ishtar_common/fixtures/initial_dpts-fr.json1127
-rw-r--r--ishtar_common/forms.py2
-rw-r--r--ishtar_common/forms_common.py48
-rw-r--r--ishtar_common/ishtar_menu.py7
-rw-r--r--ishtar_common/locale/fr/LC_MESSAGES/django.po416
-rw-r--r--ishtar_common/management/commands/update_towns.py22
-rw-r--r--ishtar_common/migrations/0030_auto__add_state__chg_field_sourcetype_txt_idx__chg_field_authortype_tx.py371
-rw-r--r--ishtar_common/models.py40
-rw-r--r--ishtar_common/ooo_replace.py35
-rw-r--r--ishtar_common/ooo_translation.py37
-rw-r--r--ishtar_common/static/media/style.css62
-rw-r--r--ishtar_common/tasks.py35
-rw-r--r--ishtar_common/templates/base.html1
-rw-r--r--ishtar_common/templates/blocks/JQueryAdvancedTown.html99
-rw-r--r--ishtar_common/templates/blocks/JQueryAdvancedTown.js1
-rw-r--r--ishtar_common/templates/blocks/JQueryPersonOrganization.js50
-rw-r--r--ishtar_common/templates/blocks/PersonOrganization.html9
-rw-r--r--ishtar_common/templates/blocks/extra_head.html2
-rw-r--r--ishtar_common/templates/ishtar/organization_form.html30
-rw-r--r--ishtar_common/templates/ishtar/organization_person_form.html131
-rw-r--r--ishtar_common/templates/ishtar/person_form.html113
-rw-r--r--ishtar_common/templates/ishtar/wizard/confirm_wizard.html2
-rw-r--r--ishtar_common/templates/ishtar/wizard/default_wizard.html8
-rw-r--r--ishtar_common/templates/ishtar/wizard/parcels_wizard.html6
-rw-r--r--ishtar_common/templates/ishtar/wizard/towns_wizard.html5
-rw-r--r--ishtar_common/templates/ishtar/wizard/validation_bar.html7
-rw-r--r--ishtar_common/urls.py23
-rw-r--r--ishtar_common/views.py139
-rw-r--r--ishtar_common/widgets.py204
-rw-r--r--ishtar_common/wizards.py14
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>&nbsp;</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,