Browse Source

Moved all modules related to DB in the DataSource folder ( fixes #88 )

Yann Weber 9 years ago
parent
commit
3ae2e6e613

DataSource/MySQL/MySQL.py → DataSource/MySQL/common_utils.py View File


leapi/datasources/ledatasourcesql.py → DataSource/MySQL/leapidatasource.py View File

@@ -4,7 +4,6 @@ import pymysql
4 4
 import copy
5 5
 
6 6
 import leapi
7
-from leapi.datasources.dummy import DummyDatasource
8 7
 from leapi.leobject import REL_SUB, REL_SUP
9 8
 
10 9
 from leapi.lecrud import _LeCrud
@@ -14,7 +13,8 @@ from mosql.query import select, insert, update, delete, join, left_join
14 13
 from mosql.util import raw, or_
15 14
 import mosql.mysql
16 15
 
17
-from DataSource.MySQL.MySQL import MySQL
16
+from DataSource.dummy.leapidatasource import DummyDatasource
17
+from DataSource.MySQL.common_utils import MySQL
18 18
 from EditorialModel.classtypes import EmNature, common_fields
19 19
 
20 20
 

EditorialModel/migrationhandler/sql.py → DataSource/MySQL/migrationhandler.py View File

@@ -7,7 +7,7 @@
7 7
 #
8 8
 
9 9
 import EditorialModel
10
-from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
10
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
11 11
 from EditorialModel.fieldtypes.generic import GenericFieldType
12 12
 from EditorialModel.model import Model
13 13
 from mosql.db import Database

leapi/datasources/dummy.py → DataSource/dummy/leapidatasource.py View File


EditorialModel/migrationhandler/dummy.py → DataSource/dummy/migrationhandler.py View File


+ 0
- 6
EditorialModel/migrationhandler/__init__.py View File

@@ -1,6 +0,0 @@
1
-## @package EditorialModel.migrationhandler
2
-# @brief Tell to an editorial model wether or not a wanted modification is possible
3
-#
4
-# The migration handler provide an API for the EditorialModel to ask wether or not a model modification
5
-# is possible/allowed.
6
-#

+ 0
- 545
EditorialModel/migrationhandler/mysql.py View File

@@ -1,545 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-
3
-import copy
4
-import pymysql
5
-
6
-import EditorialModel
7
-from DataSource.MySQL.MySQL import MySQL
8
-
9
-# The global MH algorithm is as follow :
10
-# A create_table(table_name, pk_name, pk_opt) method that create a table
11
-# with one pk field
12
-# An add_column(table_name, field_name, field_opt) method that add a column to a table
13
-#
14
-# The create_default_table method will call both methods to create the object and relation tables
15
-#
16
-# Supported operations :
17
-# - EmClass creation
18
-# - EmClass deletion (untested)
19
-# - EmField creation
20
-# - EmField deletion (untested)
21
-# - rel2type attribute creation
22
-# - rel2type attribute deletion (unstested)
23
-#
24
-# Unsupported operations :
25
-# - EmClass rename
26
-# - EmField rename
27
-# - rel2type field rename
28
-# - rel2type attribute rename
29
-# - EmFieldType changes
30
-#
31
-# @todo Unified datasources and migration handlers via utils functions
32
-
33
-
34
-## @brief Modify a MySQL database given editorial model changes
35
-class MysqlMigrationHandler(EditorialModel.migrationhandler.dummy.DummyMigrationHandler):
36
-
37
-    ## @brief Construct a MysqlMigrationHandler
38
-    # @param host str : The db host
39
-    # @param user str : The db user
40
-    # @param password str : The db password
41
-    # @param db str : The db name
42
-    def __init__(self, host, user, password, db, db_engine='InnoDB', foreign_keys=True, debug=False, dryrun=False, drop_if_exists=False):
43
-        self.datasource = MySQL
44
-        #Connect to MySQL
45
-        self.db = pymysql.connect(host=host, user=user, passwd=password, db=db)
46
-        self.debug = debug
47
-        self.dryrun = dryrun
48
-        self.db_engine = db_engine
49
-        self.foreign_keys = foreign_keys if db_engine == 'InnoDB' else False
50
-        self.drop_if_exists = drop_if_exists
51
-        #Create default tables
52
-        self._create_default_tables(self.drop_if_exists)
53
-
54
-    ## @brief Delete all table created by the MH
55
-    # @param model Model : the Editorial model
56
-    def __purge_db(self, model):
57
-        for uid in [c.uid for c in model.components('EmClass')]:
58
-            try:
59
-                self.delete_emclass_table(model, uid)
60
-            except pymysql.err.InternalError as e:
61
-                print(e)
62
-
63
-        for tname in [MySQL.get_r2t2table_name(f.em_class.name, model.component(f.rel_to_type_id).name) for f in model.components('EmField') if f.fieldtype == 'rel2type']:
64
-            try:
65
-                self._query("DROP TABLE %s;" % tname)
66
-            except pymysql.err.InternalError as e:
67
-                print(e)
68
-
69
-        for tname in [MySQL.relations_table_name, MySQL.objects_table_name]:
70
-            try:
71
-                self._query("DROP TABLE %s;" % tname)
72
-            except pymysql.err.InternalError as e:
73
-                print(e)
74
-
75
-    ## @brief Modify the db given an EM change
76
-    # @param em model : The EditorialModel.model object to provide the global context
77
-    # @param uid int : The uid of the change EmComponent
78
-    # @param initial_state dict | None : dict with field name as key and field value as value. Representing the original state. None mean creation of a new component.
79
-    # @param new_state dict | None : dict with field name as key and field value as value. Representing the new state. None mean component deletion
80
-    # @throw EditorialModel.exceptions.MigrationHandlerChangeError if the change was refused
81
-    def register_change(self, em, uid, initial_state, new_state, engine=None):
82
-        if engine is None:
83
-            engine = self.db_engine
84
-        if isinstance(em.component(uid), EditorialModel.classes.EmClass):
85
-            if initial_state is None:
86
-                #EmClass creation
87
-                self.create_emclass_table(em, uid, engine)
88
-            elif new_state is None:
89
-                #EmClass deletion
90
-                self.delete_emclass_table(em, uid)
91
-        elif isinstance(em.component(uid), EditorialModel.fields.EmField):
92
-            emfield = em.component(uid)
93
-            if emfield.rel_field_id is None:
94
-                #non relationnal field
95
-                if initial_state is None:
96
-                    #non relationnal EmField creation
97
-                    if emfield.name not in EditorialModel.classtypes.common_fields.keys():
98
-                        self.add_col_from_emfield(em, uid)
99
-                elif new_state is None:
100
-                    #non relationnal EmField deletion
101
-                    if emfield.name not in EditorialModel.classtypes.common_fields.keys():
102
-                        self.del_col_from_emfield(em, uid)
103
-            else:
104
-                #relationnal field
105
-                if initial_state is None:
106
-                    #Rel2type attr creation
107
-                    self.add_relationnal_field(em, uid)
108
-                elif new_state is None:
109
-                    #Rel2type attr deletion
110
-                    self.del_relationnal_field(em, uid)
111
-
112
-    ## @brief dumdumdummy
113
-    # @note implemented to avoid the log message of EditorialModel.migrationhandler.dummy.DummyMigrationHandler
114
-    def register_model_state(self, em, state_hash):
115
-        pass
116
-
117
-    ## @brief Exec a query
118
-    # @param query str : SQL query
119
-    def _query(self, query):
120
-        if self.debug:
121
-            print(query + "\n")
122
-        if not self.dryrun:
123
-            with self.db.cursor() as cur:
124
-                cur.execute(query)
125
-        self.db.commit()  # autocommit
126
-
127
-    ## @brief Add a relationnal field
128
-    # Add a rel2type attribute
129
-    # @note this function handles the table creation
130
-    # @param em Model : EditorialModel.model.Model instance
131
-    # @param rfuid int : Relationnal field uid
132
-    def add_relationnal_field(self, em, rfuid):
133
-        emfield = em.component(rfuid)
134
-        if not isinstance(emfield, EditorialModel.fields.EmField):
135
-            raise ValueError("The given uid is not an EmField uid")
136
-
137
-        r2tf = em.component(emfield.rel_field_id)
138
-        tname = self._r2t2table_name(em, r2tf)
139
-        pkname, pkftype = self._relation_pk
140
-
141
-        #If not exists create a relational table
142
-        self._create_table(tname, pkname, pkftype, self.db_engine, if_exists='nothing')
143
-        #Add a foreign key if wanted
144
-        if self.foreign_keys:
145
-            self._add_fk(tname, self.datasource.relations_table_name, pkname, pkname)
146
-        #Add the column
147
-        self._add_column(tname, emfield.name, emfield.fieldtype_instance())
148
-        #Update table triggers
149
-        self._generate_triggers(tname, self._r2type2cols(em, r2tf))
150
-
151
-    ## @brief Delete a rel2type attribute
152
-    #
153
-    # Delete a rel2type attribute
154
-    # @note this method handles the table deletion
155
-    # @param em Model : EditorialModel.model.Model instance
156
-    # @param rfuid int : Relationnal field uid
157
-    def del_relationnal_field(self, em, rfuid):
158
-        emfield = em.component(rfuid)
159
-        if not isinstance(emfield, EditorialModel.fields.EmField):
160
-            raise ValueError("The given uid is not an EmField uid")
161
-
162
-        r2tf = em.component(emfield.rel_field_id)
163
-        tname = self._r2t2table_name(em, r2tf)
164
-
165
-        if len(self._r2type2cols(em, r2tf)) == 1:
166
-            #The table can be deleted (no more attribute for this rel2type)
167
-            self._query("""DROP TABLE {table_name}""".format(table_name=tname))
168
-        else:
169
-            self._del_column(tname, emfield.name)
170
-            #Update table triggers
171
-            self._generate_triggers(tname, self._r2type2cols(em, r2tf))
172
-
173
-    ## @brief Given an EmField uid add a column to the corresponding table
174
-    # @param em Model : A Model instance
175
-    # @param uid int : An EmField uid
176
-    def add_col_from_emfield(self, em, uid):
177
-        emfield = em.component(uid)
178
-        if not isinstance(emfield, EditorialModel.fields.EmField):
179
-            raise ValueError("The given uid is not an EmField uid")
180
-
181
-        emclass = emfield.em_class
182
-        tname = self._emclass2table_name(emclass)
183
-        self._add_column(tname, emfield.name, emfield.fieldtype_instance())
184
-        # Refresh the table triggers
185
-        cols_l = self._class2cols(emclass)
186
-        self._generate_triggers(tname, cols_l)
187
-
188
-    ## @brief Given a class uid create the coressponding table
189
-    # @param em Model : A Model instance
190
-    # @param uid int : An EmField uid
191
-    def create_emclass_table(self, em, uid, engine):
192
-        emclass = em.component(uid)
193
-        if not isinstance(emclass, EditorialModel.classes.EmClass):
194
-            raise ValueError("The given uid is not an EmClass uid")
195
-        pkname, pktype = self._common_field_pk
196
-        table_name = self._emclass2table_name(emclass)
197
-        self._create_table(table_name, pkname, pktype, engine=engine)
198
-
199
-        if self.foreign_keys:
200
-            self._add_fk(table_name, self.datasource.objects_table_name, pkname, pkname)
201
-
202
-    ## @brief Given an EmClass uid delete the corresponding table
203
-    # @param em Model : A Model instance
204
-    # @param uid int : An EmField uid
205
-    def delete_emclass_table(self, em, uid):
206
-        emclass = em.component(uid)
207
-        if not isinstance(emclass, EditorialModel.classes.EmClass):
208
-            raise ValueError("The give uid is not an EmClass uid")
209
-        tname = self._emclass2table_name(emclass)
210
-        # Delete the table triggers to prevent errors
211
-        self._generate_triggers(tname, dict())
212
-
213
-        tname = self.datasource.escape_idname(tname)
214
-
215
-        self._query("""DROP TABLE {table_name};""".format(table_name=tname))
216
-
217
-    ## @brief Given an EmField delete the corresponding column
218
-    # @param em Model : an @ref EditorialModel.model.Model instance
219
-    # @param uid int : an EmField uid
220
-    def delete_col_from_emfield(self, em, uid):
221
-        emfield = em.component(uid)
222
-        if not isinstance(emfield, EditorialModel.fields.EmField):
223
-            raise ValueError("The given uid is not an EmField uid")
224
-
225
-        emclass = emfield.em_class
226
-        tname = self._emclass2table_name(emclass)
227
-        # Delete the table triggers to prevent errors
228
-        self._generate_triggers(tname, dict())
229
-
230
-        self._del_column(tname, emfield.name)
231
-        # Refresh the table triggers
232
-        cols_ls = self._class2cols(emclass)
233
-        self._generate_triggers(tname, cols_l)
234
-
235
-    ## @brief Delete a column from a table
236
-    # @param tname str : The table name
237
-    # @param fname str : The column name
238
-    def _del_column(self, tname, fname):
239
-        tname = self.datasource.escape_idname(tname)
240
-        fname = self.datasource.escape_idname(fname)
241
-
242
-        self._query("""ALTER TABLE {table_name} DROP COLUMN {col_name};""".format(table_name=tname, col_name=fname))
243
-
244
-    ## @brief Construct a table name given an EmClass instance
245
-    # @param emclass EmClass : An EmClass instance
246
-    # @return a table name
247
-    def _emclass2table_name(self, emclass):
248
-        return self.datasource.get_table_name_from_class(emclass.name)
249
-        #return "class_%s"%emclass.name
250
-
251
-    ## @brief Construct a table name given a rela2type EmField instance
252
-    # @param em Model : A Model instance
253
-    # @param emfield EmField : An EmField instance
254
-    # @return a table name
255
-    def _r2t2table_name(self, em, emfield):
256
-        emclass = emfield.em_class
257
-        emtype = em.component(emfield.rel_to_type_id)
258
-        return self.datasource.get_r2t2table_name(emclass.name, emtype.name)
259
-        #return "%s_%s_%s"%(emclass.name, emtype.name, emfield.name)
260
-
261
-    ## @brief Generate a columns_fieldtype dict given a rel2type EmField
262
-    # @param em Model : an @ref EditorialModel.model.Model instance
263
-    # @param emfield EmField : and @ref EditorialModel.fields.EmField instance
264
-    def _r2type2cols(self, em, emfield):
265
-        return {f.name: f.fieldtype_instance() for f in em.components('EmField') if f.rel_field_id == emfield.uid}
266
-
267
-    ## @brief Generate a columns_fieldtype dict given an EmClass
268
-    # @param emclass EmClass : An EmClass instance
269
-    # @return A dict with column name as key and EmFieldType instance as value
270
-    def _class2cols(self, emclass):
271
-        if not isinstance(emclass, EditorialModel.classes.EmClass):
272
-            raise ValueError("The given uid is not an EmClass uid")
273
-        return {f.name: f.fieldtype_instance() for f in emclass.fields() if f.name not in EditorialModel.classtypes.common_fields.keys()}
274
-
275
-    ## @brief Create object and relations tables
276
-    # @param drop_if_exist bool : If true drop tables if exists
277
-    def _create_default_tables(self, drop_if_exist=False):
278
-        if_exists = 'drop' if drop_if_exist else 'nothing'
279
-        #Object tablea
280
-        tname = self.datasource.objects_table_name
281
-        pk_name, pk_ftype = self._common_field_pk
282
-        self._create_table(tname, pk_name, pk_ftype, engine=self.db_engine, if_exists=if_exists)
283
-        #Adding columns
284
-        cols = {fname: self._common_field_to_ftype(fname) for fname in EditorialModel.classtypes.common_fields}
285
-        for fname, ftype in cols.items():
286
-            if fname != pk_name:
287
-                self._add_column(tname, fname, ftype)
288
-        #Creating triggers
289
-        self._generate_triggers(tname, cols)
290
-
291
-        #Relation table
292
-        tname = self.datasource.relations_table_name
293
-        pk_name, pk_ftype = self._relation_pk
294
-        self._create_table(tname, pk_name, pk_ftype, engine=self.db_engine, if_exists=if_exists)
295
-        #Adding columns
296
-        for fname, ftype in self._relation_cols.items():
297
-            self._add_column(tname, fname, ftype)
298
-        #Creating triggers
299
-        self._generate_triggers(tname, self._relation_cols)
300
-
301
-    ## @return true if the name changes
302
-    def _name_change(self, initial_state, new_state):
303
-        return 'name' in initial_state and initial_state['name'] != new_state['name']
304
-
305
-    ## @brief Create a table with primary key
306
-    # @param table_name str : table name
307
-    # @param pk_name str : pk column name
308
-    # @param pk_specs str : see @ref _field_to_sql()
309
-    # @param engine str : The engine to use with this table
310
-    # @param charset str : The charset of this table
311
-    # @param if_exist str : takes values in ['nothing', 'drop']
312
-    def _create_table(self, table_name, pk_name, pk_ftype, engine, charset='utf8', if_exists='nothing'):
313
-        #Escaped table name
314
-        etname = self.datasource.escape_idname(table_name)
315
-        pk_type = self._field_to_type(pk_ftype)
316
-        pk_specs = self._field_to_specs(pk_ftype)
317
-
318
-        if if_exists == 'drop':
319
-            self._query("""DROP TABLE IF EXISTS {table_name};""".format(table_name=etname))
320
-            qres = """
321
-CREATE TABLE {table_name} (
322
-{pk_name} {pk_type} {pk_specs},
323
-PRIMARY KEY({pk_name})
324
-) ENGINE={engine} DEFAULT CHARSET={charset};"""
325
-        elif if_exists == 'nothing':
326
-            qres = """CREATE TABLE IF NOT EXISTS {table_name} (
327
-{pk_name} {pk_type} {pk_specs},
328
-PRIMARY KEY({pk_name})
329
-) ENGINE={engine} DEFAULT CHARSET={charset};"""
330
-        else:
331
-            raise ValueError("Unexpected value for argument if_exists '%s'." % if_exists)
332
-
333
-        self._query(qres.format(
334
-            table_name=self.datasource.escape_idname(table_name),
335
-            pk_name=self.datasource.escape_idname(pk_name),
336
-            pk_type=pk_type,
337
-            pk_specs=pk_specs,
338
-            engine=engine,
339
-            charset=charset
340
-        ))
341
-
342
-    ## @brief Add a column to a table
343
-    # @param table_name str : The table name
344
-    # @param col_name str : The columns name
345
-    # @param col_fieldtype EmFieldype the fieldtype
346
-    def _add_column(self, table_name, col_name, col_fieldtype, drop_if_exists=False):
347
-        add_col = """ALTER TABLE {table_name}
348
-ADD COLUMN {col_name} {col_type} {col_specs};"""
349
-
350
-        etname = self.datasource.escape_idname(table_name)
351
-        ecname = self.datasource.escape_idname(col_name)
352
-
353
-        add_col = add_col.format(
354
-            table_name=etname,
355
-            col_name=ecname,
356
-            col_type=self._field_to_type(col_fieldtype),
357
-            col_specs=self._field_to_specs(col_fieldtype),
358
-        )
359
-        try:
360
-            self._query(add_col)
361
-        except pymysql.err.InternalError as e:
362
-            if drop_if_exists:
363
-                self._del_column(table_name, col_name)
364
-                self._add_column(table_name, col_name, col_fieldtype, drop_if_exists)
365
-            else:
366
-                #LOG
367
-                print("Aborded, column `%s` exists" % col_name)
368
-
369
-    ## @brief Add a foreign key
370
-    # @param src_table_name str : The name of the table where we will add the FK
371
-    # @param dst_table_name str : The name of the table the FK will point on
372
-    # @param src_col_name str : The name of the concerned column in the src_table
373
-    # @param dst_col_name str : The name of the concerned column in the dst_table
374
-    def _add_fk(self, src_table_name, dst_table_name, src_col_name, dst_col_name):
375
-        stname = self.datasource.escape_idname(src_table_name)
376
-        dtname = self.datasource.escape_idname(dst_table_name)
377
-        scname = self.datasource.escape_idname(src_col_name)
378
-        dcname = self.datasource.escape_idname(dst_col_name)
379
-
380
-        fk_name = self.datasource.get_fk_name(src_table_name, dst_table_name)
381
-
382
-        self._del_fk(src_table_name, dst_table_name)
383
-
384
-        self._query("""ALTER TABLE {src_table}
385
-ADD CONSTRAINT {fk_name}
386
-FOREIGN KEY ({src_col}) references {dst_table}({dst_col});""".format(
387
-            fk_name=self.datasource.escape_idname(fk_name),
388
-            src_table=stname,
389
-            src_col=scname,
390
-            dst_table=dtname,
391
-            dst_col=dcname
392
-        ))
393
-
394
-    ## @brief Given a source and a destination table, delete the corresponding FK
395
-    # @param src_table_name str : The name of the table where the FK is
396
-    # @param dst_table_name str : The name of the table the FK point on
397
-    # @warning fails silently
398
-    def _del_fk(self, src_table_name, dst_table_name):
399
-        try:
400
-            self._query("""ALTER TABLE {src_table}
401
-DROP FOREIGN KEY {fk_name}""".format(
402
-                src_table=self.datasource.escape_idname(src_table_name),
403
-                fk_name=self.datasource.escape_idname(self.datasource.get_fk_name(src_table_name, dst_table_name))
404
-            ))
405
-        except pymysql.err.InternalError:
406
-            # If the FK don't exists we do not care
407
-            pass
408
-
409
-    ## @brief Generate triggers given a table_name and its columns fieldtypes
410
-    # @param table_name str : Table name
411
-    # @param cols_ftype dict : with col name as key and column fieldtype as value
412
-    def _generate_triggers(self, table_name, cols_ftype):
413
-        colval_l_upd = dict()  # param for update trigger
414
-        colval_l_ins = dict()  # param for insert trigger
415
-
416
-        for cname, cftype in cols_ftype.items():
417
-            if cftype.ftype == 'datetime':
418
-                if cftype.now_on_update:
419
-                    colval_l_upd[cname] = 'NOW()'
420
-                if cftype.now_on_create:
421
-                    colval_l_ins[cname] = 'NOW()'
422
-
423
-        self._table_trigger(table_name, 'UPDATE', colval_l_upd)
424
-        self._table_trigger(table_name, 'INSERT', colval_l_ins)
425
-
426
-    ## @brief Create trigger for a table
427
-    #
428
-    # Primarly designed to create trigger for DATETIME types
429
-    # The method generates triggers of the form
430
-    #
431
-    # CREATE TRIGGER BEFORE <moment> ON <table_name>
432
-    # FOR EACH ROW SET <for colname, colval in cols_val>
433
-    # NEW.<colname> = <colval>,
434
-    # <endfor>;
435
-    # @param table_name str : The table name
436
-    # @param moment str : can be 'update' or 'insert'
437
-    # @param cols_val dict : Dict with column name as key and column value as value
438
-    def _table_trigger(self, table_name, moment, cols_val):
439
-        trigger_name = self.datasource.escape_idname("%s_%s_trig" % (table_name, moment))
440
-        #Try to delete the trigger
441
-        drop_trig = """DROP TRIGGER IF EXISTS {trigger_name};""".format(trigger_name=trigger_name)
442
-        self._query(drop_trig)
443
-
444
-        col_val_l = ', '.join(["NEW.%s = %s" % (self.datasource.escape_idname(cname), cval)for cname, cval in cols_val.items()])
445
-        #Create a trigger if needed
446
-        if len(col_val_l) > 0:
447
-            trig_q = """CREATE TRIGGER {trigger_name} BEFORE {moment} ON {table_name}
448
-FOR EACH ROW SET {col_val_list};""".format(
449
-                trigger_name=trigger_name,
450
-                table_name=self.datasource.escape_idname(table_name),
451
-                moment=moment, col_val_list=col_val_l
452
-            )
453
-            self._query(trig_q)
454
-
455
-    ## @brief Identifier escaping
456
-    # @param idname str : An SQL identifier
457
-    #def _idname_escape(self, idname):
458
-    #    if '`' in idname:
459
-    #        raise ValueError("Invalid name : '%s'"%idname)
460
-    #    return '`%s`'%idname
461
-
462
-    ## @brief Returns column specs from fieldtype
463
-    # @param emfieldtype EmFieldType : An EmFieldType insance
464
-    # @todo escape default value
465
-    def _field_to_specs(self, emfieldtype):
466
-        colspec = ''
467
-        if not emfieldtype.nullable:
468
-            colspec = 'NOT NULL'
469
-        if hasattr(emfieldtype, 'default'):
470
-            colspec += ' DEFAULT '
471
-            if emfieldtype.default is None:
472
-                colspec += 'NULL '
473
-            else:
474
-                colspec += emfieldtype.default  # ESCAPE VALUE HERE !!!!
475
-
476
-        if emfieldtype.name == 'pk':
477
-            colspec += ' AUTO_INCREMENT'
478
-
479
-        return colspec
480
-
481
-    ## @brief Given a fieldtype return a MySQL type specifier
482
-    # @param emfieldtype EmFieldType : A fieldtype
483
-    # @return the corresponding MySQL type
484
-    def _field_to_type(self, emfieldtype):
485
-        ftype = emfieldtype.ftype
486
-
487
-        if ftype == 'char' or ftype == 'str':
488
-            res = "VARCHAR(%d)" % emfieldtype.max_length
489
-        elif ftype == 'text':
490
-            res = "TEXT"
491
-        elif ftype == 'datetime':
492
-            res = "DATETIME"
493
-            # client side workaround for only one column with CURRENT_TIMESTAMP : giving NULL to timestamp that don't allows NULL
494
-            # cf. https://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html#idm139961275230400
495
-            # The solution for the migration handler is to create triggers :
496
-            # CREATE TRIGGER trigger_name BEFORE INSERT ON `my_super_table`
497
-            # FOR EACH ROW SET NEW.my_date_column = NOW();
498
-            # and
499
-            # CREATE TRIGGER trigger_name BEFORE UPDATE ON
500
-
501
-        elif ftype == 'bool':
502
-            res = "BOOL"
503
-        elif ftype == 'int':
504
-            res = "INT"
505
-        elif ftype == 'rel2type':
506
-            res = "INT"
507
-        else:
508
-            raise ValueError("Unsuported fieldtype ftype : %s" % ftype)
509
-
510
-        return res
511
-
512
-    ## @brief Returns a tuple (pkname, pk_ftype)
513
-    @property
514
-    def _common_field_pk(self):
515
-        for fname, fta in EditorialModel.classtypes.common_fields.items():
516
-            if fta['fieldtype'] == 'pk':
517
-                return (fname, self._common_field_to_ftype(fname))
518
-        return (None, None)
519
-
520
-    ## @brief Returns a tuple (rel_pkname, rel_ftype)
521
-    # @todo do it
522
-    @property
523
-    def _relation_pk(self):
524
-        return (MySQL.relations_pkname, EditorialModel.fieldtypes.pk.EmFieldType())
525
-
526
-    ## @brief Returns a dict { colname:fieldtype } of relation table columns
527
-    @property
528
-    def _relation_cols(self):
529
-        from_name = EditorialModel.fieldtypes.generic.GenericFieldType.from_name
530
-        return {
531
-            'id_sup': from_name('integer')(),
532
-            'id_sub': from_name('integer')(),
533
-            'rank': from_name('integer')(nullable=True),
534
-            'depth': from_name('integer')(nullable=True),
535
-            'nature': from_name('char')(max_lenght=10, nullable=True),
536
-        }
537
-
538
-    ## @brief Given a common field name return an EmFieldType instance
539
-    # @param cname str : Common field name
540
-    # @return An EmFieldType instance
541
-    def _common_field_to_ftype(self, cname):
542
-        fta = copy.copy(EditorialModel.classtypes.common_fields[cname])
543
-        fto = EditorialModel.fieldtypes.generic.GenericFieldType.from_name(fta['fieldtype'])
544
-        del fta['fieldtype']
545
-        return fto(**fta)

+ 1
- 2
EditorialModel/model.py View File

@@ -4,10 +4,9 @@
4 4
 # Contains the class managing and editorial model
5 5
 
6 6
 import EditorialModel
7
-from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
7
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
8 8
 from EditorialModel.backend.dummy_backend import EmBackendDummy
9 9
 from EditorialModel.classes import EmClass
10
-#from EditorialModel.fieldgroups import EmFieldGroup
11 10
 from EditorialModel.fields import EmField
12 11
 from EditorialModel.types import EmType
13 12
 from EditorialModel.exceptions import EmComponentCheckError, EmComponentNotExistError, MigrationHandlerChangeError

+ 1
- 1
EditorialModel/test/test_classes.py View File

@@ -15,7 +15,7 @@ from EditorialModel.types import EmType
15 15
 from EditorialModel.fields import EmField
16 16
 from EditorialModel.model import Model
17 17
 from EditorialModel.backend.json_backend import EmBackendJson
18
-from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
18
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
19 19
 
20 20
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Lodel.settings")
21 21
 EM_TEST = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'me.json')

+ 1
- 1
EditorialModel/test/test_component.py View File

@@ -9,7 +9,7 @@ from EditorialModel.fields import EmField
9 9
 from Lodel.utils.mlstring import MlString
10 10
 
11 11
 from EditorialModel.backend.json_backend import EmBackendJson
12
-from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
12
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
13 13
 
14 14
 
15 15
 class TestEmComponent(unittest.TestCase):

+ 1
- 1
EditorialModel/test/test_model.py View File

@@ -11,7 +11,7 @@ from Lodel.utils.mlstring import MlString
11 11
 
12 12
 from EditorialModel.backend.json_backend import EmBackendJson
13 13
 from EditorialModel.backend.dummy_backend import EmBackendDummy
14
-from EditorialModel.migrationhandler.dummy import DummyMigrationHandler
14
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
15 15
 
16 16
 
17 17
 class TestModel(unittest.TestCase):

+ 0
- 0
leapi/datasources/__init__.py View File


+ 5
- 4
leapi/test/test_lecrud.py View File

@@ -7,6 +7,7 @@ from unittest import TestCase
7 7
 from unittest.mock import patch
8 8
 
9 9
 import EditorialModel
10
+import DataSource.dummy
10 11
 import leapi
11 12
 import leapi.test.utils
12 13
 from leapi.lecrud import _LeCrud
@@ -147,7 +148,7 @@ class LeCrudTestCase(TestCase):
147 148
     #   Tests mocking the datasource
148 149
149 150
 
150
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
151
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
151 152
     def test_insert(self, dsmock):
152 153
         from dyncode import Publication, Numero, LeObject, Personne, Article
153 154
         ndatas = [
@@ -166,7 +167,7 @@ class LeCrudTestCase(TestCase):
166 167
             dsmock.reset_mock()
167 168
     
168 169
     ## @todo try failing on inserting from LeClass child or LeObject
169
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
170
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
170 171
     def test_insert_fails(self, dsmock):
171 172
         from dyncode import Publication, Numero, LeObject, Personne, Article
172 173
         ndatas = [
@@ -182,7 +183,7 @@ class LeCrudTestCase(TestCase):
182 183
                 assert not dsmock.called
183 184
         pass
184 185
     
185
-    @patch('leapi.datasources.dummy.DummyDatasource.update')
186
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
186 187
     def test_update(self, dsmock):
187 188
         from dyncode import Publication, Numero, LeObject
188 189
         
@@ -203,7 +204,7 @@ class LeCrudTestCase(TestCase):
203 204
             dsmock.assert_called_once_with(ccls, efilters, erelfilters, **qdatas)
204 205
     
205 206
     ## @todo test invalid get
206
-    @patch('leapi.datasources.dummy.DummyDatasource.select')
207
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.select')
207 208
     def test_get(self, dsmock):
208 209
         from dyncode import Publication, Numero, LeObject, Textes
209 210
         

+ 1
- 1
leapi/test/test_ledatasourcesql.py View File

@@ -4,7 +4,7 @@ import unittest
4 4
 import sqlite3
5 5
 import pymysql
6 6
 from unittest import TestCase
7
-from leapi.datasources.ledatasourcesql import LeDataSourceSQL
7
+from DataSource.MySQL.leapidatasource import LeDataSourceSQL
8 8
 from mosql.db import Database
9 9
 
10 10
 import pymysql

+ 4
- 3
leapi/test/test_leobject.py View File

@@ -7,6 +7,7 @@ from unittest import TestCase
7 7
 from unittest.mock import patch
8 8
 
9 9
 import EditorialModel
10
+import DataSource.dummy
10 11
 import leapi
11 12
 import leapi.test.utils
12 13
 from leapi.leobject import _LeObject
@@ -104,7 +105,7 @@ class LeObjectMockDatasourceTestCase(TestCase):
104 105
         """ Remove the temporary directory created at class setup """
105 106
         leapi.test.utils.cleanup(cls.tmpdir)
106 107
     
107
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
108
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
108 109
     def test_insert(self, dsmock):
109 110
         from dyncode import Publication, Numero, LeObject
110 111
         ndatas = [
@@ -122,7 +123,7 @@ class LeObjectMockDatasourceTestCase(TestCase):
122 123
             dsmock.reset_mock()
123 124
 
124 125
     @unittest.skip("Wait FieldTypes modification (cf. #90) and classmethod capabilities for update")
125
-    @patch('leapi.datasources.dummy.DummyDatasource.update')
126
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
126 127
     def test_update(self, dsmock):
127 128
         from dyncode import Publication, Numero, LeObject
128 129
 
@@ -148,7 +149,7 @@ class LeObjectMockDatasourceTestCase(TestCase):
148 149
             dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
149 150
             dsmock.reset_mock()
150 151
     
151
-    @patch('leapi.datasources.dummy.DummyDatasource.delete')
152
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.delete')
152 153
     def test_delete(self, dsmock):
153 154
         from dyncode import Publication, Numero, LeObject, LeType
154 155
 

+ 8
- 7
leapi/test/test_lerelation.py View File

@@ -7,6 +7,7 @@ from unittest import TestCase
7 7
 from unittest.mock import patch
8 8
 
9 9
 import EditorialModel
10
+import DataSource.dummy
10 11
 import leapi
11 12
 import leapi.test.utils
12 13
 import leapi.lecrud as lecrud
@@ -67,7 +68,7 @@ class LeRelationTestCase(TestCase):
67 68
                 self.assertEqual(filter_res[i], res[i], "%s != %s"%(filter_res, res))
68 69
 
69 70
     @unittest.skip("Wait LeRelation._prepare_filters() and LeRelation.delete() to unskip")
70
-    @patch('leapi.datasources.dummy.DummyDatasource.delete')
71
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.delete')
71 72
     def test_delete(self, dsmock):
72 73
         """ Testing LeHierarch insert method """
73 74
         from dyncode import LeCrud, Publication, Numero, Personnes, LeObject, Rubrique, LeHierarch, LeRelation
@@ -79,7 +80,7 @@ class LeRelationTestCase(TestCase):
79 80
 
80 81
 class LeHierarch(LeRelationTestCase):
81 82
     
82
-    @patch('leapi.datasources.dummy.DummyDatasource.select')
83
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.select')
83 84
     def test_get(self, dsmock):
84 85
         """ Tests the LeHierarch.get() method """
85 86
         from dyncode import LeCrud, Publication, Numero, Personnes, LeObject, Rubrique, LeHierarch, LeRelation
@@ -122,7 +123,7 @@ class LeHierarch(LeRelationTestCase):
122 123
 
123 124
             dsmock.reset_mock()
124 125
     
125
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
126
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
126 127
     def test_insert(self, dsmock):
127 128
         """ Testing LeHierarch insert method """
128 129
         from dyncode import LeCrud, Publication, Numero, Personnes, LeObject, Rubrique, LeHierarch, LeRelation
@@ -187,7 +188,7 @@ class LeHierarch(LeRelationTestCase):
187 188
     
188 189
 
189 190
     @unittest.skip("Wait for LeRelation.delete()")
190
-    @patch('leapi.datasources.dummy.DummyDatasource.delete')
191
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.delete')
191 192
     def test_delete(self, dsmock):
192 193
         """ Testing LeHierarch insert method """
193 194
         from dyncode import LeCrud, Publication, Numero, Personnes, LeObject, Rubrique, LeHierarch, LeRelation
@@ -197,7 +198,7 @@ class LeHierarch(LeRelationTestCase):
197 198
         
198 199
     
199 200
     @unittest.skip("Wait for LeRelation.update() to unskip")
200
-    @patch('leapi.datasources.dummy.DummyDatasource.update')
201
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
201 202
     def test_update(self, dsmock):
202 203
         """ test LeHierach update method"""
203 204
         from dyncode import LeHierarch
@@ -209,7 +210,7 @@ class LeHierarch(LeRelationTestCase):
209 210
 class LeRel2TypeTestCase(LeRelationTestCase):
210 211
     
211 212
     @unittest.skip("Wait implementation to unskip")
212
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
213
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
213 214
     def test_insert(self, dsmock):
214 215
         """ test LeHierach update method"""
215 216
         from dyncode import LeObject, Article, Textes, Personne, Personnes, LeHierarch, LeRel2Type, Rel_textes2personne
@@ -261,7 +262,7 @@ class LeRel2TypeTestCase(LeRelationTestCase):
261 262
             dsmock.reset_mock()
262 263
 
263 264
     @unittest.skip("Wait implementation to unskip")
264
-    @patch('leapi.datasources.dummy.DummyDatasource.insert')
265
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
265 266
     def test_insert_fails(self, dsmock):
266 267
         """ test LeHierach update method"""
267 268
         from dyncode import LeObject, Rubrique, Numero, Article, Textes, Personne, Personnes, LeHierarch, LeRel2Type, Rel_textes2personne

+ 4
- 3
leapi/test/test_letype.py View File

@@ -8,6 +8,7 @@ from unittest.mock import patch
8 8
 
9 9
 import EditorialModel
10 10
 import leapi
11
+import DataSource.dummy
11 12
 import leapi.test.utils
12 13
 
13 14
 class LeTypeTestCase(TestCase):
@@ -65,7 +66,7 @@ class LeTypeMockDsTestCase(TestCase):
65 66
         """ Remove the temporary directory created at class setup """
66 67
         leapi.test.utils.cleanup(cls.tmpdir)
67 68
 
68
-    @patch('leapi.datasources.dummy.DummyDatasource.select')
69
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.select')
69 70
     def test_populate(self, dsmock):
70 71
         from dyncode import Publication, Numero, LeObject
71 72
 
@@ -74,7 +75,7 @@ class LeTypeMockDsTestCase(TestCase):
74 75
         num.populate()
75 76
         dsmock.assert_called_once_with(Numero, missing_fields, [('lodel_id','=',1)],[])
76 77
 
77
-    @patch('leapi.datasources.dummy.DummyDatasource.update')
78
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
78 79
     def test_update(self, dsmock):
79 80
         from dyncode import Publication, Numero, LeObject
80 81
         
@@ -85,7 +86,7 @@ class LeTypeMockDsTestCase(TestCase):
85 86
         num.update(datas)
86 87
         dsmock.assert_called_once_with(Numero, [('lodel_id','=',1)], [], **datas)
87 88
     
88
-    @patch('leapi.datasources.dummy.DummyDatasource.delete')
89
+    @patch('DataSource.dummy.leapidatasource.DummyDatasource.delete')
89 90
     def test_delete(self, dsmock):
90 91
         from dyncode import Publication, Numero, LeObject
91 92
         

+ 1
- 1
leapi/test/utils.py View File

@@ -5,7 +5,7 @@ import sys
5 5
 from EditorialModel.model import Model
6 6
 import leapi
7 7
 from EditorialModel.backend.json_backend import EmBackendJson
8
-from leapi.datasources.dummy import DummyDatasource
8
+from DataSource.MySQL.leapidatasource import DummyDatasource
9 9
 from leapi.lefactory import LeFactory
10 10
 
11 11
 

Loading…
Cancel
Save