Allow to sync XML data

- Adds menu entry for ir.model.data
- Remove write access on ir.model.data to administrator group
- Add sync button on ir.model.data
- Change storage format of values to be comparable

issue3918
review8351002
This commit is contained in:
Cédric Krier committed 2014-06-13 19:14:48 +02:00
1 parent 3c53e43d25
commit b955ab3070
7 files changed
+170 -25

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Allow to sync XML data
* Remove on_change calls in Model.default_get
* Add group call to on_change
* Add UnionMixin
+8 -10
View File
@@ -2,8 +2,6 @@
#this repository contains the full copyright notices and license terms.
import time
from xml import sax
from decimal import Decimal
import datetime
import logging
import traceback
import sys
@@ -604,10 +602,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
if not old_values:
old_values = {}
else:
old_values = safe_eval(old_values, {
'Decimal': Decimal,
'datetime': datetime,
})
old_values = self.ModelData.load_values(old_values)
for key in old_values:
if isinstance(old_values[key], str):
@@ -691,6 +686,9 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
else:
self.write_records(module, model, record, to_update,
old_values, fs_id, mdata_id)
self.grouped_model_data.extend(([self.ModelData(mdata_id)], {
'fs_values': self.ModelData.dump_values(values),
}))
else:
if self.grouped:
self.grouped_creations[model][fs_id] = values
@@ -713,7 +711,8 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
'model': model,
'module': self.module,
'db_id': record.id,
'values': str(values),
'values': self.ModelData.dump_values(values),
'fs_values': self.ModelData.dump_values(values),
'noupdate': self.noupdate,
})
@@ -725,7 +724,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
'db_id': record.id,
'model': model,
'id': mdata.id,
'values': str(values),
'values': self.ModelData.dump_values(values),
})
self.fs2db.reset_browsercord(self.module, model,
[r.id for r in records])
@@ -774,8 +773,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
'model': model,
'module': module,
'db_id': record.id,
'values': str(values),
'date_update': datetime.datetime.now(),
'values': self.ModelData.dump_values(values),
}))
# reset_browsercord to keep cache memory low
+67 -7
View File
@@ -1,11 +1,14 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import datetime
import re
import heapq
from sql.aggregate import Max
from sql.conditionals import Case
from collections import defaultdict
try:
import simplejson as json
except ImportError:
import json
from ..model import ModelView, ModelSQL, fields
from ..report import Report
@@ -16,6 +19,7 @@ from ..pool import Pool
from ..pyson import Bool, Eval
from ..rpc import RPC
from .. import backend
from ..protocols.jsonrpc import object_hook, JSONEncoder
try:
from ..tools.StringMatcher import StringMatcher
except ImportError:
@@ -813,10 +817,11 @@ class ModelData(ModelSQL, ModelView):
db_id = fields.Integer('Resource ID',
help="The id of the record in the database.", select=True,
required=True)
date_update = fields.DateTime('Update Date')
date_init = fields.DateTime('Init Date')
values = fields.Text('Values')
fs_values = fields.Text('Values on File System')
noupdate = fields.Boolean('No Update')
out_of_sync = fields.Function(fields.Boolean('Out of Sync'),
'get_out_of_sync', searcher='search_out_of_sync')
_get_id_cache = Cache('ir_model_data.get_id', context=False)
@classmethod
@@ -826,6 +831,11 @@ class ModelData(ModelSQL, ModelView):
('fs_id_module_model_uniq', 'UNIQUE("fs_id", "module", "model")',
'The triple (fs_id, module, model) must be unique!'),
]
cls._buttons.update({
'sync': {
'invisible': ~Eval('out_of_sync'),
},
})
@classmethod
def __register__(cls, module_name):
@@ -843,14 +853,24 @@ class ModelData(ModelSQL, ModelView):
where=model_data.inherit == True))
table.drop_column('inherit', True)
@staticmethod
def default_date_init():
return datetime.datetime.now()
@staticmethod
def default_noupdate():
return False
def get_out_of_sync(self, name):
return self.values != self.fs_values and self.fs_values is not None
@classmethod
def search_out_of_sync(cls, name, clause):
table = cls.__table__()
name, operator, value = clause
Operator = fields.SQL_OPERATORS[operator]
query = table.select(table.id,
where=Operator(
(table.fs_values != table.values) & (table.fs_values != None),
value))
return [('id', 'in', query)]
@classmethod
def write(cls, data, values, *args):
super(ModelData, cls).write(data, values, *args)
@@ -877,6 +897,46 @@ class ModelData(ModelSQL, ModelView):
cls._get_id_cache.set(key, id_)
return id_
@classmethod
def dump_values(cls, values):
return json.dumps(sorted(values.iteritems()), cls=JSONEncoder)
@classmethod
def load_values(cls, values):
try:
return dict(json.loads(values, object_hook=object_hook))
except ValueError:
# Migration from 3.2
from ..tools import safe_eval
from decimal import Decimal
import datetime
return safe_eval(values, {
'Decimal': Decimal,
'datetime': datetime,
})
@classmethod
@ModelView.button
def sync(cls, records):
pool = Pool()
to_write = []
with Transaction().set_user(0):
for data in records:
Model = pool.get(data.model)
values = cls.load_values(data.values)
fs_values = cls.load_values(data.fs_values)
# values could be the same once loaded
# if they come from version < 3.2
if values != fs_values:
record = Model(data.db_id)
Model.write([record], fs_values)
values.update(fs_values)
to_write.extend([[data], {
'values': cls.dump_values(values),
}])
if to_write:
cls.write(*to_write)
class PrintModelGraphStart(ModelView):
'Print Model Graph'
+44
View File
@@ -173,5 +173,49 @@ this repository contains the full copyright notices and license terms. -->
<menuitem parent="menu_model_access_form"
action="act_model_button_form" id="menu_model_button_form"/>
<record model="ir.ui.view" id="model_data_view_list">
<field name="model">ir.model.data</field>
<field name="type">tree</field>
<field name="name">model_data_list</field>
</record>
<record model="ir.ui.view" id="model_data_view_form">
<field name="model">ir.model.data</field>
<field name="type">form</field>
<field name="name">model_data_form</field>
</record>
<record model="ir.action.act_window" id="act_model_data_form">
<field name="name">Data</field>
<field name="res_model">ir.model.data</field>
</record>
<record model="ir.action.act_window.view"
id="act_model_data_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="model_data_view_list"/>
<field name="act_window" ref="act_model_data_form"/>
</record>
<record model="ir.action.act_window.view"
id="act_model_data_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="model_data_view_form"/>
<field name="act_window" ref="act_model_data_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_model_data_form_domain_out_of_sync">
<field name="name">Out of Sync</field>
<field name="sequence" eval="10"/>
<field name="domain">[('out_of_sync', '=', True)]</field>
<field name="act_window" ref="act_model_data_form"/>
</record>
<record model="ir.action.act_window.domain"
id="act_model_data_form_domain_all">
<field name="name">All</field>
<field name="sequence" eval="9999"/>
<field name="domain"></field>
<field name="act_window" ref="act_model_data_form"/>
</record>
<menuitem parent="menu_model_form" action="act_model_data_form"
id="menu_model_data_form"/>
</data>
</tryton>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form string="Model Data">
<label name="module"/>
<field name="module"/>
<label name="model"/>
<field name="model"/>
<label name="fs_id"/>
<field name="fs_id"/>
<label name="db_id"/>
<field name="db_id"/>
<label name="noupdate"/>
<field name="noupdate"/>
<newline/>
<label name="out_of_sync"/>
<field name="out_of_sync"/>
<button string="Sync" name="sync" colspan="2"/>
<group id="values" colspan="2" yexpand="1" yfill="1" col="1">
<separator name="values"/>
<field name="values"/>
</group>
<group id="fs_values" colspan="2" yexpand="1" yfill="1" col="1">
<separator name="fs_values"/>
<field name="fs_values"/>
</group>
</form>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree string="Model Data">
<field name="module"/>
<field name="model"/>
<field name="fs_id"/>
<field name="db_id"/>
<field name="noupdate"/>
<field name="out_of_sync"/>
<button string="Sync" name="sync"/>
</tree>
+10 -8
View File
@@ -265,14 +265,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_ir_model_data_admin">
<field name="model" search="[('model', '=', 'ir.model.data')]"/>
<field name="group" ref="group_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.model.access" id="access_ir_cron">
<field name="model" search="[('model', '=', 'ir.cron')]"/>
<field name="perm_read" eval="True"/>
@@ -746,6 +738,16 @@ this repository contains the full copyright notices and license terms. -->
<field name="group" ref="group_admin"/>
</record>
<record model="ir.model.button" id="model_data_sync_button">
<field name="name">sync</field>
<field name="model" search="[('model', '=', 'ir.model.data')]"/>
</record>
<record model="ir.model.button-res.group"
id="model_data_sync_button_group_admin">
<field name="button" ref="model_data_sync_button"/>
<field name="group" ref="group_admin"/>
</record>
<record model="ir.ui.view" id="sequence_type_view_form">
<field name="model">ir.sequence.type</field>
<field name="inherit" ref="ir.sequence_type_view_form"/>