Compare commits

..

No commits in common. "10.0" and "10.0-purepy" have entirely different histories.

6 changed files with 15 additions and 118 deletions

View File

@ -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 .

View File

@ -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']}
} }

View File

@ -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,26 +35,16 @@ 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 = '%s Action' % ' '.join(map(lambda w: w.capitalize(), model.split('.')))
name = attrs['name']
else:
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})
return act_window(attrs_clone) return act_window(attrs_clone)
@ -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)

View File

@ -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)

View File

@ -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', [])

View File

@ -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()