Browse Source

Write an instance creation script and replace the SQLMigrationHandler by the MysqlMigrationHandler

see README.md for more informations about instance creation
Yann Weber 8 years ago
parent
commit
4383606fbb

+ 521
- 130
DataSource/MySQL/migrationhandler.py View File

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

+ 156
- 0
DataSource/MySQL/migrationhandler__future__.py View File

@@ -0,0 +1,156 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+## @package EditorialModel.migrationhandler.sql
4
+# @brief A dummy migration handler
5
+#
6
+# According to it every modifications are possible
7
+#
8
+
9
+import EditorialModel
10
+from DataSource.dummy.migrationhandler import DummyMigrationHandler
11
+from EditorialModel.fieldtypes.generic import GenericFieldType
12
+from EditorialModel.model import Model
13
+from mosql.db import Database
14
+from Lodel.utils.mosql import create, alter_add
15
+
16
+
17
+## Manage Model changes
18
+class SQLMigrationHandler(DummyMigrationHandler):
19
+
20
+    fieldtype_to_sql = {
21
+        'char': "CHAR(255)",
22
+        'integer': 'INT'
23
+    }
24
+
25
+    def __init__(self, module=None, *conn_args, **conn_kargs):
26
+        super(SQLMigrationHandler, self).__init__(False)
27
+
28
+        self.db = Database(module, *conn_args, **conn_kargs)
29
+        self._pk_column = (EditorialModel.classtypes.pk_name(), 'INTEGER PRIMARY KEY AUTO_INCREMENT')
30
+        self._main_table_name = 'object'
31
+        self._relation_table_name = 'relation'
32
+
33
+        self._install_tables()
34
+
35
+    ## @brief Record a change in the EditorialModel and indicate wether or not it is possible to make it
36
+    # @note The states ( initial_state and new_state ) contains only fields that changes
37
+    # @param model model : The EditorialModel.model object to provide the global context
38
+    # @param uid int : The uid of the change EmComponent
39
+    # @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.
40
+    # @param new_state dict | None : dict with field name as key and field value as value. Representing the new state. None mean component deletion
41
+    # @throw EditorialModel.exceptions.MigrationHandlerChangeError if the change was refused
42
+    def register_change(self, model, uid, initial_state, new_state):
43
+        # find type of component change
44
+        if initial_state is None:
45
+            state_change = 'new'
46
+        elif new_state is None:
47
+            state_change = 'del'
48
+        else:
49
+            state_change = 'upgrade'
50
+
51
+        # call method to handle the database change
52
+        component_name = Model.name_from_emclass(type(model.component(uid)))
53
+        handler_func = component_name.lower() + '_' + state_change
54
+        if hasattr(self, handler_func):
55
+            getattr(self, handler_func)(model, uid, initial_state, new_state)
56
+
57
+    # New Class, a table must be created
58
+    def emclass_new(self, model, uid, initial_state, new_state):
59
+        class_table_name = self._class_table_name(new_state['name'])
60
+        self._query_bd(
61
+            create(table=class_table_name, column=[self._pk_column])
62
+        )
63
+
64
+    # New Field, must create a column in Class table or in Class_Type relational attribute table
65
+    # @todo common fields creation does not allow to add new common fields. It should
66
+    def emfield_new(self, model, uid, initial_state, new_state):
67
+
68
+        # field is of type rel2type, create the relational class_type table and return
69
+        if new_state['fieldtype'] == 'rel2type':
70
+            # find relational_type name, and class name of the field
71
+            class_name = self._class_table_name_from_field(model, new_state)
72
+            type_name = model.component(new_state['rel_to_type_id']).name
73
+            table_name = self._relational_table_name(class_name, type_name)
74
+            self._query_bd(
75
+                create(table=table_name, column=[self._pk_column]),
76
+            )
77
+            return
78
+
79
+        # Column creation
80
+        #
81
+        # field is internal, create a column in the objects table
82
+        if new_state['internal']:
83
+            if new_state['fieldtype'] == 'pk':  # this column has already beeen created by self._install_tables()
84
+                return
85
+            if new_state['name'] in EditorialModel.classtypes.common_fields:  # this column has already beeen created by self._install_tables()
86
+                return
87
+
88
+        # field is relational (rel_field_id), create a column in the class_type table
89
+        elif new_state['rel_field_id']:
90
+            class_name = self._class_table_name_from_field(model, new_state)
91
+            rel_type_id = model.component(new_state['rel_field_id']).rel_to_type_id
92
+            type_name = model.component(rel_type_id).name
93
+            table_name = self._relational_table_name(class_name, type_name)
94
+
95
+        # else create a column in the class table
96
+        else:
97
+            table_name = self._class_table_name_from_field(model, new_state)
98
+
99
+        field_definition = self._fieldtype_definition(new_state['fieldtype'], new_state)
100
+        self._query_bd(
101
+            alter_add(table=table_name, column=[(new_state['name'],field_definition)])
102
+        )
103
+
104
+    ## convert fieldtype name to SQL definition
105
+    def _fieldtype_definition(self, fieldtype, options):
106
+        basic_type = GenericFieldType.from_name(fieldtype).ftype
107
+        if basic_type == 'int':
108
+            return 'INT'
109
+        elif basic_type == 'char':
110
+            max_length = options['max_length'] if 'max_length' in options else 255
111
+            return 'CHAR(%s)' % max_length
112
+        elif basic_type == 'text':
113
+            return 'TEXT'
114
+        elif basic_type == 'bool':
115
+            return 'BOOLEAN'
116
+        elif basic_type == 'datetime':
117
+            definition = 'DATETIME'
118
+            if 'now_on_create' in options and options['now_on_create']:
119
+                definition += ' DEFAULT CURRENT_TIMESTAMP'
120
+            #if 'now_on_update' in options and options['now_on_update']:
121
+                #definition += ' ON UPDATE CURRENT_TIMESTAMP'
122
+            return definition
123
+
124
+        raise EditorialModel.exceptions.MigrationHandlerChangeError("Basic type '%s' of fieldtype '%s' is not compatible with SQL migration Handler" % basic_type, fieldtype)
125
+
126
+    ## Test if internal tables must be created, create it if it must
127
+    def _install_tables(self):
128
+        # create common fields definition
129
+        common_fields = [self._pk_column]
130
+        for name, options in EditorialModel.classtypes.common_fields.items():
131
+            if options['fieldtype'] != 'pk':
132
+                common_fields.append((name, self._fieldtype_definition(options['fieldtype'], options)))
133
+
134
+        # create common tables
135
+        self._query_bd(
136
+            create(table=self._main_table_name, column=common_fields),
137
+            create(table=self._relation_table_name, column=[('relation_id','INTEGER PRIMARY KEY AUTOINCREMENT'), ('superior_id','INT'), ('subdordinate_id','INT'), ('nature','CHAR(255)'), ('depth','INT'), ('rank','INT')])
138
+        )
139
+
140
+    def _query_bd(self, *queries):
141
+        with self.db as cur:
142
+            for query in queries:
143
+                print(query)
144
+                cur.execute(query)
145
+
146
+    def _class_table_name(self, class_name):
147
+        return 'class_' + class_name
148
+
149
+    def _relational_table_name(self, class_name, type_name):
150
+        return 'r2t_' + class_name + '_' + type_name
151
+
152
+    def _class_table_name_from_field(self, model, field):
153
+        class_id = field['class_id']
154
+        class_name = model.component(class_id).name
155
+        class_table_name = self._class_table_name(class_name)
156
+        return class_table_name

+ 10
- 0
README.md View File

@@ -1,3 +1,13 @@
1
+Creating a Lodel "instance":
2
+
3
+use the lodel_init.sh script :
4
+	lodel_init.sh INSTANCE_NAME INSTANCE_WANTED_PATH [LODEL2_LIB_PATH]
5
+
6
+Once the instance is created you can run an interactive python interpreter using :
7
+	cd INSTANCE_PATH; python loader.py
8
+
9
+If you want to write a script that run is the instance env you have to from loader import *
10
+
1 11
 First test installation :
2 12
 
3 13
 - use python 3.4

+ 17
- 0
install/Makefile View File

@@ -0,0 +1,17 @@
1
+all:
2
+
3
+refreshdyn:
4
+	python -c "import utils; utils.refreshdyn()"
5
+
6
+dbinit:
7
+	python -c "import utils; utils.db_init()"
8
+
9
+.PHONY: clean cleanpycache cleanpyc refreshdyn
10
+
11
+clean: cleanpycache
12
+
13
+cleanpyc:
14
+	-@rm -v *.pyc
15
+
16
+cleanpycache: cleanpyc
17
+	-@rm -vR __pycache__

+ 171
- 0
install/dynleapi.py View File

@@ -0,0 +1,171 @@
1
+## @author LeFactory
2
+
3
+import EditorialModel
4
+from EditorialModel import fieldtypes
5
+from EditorialModel.fieldtypes import naturerelation, char, integer, datetime, pk
6
+
7
+import leapi
8
+import leapi.lecrud
9
+import leapi.leobject
10
+import leapi.lerelation
11
+from leapi.leclass import _LeClass
12
+from leapi.letype import _LeType
13
+
14
+import DataSource.MySQL.leapidatasource
15
+
16
+
17
+## @brief _LeCrud concret class
18
+# @see leapi.lecrud._LeCrud
19
+class LeCrud(leapi.lecrud._LeCrud):
20
+    _datasource = DataSource.MySQL.leapidatasource.LeDataSourceSQL(**{})
21
+    _uid_fieldtype = None
22
+
23
+## @brief _LeObject concret class
24
+# @see leapi.leobject._LeObject
25
+class LeObject(LeCrud, leapi.leobject._LeObject):
26
+    _me_uid = {1: 'Textes', 2: 'Personnes', 19: 'Numero', 5: 'Article', 6: 'Personne', 13: 'Publication', 14: 'Rubrique'}
27
+    _uid_fieldtype = { 'lodel_id': EditorialModel.fieldtypes.pk.EmFieldType(**{'internal': 'automatic'}) }
28
+    _leo_fieldtypes = {
29
+	'creation_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'now_on_create': True, 'internal': 'automatic'}),
30
+	'string': EditorialModel.fieldtypes.char.EmFieldType(**{'max_length': 128, 'internal': 'automatic'}),
31
+	'modification_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'now_on_update': True, 'now_on_create': True, 'internal': 'automatic'}),
32
+	'type_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'internal': 'automatic'}),
33
+	'class_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'internal': 'automatic'})
34
+	}
35
+
36
+## @brief _LeRelation concret class
37
+# @see leapi.lerelation._LeRelation
38
+class LeRelation(LeCrud, leapi.lerelation._LeRelation):
39
+    _uid_fieldtype = { 'id_relation': EditorialModel.fieldtypes.pk.EmFieldType(**{'internal': 'automatic'}) }
40
+    _rel_fieldtypes = {
41
+	'rank': EditorialModel.fieldtypes.integer.EmFieldType(**{'internal': 'automatic'}),
42
+	'nature': EditorialModel.fieldtypes.naturerelation.EmFieldType(**{}),
43
+	'depth': EditorialModel.fieldtypes.integer.EmFieldType(**{'internal': 'automatic'})
44
+	}
45
+    _rel_attr_fieldtypes = dict()
46
+
47
+class LeHierarch(LeRelation, leapi.lerelation._LeHierarch):
48
+    _rel_attr_fieldtypes = dict()
49
+
50
+class LeRel2Type(LeRelation, leapi.lerelation._LeRel2Type):
51
+    pass
52
+
53
+class LeClass(LeObject, _LeClass):
54
+    pass
55
+
56
+class LeType(LeClass, _LeType):
57
+    pass
58
+
59
+## @brief EmClass Textes LeClass child class
60
+# @see leapi.leclass.LeClass
61
+class Textes(LeClass, LeObject):
62
+    _class_id = 1
63
+
64
+
65
+## @brief EmClass Personnes LeClass child class
66
+# @see leapi.leclass.LeClass
67
+class Personnes(LeClass, LeObject):
68
+    _class_id = 2
69
+
70
+
71
+## @brief EmClass Publication LeClass child class
72
+# @see leapi.leclass.LeClass
73
+class Publication(LeClass, LeObject):
74
+    _class_id = 13
75
+
76
+
77
+## @brief EmType Article LeType child class
78
+# @see leobject::letype::LeType
79
+class Article(LeType, Textes):
80
+    _type_id = 5
81
+
82
+
83
+## @brief EmType Personne LeType child class
84
+# @see leobject::letype::LeType
85
+class Personne(LeType, Personnes):
86
+    _type_id = 6
87
+
88
+
89
+## @brief EmType Rubrique LeType child class
90
+# @see leobject::letype::LeType
91
+class Rubrique(LeType, Publication):
92
+    _type_id = 14
93
+
94
+
95
+## @brief EmType Numero LeType child class
96
+# @see leobject::letype::LeType
97
+class Numero(LeType, Publication):
98
+    _type_id = 19
99
+
100
+
101
+class Rel_textes2personne(LeRel2Type):
102
+    _rel_attr_fieldtypes = {
103
+    'adresse': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False})
104
+}
105
+
106
+
107
+#Initialisation of Textes class attributes
108
+Textes._fieldtypes = {
109
+    'modification_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'nullable': False, 'uniq': False, 'now_on_update': True, 'now_on_create': True, 'internal': 'automatic'}),
110
+    'soustitre': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
111
+    'string': EditorialModel.fieldtypes.char.EmFieldType(**{'nullable': True, 'uniq': False, 'max_length': 128, 'internal': 'automatic'}),
112
+    'titre': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
113
+    'bleu': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
114
+    'lodel_id': EditorialModel.fieldtypes.pk.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
115
+    'type_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
116
+    'class_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
117
+    'creation_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'uniq': False, 'nullable': False, 'now_on_create': True, 'internal': 'automatic'})
118
+}
119
+Textes._linked_types = [Personne]
120
+Textes._classtype = 'entity'
121
+
122
+#Initialisation of Personnes class attributes
123
+Personnes._fieldtypes = {
124
+    'nom': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
125
+    'age': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
126
+    'prenom': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
127
+    'string': EditorialModel.fieldtypes.char.EmFieldType(**{'nullable': True, 'uniq': False, 'max_length': 128, 'internal': 'automatic'}),
128
+    'lodel_id': EditorialModel.fieldtypes.pk.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
129
+    'modification_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'nullable': False, 'uniq': False, 'now_on_update': True, 'now_on_create': True, 'internal': 'automatic'}),
130
+    'type_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
131
+    'class_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
132
+    'creation_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'uniq': False, 'nullable': False, 'now_on_create': True, 'internal': 'automatic'})
133
+}
134
+Personnes._linked_types = []
135
+Personnes._classtype = 'person'
136
+
137
+#Initialisation of Publication class attributes
138
+Publication._fieldtypes = {
139
+    'modification_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'nullable': False, 'uniq': False, 'now_on_update': True, 'now_on_create': True, 'internal': 'automatic'}),
140
+    'creation_date': EditorialModel.fieldtypes.datetime.EmFieldType(**{'uniq': False, 'nullable': False, 'now_on_create': True, 'internal': 'automatic'}),
141
+    'string': EditorialModel.fieldtypes.char.EmFieldType(**{'nullable': True, 'uniq': False, 'max_length': 128, 'internal': 'automatic'}),
142
+    'lodel_id': EditorialModel.fieldtypes.pk.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
143
+    'titre': EditorialModel.fieldtypes.char.EmFieldType(**{'uniq': False, 'nullable': True, 'internal': False}),
144
+    'type_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'}),
145
+    'class_id': EditorialModel.fieldtypes.integer.EmFieldType(**{'uniq': False, 'nullable': False, 'internal': 'automatic'})
146
+}
147
+Publication._linked_types = []
148
+Publication._classtype = 'entity'
149
+
150
+#Initialisation of Article class attributes
151
+Article._fields = ['titre', 'class_id', 'soustitre', 'string', 'type_id', 'lodel_id', 'modification_date', 'creation_date']
152
+Article._superiors = {'parent': [Rubrique]}
153
+Article._leclass = Textes
154
+
155
+#Initialisation of Personne class attributes
156
+Personne._fields = ['nom', 'class_id', 'prenom', 'string', 'type_id', 'lodel_id', 'modification_date', 'creation_date']
157
+Personne._superiors = {}
158
+Personne._leclass = Personnes
159
+
160
+#Initialisation of Rubrique class attributes
161
+Rubrique._fields = ['titre', 'class_id', 'string', 'type_id', 'lodel_id', 'modification_date', 'creation_date']
162
+Rubrique._superiors = {'parent': [Rubrique, Numero]}
163
+Rubrique._leclass = Publication
164
+
165
+#Initialisation of Numero class attributes
166
+Numero._fields = ['titre', 'class_id', 'string', 'type_id', 'lodel_id', 'modification_date', 'creation_date']
167
+Numero._superiors = {}
168
+Numero._leclass = Publication
169
+
170
+## @brief Dict for getting LeClass and LeType child classes given an EM uid
171
+LeObject._me_uid = {1: Textes, 2: Personnes, 19: Numero, 5: Article, 6: Personne, 13: Publication, 14: Rubrique}

+ 579
- 0
install/em.json View File

@@ -0,0 +1,579 @@
1
+{
2
+ "1": {
3
+  "component": "EmClass",
4
+  "date_create": "Fri Oct 16 11:05:04 2015",
5
+  "date_update": "Fri Oct 16 11:05:04 2015",
6
+  "sortcolumn": "rank",
7
+  "classtype": "entity",
8
+  "icon": "0",
9
+  "rank": 1,
10
+  "name": "textes",
11
+  "help_text": "",
12
+  "string": "{\"fre\": \"Texte\"}"
13
+ },
14
+ "2": {
15
+  "component": "EmClass",
16
+  "date_create": "Fri Oct 16 11:05:04 2015",
17
+  "date_update": "Fri Oct 16 11:05:04 2015",
18
+  "sortcolumn": "rank",
19
+  "classtype": "person",
20
+  "icon": "0",
21
+  "rank": 1,
22
+  "name": "personnes",
23
+  "help_text": "",
24
+  "string": "{\"fre\": \"Personnes\"}"
25
+ },
26
+ "4": {
27
+  "nullable": true,
28
+  "date_update": "Fri Oct 16 11:05:04 2015",
29
+  "optional": false,
30
+  "icon": "0",
31
+  "uniq": false,
32
+  "component": "EmField",
33
+  "string": "{\"fre\": \"Titre\"}",
34
+  "class_id": 1,
35
+  "date_create": "Fri Oct 16 11:05:04 2015",
36
+  "rank": 1,
37
+  "fieldtype": "char",
38
+  "help_text": "",
39
+  "internal": false,
40
+  "rel_field_id": null,
41
+  "name": "titre"
42
+ },
43
+ "5": {
44
+  "date_update": "Fri Oct 16 11:05:04 2015",
45
+  "class_id": 1,
46
+  "icon": "0",
47
+  "name": "article",
48
+  "superiors_list": {
49
+   "parent": [
50
+    14
51
+   ]
52
+  },
53
+  "fields_list": [
54
+   7
55
+  ],
56
+  "string": "{\"fre\": \"Article\"}",
57
+  "date_create": "Fri Oct 16 11:05:04 2015",
58
+  "component": "EmType",
59
+  "rank": 1,
60
+  "help_text": "",
61
+  "sortcolumn": "rank"
62
+ },
63
+ "6": {
64
+  "date_update": "Fri Oct 16 11:05:04 2015",
65
+  "class_id": 2,
66
+  "icon": "0",
67
+  "name": "personne",
68
+  "superiors_list": {},
69
+  "fields_list": [
70
+   10
71
+  ],
72
+  "string": "{\"fre\": \"Personne\"}",
73
+  "date_create": "Fri Oct 16 11:05:04 2015",
74
+  "component": "EmType",
75
+  "rank": 1,
76
+  "help_text": "",
77
+  "sortcolumn": "rank"
78
+ },
79
+ "7": {
80
+  "nullable": true,
81
+  "date_update": "Fri Oct 16 11:05:04 2015",
82
+  "optional": true,
83
+  "icon": "0",
84
+  "uniq": false,
85
+  "component": "EmField",
86
+  "string": "{\"fre\": \"Sous-titre\"}",
87
+  "class_id": 1,
88
+  "date_create": "Fri Oct 16 11:05:04 2015",
89
+  "rank": 2,
90
+  "fieldtype": "char",
91
+  "help_text": "",
92
+  "internal": false,
93
+  "rel_field_id": null,
94
+  "name": "soustitre"
95
+ },
96
+ "9": {
97
+  "nullable": true,
98
+  "date_update": "Fri Oct 16 11:05:04 2015",
99
+  "optional": false,
100
+  "icon": "0",
101
+  "uniq": false,
102
+  "component": "EmField",
103
+  "string": "{\"fre\": \"Nom\"}",
104
+  "class_id": 2,
105
+  "date_create": "Fri Oct 16 11:05:04 2015",
106
+  "rank": 1,
107
+  "fieldtype": "char",
108
+  "help_text": "",
109
+  "internal": false,
110
+  "rel_field_id": null,
111
+  "name": "nom"
112
+ },
113
+ "10": {
114
+  "nullable": true,
115
+  "date_update": "Fri Oct 16 11:05:04 2015",
116
+  "optional": true,
117
+  "icon": "0",
118
+  "uniq": false,
119
+  "component": "EmField",
120
+  "string": "{\"fre\": \"Pr\\u00e9nom\"}",
121
+  "class_id": 2,
122
+  "date_create": "Fri Oct 16 11:05:04 2015",
123
+  "rank": 2,
124
+  "fieldtype": "char",
125
+  "help_text": "",
126
+  "internal": false,
127
+  "rel_field_id": null,
128
+  "name": "prenom"
129
+ },
130
+ "11": {
131
+  "nullable": true,
132
+  "date_update": "Fri Oct 16 11:05:04 2015",
133
+  "optional": false,
134
+  "icon": "0",
135
+  "uniq": false,
136
+  "component": "EmField",
137
+  "string": "{\"fre\": \"Auteur\"}",
138
+  "class_id": 1,
139
+  "date_create": "Fri Oct 16 11:05:04 2015",
140
+  "rel_to_type_id": 6,
141
+  "rank": 1,
142
+  "fieldtype": "rel2type",
143
+  "help_text": "",
144
+  "internal": false,
145
+  "rel_field_id": null,
146
+  "name": "auteur"
147
+ },
148
+ "12": {
149
+  "nullable": true,
150
+  "date_update": "Fri Oct 16 11:05:04 2015",
151
+  "optional": false,
152
+  "icon": "0",
153
+  "uniq": false,
154
+  "component": "EmField",
155
+  "string": "{\"fre\": \"Adresse\"}",
156
+  "class_id": 1,
157
+  "date_create": "Fri Oct 16 11:05:04 2015",
158
+  "rank": 2,
159
+  "fieldtype": "char",
160
+  "help_text": "",
161
+  "internal": false,
162
+  "rel_field_id": 11,
163
+  "name": "adresse"
164
+ },
165
+ "13": {
166
+  "component": "EmClass",
167
+  "date_create": "Fri Oct 16 11:05:04 2015",
168
+  "date_update": "Fri Oct 16 11:05:04 2015",
169
+  "sortcolumn": "rank",
170
+  "classtype": "entity",
171
+  "icon": "0",
172
+  "rank": 2,
173
+  "name": "publication",
174
+  "help_text": "",
175
+  "string": "{\"fre\": \"Publication\"}"
176
+ },
177
+ "14": {
178
+  "date_update": "Fri Oct 16 11:05:04 2015",
179
+  "class_id": 13,
180
+  "icon": "0",
181
+  "name": "rubrique",
182
+  "superiors_list": {
183
+   "parent": [
184
+    14,
185
+    19
186
+   ]
187
+  },
188
+  "fields_list": [],
189
+  "string": "{\"fre\": \"Rubrique\"}",
190
+  "date_create": "Fri Oct 16 11:05:04 2015",
191
+  "component": "EmType",
192
+  "rank": 1,
193
+  "help_text": "",
194
+  "sortcolumn": "rank"
195
+ },
196
+ "16": {
197
+  "nullable": true,
198
+  "date_update": "Fri Oct 16 11:05:04 2015",
199
+  "optional": false,
200
+  "icon": "0",
201
+  "uniq": false,
202
+  "component": "EmField",
203
+  "string": "{\"fre\": \"Titre\"}",
204
+  "class_id": 13,
205
+  "date_create": "Fri Oct 16 11:05:04 2015",
206
+  "rank": 1,
207
+  "fieldtype": "char",
208
+  "help_text": "",
209
+  "internal": false,
210
+  "rel_field_id": null,
211
+  "name": "titre"
212
+ },
213
+ "18": {
214
+  "nullable": true,
215
+  "date_update": "Fri Oct 16 11:05:04 2015",
216
+  "optional": true,
217
+  "icon": "0",
218
+  "uniq": false,
219
+  "component": "EmField",
220
+  "string": "{\"fre\": \"Age\"}",
221
+  "class_id": 2,
222
+  "date_create": "Fri Oct 16 11:05:04 2015",
223
+  "rank": 3,
224
+  "fieldtype": "char",
225
+  "help_text": "",
226
+  "internal": false,
227
+  "rel_field_id": null,
228
+  "name": "age"
229
+ },
230
+ "19": {
231
+  "date_update": "Fri Oct 16 11:05:04 2015",
232
+  "class_id": 13,
233
+  "icon": "0",
234
+  "name": "numero",
235
+  "superiors_list": {},
236
+  "fields_list": [],
237
+  "string": "{\"fre\": \"Num\\u00e9ro\"}",
238
+  "date_create": "Fri Oct 16 11:05:04 2015",
239
+  "component": "EmType",
240
+  "rank": 2,
241
+  "help_text": "",
242
+  "sortcolumn": "rank"
243
+ },
244
+ "21": {
245
+  "nullable": true,
246
+  "date_update": "Fri Oct 16 11:05:04 2015",
247
+  "optional": true,
248
+  "icon": "0",
249
+  "uniq": false,
250
+  "component": "EmField",
251
+  "string": "{\"fre\": \"Bleu\"}",
252
+  "class_id": 1,
253
+  "date_create": "Fri Oct 16 11:05:04 2015",
254
+  "rank": 1,
255
+  "fieldtype": "char",
256
+  "help_text": "",
257
+  "internal": false,
258
+  "rel_field_id": null,
259
+  "name": "bleu"
260
+ },
261
+ "23": {
262
+  "nullable": false,
263
+  "date_update": "Fri Oct 16 11:05:04 2015",
264
+  "optional": false,
265
+  "icon": "0",
266
+  "uniq": false,
267
+  "component": "EmField",
268
+  "string": "",
269
+  "class_id": 1,
270
+  "date_create": "Fri Oct 16 11:05:04 2015",
271
+  "rank": 1,
272
+  "fieldtype": "integer",
273
+  "help_text": "",
274
+  "internal": "automatic",
275
+  "rel_field_id": null,
276
+  "name": "class_id"
277
+ },
278
+ "24": {
279
+  "nullable": true,
280
+  "date_update": "Fri Oct 16 11:05:04 2015",
281
+  "max_length": 128,
282
+  "icon": "0",
283
+  "optional": false,
284
+  "component": "EmField",
285
+  "string": "",
286
+  "class_id": 1,
287
+  "date_create": "Fri Oct 16 11:05:04 2015",
288
+  "name": "string",
289
+  "rank": 2,
290
+  "fieldtype": "char",
291
+  "help_text": "",
292
+  "internal": "automatic",
293
+  "rel_field_id": null,
294
+  "uniq": false
295
+ },
296
+ "25": {
297
+  "nullable": false,
298
+  "date_update": "Fri Oct 16 11:05:04 2015",
299
+  "optional": false,
300
+  "icon": "0",
301
+  "uniq": false,
302
+  "component": "EmField",
303
+  "string": "",
304
+  "class_id": 1,
305
+  "date_create": "Fri Oct 16 11:05:04 2015",
306
+  "rank": 3,
307
+  "fieldtype": "integer",
308
+  "help_text": "",
309
+  "internal": "automatic",
310
+  "rel_field_id": null,
311
+  "name": "type_id"
312
+ },
313
+ "26": {
314
+  "nullable": false,
315
+  "date_update": "Fri Oct 16 11:05:04 2015",
316
+  "optional": false,
317
+  "icon": "0",
318
+  "uniq": false,
319
+  "component": "EmField",
320
+  "string": "",
321
+  "class_id": 1,
322
+  "date_create": "Fri Oct 16 11:05:04 2015",
323
+  "rank": 4,
324
+  "fieldtype": "pk",
325
+  "help_text": "",
326
+  "internal": "automatic",
327
+  "rel_field_id": null,
328
+  "name": "lodel_id"
329
+ },
330
+ "28": {
331
+  "nullable": false,
332
+  "date_update": "Fri Oct 16 11:05:04 2015",
333
+  "optional": false,
334
+  "icon": "0",
335
+  "uniq": false,
336
+  "component": "EmField",
337
+  "string": "",
338
+  "class_id": 2,
339
+  "date_create": "Fri Oct 16 11:05:04 2015",
340
+  "rank": 1,
341
+  "fieldtype": "integer",
342
+  "help_text": "",
343
+  "internal": "automatic",
344
+  "rel_field_id": null,
345
+  "name": "class_id"
346
+ },
347
+ "29": {
348
+  "nullable": true,
349
+  "date_update": "Fri Oct 16 11:05:04 2015",
350
+  "max_length": 128,
351
+  "icon": "0",
352
+  "optional": false,
353
+  "component": "EmField",
354
+  "string": "",
355
+  "class_id": 2,
356
+  "date_create": "Fri Oct 16 11:05:04 2015",
357
+  "name": "string",
358
+  "rank": 2,
359
+  "fieldtype": "char",
360
+  "help_text": "",
361
+  "internal": "automatic",
362
+  "rel_field_id": null,
363
+  "uniq": false
364
+ },
365
+ "30": {
366
+  "nullable": false,
367
+  "date_update": "Fri Oct 16 11:05:04 2015",
368
+  "optional": false,
369
+  "icon": "0",
370
+  "uniq": false,
371
+  "component": "EmField",
372
+  "string": "",
373
+  "class_id": 2,
374
+  "date_create": "Fri Oct 16 11:05:04 2015",
375
+  "rank": 3,
376
+  "fieldtype": "integer",
377
+  "help_text": "",
378
+  "internal": "automatic",
379
+  "rel_field_id": null,
380
+  "name": "type_id"
381
+ },
382
+ "31": {
383
+  "nullable": false,
384
+  "date_update": "Fri Oct 16 11:05:04 2015",
385
+  "optional": false,
386
+  "icon": "0",
387
+  "uniq": false,
388
+  "component": "EmField",
389
+  "string": "",
390
+  "class_id": 2,
391
+  "date_create": "Fri Oct 16 11:05:04 2015",
392
+  "rank": 4,
393
+  "fieldtype": "pk",
394
+  "help_text": "",
395
+  "internal": "automatic",
396
+  "rel_field_id": null,
397
+  "name": "lodel_id"
398
+ },
399
+ "33": {
400
+  "nullable": false,
401
+  "date_update": "Fri Oct 16 11:05:04 2015",
402
+  "optional": false,
403
+  "icon": "0",
404
+  "uniq": false,
405
+  "component": "EmField",
406
+  "string": "",
407
+  "class_id": 13,
408
+  "date_create": "Fri Oct 16 11:05:04 2015",
409
+  "rank": 1,
410
+  "fieldtype": "integer",
411
+  "help_text": "",
412
+  "internal": "automatic",
413
+  "rel_field_id": null,
414
+  "name": "class_id"
415
+ },
416
+ "34": {
417
+  "nullable": true,
418
+  "date_update": "Fri Oct 16 11:05:04 2015",
419
+  "max_length": 128,
420
+  "icon": "0",
421
+  "optional": false,
422
+  "component": "EmField",
423
+  "string": "",
424
+  "class_id": 13,
425
+  "date_create": "Fri Oct 16 11:05:04 2015",
426
+  "name": "string",
427
+  "rank": 2,
428
+  "fieldtype": "char",
429
+  "help_text": "",
430
+  "internal": "automatic",
431
+  "rel_field_id": null,
432
+  "uniq": false
433
+ },
434
+ "35": {
435
+  "nullable": false,
436
+  "date_update": "Fri Oct 16 11:05:04 2015",
437
+  "optional": false,
438
+  "icon": "0",
439
+  "uniq": false,
440
+  "component": "EmField",
441
+  "string": "",
442
+  "class_id": 13,
443
+  "date_create": "Fri Oct 16 11:05:04 2015",
444
+  "rank": 3,
445
+  "fieldtype": "integer",
446
+  "help_text": "",
447
+  "internal": "automatic",
448
+  "rel_field_id": null,
449
+  "name": "type_id"
450
+ },
451
+ "36": {
452
+  "nullable": false,
453
+  "date_update": "Fri Oct 16 11:05:04 2015",
454
+  "optional": false,
455
+  "icon": "0",
456
+  "uniq": false,
457
+  "component": "EmField",
458
+  "string": "",
459
+  "class_id": 13,
460
+  "date_create": "Fri Oct 16 11:05:04 2015",
461
+  "rank": 4,
462
+  "fieldtype": "pk",
463
+  "help_text": "",
464
+  "internal": "automatic",
465
+  "rel_field_id": null,
466
+  "name": "lodel_id"
467
+ },
468
+ "37": {
469
+  "nullable": false,
470
+  "date_update": "Wed Nov  4 10:52:13 2015",
471
+  "optional": false,
472
+  "rank": 5,
473
+  "icon": "0",
474
+  "name": "modification_date",
475
+  "component": "EmField",
476
+  "string": "",
477
+  "class_id": 1,
478
+  "date_create": "Wed Nov  4 10:52:13 2015",
479
+  "now_on_create": true,
480
+  "internal": "automatic",
481
+  "fieldtype": "datetime",
482
+  "now_on_update": true,
483
+  "help_text": "",
484
+  "rel_field_id": null,
485
+  "uniq": false
486
+ },
487
+ "38": {
488
+  "nullable": false,
489
+  "date_update": "Wed Nov  4 10:52:13 2015",
490
+  "rel_field_id": null,
491
+  "icon": "0",
492
+  "uniq": false,
493
+  "component": "EmField",
494
+  "now_on_create": true,
495
+  "class_id": 1,
496
+  "date_create": "Wed Nov  4 10:52:13 2015",
497
+  "rank": 6,
498
+  "string": "",
499
+  "help_text": "",
500
+  "internal": "automatic",
501
+  "optional": false,
502
+  "name": "creation_date",
503
+  "fieldtype": "datetime"
504
+ },
505
+ "39": {
506
+  "nullable": false,
507
+  "date_update": "Wed Nov  4 10:52:13 2015",
508
+  "rel_field_id": null,
509
+  "icon": "0",
510
+  "uniq": false,
511
+  "component": "EmField",
512
+  "now_on_create": true,
513
+  "class_id": 2,
514
+  "date_create": "Wed Nov  4 10:52:13 2015",
515
+  "rank": 5,
516
+  "string": "",
517
+  "now_on_update": true,
518
+  "help_text": "",
519
+  "internal": "automatic",
520
+  "optional": false,
521
+  "name": "modification_date",
522
+  "fieldtype": "datetime"
523
+ },
524
+ "40": {
525
+  "nullable": false,
526
+  "date_update": "Wed Nov  4 10:52:13 2015",
527
+  "rel_field_id": null,
528
+  "icon": "0",
529
+  "uniq": false,
530
+  "component": "EmField",
531
+  "now_on_create": true,
532
+  "class_id": 2,
533
+  "date_create": "Wed Nov  4 10:52:13 2015",
534
+  "rank": 6,
535
+  "string": "",
536
+  "help_text": "",
537
+  "internal": "automatic",
538
+  "optional": false,
539
+  "name": "creation_date",
540
+  "fieldtype": "datetime"
541
+ },
542
+ "41": {
543
+  "nullable": false,
544
+  "date_update": "Wed Nov  4 10:52:13 2015",
545
+  "rel_field_id": null,
546
+  "icon": "0",
547
+  "uniq": false,
548
+  "component": "EmField",
549
+  "now_on_create": true,
550
+  "class_id": 13,
551
+  "date_create": "Wed Nov  4 10:52:13 2015",
552
+  "rank": 5,
553
+  "string": "",
554
+  "now_on_update": true,
555
+  "help_text": "",
556
+  "internal": "automatic",
557
+  "optional": false,
558
+  "name": "modification_date",
559
+  "fieldtype": "datetime"
560
+ },
561
+ "42": {
562
+  "nullable": false,
563
+  "date_update": "Wed Nov  4 10:52:13 2015",
564
+  "rel_field_id": null,
565
+  "icon": "0",
566
+  "uniq": false,
567
+  "component": "EmField",
568
+  "now_on_create": true,
569
+  "class_id": 13,
570
+  "date_create": "Wed Nov  4 10:52:13 2015",
571
+  "rank": 6,
572
+  "string": "",
573
+  "help_text": "",
574
+  "internal": "automatic",
575
+  "optional": false,
576
+  "name": "creation_date",
577
+  "fieldtype": "datetime"
578
+ }
579
+}

+ 28
- 0
install/loader.py View File

@@ -0,0 +1,28 @@
1
+import settings
2
+import importlib
3
+import sys
4
+import os
5
+
6
+sys.path.append(settings.lodel2_lib_path)
7
+
8
+# Import dynamic code
9
+if os.path.isfile(settings.dynamic_code):
10
+    from dynleapi import *
11
+
12
+# Import wanted datasource objects
13
+for db_modname in ['leapidatasource', 'migrationhandler']:
14
+    mod = importlib.import_module("DataSource.{pkg_name}.{mod_name}".format(
15
+            pkg_name=settings.ds_package,
16
+            mod_name=db_modname,
17
+        )
18
+    )
19
+    # Expose the module in globals
20
+    globals()[db_modname] = mod
21
+
22
+if __name__ == '__main__':
23
+    import code
24
+    print("""
25
+     Running interactive python in Lodel2 %s instance environment
26
+
27
+"""%settings.name)
28
+    code.interact(local=locals())

+ 21
- 0
install/settings.py View File

@@ -0,0 +1,21 @@
1
+#-*- coding:utf8 -*-
2
+
3
+import pymysql
4
+
5
+name = 'LODEL2_INSTANCE_NAME'
6
+lodel2_lib_path = 'LODEL2_LIB_ABS_PATH'
7
+
8
+emfile = 'em.json'
9
+dynamic_code = 'dynleapi.py'
10
+
11
+ds_package = 'MySQL'
12
+mh_classname = 'MysqlMigrationHandler'
13
+datasource = {
14
+    'default': {
15
+        'module': pymysql,
16
+        'host': '127.0.0.1',
17
+        'user': 'DBUSER',
18
+        'passwd': 'DBPASSWORD',
19
+        'db': 'DBNAME'
20
+    }
21
+}

+ 29
- 0
install/utils.py View File

@@ -0,0 +1,29 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import settings
4
+from settings import *
5
+from loader import *
6
+
7
+def refreshdyn():
8
+    import sys
9
+    from EditorialModel.model import Model
10
+    from leapi.lefactory import LeFactory
11
+    from EditorialModel.backend.json_backend import EmBackendJson
12
+    from DataSource.MySQL.leapidatasource import LeDataSourceSQL
13
+    OUTPUT = dynamic_code
14
+    EMJSON = emfile
15
+    # Load editorial model
16
+    em = Model(EmBackendJson(EMJSON))
17
+    # Generate dynamic code
18
+    fact = LeFactory(OUTPUT)
19
+    # Create the python file
20
+    fact.create_pyfile(em, LeDataSourceSQL, {})
21
+
22
+def db_init():
23
+    from EditorialModel.backend.json_backend import EmBackendJson
24
+    from EditorialModel.model import Model
25
+    mh = getattr(migrationhandler,settings.mh_classname)(**(settings.datasource['default']))
26
+    em = Model(EmBackendJson(settings.emfile))
27
+    em.migrate_handler(mh)
28
+
29
+

+ 47
- 0
lodel_init.sh View File

@@ -0,0 +1,47 @@
1
+#!/bin/bash
2
+
3
+usage() {
4
+	echo "Usage : $0 instance_name instance_dir [lodel_libdir]" 1>&2
5
+	exit 1
6
+}
7
+
8
+if [ $# -lt 2 ]
9
+then
10
+	echo "Not enough arguments" 1>&2
11
+	usage
12
+fi
13
+
14
+
15
+name="$1"
16
+instdir="$2"
17
+libdir="$3"
18
+libdir="${libdir:=$(realpath $(dirname $0))}"
19
+
20
+emfilename="em.json"
21
+settings="$instdir/settings.py"
22
+em="$instdir/em.json"
23
+dyncode="$instdir/${name}.py"
24
+
25
+if [ -e "$instdir" ]
26
+then
27
+	echo "Abording... "$instdir" exists" 1>&2
28
+	exit 1
29
+fi
30
+
31
+echo "Creating lodel instance directory '$instdir'"
32
+mkdir -pv "$instdir"
33
+
34
+cp -v $libdir/install/* $instdir
35
+
36
+sed -i -e "s#LODEL2_LIB_ABS_PATH#$libdir#" "$settings"
37
+sed -i -e "s#LODEL2_INSTANCE_NAME#$name#" "$settings"
38
+
39
+echo "Generating dynamic code"
40
+cd "$instdir"
41
+make refreshdyn
42
+echo "Cleaning instance directory"
43
+make clean
44
+
45
+echo -e "\nInstance successfully created in $instdir"
46
+echo -e "============================\n"
47
+echo "Now you should edit '$settings' and then run : cd $instdir && make dbinit"

+ 5
- 3
refreshdyn.py View File

@@ -1,15 +1,17 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3
+import sys
3 4
 from EditorialModel.model import Model
4 5
 from leapi.lefactory import LeFactory
5 6
 from EditorialModel.backend.json_backend import EmBackendJson
6 7
 from leapi.datasources.ledatasourcesql import LeDataSourceSQL
7 8
 
8
-OUTPUT = 'leapi/dyn.py'
9
+OUTPUT = 'leapi/dyn.py' if len(sys.argv) == 1 else sys.argv[1]
10
+EMJSON = 'EditorialModel/test/me.json' if len(sys.argv) < 3 else sys.argv[2]
9 11
 
10
-em = Model(EmBackendJson('EditorialModel/test/me.json'))
12
+em = Model(EmBackendJson(EMJSON))
11 13
 
12
-fact = LeFactory('leapi/dyn.py')
14
+fact = LeFactory(OUTPUT)
13 15
 fact.create_pyfile(em, LeDataSourceSQL, {})
14 16
 print(fact.generate_python(em, LeDataSourceSQL, {}))
15 17
 

Loading…
Cancel
Save