14 |
14 |
from wcs_olap.wcs_api import WcsApiError
|
15 |
15 |
|
16 |
16 |
|
|
17 |
def quote(name):
|
|
18 |
return '"%s"' % name
|
|
19 |
|
|
20 |
|
17 |
21 |
def slugify(s):
|
18 |
22 |
return s.replace('-', '_').replace(' ', '_')
|
19 |
23 |
|
... | ... | |
250 |
254 |
'name': 'geolocation',
|
251 |
255 |
'label': 'localisation géographique',
|
252 |
256 |
'type': 'point',
|
253 |
|
'expression': 'array_agg("{fact_table}".geolocation_base) FILTER (WHERE "{fact_table}".geolocation_base IS NOT NULL)',
|
|
257 |
'expression': 'array_agg({fact_table}.geolocation_base) '
|
|
258 |
'FILTER (WHERE {fact_table}.geolocation_base IS NOT NULL)',
|
254 |
259 |
}
|
255 |
260 |
]
|
256 |
261 |
}
|
... | ... | |
344 |
349 |
AS the_date(the_date));''' % last_date.strftime('%Y-%m-%d'))
|
345 |
350 |
|
346 |
351 |
def create_table(self, name, columns, inherits=None, comment=None):
|
347 |
|
sql = 'CREATE TABLE "%s"' % name
|
|
352 |
sql = 'CREATE TABLE %s' % quote(name)
|
348 |
353 |
sql += '(' + ', '.join('"%s" %s' % (n, t) for n, t in columns) + ')'
|
349 |
354 |
if inherits:
|
350 |
355 |
sql += ' INHERITS ("%s")' % inherits
|
... | ... | |
365 |
370 |
if self.prev_table_exists(name):
|
366 |
371 |
# Insert data from previous table
|
367 |
372 |
self.ex(
|
368 |
|
'INSERT INTO {schema_temp}."{name}" SELECT * FROM {schema}."{name}"',
|
369 |
|
ctx={'name': name}
|
|
373 |
'INSERT INTO {schema_temp}.{name} SELECT * FROM {schema}.{name}',
|
|
374 |
ctx={'name': quote(name)}
|
370 |
375 |
)
|
371 |
376 |
# Update sequence
|
372 |
|
self.ex("""SELECT setval(pg_get_serial_sequence('"{name}"', 'id'),
|
373 |
|
(SELECT GREATEST(1, MAX(id)) FROM "{name}"))""", ctx={'name': name})
|
|
377 |
self.ex("""SELECT setval(pg_get_serial_sequence('{name}', 'id'),
|
|
378 |
(SELECT GREATEST(1, MAX(id)) FROM {name}))""", ctx={'name': quote(name)})
|
374 |
379 |
|
375 |
380 |
def create_labeled_table(self, name, labels, comment=None):
|
376 |
381 |
self.create_table(
|
... | ... | |
383 |
388 |
if self.prev_table_exists(name):
|
384 |
389 |
# Insert data from previous table
|
385 |
390 |
self.ex(
|
386 |
|
'INSERT INTO {schema_temp}."{name}" select * FROM {schema}."{name}"',
|
387 |
|
ctx={'name': name}
|
|
391 |
'INSERT INTO {schema_temp}.{name} select * FROM {schema}.{name}',
|
|
392 |
ctx={'name': quote(name)}
|
388 |
393 |
)
|
389 |
394 |
# Find what is missing
|
390 |
395 |
to_insert = []
|
391 |
396 |
for _id, _label in labels:
|
392 |
397 |
self.ex(
|
393 |
|
'SELECT * FROM "{name}" WHERE label = %s', ctx={'name': name}, vars=(_label,))
|
|
398 |
'SELECT * FROM {name} WHERE label = %s', ctx={'name': quote(name)}, vars=(_label,))
|
394 |
399 |
if self.cur.fetchone() is None:
|
395 |
400 |
to_insert.append(_label)
|
396 |
401 |
|
397 |
402 |
labels = None
|
398 |
403 |
if to_insert:
|
399 |
|
self.ex('SELECT MAX(id) FROM "{name}"', ctx={'name': name})
|
|
404 |
self.ex('SELECT MAX(id) FROM {name}', ctx={'name': quote(name)})
|
400 |
405 |
next_id = (self.cur.fetchone()[0] or 0) + 1
|
401 |
406 |
ids = range(next_id, next_id + len(to_insert))
|
402 |
407 |
labels = zip(ids, to_insert)
|
... | ... | |
407 |
412 |
|
408 |
413 |
if labels:
|
409 |
414 |
tmpl = ', '.join(['(%s, %s)'] * len(labels))
|
410 |
|
query_str = 'INSERT INTO "{name}" (id, label) VALUES %s' % tmpl
|
411 |
|
self.ex(query_str, ctx={'name': name}, vars=list(itertools.chain(*labels)))
|
|
415 |
query_str = 'INSERT INTO {name} (id, label) VALUES %s' % tmpl
|
|
416 |
self.ex(query_str, ctx={'name': quote(name)}, vars=list(itertools.chain(*labels)))
|
412 |
417 |
|
413 |
418 |
res = {}
|
414 |
|
self.ex('SELECT id, label FROM "{name}"', ctx={'name': name})
|
|
419 |
self.ex('SELECT id, label FROM {name}', ctx={'name': quote(name)})
|
415 |
420 |
for id_, label in self.cur.fetchall():
|
416 |
421 |
res[label] = id_
|
417 |
422 |
return res
|
... | ... | |
457 |
462 |
|
458 |
463 |
self.create_labeled_table('status', self.status,
|
459 |
464 |
comment=u'statuts simplifiés')
|
460 |
|
self.ex('CREATE TABLE "{form_table}" (id serial PRIMARY KEY,'
|
|
465 |
self.ex('CREATE TABLE {form_table} (id serial PRIMARY KEY,'
|
461 |
466 |
' category_id integer REFERENCES {category_table} (id),'
|
462 |
467 |
' label varchar)')
|
463 |
|
self.ex('COMMENT ON TABLE "{form_table}" IS %s', vars=(u'types de formulaire',))
|
|
468 |
self.ex('COMMENT ON TABLE {form_table} IS %s', vars=(u'types de formulaire',))
|
464 |
469 |
# agents
|
465 |
470 |
self.create_labeled_table_serial('agent', comment=u'agents')
|
466 |
471 |
|
... | ... | |
552 |
557 |
self.agents_mapping[user.id] = self.insert_agent(user.name)
|
553 |
558 |
return self.agents_mapping[user.id]
|
554 |
559 |
|
555 |
|
def create_formdata_json_index(self, varname):
|
|
560 |
def create_formdata_json_index(self, table_name, varname):
|
556 |
561 |
if varname in self.formdata_json_index:
|
557 |
562 |
return
|
558 |
|
index_name = self.hash_table_name('{formdata_table}_%s_json_idx' % varname,
|
559 |
|
hash_length=8, force_hash=True)
|
|
563 |
index_name = self.hash_table_name('%s_%s_json_idx' % (table_name, varname), hash_length=8,
|
|
564 |
force_hash=True)
|
560 |
565 |
self.ex('CREATE INDEX {index_name} ON {generic_formdata_table} (("json_data"->>%s))',
|
561 |
|
ctx={'index_name': index_name}, vars=[varname])
|
|
566 |
ctx={'index_name': quote(index_name)}, vars=[varname])
|
562 |
567 |
# prevent double creation
|
563 |
568 |
self.formdata_json_index.append(varname)
|
564 |
569 |
|
... | ... | |
601 |
606 |
self.formdef.schema.name])
|
602 |
607 |
self.formdef_sql_id = self.cur.fetchone()[0]
|
603 |
608 |
|
604 |
|
columns = [['status_id', 'smallint REFERENCES "{status_table}" (id)']]
|
|
609 |
columns = [['status_id', 'smallint REFERENCES {status_table} (id)']]
|
605 |
610 |
|
606 |
611 |
comments = {}
|
607 |
612 |
|
... | ... | |
623 |
628 |
if field.type == 'item':
|
624 |
629 |
comment = (u'valeurs du champ « %s » du formulaire %s'
|
625 |
630 |
% (field.label, self.formdef.schema.name))
|
626 |
|
table_name = self.hash_table_name('{formdata_table}_field_%s' % field.varname)
|
|
631 |
table_name = self.hash_table_name('%s_field_%s' % (self.table_name, field.varname))
|
627 |
632 |
# create table and mapping
|
628 |
633 |
if field.items:
|
629 |
634 |
self.items_mappings[field.varname] = self.create_labeled_table(
|
... | ... | |
659 |
664 |
name for name, _type in columns])
|
660 |
665 |
self.columns.remove('geolocation_base')
|
661 |
666 |
|
662 |
|
self.create_table('{formdata_table}', columns, inherits='{generic_formdata_table}',
|
|
667 |
self.create_table(self.table_name, columns, inherits='{generic_formdata_table}',
|
663 |
668 |
comment=u'formulaire %s' % self.formdef.schema.name)
|
664 |
669 |
for at, comment in comments.iteritems():
|
665 |
|
self.ex('COMMENT ON COLUMN "{formdata_table}"."%s" IS %%s' % at, vars=(comment,))
|
|
670 |
self.ex('COMMENT ON COLUMN {formdata_table}.%s IS %%s' % quote(at), vars=(comment,))
|
666 |
671 |
|
667 |
672 |
# Creat index for JSON fields
|
668 |
673 |
if self.has_jsonb:
|
669 |
674 |
for field in fields:
|
670 |
675 |
if field.varname and '-' not in field.varname:
|
671 |
|
self.create_formdata_json_index(field.varname)
|
|
676 |
self.create_formdata_json_index(self.table_name, field.varname)
|
672 |
677 |
|
673 |
678 |
# PostgreSQL does not propagate foreign key constraints to child tables
|
674 |
679 |
# so we must recreate them manually
|
... | ... | |
677 |
682 |
continue
|
678 |
683 |
i = _type.index('REFERENCES')
|
679 |
684 |
constraint = '%s_fk_constraint FOREIGN KEY (%s) %s' % (name, name, _type[i:])
|
680 |
|
self.ex('ALTER TABLE "{formdata_table}" ADD CONSTRAINT %s' % constraint)
|
681 |
|
self.ex('ALTER TABLE "{formdata_table}" ADD PRIMARY KEY (id)')
|
|
685 |
self.ex('ALTER TABLE {formdata_table} ADD CONSTRAINT %s' % constraint)
|
|
686 |
self.ex('ALTER TABLE {formdata_table} ADD PRIMARY KEY (id)')
|
682 |
687 |
# table des evolutions
|
683 |
|
self.create_table('{evolution_table}', [
|
|
688 |
self.create_table(self.evolution_table_name, [
|
684 |
689 |
['id', 'serial primary key'],
|
685 |
|
['status_id', 'smallint REFERENCES "{status_table}" (id)'],
|
686 |
|
['formdata_id', 'integer REFERENCES "{formdata_table}" (id)'],
|
|
690 |
['status_id', 'smallint REFERENCES {status_table} (id)'],
|
|
691 |
['formdata_id', 'integer REFERENCES {formdata_table} (id)'],
|
687 |
692 |
['time', 'timestamp'],
|
688 |
693 |
['date', 'date'],
|
689 |
694 |
['hour_id', 'smallint REFERENCES {hour_table} (id)'],
|
690 |
695 |
])
|
691 |
|
self.ex('COMMENT ON TABLE "{evolution_table}" IS %s',
|
|
696 |
self.ex('COMMENT ON TABLE {evolution_table} IS %s',
|
692 |
697 |
vars=(u'evolution des demandes %s' % self.formdef.schema.name,))
|
693 |
698 |
|
694 |
699 |
def insert_item_value(self, field, value):
|
695 |
|
table_name = self.hash_table_name('{formdata_table}_field_%s' % field.varname)
|
696 |
|
self.ex('SELECT id FROM "{item_table}" WHERE label = %s',
|
697 |
|
ctx={'item_table': table_name}, vars=(value,))
|
|
700 |
table_name = self.hash_table_name('%s_field_%s' % (self.table_name, field.varname))
|
|
701 |
self.ex('SELECT id FROM {item_table} WHERE label = %s',
|
|
702 |
ctx={'item_table': quote(table_name)}, vars=(value,))
|
698 |
703 |
res = self.cur.fetchone()
|
699 |
704 |
if res:
|
700 |
705 |
return res[0]
|
701 |
|
self.ex('INSERT INTO "{item_table}" (label) VALUES (%s) RETURNING (id)', vars=(value,),
|
702 |
|
ctx={'item_table': table_name})
|
|
706 |
self.ex('INSERT INTO {item_table} (label) VALUES (%s) RETURNING (id)', vars=(value,),
|
|
707 |
ctx={'item_table': quote(table_name)})
|
703 |
708 |
return self.cur.fetchone()[0]
|
704 |
709 |
|
705 |
710 |
def get_item_id(self, field, value):
|
... | ... | |
837 |
842 |
if not values:
|
838 |
843 |
self.logger.warning('no data')
|
839 |
844 |
return
|
840 |
|
self.ex('INSERT INTO "{formdata_table}" ({columns}) VALUES {values} RETURNING id',
|
|
845 |
self.ex('INSERT INTO {formdata_table} ({columns}) VALUES {values} RETURNING id',
|
841 |
846 |
ctx=dict(columns=', '.join(['"%s"' % column for column in self.columns[1:]]), values=', '.join(values)))
|
842 |
847 |
|
843 |
848 |
# insert generic evolutions
|
... | ... | |
864 |
869 |
row[0] = formdata_id
|
865 |
870 |
evolutions.append(tuple(row))
|
866 |
871 |
if len(evolutions) == 500:
|
867 |
|
self.ex('INSERT INTO "{evolution_table}" (%s) VALUES %s' % (
|
|
872 |
self.ex('INSERT INTO {evolution_table} (%s) VALUES %s' % (
|
868 |
873 |
', '.join(['formdata_id', 'status_id', 'time', 'date', 'hour_id']),
|
869 |
874 |
', '.join(['%s'] * len(evolutions))), vars=evolutions)
|
870 |
875 |
evolutions = []
|
871 |
876 |
if evolutions:
|
872 |
|
self.ex('INSERT INTO "{evolution_table}" (%s) VALUES %s' % (
|
|
877 |
self.ex('INSERT INTO {evolution_table} (%s) VALUES %s' % (
|
873 |
878 |
', '.join(['formdata_id', 'status_id', 'time', 'date', 'hour_id']),
|
874 |
879 |
', '.join(['%s'] * len(evolutions))), vars=evolutions)
|
875 |
880 |
|
... | ... | |
880 |
885 |
|
881 |
886 |
def feed(self):
|
882 |
887 |
self.olap_feeder.ctx.push({
|
883 |
|
'formdata_table': self.table_name,
|
884 |
|
'status_table': self.status_table_name,
|
885 |
|
'evolution_table': self.evolution_table_name
|
|
888 |
'formdata_table': quote(self.table_name),
|
|
889 |
'status_table': quote(self.status_table_name),
|
|
890 |
'evolution_table': quote(self.evolution_table_name),
|
886 |
891 |
})
|
887 |
892 |
|
888 |
893 |
# create cube
|
... | ... | |
892 |
897 |
cube.update({
|
893 |
898 |
'name': self.table_name,
|
894 |
899 |
'label': self.formdef.schema.name,
|
895 |
|
'fact_table': self.table_name,
|
|
900 |
'fact_table': quote(self.table_name),
|
896 |
901 |
'key': 'id',
|
897 |
902 |
})
|
898 |
903 |
cube['dimensions'] = [dimension for dimension in cube['dimensions']
|
... | ... | |
920 |
925 |
cube['joins'].append({
|
921 |
926 |
'name': at,
|
922 |
927 |
'table': 'role',
|
923 |
|
'master': at,
|
|
928 |
'master': quote(at),
|
924 |
929 |
'detail': 'id',
|
925 |
930 |
})
|
926 |
931 |
cube['dimensions'].append({
|
... | ... | |
928 |
933 |
'label': u'fonction %s' % name.lower(),
|
929 |
934 |
'join': [at],
|
930 |
935 |
'type': 'integer',
|
931 |
|
'value': '"%s".id' % at,
|
932 |
|
'value_label': '"%s".label' % at,
|
|
936 |
'value': '%s.id' % quote(at),
|
|
937 |
'value_label': '%s.label' % quote(at),
|
933 |
938 |
'filter': False,
|
934 |
939 |
})
|
935 |
940 |
|
... | ... | |
947 |
952 |
join = None
|
948 |
953 |
|
949 |
954 |
if field.type == 'item':
|
950 |
|
table_name = self.hash_table_name('{formdata_table}_field_%s' % field.varname)
|
|
955 |
table_name = self.hash_table_name('%s_field_%s' % (self.table_name, field.varname))
|
951 |
956 |
join = {
|
952 |
957 |
'name': field.varname,
|
953 |
|
'table': table_name,
|
954 |
|
'master': '"field_%s"' % field.varname,
|
|
958 |
'table': quote(table_name),
|
|
959 |
'master': quote('field_%s' % field.varname),
|
955 |
960 |
'detail': 'id',
|
956 |
961 |
}
|
957 |
962 |
if not field.required:
|
... | ... | |
961 |
966 |
'label': field.label.lower(),
|
962 |
967 |
'join': [field.varname],
|
963 |
968 |
'type': 'integer',
|
964 |
|
'value': '"%s".id' % field.varname,
|
965 |
|
'value_label': '"%s".label' % field.varname,
|
|
969 |
'value': '%s.id' % quote(field.varname),
|
|
970 |
'value_label': '%s.label' % quote(field.varname),
|
966 |
971 |
'filter': True,
|
967 |
972 |
}
|
968 |
973 |
elif field.type == 'bool':
|
... | ... | |
970 |
975 |
'name': field.varname,
|
971 |
976 |
'label': field.label.lower(),
|
972 |
977 |
'type': 'bool',
|
973 |
|
'value': '"field_%s"' % field.varname,
|
974 |
|
'value_label': '(CASE WHEN "field_%(varname)s" IS NULL THEN NULL'
|
975 |
|
' WHEN "field_%(varname)s" THEN \'Oui\''
|
|
978 |
'value': quote('field_%s' % field.varname),
|
|
979 |
'value_label': '(CASE WHEN %(field)s IS NULL THEN NULL'
|
|
980 |
' WHEN %(field)s THEN \'Oui\''
|
976 |
981 |
' ELSE \'Non\' END)' % {
|
977 |
|
'varname': field.varname,
|
|
982 |
'field': quote('field_%s' % field.varname),
|
978 |
983 |
},
|
979 |
984 |
'filter': True,
|
980 |
985 |
}
|
... | ... | |
983 |
988 |
'name': field.varname,
|
984 |
989 |
'label': field.label.lower(),
|
985 |
990 |
'type': 'string',
|
986 |
|
'value': '"field_%s"' % field.varname,
|
|
991 |
'value': quote('field_%s' % field.varname),
|
987 |
992 |
'filter': True,
|
988 |
993 |
}
|
989 |
994 |
else:
|
990 |
|
-
|