Projet

Général

Profil

0001-general-add-geolocation-to-formdata-10581.patch

Frédéric Péters, 02 mai 2016 16:41

Télécharger (11,5 ko)

Voir les différences:

Subject: [PATCH 1/4] general: add geolocation to formdata (#10581)

 tests/test_formdef_import.py | 10 +++++++++
 tests/test_sql.py            | 49 ++++++++++++++++++++++++++++++++++++++++++++
 wcs/formdata.py              | 10 +++++++++
 wcs/formdef.py               | 22 ++++++++++++++++++++
 wcs/sql.py                   | 40 +++++++++++++++++++++++++++++++-----
 5 files changed, 126 insertions(+), 5 deletions(-)
tests/test_formdef_import.py
340 340
    role.remove_self()
341 341
    fd2 = FormDef.import_from_xml_tree(xml_export, include_id=True)
342 342
    assert fd2.workflow_roles.get('_receiver') is None
343

  
344
def test_geolocations():
345
    formdef = FormDef()
346
    formdef.name = 'foo'
347
    formdef.fields = []
348
    formdef.geolocations = {'base': 'Base'}
349
    fd2 = assert_xml_import_export_works(formdef, include_id=True)
350
    assert fd2.geolocations == formdef.geolocations
351
    fd3 = assert_json_import_export_works(formdef)
352
    assert fd3.geolocations == formdef.geolocations
tests/test_sql.py
178 178
    check_sql_field('6', ['apricot', 'pear'])
179 179

  
180 180
@postgresql
181
def test_sql_geoloc():
182
    test_formdef = FormDef()
183
    test_formdef.name = 'geoloc'
184
    test_formdef.fields = []
185
    test_formdef.geolocations = {'base': 'Plop'}
186
    test_formdef.store()
187
    data_class = test_formdef.data_class(mode='sql')
188
    formdata = data_class()
189
    formdata.data = {}
190
    formdata.geolocations = {'base': {'lat': 12, 'lon': 21}}
191
    formdata.store()
192

  
193
    formdata2 = data_class.get(formdata.id)
194
    assert formdata2.geolocations == formdata.geolocations
195

  
196
    formdata.geolocations = {}
197
    formdata.store()
198
    formdata2 = data_class.get(formdata.id)
199
    assert formdata2.geolocations == formdata.geolocations
200

  
201
@postgresql
202
def test_sql_multi_geoloc():
203
    test_formdef = FormDef()
204
    test_formdef.name = 'geoloc'
205
    test_formdef.fields = []
206
    test_formdef.geolocations = {'base': 'Plop'}
207
    test_formdef.store()
208
    data_class = test_formdef.data_class(mode='sql')
209
    formdata = data_class()
210
    formdata.data = {}
211
    formdata.geolocations = {'base': {'lat': 12, 'lon': 21}}
212
    formdata.store()
213

  
214
    formdata2 = data_class.get(formdata.id)
215
    assert formdata2.geolocations == formdata.geolocations
216

  
217
    test_formdef.geolocations = {'base': 'Plop', '2nd': 'XXX'}
218
    test_formdef.store()
219
    formdata.geolocations = {'base': {'lat': 12, 'lon': 21}, '2nd': {'lat': -34, 'lon': -12}}
220
    formdata.store()
221
    formdata2 = data_class.get(formdata.id)
222
    assert formdata2.geolocations == formdata.geolocations
223

  
224
    test_formdef.geolocations = {'base': 'Plop'}
225
    test_formdef.store()
226
    formdata2 = data_class.get(formdata.id)
227
    assert formdata2.geolocations == {'base': {'lat': 12, 'lon': 21}}
228

  
229
@postgresql
181 230
def test_sql_change():
182 231
    data_class = formdef.data_class(mode='sql')
183 232
    formdata = data_class()
wcs/formdata.py
175 175

  
176 176
    workflow_data = None
177 177
    workflow_roles = None
178
    geolocations = None
178 179

  
179 180
    _formdef = None
180 181
    def get_formdef(self):
......
505 506
            except KeyError:
506 507
                pass
507 508

  
509
        if self.geolocations:
510
            for k, v in self.geolocations.items():
511
                d['form_geoloc_%s' % k] = v
512

  
508 513
        if self.evolution and self.evolution[-1].comment:
509 514
            d['form_comment'] = self.evolution[-1].comment
510 515
        else:
......
548 553
                            d['form_url'], formvar, fieldvar,
549 554
                            self.workflow_data['%s_var_%s' % (formvar, fieldvar)])
550 555

  
556
        if self.geolocations:
557
            for k, v in self.geolocations.items():
558
                d['form_geoloc_%s_lat' % k] = v.get('lat')
559
                d['form_geoloc_%s_lon' % k] = v.get('lon')
560

  
551 561
        d = copy.deepcopy(d)
552 562
        flatten_dict(d)
553 563

  
wcs/formdef.py
88 88
    skip_from_360_view = False
89 89
    private_status_and_history = False
90 90

  
91
    geolocations = None
92

  
91 93
    last_modification_time = None
92 94
    last_modification_user_id = None
93 95

  
......
549 551
            for field in self.fields:
550 552
                root['fields'].append(field.export_to_json(include_id=include_id))
551 553

  
554
        if self.geolocations:
555
            root['geolocations'] = self.geolocations.copy()
556

  
552 557
        if self.workflow_options:
553 558
            root['options'] = self.workflow_options
554 559

  
......
632 637
        if value.get('options'):
633 638
            formdef.workflow_options = value.get('options')
634 639

  
640
        if value.get('geolocations'):
641
            formdef.geolocations = value.get('geolocations')
642

  
635 643
        return formdef
636 644

  
637 645
    def export_to_xml(self, include_id=False):
......
711 719
            else:
712 720
                pass # TODO: extend support to other types
713 721

  
722
        geolocations = ET.SubElement(root, 'geolocations')
723
        for geoloc_key, geoloc_label in (self.geolocations or {}).items():
724
            element = ET.SubElement(geolocations, 'geolocation')
725
            element.attrib['key'] = geoloc_key
726
            element.text = unicode(geoloc_label, charset)
727

  
714 728
        return root
715 729

  
716 730
    @classmethod
......
861 875

  
862 876
                formdef.workflow_roles[role_key] = role_id
863 877

  
878
        if tree.find('geolocations') is not None:
879
            geolocations_node = tree.find('geolocations')
880
            formdef.geolocations = {}
881
            for child in geolocations_node.getchildren():
882
                geoloc_key = child.attrib['key']
883
                geoloc_value = child.text.encode(charset)
884
                formdef.geolocations[geoloc_key] = geoloc_value
885

  
864 886
        return formdef
865 887

  
866 888
    def get_detailed_email_form(self, formdata, url):
wcs/sql.py
17 17
import psycopg2
18 18
import datetime
19 19
import time
20
import re
20 21
import cPickle
21 22

  
22 23
from quixote import get_publisher
......
399 400
                cur.execute('''ALTER TABLE %s ADD COLUMN %s bytea''' % (
400 401
                                        table_name, 'f%s_structured' % field.id))
401 402

  
403
    for field in (formdef.geolocations or {}).keys():
404
        column_name = 'geoloc_%s' % field
405
        needed_fields.add(column_name)
406
        if column_name not in existing_fields:
407
            cur.execute('ALTER TABLE %s ADD COLUMN %s %s''' % (
408
                    table_name, column_name, 'POINT'))
409

  
402 410
    # delete obsolete fields
403 411
    for field in (existing_fields - needed_fields):
404 412
        cur.execute('''ALTER TABLE %s DROP COLUMN %s CASCADE''' % (table_name, field))
......
922 930
    def _row2obdata(cls, row, formdef):
923 931
        obdata = {}
924 932
        i = len(cls._table_static_fields)
933
        if formdef.geolocations:
934
            i += len(formdef.geolocations.keys())
925 935
        for field in formdef.fields:
926 936
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
927 937
            if sql_type is None:
......
958 968
                    if obdata['%s_structured' % field.id] is None:
959 969
                        del obdata['%s_structured' % field.id]
960 970
                i += 1
971

  
961 972
        return obdata
962 973

  
963 974
    @classmethod
......
1128 1139
        else:
1129 1140
            sql_dict['submission_context'] = None
1130 1141

  
1142
        for field in (self._formdef.geolocations or {}).keys():
1143
            value = (self.geolocations or {}).get(field)
1144
            if value:
1145
                value = '(%.6f, %.6f)' % (value.get('lon'), value.get('lat'))
1146
            sql_dict['geoloc_%s' % field] = value
1147

  
1131 1148
        sql_dict['concerned_roles_array'] = [str(x) for x in self.concerned_roles if x]
1132 1149
        sql_dict['actions_roles_array'] = [str(x) for x in self.actions_roles if x]
1133 1150

  
......
1242 1259
            o.workflow_roles = cPickle.loads(str(o.workflow_roles))
1243 1260
        if o.submission_context:
1244 1261
            o.submission_context = cPickle.loads(str(o.submission_context))
1262

  
1263
        o.geolocations = {}
1264
        for i, field in enumerate((cls._formdef.geolocations or {}).keys()):
1265
            value = row[len(cls._table_static_fields)+i]
1266
            if not value:
1267
                continue
1268
            m = re.match(r"\(([^)]+),([^)]+)\)", value)
1269
            o.geolocations[field] = {'lon': float(m.group(1)),
1270
                                     'lat': float(m.group(2))}
1271

  
1245 1272
        o.data = cls._row2obdata(row, cls._formdef)
1246 1273
        return o
1247 1274

  
1248 1275
    @classmethod
1249 1276
    def get_data_fields(cls):
1250
        data_fields = []
1277
        data_fields = ['geoloc_%s' % x for x in (cls._formdef.geolocations or {}).keys()]
1251 1278
        for field in cls._formdef.fields:
1252 1279
            sql_type = SQL_TYPE_MAPPING.get(field.key, 'varchar')
1253 1280
            if sql_type is None:
......
1274 1301
                raise KeyError()
1275 1302
        conn, cur = get_connection_and_cursor()
1276 1303

  
1304
        fields = cls.get_data_fields()
1305

  
1277 1306
        potential_comma = ', '
1278
        if not cls.get_data_fields():
1307
        if not fields:
1279 1308
            potential_comma = ''
1280 1309

  
1281 1310
        sql_statement = '''SELECT %s
......
1285 1314
                            WHERE id = %%(id)s''' % (
1286 1315
                                    ', '.join([x[0] for x in cls._table_static_fields]),
1287 1316
                                    potential_comma,
1288
                                    ', '.join(cls.get_data_fields()),
1317
                                    ', '.join(fields),
1289 1318
                                    cls._table_name)
1290 1319
        cur.execute(sql_statement, {'id': str(id)})
1291 1320
        row = cur.fetchone()
......
1814 1843
    return result
1815 1844

  
1816 1845

  
1817
SQL_LEVEL = 14
1846
SQL_LEVEL = 15
1818 1847

  
1819 1848
def migrate_global_views(conn, cur):
1820 1849
    cur.execute('''SELECT COUNT(*) FROM information_schema.tables
......
1848 1877
        raise RuntimeError()
1849 1878
    if sql_level < 1: # 1: introduction of tracking_code table
1850 1879
        do_tracking_code_table()
1851
    if sql_level < 14:
1880
    if sql_level < 15:
1852 1881
        # 2: introduction of formdef_id in views
1853 1882
        # 5: add concerned_roles_array, is_at_endpoint and fts to views
1854 1883
        # 7: add backoffice_submission to tables
......
1858 1887
        # 11: add formdef_name and user_name to views
1859 1888
        # 13: add backoffice_submission to views
1860 1889
        # 14: add criticality_level to tables & views
1890
        # 15: add geolocation to formdata
1861 1891
        migrate_views(conn, cur)
1862 1892
    if sql_level < 12:
1863 1893
        # 3: introduction of _structured for user fields
1864
-