Compare commits
No commits in common. "10.0" and "10.0-purepy" have entirely different histories.
10.0
...
10.0-purep
@ -1,13 +1,3 @@
|
|||||||
.PHONY: setup
|
|
||||||
setup:
|
|
||||||
pip install --user -U coconut[watch,jobs,backports,mypy]
|
|
||||||
|
|
||||||
.PHONY: testdev
|
|
||||||
testdev: clean build test
|
|
||||||
|
|
||||||
.PHONY: testprod
|
|
||||||
testprod: clean prod test
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
find . -name '*.pyc' -delete
|
find . -name '*.pyc' -delete
|
||||||
@ -17,15 +7,3 @@ clean:
|
|||||||
test:
|
test:
|
||||||
python2 tests/test_xml_base.py
|
python2 tests/test_xml_base.py
|
||||||
python2 tests/test_odoo.py
|
python2 tests/test_odoo.py
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
build:
|
|
||||||
coconut -t 2.7 -j sys .
|
|
||||||
|
|
||||||
.PHONY: prod
|
|
||||||
prod:
|
|
||||||
coconut --notco -t 2.7 -j sys -f .
|
|
||||||
|
|
||||||
.PHONY: watch
|
|
||||||
watch:
|
|
||||||
coconut --strict -t 2.7 -j sys -w .
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright 2019-2025 Fabien Bourgeois <fabien@yaltik.com>
|
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
@ -19,11 +19,12 @@
|
|||||||
'name': 'Yaltik Odoo DSL base module and fns',
|
'name': 'Yaltik Odoo DSL base module and fns',
|
||||||
'summary': 'Yaltik Odoo Domain Specific Language base module and functions',
|
'summary': 'Yaltik Odoo Domain Specific Language base module and functions',
|
||||||
'description': """ Yaltik Odoo Domain Specific Language base module and functions """,
|
'description': """ Yaltik Odoo Domain Specific Language base module and functions """,
|
||||||
'version': '10.0.0.5.19',
|
'version': '10.0.0.4.0',
|
||||||
'category': 'Yaltik',
|
'category': 'Yaltik',
|
||||||
'author': 'Fabien Bourgeois',
|
'author': 'Fabien Bourgeois',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'application': False,
|
'application': False,
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'depends': ['base']
|
'depends': ['base'],
|
||||||
|
"external_dependencies": {'python' : ['function-pattern-matching']}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright 2019-2025 Fabien Bourgeois <fabien@yaltik.com>
|
# Copyright 2019-2020 Fabien Bourgeois <fabien@yaltik.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
@ -35,25 +35,15 @@ def data(*args):
|
|||||||
# Aliases
|
# Aliases
|
||||||
function = lambda *args: xmln('function', *args)
|
function = lambda *args: xmln('function', *args)
|
||||||
record = lambda *args: xmln('record', *args)
|
record = lambda *args: xmln('record', *args)
|
||||||
delete = lambda *args: xmln('delete', *args)
|
|
||||||
form = lambda *args: xmln('form', *args)
|
form = lambda *args: xmln('form', *args)
|
||||||
tree = lambda *args: xmln('tree', *args)
|
tree = lambda *args: xmln('tree', *args)
|
||||||
search = lambda *args: xmln('search', *args)
|
search = lambda *args: xmln('search', *args)
|
||||||
template = lambda *args: xmln('template', *args)
|
|
||||||
templates = lambda *args: xmln('templates', *args)
|
|
||||||
kanban = lambda *args: xmln('kanban', *args)
|
|
||||||
calendar = lambda *args: xmln('calendar', *args)
|
|
||||||
activity = lambda *args: xmln('activity', *args)
|
|
||||||
widget = lambda *args: xmln('widget', *args)
|
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
act_window = lambda *args: xmln('act_window', *args)
|
act_window = lambda *args: xmln('act_window', *args)
|
||||||
def act_window_model(model, attrs):
|
def act_window_model(model, attrs):
|
||||||
""" Build new act_window from model and args """
|
""" Build new act_window from model and args """
|
||||||
xmlid = '%s_view_action' % (model.replace('.', '_'))
|
xmlid = '%s_view_action' % (model.replace('.', '_'))
|
||||||
if 'name' in attrs:
|
|
||||||
name = attrs['name']
|
|
||||||
else:
|
|
||||||
name = '%s Action' % ' '.join(map(lambda w: w.capitalize(), model.split('.')))
|
name = '%s Action' % ' '.join(map(lambda w: w.capitalize(), model.split('.')))
|
||||||
attrs_clone = attrs.copy() # Avoid side-effect
|
attrs_clone = attrs.copy() # Avoid side-effect
|
||||||
attrs_clone.update({'id': xmlid, 'name': name, 'res_model': model})
|
attrs_clone.update({'id': xmlid, 'name': name, 'res_model': model})
|
||||||
@ -95,54 +85,18 @@ footer = lambda *args: xmln('footer', *args)
|
|||||||
sheet = lambda *args: xmln('sheet', *args)
|
sheet = lambda *args: xmln('sheet', *args)
|
||||||
button = lambda *args: xmln('button', *args)
|
button = lambda *args: xmln('button', *args)
|
||||||
p = lambda *args: xmln('p', *args)
|
p = lambda *args: xmln('p', *args)
|
||||||
i = lambda *args: xmln('i', *args)
|
|
||||||
t = lambda *args: xmln('t', *args)
|
|
||||||
div = lambda *args: xmln('div', *args)
|
|
||||||
span = lambda *args: xmln('span', *args)
|
|
||||||
br = lambda *args: xmln('br', *args)
|
|
||||||
h1 = lambda *args: xmln('h1', *args)
|
|
||||||
h2 = lambda *args: xmln('h2', *args)
|
|
||||||
h3 = lambda *args: xmln('h3', *args)
|
|
||||||
h4 = lambda *args: xmln('h4', *args)
|
|
||||||
h5 = lambda *args: xmln('h5', *args)
|
|
||||||
ul = lambda *args: xmln('ul', *args)
|
|
||||||
li = lambda *args: xmln('li', *args)
|
|
||||||
label = lambda *args: xmln('label', *args)
|
|
||||||
strong = lambda *args: xmln('strong', *args)
|
|
||||||
small = lambda *args: xmln('small', *args)
|
|
||||||
table = lambda *args: xmln('table', *args)
|
|
||||||
tr = lambda *args: xmln('tr', *args)
|
|
||||||
th = lambda *args: xmln('th', *args)
|
|
||||||
td = lambda *args: xmln('td', *args)
|
|
||||||
style = lambda *args: xmln('style', *args)
|
|
||||||
|
|
||||||
notebook = lambda *args: xmln('notebook', *args)
|
|
||||||
page = lambda *args: xmln('page', *args)
|
|
||||||
xpath = lambda *args: xmln('xpath', *args)
|
xpath = lambda *args: xmln('xpath', *args)
|
||||||
attribute = lambda name, value: xmln('attribute', {'name': name}, [value])
|
attribute = lambda name, value: xmln('attribute', {'name': name}, [value])
|
||||||
|
|
||||||
# Fields
|
# Fields
|
||||||
field = lambda *args: xmln('field', *args)
|
field = lambda *args: xmln('field', *args)
|
||||||
field_name = lambda name: field({'name': 'name'}, [name])
|
field_name = lambda name: field({'name': 'name'}, [name])
|
||||||
field_nval = lambda name, value: field({'name': name}, [value])
|
|
||||||
field_model = lambda model: field({'name': 'model'}, [model])
|
field_model = lambda model: field({'name': 'model'}, [model])
|
||||||
field_inherit = lambda xmlid: field({'name': 'inherit_id', 'ref': xmlid}, [])
|
field_inherit = lambda xmlid: field({'name': 'inherit_id', 'ref': xmlid}, [])
|
||||||
field_arch = lambda *args: field({'name': 'arch', 'type': 'xml'}, *args)
|
field_arch = lambda *args: field({'name': 'arch', 'type': 'xml'}, *args)
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
filter = lambda *args: xmln('filter', *args)
|
filter = lambda *args: xmln('filter', *args)
|
||||||
separator = lambda *args: xmln('separator', *args)
|
|
||||||
|
|
||||||
def filter_yes_no(field, str_yes=False, str_no=False):
|
|
||||||
""" Double filter for boolean : True and False """
|
|
||||||
res = []
|
|
||||||
if str_yes:
|
|
||||||
res.append(filter({'name': '%s_yes' % field, 'string': str_yes,
|
|
||||||
'domain': "[('%s', '=', True)]" % field}))
|
|
||||||
if str_no:
|
|
||||||
res.append(filter({'name': '%s_no' % field, 'string': str_no,
|
|
||||||
'domain': "[('%s', '=', False)]" % field}))
|
|
||||||
return res
|
|
||||||
|
|
||||||
# Views
|
# Views
|
||||||
view = lambda xmlid, children: record({'id': xmlid, 'model': 'ir.ui.view'}, children)
|
view = lambda xmlid, children: record({'id': xmlid, 'model': 'ir.ui.view'}, children)
|
||||||
|
@ -36,16 +36,16 @@ def xmlroot(tree):
|
|||||||
def xmlchild(parent, children):
|
def xmlchild(parent, children):
|
||||||
""" Handling of children (ie non root) XML Nodes with/o text and
|
""" Handling of children (ie non root) XML Nodes with/o text and
|
||||||
subchildren (recursive) """
|
subchildren (recursive) """
|
||||||
if isinstance(children, basestring):
|
if isinstance(children, str):
|
||||||
parent.text = children
|
parent.text = children
|
||||||
elif isinstance(children, XMLDictElement):
|
elif isinstance(children, XMLDictElement):
|
||||||
attrs = {str(k): str(v) for [k, v] in children.attrs.items()}
|
attrs = {unicode(k): unicode(v) for [k, v] in children.attrs.items()}
|
||||||
new_parent = ET.SubElement(parent, children.tag, attrs)
|
new_parent = ET.SubElement(parent, children.tag, attrs)
|
||||||
subchildren = children.children
|
subchildren = children.children
|
||||||
if subchildren:
|
if subchildren:
|
||||||
xmlchild(new_parent, subchildren)
|
xmlchild(new_parent, subchildren)
|
||||||
elif isinstance(children, list):
|
elif isinstance(children, list):
|
||||||
list(map(partial(xmlchild, parent), children))
|
map(partial(xmlchild, parent), children)
|
||||||
else:
|
else:
|
||||||
raise TypeError('Invalid arguments for xmlchild')
|
raise TypeError('Invalid arguments for xmlchild')
|
||||||
|
|
||||||
@ -55,21 +55,21 @@ def xmln(tag='', attrs={}, children=[]):
|
|||||||
children = attrs
|
children = attrs
|
||||||
attrs = {}
|
attrs = {}
|
||||||
xmldictel = partial(XMLDictElement, tag, attrs)
|
xmldictel = partial(XMLDictElement, tag, attrs)
|
||||||
if isinstance(children, basestring):
|
if isinstance(children, str):
|
||||||
return xmldictel([children])
|
return xmldictel([children])
|
||||||
if isinstance(children, list):
|
if isinstance(children, list):
|
||||||
return xmldictel(children)
|
return xmldictel(children)
|
||||||
raise TypeError('Invalid arguments for xmln')
|
raise TypeError('Invalid arguments for xmln')
|
||||||
|
|
||||||
|
|
||||||
def xml_write(filepath, tree, pretty=True, suffix='_views'):
|
def xml_write(filepath, tree, pretty=True):
|
||||||
""" Write XML file according to filename and given tree """
|
""" Write XML file according to filename and given tree """
|
||||||
if filepath.endswith('.py'): # if .pyc, no need to generate XML
|
if filepath.endswith('.py'): # if .pyc, no need to generate XML
|
||||||
output_xml = ET.tostring(tree)
|
output_xml = ET.tostring(tree)
|
||||||
if pretty:
|
if pretty:
|
||||||
output_xml = minidom.parseString(output_xml).toprettyxml(indent=' ')
|
output_xml = minidom.parseString(output_xml).toprettyxml(indent=' ')
|
||||||
output_path = path.abspath(filepath).split('/')
|
output_path = path.abspath(filepath).split(u'/')
|
||||||
output_path[-1] = output_path[-1].replace('.py', '%s.xml' % suffix)
|
output_path[-1] = output_path[-1].replace(u'.py', u'_views.xml')
|
||||||
output_path = '/'.join(output_path)
|
output_path = u'/'.join(output_path)
|
||||||
with open(output_path, 'w') as output_file:
|
with open(output_path, 'w') as output_file:
|
||||||
output_file.write(output_xml)
|
output_file.write(output_xml)
|
||||||
|
@ -114,33 +114,6 @@ class TestOdooBase(unittest.TestCase):
|
|||||||
self.assertEqual(element.attrs['type'], 'xml')
|
self.assertEqual(element.attrs['type'], 'xml')
|
||||||
self.assertFalse(element.children)
|
self.assertFalse(element.children)
|
||||||
|
|
||||||
def test_filter_yes_no(self):
|
|
||||||
""" Test Filter Yes No """
|
|
||||||
elements = od.filter_yes_no('some_field')
|
|
||||||
self.assertIsInstance(elements, list)
|
|
||||||
self.assertFalse(elements)
|
|
||||||
|
|
||||||
elements = od.filter_yes_no('some_field', 'Some field')
|
|
||||||
self.assertEqual(len(elements), 1)
|
|
||||||
self.assertIsInstance(elements[0], XMLDictElement)
|
|
||||||
self.assertEqual(elements[0].tag, 'filter')
|
|
||||||
self.assertEqual(elements[0].attrs['name'], 'some_field_yes')
|
|
||||||
self.assertEqual(elements[0].attrs['string'], 'Some field')
|
|
||||||
self.assertEqual(elements[0].attrs['domain'], "[('some_field', '=', True)]")
|
|
||||||
|
|
||||||
elements = od.filter_yes_no('some_field', 'Some field', 'Not some field')
|
|
||||||
self.assertEqual(len(elements), 2)
|
|
||||||
self.assertIsInstance(elements[0], XMLDictElement)
|
|
||||||
self.assertEqual(elements[0].tag, 'filter')
|
|
||||||
self.assertEqual(elements[0].attrs['name'], 'some_field_yes')
|
|
||||||
self.assertEqual(elements[0].attrs['string'], 'Some field')
|
|
||||||
self.assertEqual(elements[0].attrs['domain'], "[('some_field', '=', True)]")
|
|
||||||
self.assertIsInstance(elements[1], XMLDictElement)
|
|
||||||
self.assertEqual(elements[1].tag, 'filter')
|
|
||||||
self.assertEqual(elements[1].attrs['name'], 'some_field_no')
|
|
||||||
self.assertEqual(elements[1].attrs['string'], 'Not some field')
|
|
||||||
self.assertEqual(elements[1].attrs['domain'], "[('some_field', '=', False)]")
|
|
||||||
|
|
||||||
def test_view(self):
|
def test_view(self):
|
||||||
""" Test view function """
|
""" Test view function """
|
||||||
element = od.view('view_xmlid', [])
|
element = od.view('view_xmlid', [])
|
||||||
|
@ -88,7 +88,7 @@ class TestXMLBase(unittest.TestCase):
|
|||||||
root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
|
root = xmlroot({'tag': 'root', 'attrs': {}, 'children': []})
|
||||||
self.assertIsInstance(root, ET.Element)
|
self.assertIsInstance(root, ET.Element)
|
||||||
|
|
||||||
with self.assertRaisesRegexp(TypeError, 'no attribute'):
|
with self.assertRaisesRegexp(TypeError, 'has no attribute'):
|
||||||
xmlroot(False)
|
xmlroot(False)
|
||||||
with self.assertRaisesRegexp(KeyError, 'tag'):
|
with self.assertRaisesRegexp(KeyError, 'tag'):
|
||||||
xmlroot({})
|
xmlroot({})
|
||||||
@ -115,15 +115,6 @@ class TestXMLBase(unittest.TestCase):
|
|||||||
self.assertIn('<child1 attr="value"/>', output_xml)
|
self.assertIn('<child1 attr="value"/>', output_xml)
|
||||||
self.assertIn('<child2>Some text</child2>', output_xml)
|
self.assertIn('<child2>Some text</child2>', output_xml)
|
||||||
unlink(filepath)
|
unlink(filepath)
|
||||||
xml_write(__file__, tree, suffix='_data')
|
|
||||||
filepath = __file__.replace('.py', '_data.xml')
|
|
||||||
with open(filepath, 'r') as output_file:
|
|
||||||
output_xml = output_file.read()
|
|
||||||
self.assertIn('<?xml version', output_xml)
|
|
||||||
self.assertIn('<root>', output_xml)
|
|
||||||
self.assertIn('<child1 attr="value"/>', output_xml)
|
|
||||||
self.assertIn('<child2>Some text</child2>', output_xml)
|
|
||||||
unlink(filepath)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user