Compare commits

...

9 Commits

15 changed files with 521 additions and 339 deletions

View File

@ -5,5 +5,5 @@ clean:
.PHONY: test .PHONY: test
test: test:
hy2 hy_odoo/tests/test_xml_base.hy hy hy_odoo/tests/test_xml.hy
hy2 hy_odoo/tests/test_odoo.hy hy hy_odoo/tests/test_odoo.hy

View File

@ -1,19 +1,7 @@
# -*- coding: utf-8 -*- # Copyright 2020-2022 Fabien Bourgeois <fabien@yaltik.com>
# Copyright 2020-2021 Fabien Bourgeois <fabien@yaltik.com>
# #
# This program is free software: you can redistribute it and/or modify # This Source Code Form is subject to the terms of the Mozilla Public
# it under the terms of the GNU Affero General Public License as # License, v. 2.0. If a copy of the MPL was not distributed with this
# published by the Free Software Foundation, either version 3 of the # file, You can obtain one at https://mozilla.org/MPL/2.0/.
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import hy import hy
import odoo, xml_base, utils

View File

@ -1,18 +0,0 @@
;; -*- coding: utf-8 -*-
;;
;; Copyright 2021 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
; (require [hy-odoo.macros [general odoo test]])

View File

@ -1,43 +0,0 @@
;; -*- coding: utf-8 -*-
;;
;; Copyright 2021 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" Hy General Macros "
(defmacro if-python2 [python2-form python3-form]
"If running on python2, execute python2-form, else, execute python3-form"
(import sys)
(if (< (get sys.version_info 0) 3)
python2-form
python3-form))
(defmacro ustr-cast [s]
"If python2, return unicode casting, else str casting"
(if-python2 `(unicode ~s) `(str ~s)))
(defmacro hydict [dic]
"Generate dict with mangled keys, from HyDict list"
(setv mangled-dic
(list
(map
(fn [pair]
(if (even? (nth pair 0))
(mangle (nth pair 1))
(nth pair 1)))
(enumerate dic))))
`{~@mangled-dic})

View File

@ -1,58 +0,0 @@
;; -*- coding: utf-8 -*-
;;
;; Copyright 2019-2021 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" Odoo related macros "
(defmacro o-cmod [model-name] `(. cls env [~model-name]))
(defmacro o-mod [model-name] `(. self env [~model-name]))
(defmacro o-cref [ref-name] `((. cls env ref) ~ref-name))
(defmacro o-ref [ref-name] `((. self env ref) ~ref-name))
(defmacro hydm [hy-domain]
"Generate Odoo domain from Hy like tuple domain"
(setv op (second hy-domain)
field (mangle (nth hy-domain 2))
value (nth hy-domain 3))
`(, ~field ~op ~value))
; Odoo ORM macros
(defmacro/g! compute-fn [field dependencies body]
"Macro to make computed definition smoother"
(setv fname f"_compute_{(mangle field)}" descr f"Computes {field}"
dependencies (list (map mangle dependencies)))
(import [hy.models [HySymbol]])
`(with-decorator (.depends api ~@dependencies)
(defn ~(HySymbol fname) [self]
~descr
~body)))
(defmacro compute-field [fname body]
"Takes fname Symbol and body to create computed field"
(setv fn-name f"_compute_{(mangle fname)}")
`(setv ~fname (~@body :compute ~fn-name)))
; Backend macros
(defmacro __ [sentence] `((py "_") ~sentence))
(defmacro logger []
`(do
(import logging)
(setv _logger (.getLogger logging --name--))))
(defmacro pdb [] `(do (import [pdb [set-trace]]) (set-trace)))

22
hy_odoo/mgeneral.hy Normal file
View File

@ -0,0 +1,22 @@
;; Copyright 2021-2022 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
" Hy General Macros "
(defmacro instance? [type record] `(isinstance ~record ~type))
(defmacro hydict [dic]
"Generate dict with mangled keys, from HyDict list"
(setv mangled-dic
(list
(map
(fn [pair]
(if (even? (nth pair 0))
(hy.mangle (nth pair 1))
(nth pair 1)))
(enumerate dic))))
`{~@mangled-dic})

46
hy_odoo/modoo.hy Normal file
View File

@ -0,0 +1,46 @@
;; Copyright 2019-2022 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
" Odoo related macros "
(defmacro o-cmod [model-name] `(. cls env [~model-name]))
(defmacro o-mod [model-name] `(. self env [~model-name]))
(defmacro o-cref [ref-name] `((. cls env ref) ~ref-name))
(defmacro o-ref [ref-name] `((. self env ref) ~ref-name))
(defmacro hydm [hy-domain]
"Generate Odoo domain from Hy like tuple domain"
(setv op (second hy-domain)
field (hy.mangle (get hy-domain 2))
value (get hy-domain 3))
`#(~field ~op ~value))
; Odoo ORM macros
#_(defmacro/g! compute-fn [field dependencies body]
"Macro to make computed definition smoother"
(setv fname f"_compute_{(hy.mangle field)}" descr f"Computes {field}"
dependencies (list (map hy.mangle dependencies)))
(import hy.models [HySymbol]
`(defn [(.depends api ~@dependencies)] ~(HySymbol fname) [self]
~descr
~body)))
(defmacro compute-field [fname body]
"Takes fname Symbol and body to create computed field"
(setv fn-name f"_compute_{(hy.mangle fname)}")
`(setv ~fname (~@body :compute ~fn-name)))
; Backend macros
(defmacro __ [sentence] `((py "_") ~sentence))
(defmacro logger []
`(do
(import logging)
(setv _logger (.getLogger logging --name--))))
(defmacro pdb [] `(do (import [pdb [set-trace]]) (set-trace)))

View File

@ -1,24 +1,11 @@
;; -*- coding: utf-8 -*- ;; Copyright 2021-2022 Fabien Bourgeois <fabien@yaltik.com>
;; ;;
;; Copyright 2021 Fabien Bourgeois <fabien@yaltik.com> ;; This Source Code Form is subject to the terms of the Mozilla Public
;; ;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; This program is free software: you can redistribute it and/or modify ;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" Hy Odoo Tests Helpers and Macros " " Hy Odoo Tests Helpers and Macros "
(require [hy-odoo.macros.general [if-python2]])
(defmacro o-assert-equal [left right] `(.assertEqual self ~left ~right)) (defmacro o-assert-equal [left right] `(.assertEqual self ~left ~right))
(defmacro o-assert-list-equal [left right] `(.assertListEqual self ~left ~right)) (defmacro o-assert-list-equal [left right] `(.assertListEqual self ~left ~right))
(defmacro o-assert-dict-equal [left right] `(.assertDictEqual self ~left ~right)) (defmacro o-assert-dict-equal [left right] `(.assertDictEqual self ~left ~right))
@ -34,10 +21,7 @@
(defmacro o-assert-is-instance [left right] `(.assertIsInstance self ~left ~right)) (defmacro o-assert-is-instance [left right] `(.assertIsInstance self ~left ~right))
(defmacro o-assert-not-is-instance [left right] `(.assertNotIsInstance self ~left ~right)) (defmacro o-assert-not-is-instance [left right] `(.assertNotIsInstance self ~left ~right))
(defmacro o-assert-raises [Error] `(.assertRaises self ~Error)) (defmacro o-assert-raises [Error] `(.assertRaises self ~Error))
(defmacro o-assert-raises-regex [Error regexp] (defmacro o-assert-raises-regex [Error regexp] `(.assertRaisesRegex self ~Error ~regexp))
(if-python2
`(.assertRaisesRegexp self ~Error ~regexp)
`(.assertRaisesRegex self ~Error ~regexp)))
(defmacro odo-assert-raises [Error body] (defmacro odo-assert-raises [Error body]
"Macro to test Error with self.assertRaises and do block" "Macro to test Error with self.assertRaises and do block"

View File

@ -1,54 +1,45 @@
;; -*- coding: utf-8 -*- ;; Copyright 2019-2022 Fabien Bourgeois <fabien@yaltik.com>
;; ;;
;; Copyright 2019-2021 Fabien Bourgeois <fabien@yaltik.com> ;; This Source Code Form is subject to the terms of the Mozilla Public
;; ;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; This program is free software: you can redistribute it and/or modify ;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" Odoo macros and helpers " " Odoo macros and helpers "
(require [hy-odoo.macros.general [if-python2]]) (require hyrule.collections [assoc]
(import [os [path]] hy-odoo.mgeneral [instance?])
[hy-odoo.xml_base [xmlroot xmln]]) (import os [path]
hy-odoo.xml [xmlroot xmln])
; Global helpers ; Global helpers
(defn strdm [hy-domain] (defn strdm [hy-domain]
"Generate Odoo domain from Hy like tuple domain" "Generate Odoo domain from Hy like tuple domain"
(do (do
(setv (, op field value) hy-domain (setv #(op field value) hy-domain
field (mangle field) field (hy.mangle field)
value (if (string? value) f"'{value}'" value)) value (when (string? value) f"'{value}'" value))
(return f"('{field}', '{op}', {value})"))) (return f"('{field}', '{op}', {value})")))
; XML helpers functions and macros ; XML helpers functions and macros
(defn odoo [children] (xmlroot (xmln "odoo" {} children))) (defn odoo [children] (xmlroot {"tag" "odoo" "attrs" {} "children" children}))
(defn data [&rest args] (defn data [#* args]
"Special data node, allow optional args on data tag" "Special data node, allow optional args on data tag"
(when (= (len args) 1) (setv args (.insert (list args) 0 {}))) (when (= (len args) 1) (do (setv args (list args))
(xmln "data" #*args)) (.insert args 0 {})))
(xmln "data" #* args))
; Aliases ; Aliases
(defn function [&rest args] (xmln "function" #*args)) (defn function [#* args] (xmln "function" #*args))
(defn record [&rest args] (xmln "record" #*args)) (defn record [#* args] (xmln "record" #*args))
(defn form [&rest args] (xmln "form" #*args)) (defn form [#* args] (xmln "form" #*args))
(defn tree [&rest args] (xmln "tree" #*args)) (defn tree [#* args] (xmln "tree" #*args))
(defn search [&rest args] (xmln "search" #*args)) (defn search [#* args] (xmln "search" #*args))
; Actions ; Actions
(defn act-window [&rest args] (xmln "act_window" #*args)) (defn act-window [#* args] (xmln "act_window" #*args))
(defn act-window-model [model attrs] (defn act-window-model [model attrs]
" Build new act_window from model and args" " Build new act_window from model and args"
@ -79,7 +70,7 @@
(field {"name" "value" "eval" action})])) (field {"name" "value" "eval" action})]))
; Menus ; Menus
(defn menuitem [&rest args] (xmln "menuitem" #*args)) (defn menuitem [#* args] (xmln "menuitem" #*args))
(defn menuitem-model [model attrs] (defn menuitem-model [model attrs]
" Build new menuitem from model and attrs" " Build new menuitem from model and attrs"
@ -91,21 +82,21 @@
(menuitem cloned-attrs)) (menuitem cloned-attrs))
; Form aliases ; Form aliases
(defn group [&rest args] (xmln "group" #*args)) (defn group [#* args] (xmln "group" #*args))
(defn header [&rest args] (xmln "header" #*args)) (defn header [#* args] (xmln "header" #*args))
(defn footer [&rest args] (xmln "footer" #*args)) (defn footer [#* args] (xmln "footer" #*args))
(defn sheet [&rest args] (xmln "sheet" #*args)) (defn sheet [#* args] (xmln "sheet" #*args))
(defn button [&rest args] (xmln "button" #*args)) (defn button [#* args] (xmln "button" #*args))
(defn p [&rest args] (xmln "p" #*args)) (defn p [#* args] (xmln "p" #*args))
(defn xpath [&rest args] (xmln "xpath" #*args)) (defn xpath [#* args] (xmln "xpath" #*args))
(defn attribute [name value] (xmln "attribute" {"name" name} [value])) (defn attribute [name value] (xmln "attribute" {"name" name} [value]))
; Fields ; Fields
(defn field [&rest args] (defn field [#* args]
"Special field allowing mangling name attribute" "Special field allowing mangling name attribute"
(setv attrs (nth args 0)) (setv attrs (get args 0))
(when (and (instance? dict attrs) (in "name" attrs)) (when (and (instance? dict attrs) (in "name" attrs))
(assoc attrs "name" (mangle (get attrs "name"))) (assoc attrs "name" (hy.mangle (get attrs "name")))
(setv args (list args)) (setv args (list args))
(assoc args 0 attrs) (assoc args 0 attrs)
(setv args (tuple args))) (setv args (tuple args)))
@ -114,10 +105,10 @@
(defn field-name [name] (field {"name" "name"} [name])) (defn field-name [name] (field {"name" "name"} [name]))
(defn field-model [model] (field {"name" "model"} [model])) (defn field-model [model] (field {"name" "model"} [model]))
(defn field-inherit [xmlid] (field {"name" "inherit_id" "ref" xmlid} [])) (defn field-inherit [xmlid] (field {"name" "inherit_id" "ref" xmlid} []))
(defn field-arch [&rest args] (field {"name" "arch" "type" "xml"} #*args)) (defn field-arch [#* args] (field {"name" "arch" "type" "xml"} #*args))
; Search ; Search
(defn filter [&rest args] (xmln "filter" #*args)) (defn filter [#* args] (xmln "filter" #*args))
; Views ; Views
(defn view [xmlid children] (record {"id" xmlid "model" "ir.ui.view"} children)) (defn view [xmlid children] (record {"id" xmlid "model" "ir.ui.view"} children))
@ -152,11 +143,11 @@
(defn modulehytopy [module-path hy-files] (defn modulehytopy [module-path hy-files]
"Transforms hy to py for translation purpose" "Transforms hy to py for translation purpose"
(import astor) (import astor)
(import [os.path [dirname]] (import os.path [dirname]
[io [open :as iopen]] io [open :as iopen]
[hy.lex [hy-parse]] hy.lex [hy-parse]
[hy.compiler [hy-compile]] hy.compiler [hy-compile]
[hy.errors [filtered-hy-exceptions]]) hy.errors [filtered-hy-exceptions])
(defn hytopy [source path] (defn hytopy [source path]
"Hy source to Py source" "Hy source to Py source"
@ -165,14 +156,12 @@
(.to-source (. astor code-gen) -ast)) (.to-source (. astor code-gen) -ast))
(for [hy-file hy-files] (for [hy-file hy-files]
(setv hy-path (% "%s/%s.hy" (, (dirname module-path) hy-file)) (setv hy-path (% "%s/%s.hy" #((dirname module-path) hy-file))
hy-source (with [o (iopen hy-path "r" :encoding "utf-8")] (.read o)) hy-source (with [o (iopen hy-path "r" :encoding "utf-8")] (.read o))
output-path (.replace hy-path ".hy" ".py") output-path (.replace hy-path ".hy" ".py")
content ["# Generate from Hy AST, for Babel translation purpose only." content ["# Generate from Hy AST, for Babel translation purpose only."
"# For real source code, please see and use HY source." "# For real source code, please see and use HY source."
(hytopy hy-source hy-path)]) (hytopy hy-source hy-path)])
(if-python2
(.insert content 0 "# -*- coding: utf-8 -*-") (continue))
(setv output-py (.join "\n" content)) (setv output-py (.join "\n" content))
(with [f (iopen output-path "w")] (.write f output-py)))) (with [f (iopen output-path "w")] (.write f output-py))))

282
hy_odoo/tests/test_odoo.hy Normal file
View File

@ -0,0 +1,282 @@
;; Copyright 2022 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
" Odoo Helpers tests "
(require
hyrule.control [defmain]
hy-odoo.mtest *)
(import unittest
functools [partial]
xml.etree.ElementTree :as ET
hy-odoo.xml [XMLDictElement]
hy-odoo.odoo :as od)
(defclass TextOdooBase [(. unittest TestCase)]
"Odoo Helpers tests"
(defn test-odoo [self]
" Test odoo function "
(setv element (od.odoo []))
(o-assert-is-instance element (. ET Element))
(o-assert-equal (. element tag) "odoo"))
(defn test-data [self]
" Test data function "
(setv element (od.data []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "data")
(o-assert-equal element.attrs {})
(setv element (od.data {"one" "attribute"} []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "data")
(o-assert-equal element.attrs {"one" "attribute"}))
(defn test-aliases [self]
" Test simple aliases to xmln "
(setv element (od.record {"one" "attribute"} "A child"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "record")
(o-assert-equal element.attrs {"one" "attribute"})
(o-assert-equal element.children ["A child"])
(setv element (od.tree))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "tree"))
(defn test-act-window-model [self]
" Test act_window function "
(setv element (od.act_window_model "sample.model" {"view_type" "form"}))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "act_window")
(o-assert-equal (get (. element attrs) "view_type") "form")
(o-assert-equal (get (. element attrs) "id") "sample_model_view_action")
(o-assert-equal (get (. element attrs) "res_model") "sample.model")
(o-assert-equal (get (. element attrs) "name") "Sample Model Action"))
(defn test-menunitem-model [self]
" Test menuitem function "
(setv element (od.menuitem-model "sample.model" {"groups" "base.user_employee"}))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "menuitem")
(o-assert-equal (get (. element attrs) "groups") "base.user_employee")
(o-assert-equal (get (. element attrs) "id") "sample_model_menu")
(o-assert-equal (get (. element attrs) "action") "sample_model_view_action"))
(defn test-attribute [self]
" Test attribute function "
(setv element (od.attribute "invisible" "1"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "attribute")
(o-assert-equal (get (. element attrs) "name") "invisible")
(o-assert-equal (. element children) ["1"]))
(defn test-fields [self]
" Test fields function "
(setv element (od.field {"one" "attribute"} "A child"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal element.tag "field")
(setv element (od.field-name "A name"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "field")
(o-assert-equal (get (. element attrs) "name") "name")
(o-assert-equal (. element children) ["A name"])
(setv element (od.field-model "sample.model"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "field")
(o-assert-equal (get (. element attrs) "name") "model")
(o-assert-equal (. element children) ["sample.model"])
(setv element (od.field-inherit "module.xml_view"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "field")
(o-assert-equal (get (. element attrs) "name") "inherit_id")
(o-assert-equal (get (. element attrs) "ref") "module.xml_view")
(o-assert-false (. element children))
(setv element (od.field-arch))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "field")
(o-assert-equal (get (. element attrs) "name") "arch")
(o-assert-equal (get (. element attrs) "type") "xml")
(o-assert-false (. element children)))
(defn test-view [self]
" Test view function "
(setv element (od.view "view_xmlid" []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "record")
(o-assert-equal (get (. element attrs) "id") "view_xmlid")
(o-assert-equal (get (. element attrs) "model") "ir.ui.view")
(o-assert-false (. element children)))
(defn test-view-def [self]
" Test view def function "
(setv element (od.view_def "view_xmlid" "View" "sample.model" []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "record")
(o-assert-equal (get (. element attrs) "id") "view_xmlid")
(o-assert-equal (get (. element attrs) "model") "ir.ui.view")
(o-assert-equal (len (. element children)) 3)
(setv child (get (. element children) 0))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "name")
(o-assert-equal (. child children) ["View"])
(setv child (get (. element children) 1))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "model")
(o-assert-equal (. child children) ["sample.model"])
(setv child (get (. element children) 2))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "arch")
(o-assert-equal (get (. child attrs) "type") "xml")
(o-assert-false (. child children)))
(defn test-view-new [self]
" Test view-new function "
(setv element (od.view-new "tree" "sample.model" []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element.tag) "record")
(o-assert-equal (get (. element attrs) "id") "sample_model_view_tree")
(o-assert-equal (get (. element attrs) "model") "ir.ui.view")
(o-assert-equal (len (. element children)) 3)
(setv child (get (. element children) 0))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "name")
(o-assert-equal (. child children) ["Sample Model Tree"])
(setv child (get (. element children) 1))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "model")
(o-assert-equal (. child children) ["sample.model"])
(setv child (get (. element children) 2))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "arch")
(o-assert-equal (get (. child attrs) "type") "xml")
(o-assert-false (. child children)))
(defn test_view_inherit [self]
" Test view_inherit function "
(setv element (od.view-inherit "odoo.addons.module" "sample.model" "parent.view" []))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "record")
(o-assert-equal (get (. element attrs) "id") "view_inherit_module")
(o-assert-equal (get (. element attrs) "model") "ir.ui.view")
(o-assert-equal (len (. element children)) 4)
(setv child (get (. element children) 0))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "name")
(o-assert-equal (. child children) ["Sample Model Adaptations"])
(setv child (get (. element children) 1))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "model")
(o-assert-equal (. child children) ["sample.model"])
(setv child (get (. element children) 2))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "inherit_id")
(o-assert-false (. child children))
(setv child (get (. element children) 3))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "arch")
(o-assert-equal (get (. child attrs) "type") "xml")
(o-assert-false (. child children)))
(defn test-actions-server-code [self]
" Test actions-server-code function "
(setv element (od.actions-server-code "sample.xmlid" "Code" "sample.model"
"record.do_something()"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "record")
(o-assert-equal (get (. element attrs) "id") "sample.xmlid")
(o-assert-equal (get (. element attrs) "model") "ir.actions.server")
(o-assert-equal (len (. element children)) 4)
(setv child (get (. element children) 0))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "name")
(o-assert-equal (. child children) ["Code"])
(setv child (get (. element children) 1))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "model_id")
(o-assert-equal (get (. child attrs) "ref") "sample.model")
(o-assert-false (. child children))
(setv child (get (. element children) 2))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "state")
(o-assert-equal (. child children) ["code"])
(setv child (get (. element children) 3))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "code")
(o-assert-equal (. child children) ["record.do_something()"]))
(defn test-client-action-multi [self]
" Test client_action_multi function "
(setv element (od.client-action-multi "sample.xmlid" "Multi" "sample.model" "sample.action"))
(o-assert-is-instance element XMLDictElement)
(o-assert-equal (. element tag) "record")
(o-assert-equal (get (. element attrs) "id") "sample.xmlid")
(o-assert-equal (get (. element attrs) "model") "ir.values")
(o-assert-equal (len (. element children)) 4)
(setv child (get (. element children) 0))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "name")
(o-assert-equal (. child children) ["Multi"])
(setv child (get (. element children) 1))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "key2")
(o-assert-equal (get (. child attrs) "eval") "'client_action_multi'")
(o-assert-false (. child children))
(setv child (get (. element children) 2))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "model")
(o-assert-equal (get (. child attrs) "eval") "'sample.model'")
(setv child (get (. element children) 3))
(o-assert-is-instance child XMLDictElement)
(o-assert-equal (. child tag) "field")
(o-assert-equal (get (. child attrs) "name") "value")
(o-assert-equal (get (. child attrs) "eval") "'ir.actions.server,%d'%sample.action")))
(defmain [] (.main unittest))

View File

@ -1,34 +1,26 @@
;; -*- coding: utf-8 -*- ;; Copyright 2019-2022 Fabien Bourgeois <fabien@yaltik.com>
;; ;;
;; Copyright 2019-2021 Fabien Bourgeois <fabien@yaltik.com> ;; This Source Code Form is subject to the terms of the Mozilla Public
;; ;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; This program is free software: you can redistribute it and/or modify ;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
b" XML Helpers tests " " XML Helpers tests "
(require
hyrule.control [defmain]
hy-odoo.mtest *)
(require [hy-odoo.macros.test [*]])
(import unittest (import unittest
[functools [partial]] functools [partial]
[xml.etree.ElementTree :as ET] xml.etree.ElementTree :as ET
[os [unlink]] os [unlink]
[hy-odoo.xml-base [XMLDictElement xmln xmlroot xmlchild xml-write]]) hy-odoo.xml [XMLDictElement xmln xmlroot xmlchild xml-write])
(defclass TextXMLBase [(. unittest TestCase)] (defclass TextXMLBase [(. unittest TestCase)]
b"XML Helpers tests" "XML Helpers tests"
(defn test-xmln [self] (defn test-xmln [self]
b"Text xmln" "Text xmln"
; XMLDictElement ; XMLDictElement
(o-assert-is-instance (xmln) XMLDictElement) (o-assert-is-instance (xmln) XMLDictElement)
; Tags ; Tags
@ -54,7 +46,7 @@ b" XML Helpers tests "
(o-assert-is-instance element XMLDictElement)) (o-assert-is-instance element XMLDictElement))
(defn test-xmlchild [self] (defn test-xmlchild [self]
b"Test xmlchild" "Test xmlchild"
(setv parent (xmlroot {"tag" "root" "attrs" {} "children" []}) (setv parent (xmlroot {"tag" "root" "attrs" {} "children" []})
xmlc-par (partial xmlchild parent)) xmlc-par (partial xmlchild parent))
@ -68,33 +60,33 @@ b" XML Helpers tests "
(o-assert-equal (. parent text) "some text") (o-assert-equal (. parent text) "some text")
(xmlc-par[(xmln "t" {"a" "b"} [])]) (xmlc-par[(xmln "t" {"a" "b"} [])])
(setv child (.next (.iter parent "t"))) (setv child (next (.iter parent "t")))
(o-assert-equal (. child tag) "t") (o-assert-equal (. child tag) "t")
(o-assert-dict-equal (. child attrib) {"a" "b"}) (o-assert-dict-equal (. child attrib) {"a" "b"})
(o-assert-list-equal (list child) []) (o-assert-list-equal (list child) [])
(xmlc-par [(xmln "t2" {1 2} [])]) (xmlc-par [(xmln "t2" {1 2} [])])
(setv child (.next (.iter parent "t2"))) (setv child (next (.iter parent "t2")))
(o-assert-dict-equal (. child attrib) {"1" "2"}) (o-assert-dict-equal (. child attrib) {"1" "2"})
(xmlc-par [(xmln "tchildren" {} [(xmln "subchild" {} [])])]) (xmlc-par [(xmln "tchildren" {} [(xmln "subchild" {} [])])])
(setv child (.next (.iter parent "tchildren")) (setv child (next (.iter parent "tchildren"))
subchildren (list child)) subchildren (list child))
(o-assert-equal (len subchildren) 1) (o-assert-equal (len subchildren) 1)
(o-assert-equal (. (first subchildren) tag) "subchild")) (o-assert-equal (. (get subchildren 0) tag) "subchild"))
(defn test-xmlroot [self] (defn test-xmlroot [self]
b"Test xmlroot" "Test xmlroot"
(setv root (xmlroot {"tag" "root" "attrs" {} "children" []})) (setv root (xmlroot {"tag" "root" "attrs" {} "children" []}))
(o-assert-is-instance root (. ET Element)) (o-assert-is-instance root (. ET Element))
(with [(o-assert-raises-regex TypeError "has no attribute")] (xmlroot False)) (with [(o-assert-raises-regex TypeError "is not subscriptable")] (xmlroot False))
(with [(o-assert-raises-regex KeyError "tag")] (xmlroot {})) (with [(o-assert-raises-regex KeyError "tag")] (xmlroot {}))
(with [(o-assert-raises-regex KeyError "attrs")] (xmlroot {"tag" "root"}))) (with [(o-assert-raises-regex KeyError "attrs")] (xmlroot {"tag" "root"})))
(defn test-xml-write [self] (defn test-xml-write [self]
b"Test xml-write" "Test xml-write"
(setv children [(xmln "child1" {"attr" "value"} []) (setv children [(xmln "child1" {"attr" "value"} [])
(xmln "child2" {} "Some text")] (xmln "child2" {} "Some text")]
@ -103,13 +95,13 @@ b" XML Helpers tests "
(o-assert-is-none (xmlw "/badpath")) (o-assert-is-none (xmlw "/badpath"))
(o-assert-is-none (xmlw "/bad.ext")) (o-assert-is-none (xmlw "/bad.ext"))
(xmlw --file--) (xmlw __file__)
(setv filepath (.replace --file-- ".hy" "_views.xml")) (setv filepath (.replace __file__ ".hy" "_views.xml"))
(with [output-file (open filepath "r")] (with [output-file (open filepath "r")]
(setv output-xml (.read output-file)) (setv output-xml (.read output-file))
(o-assert-in "<?xml version" output-xml) (o-assert-in "<?xml version" output-xml)
(o-assert-in "<root>" output-xml) (o-assert-in "<root>" output-xml)
(o-assert-in "<child1 attr=\"value\"/>" output-xml) (o-assert-in "<child1 attr=\"value\" />" output-xml)
(o-assert-in "<child2>Some text</child2>" output-xml) (o-assert-in "<child2>Some text</child2>" output-xml)
(unlink filepath)))) (unlink filepath))))

View File

@ -1,22 +1,13 @@
;; -*- coding: utf-8 -*- ;; Copyright 2021-2022 Fabien Bourgeois <fabien@yaltik.com>
;; ;;
;; Copyright 2021 Fabien Bourgeois <fabien@yaltik.com> ;; This Source Code Form is subject to the terms of the Mozilla Public
;; ;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; This program is free software: you can redistribute it and/or modify ;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" Hy General Utils " " Hy General Utils "
(require hyrule.collections [assoc])
(defn pick [keys record] (defn pick [keys record]
(reduce (reduce
(fn [acc key] (fn [acc key]

61
hy_odoo/xml.hy Normal file
View File

@ -0,0 +1,61 @@
;; Copyright 2019-2022 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at https://mozilla.org/MPL/2.0/.
" XML helpers and macros "
(require hy-odoo.mgeneral [instance?]
hy-odoo.modoo [pdb])
(import collections [namedtuple]
functools [partial]
os [path]
xml.etree.ElementTree :as ET)
;; Types
(setv XMLDictElement (namedtuple "XMLDictElement" ["tag" "attrs" "children"]))
;; Helpers
(defn xmlroot [tree]
"Special process for root XML Node"
(setv rootel (.Element ET (get tree "tag") (get tree "attrs")))
(when (in "children" tree)
(xmlchild rootel (get tree "children")))
(return rootel))
(defn xmlchild [parent children]
"Handling of children (ie non root) XML Nodes with/o text and subchildren
(recursive)"
(cond (instance? str children) (setv (. parent text) children)
(instance? XMLDictElement children)
(do
(setv attrs (dfor [k v] (.items (. children attrs)) [(str k) (str v)]))
(setv new-parent (.SubElement ET parent (. children tag) attrs)
subchildren (. children children))
(when subchildren) (xmlchild new-parent subchildren))
(instance? list children) (list( map (partial xmlchild parent) children))
True (raise (TypeError "Invalid arguments for xmlchild"))))
(defn xmln [[tag ""] [attrs {}] [children []]]
"XMLDictElement building from dict object, with defaults"
(when (instance? list attrs) (setv children attrs attrs {}))
(setv xmldictel (partial XMLDictElement tag attrs)
inst-str? (instance? str children))
(when inst-str? (return (xmldictel [children])))
(when (instance? list children) (return (xmldictel children)))
(raise (TypeError "Invalid arguments for xmln")))
(defn xml-write [filepath tree]
"Write XML file according to filepath and given tree"
(when (.endswith filepath ".hy")
(setv output-xml (.decode (.tostring ET
tree
:xml_declaration True
:encoding "utf-8") "utf-8")
output-path (.split (.abspath path filepath) "/")
(get output-path -1) (.replace (get output-path -1) ".hy" "_views.xml")
output-path (.join "/" output-path))
(with [output-file (open output-path "w")] (.write output-file output-xml))))

View File

@ -1,75 +0,0 @@
;; -*- coding: utf-8 -*-
;;
;; Copyright 2019-2021 Fabien Bourgeois <fabien@yaltik.com>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
" XML helpers and macros "
(require [hy-odoo.macros.general [if-python2 ustr-cast]])
(require [hy-odoo.macros.odoo [pdb]])
(import [collections [namedtuple]]
[functools [partial]]
[os [path]]
[xml.etree.ElementTree :as ET])
;; Types
(setv XMLDictElement (namedtuple "XMLDictElement" ["tag" "attrs" "children"]))
;; Helpers
(defn xmlroot [tree]
"Special process for root XML Node"
(setv rootel (.Element ET (get tree "tag") (get tree "attrs")))
(when (in "children" tree)
(xmlchild rootel (get tree "children")))
(return rootel))
(defn xmlchild [parent children]
"Handling of children (ie non root) XML Nodes with/o text and subchildren
(recursive)"
(cond [(string? children) (setv (. parent text) children)]
[(instance? XMLDictElement children)
(do
(setv attrs (dfor [k v] (.items (. children attrs)) [(ustr-cast k) (ustr-cast v)]))
(setv new-parent (.SubElement ET parent (. children tag) attrs)
subchildren (. children children))
(when subchildren) (xmlchild new-parent subchildren))]
[(instance? list children) (list( map (partial xmlchild parent) children))]
[True (raise (TypeError "Invalid arguments for xmlchild"))]))
(defn xmln [&optional [tag ""] [attrs {}] [children []]]
"XMLDictElement building from dict object, with defaults"
(when (instance? list attrs) (setv children attrs attrs {}))
(setv xmldictel (partial XMLDictElement tag attrs)
inst-str? (if-python2 (instance? unicode children) (instance? str children)))
(when inst-str? (return (xmldictel [children])))
(when (instance? list children) (return (xmldictel children)))
(raise (TypeError "Invalid arguments for xmln")))
(defn xml-write [filepath tree]
"Write XML file according to filepath and given tree"
(when (.endswith filepath ".hy")
(if-python2
(do
(import [xml.etree.ElementTree :as ET]
[xml.dom [minidom]])
(setv output-xml (.toprettyxml :indent " "
(.parseString minidom (.tostring ET tree)))))
(setv output-xml (.decode (.tostring ET tree) "utf-8")))
(setv output-path (.split (.abspath path filepath) "/")
(cut output-path -1) [(.replace (last output-path) ".hy" "_views.xml")]
output-path (.join "/" output-path))
(with [output-file (open output-path "w")] (.write output-file output-xml))))

View File

@ -1,13 +1,30 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
from setuptools.command.install import install
class install(install):
""" From hyrule, thanks """
def run(self):
super().run()
import py_compile
import hy # for compile hooks
for path in set(self.get_outputs()):
if path.endswith(".hy"):
py_compile.compile(
path,
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
)
setup( setup(
name="hy_odoo", name="hy_odoo",
version="0.7.0pre", version="0.8.4",
packages=find_packages(), packages=find_packages(),
package_data={ package_data={
'hy_odoo': ['*.hy'], 'hy_odoo': ['*.hy'],
}, },
install_requires=["hy==0.17.Y"], setup_requires=["hy==0.24.*", "hyrule==0.2.*"],
install_requires=["hy==0.24.*", "hyrule==0.2.*"],
author="Fabien Bourgeois", author="Fabien Bourgeois",
author_email="fabien@yaltik.com", author_email="fabien@yaltik.com",
description="HY functions and macros for Odoo", description="HY functions and macros for Odoo",
@ -17,8 +34,12 @@ setup(
'Development Status :: 3 - Alpha', 'Development Status :: 3 - Alpha',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: MPL2 License', 'License :: OSI Approved :: MPL2 License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
] 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
],
cmdclass={'install': install}
) )