[REF][IMP][WIP]Pull improvements from purepy yaltik_dsl
This commit is contained in:
parent
9ac4e9f24e
commit
9a3dfacc31
@ -16,4 +16,4 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import hy
|
||||
from . import odoo, xml
|
||||
from . import macros,odoo, xml_base, test
|
||||
|
26
hy_odoo/macros.hy
Normal file
26
hy_odoo/macros.hy
Normal file
@ -0,0 +1,26 @@
|
||||
;; -*- 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))
|
164
hy_odoo/odoo.hy
164
hy_odoo/odoo.hy
@ -17,9 +17,9 @@
|
||||
|
||||
" Odoo macros and helpers "
|
||||
|
||||
(require [hy-odoo.xml [if-python2]])
|
||||
(import [os [path]])
|
||||
(import [hy-odoo.xml [xmlroot xmln]])
|
||||
(require [hy-odoo.macros [if-python2]])
|
||||
(import [os [path]]
|
||||
[hy-odoo.xml_base [xmlroot xmln]])
|
||||
|
||||
; Global helpers
|
||||
|
||||
@ -57,85 +57,32 @@
|
||||
|
||||
; XML helpers functions and macros
|
||||
|
||||
(defn odoo [&rest args] (xmlroot (xmln "odoo" {} #*args)))
|
||||
(defn odoo [children] (xmlroot (xmln "odoo" {} children)))
|
||||
|
||||
(defn data [&rest args]
|
||||
(if (= (len args) 1)
|
||||
(xmln "data" {} #*args)
|
||||
(xmln "data" #*args)))
|
||||
"Special data node, allow optional args on data tag"
|
||||
(when (= (len args) 1) (setv args (.insert (list args) 0 {})))
|
||||
(xmln "data" #*args))
|
||||
|
||||
; Aliases
|
||||
(defn function [&rest args] (xmln "function" #*args))
|
||||
(defn record [&rest args] (xmln "record" #*args))
|
||||
(defn form [&rest args] (xmln "form" #*args))
|
||||
(defn tree [&rest args] (xmln "tree" #*args))
|
||||
(defn search [&rest args] (xmln "search" #*args))
|
||||
|
||||
; Actions
|
||||
(defn act-window [&rest args] (xmln "act_window" #*args))
|
||||
|
||||
(defn act-window-model [model attrs]
|
||||
" Build new act_window from model and args"
|
||||
(setv model_und (.replace model "." "_")
|
||||
model_cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
xmlid f"{model_und}_view_action"
|
||||
name f"{model_cap} Action")
|
||||
(.update attrs {"id" xmlid "name" name "res_model" model})
|
||||
(act-window attrs))
|
||||
(defn menuitem [&rest args] (xmln "menuitem" #*args))
|
||||
(defn menuitem-model [model attrs]
|
||||
" Build new menuitem from model and attrs"
|
||||
(setv model_und (.replace model "." "_")
|
||||
actionid f"{model_und}_view_action"
|
||||
xmlid f"{model_und}_menu")
|
||||
(.update attrs {"id" xmlid "action" actionid})
|
||||
(menuitem attrs))
|
||||
(defn group [&rest args] (xmln "group" #*args))
|
||||
(defn header [&rest args] (xmln "header" #*args))
|
||||
(defn footer [&rest args] (xmln "footer" #*args))
|
||||
(defn sheet [&rest args] (xmln "sheet" #*args))
|
||||
(defn button [&rest args] (xmln "button" #*args))
|
||||
(defn p [&rest args] (xmln "p" #*args))
|
||||
(defn xpath [&rest args] (xmln "xpath" #*args))
|
||||
(defn attribute [name value] (xmln "attribute" {"name" name} [value]))
|
||||
|
||||
(defn field [&rest args]
|
||||
"Special field allowing mangling name attribute"
|
||||
(setv attrs (nth args 0))
|
||||
(when (and (instance? dict attrs) (in "name" attrs))
|
||||
(assoc attrs "name" (mangle (get attrs "name")))
|
||||
(setv args (list args))
|
||||
(assoc args 0 attrs)
|
||||
(setv args (tuple args)))
|
||||
(xmln "field" #*args))
|
||||
|
||||
(defn field-name [name] (field {"name" "name"} [name]))
|
||||
(defn field-model [model] (field {"name" "model"} [model]))
|
||||
(defn field-inherit [xmlid] (field {"name" "inherit_id" "ref" xmlid} []))
|
||||
(defn field-arch [&rest args] (field {"name" "arch" "type" "xml"} #*args))
|
||||
|
||||
|
||||
(defn view [xmlid children] (record {"id" xmlid "model" "ir.ui.view"} children))
|
||||
(defn view-def [xmlid name model arch]
|
||||
"View and first fields simplification with record xmlid, name, targeted model"
|
||||
(view xmlid
|
||||
[(field-name name)
|
||||
(field-model model)
|
||||
(field-arch arch)]))
|
||||
(defn view-new[view_type model arch]
|
||||
"View : new view definition, based on type (form, tree, ...) and model ID"
|
||||
(setv model_und (.replace model "." "_")
|
||||
model_cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
xmlid f"{model_und}_view_{view_type}"
|
||||
name f"{model_cap} {(.capitalize view_type)}")
|
||||
(view-def :xmlid xmlid :name name :model model :arch arch))
|
||||
(defn view-inherit [filename model inherit arch]
|
||||
"Inherited View simplification with name of the record, xmlid for model and
|
||||
inherited view"
|
||||
(setv module (get (.split filename ".") 2)
|
||||
inherited (get (.split inherit ".") 1)
|
||||
xmlid f"{inherited}_inherit_{module}"
|
||||
model_cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
name f"{model_cap} Adaptations")
|
||||
(view xmlid
|
||||
[(field-name name)
|
||||
(field-model model)
|
||||
(field-inherit inherit)
|
||||
(field-arch arch)]))
|
||||
(setv model-und (.replace model "." "_")
|
||||
model-cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
xmlid f"{model-und}_view_action"
|
||||
name f"{model-cap} Action"
|
||||
cloned-attrs (.copy attrs)) ; Avoid side-effect
|
||||
(.update cloned-attrs {"id" xmlid "name" name "res_model" model})
|
||||
(act-window cloned-attrs))
|
||||
|
||||
(defn actions-server-code [xmlid name modelref code]
|
||||
"Server actions of type code"
|
||||
@ -152,9 +99,78 @@
|
||||
(record {"id" xmlid "model" "ir.values"}
|
||||
[(field-name name)
|
||||
(field {"name" "key2" "eval" "'client_action_multi'"} [])
|
||||
(field {"name" "model" "eval" (+ "'" model "'")} [])
|
||||
(field {"name" "model" "eval" (% "'%s'" model)} [])
|
||||
(field {"name" "value" "eval" action})]))
|
||||
|
||||
; Menus
|
||||
(defn menuitem [&rest args] (xmln "menuitem" #*args))
|
||||
|
||||
(defn menuitem-model [model attrs]
|
||||
" Build new menuitem from model and attrs"
|
||||
(setv model-und (.replace model "." "_")
|
||||
actionid f"{model-und}_view_action"
|
||||
xmlid f"{model-und}_menu"
|
||||
cloned-attrs (.copy attrs))
|
||||
(.update cloned-attrs {"id" xmlid "action" actionid})
|
||||
(menuitem cloned-attrs))
|
||||
|
||||
; Form aliases
|
||||
(defn group [&rest args] (xmln "group" #*args))
|
||||
(defn header [&rest args] (xmln "header" #*args))
|
||||
(defn footer [&rest args] (xmln "footer" #*args))
|
||||
(defn sheet [&rest args] (xmln "sheet" #*args))
|
||||
(defn button [&rest args] (xmln "button" #*args))
|
||||
(defn p [&rest args] (xmln "p" #*args))
|
||||
(defn xpath [&rest args] (xmln "xpath" #*args))
|
||||
(defn attribute [name value] (xmln "attribute" {"name" name} [value]))
|
||||
|
||||
; Fields
|
||||
(defn field [&rest args]
|
||||
"Special field allowing mangling name attribute"
|
||||
(setv attrs (nth args 0))
|
||||
(when (and (instance? dict attrs) (in "name" attrs))
|
||||
(assoc attrs "name" (mangle (get attrs "name")))
|
||||
(setv args (list args))
|
||||
(assoc args 0 attrs)
|
||||
(setv args (tuple args)))
|
||||
(xmln "field" #*args))
|
||||
|
||||
(defn field-name [name] (field {"name" "name"} [name]))
|
||||
(defn field-model [model] (field {"name" "model"} [model]))
|
||||
(defn field-inherit [xmlid] (field {"name" "inherit_id" "ref" xmlid} []))
|
||||
(defn field-arch [&rest args] (field {"name" "arch" "type" "xml"} #*args))
|
||||
|
||||
; Search
|
||||
(defn filter [&rest args] (xmln "filter" #*args))
|
||||
|
||||
; Views
|
||||
(defn view [xmlid children] (record {"id" xmlid "model" "ir.ui.view"} children))
|
||||
|
||||
(defn view-def [xmlid name model arch]
|
||||
"View and first fields simplification with record xmlid, name, targeted model"
|
||||
(view xmlid [(field-name name) (field-model model) (field-arch arch)]))
|
||||
|
||||
(defn view-new[view_type model arch]
|
||||
"View : new view definition, based on type (form, tree, ...) and model ID"
|
||||
(setv model-und (.replace model "." "_")
|
||||
model-cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
xmlid f"{model-und}_view_{view_type}"
|
||||
name f"{model-cap} {(.capitalize view_type)}")
|
||||
(view-def :xmlid xmlid :name name :model model :arch arch))
|
||||
|
||||
(defn view-inherit [filename model inherit arch]
|
||||
"Inherited View simplification with name of the record, xmlid for model and
|
||||
inherited view"
|
||||
(setv module (get (.split filename ".") 2)
|
||||
inherited (get (.split inherit ".") 1)
|
||||
xmlid f"{inherited}_inherit_{module}"
|
||||
model-cap (.join " " (lfor w (.split model ".") (.capitalize w)))
|
||||
name f"{model-cap} Adaptations")
|
||||
(view xmlid [(field-name name)
|
||||
(field-model model)
|
||||
(field-inherit inherit)
|
||||
(field-arch arch)]))
|
||||
|
||||
; Odoo ORM macros
|
||||
|
||||
(defmacro/g! compute-fn [field dependencies body]
|
||||
|
@ -18,6 +18,8 @@
|
||||
" Hy Odoo Tests Helpers and Macros "
|
||||
|
||||
(defmacro o-assert-equal [left right] `(.assertEqual 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-not-equal [left right] `(.assertNotEqual self ~left ~right))
|
||||
(defmacro o-assert-true [val] `(.assertTrue self ~val))
|
||||
(defmacro o-assert-false [val] `(.assertFalse self ~val))
|
||||
@ -30,6 +32,7 @@
|
||||
(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-raises [Error] `(.assertRaises self ~Error))
|
||||
(defmacro o-assert-raises-regex [Error regexp] `(.assertRaisesRegexp self ~Error ~regexp))
|
||||
|
||||
(defmacro odo-assert-raises [Error body]
|
||||
"Macro to test Error with self.assertRaises and do block"
|
||||
|
@ -1,71 +0,0 @@
|
||||
;; -*- coding: utf-8 -*-
|
||||
;;
|
||||
;; Copyright 2019 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 "
|
||||
|
||||
(import [xml.etree.ElementTree :as ET])
|
||||
|
||||
;; Helpers
|
||||
|
||||
(defn xmlroot [tree]
|
||||
"Special process for root XML Node"
|
||||
(setv rootel (.Element ET (get tree "tag") (get tree "attrs"))
|
||||
children (get tree "children"))
|
||||
(if children (xmlchild rootel children))
|
||||
(return rootel))
|
||||
|
||||
(defn xmlchild [parent children]
|
||||
"Handling of children (ie non root) XML Nodes with/o text and subchildren
|
||||
(recursive)"
|
||||
(for [child children]
|
||||
(if (string? child)
|
||||
(setv (. parent text) child)
|
||||
(do
|
||||
(setv attrs (dfor [k v] (.items (get child "attrs")) [(str k) (str v)]))
|
||||
(setv new_parent (.SubElement ET parent (get child "tag") attrs))
|
||||
(setv subchildren (get child "children"))
|
||||
(if subchildren (xmlchild new_parent subchildren))))))
|
||||
|
||||
(defn xmln [tag &optional attrs children]
|
||||
"XMLNode with optional attributes or children, defaults according to second
|
||||
argument type or empty dict and list"
|
||||
(if (and attrs (not children))
|
||||
(if (instance? list attrs)
|
||||
(do (setv children attrs) (setv attrs {}))))
|
||||
{"tag" tag "attrs" (or attrs {}) "children" (or children [])})
|
||||
|
||||
;; 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/g! xml-write [filename tree]
|
||||
"Write XML file according to filename and given tree"
|
||||
`(do
|
||||
(import [os [path]]
|
||||
[xml.etree.ElementTree :as ET]
|
||||
[xml.dom [minidom]])
|
||||
(if-python2
|
||||
(setv ~g!output-xml (.toprettyxml :indent " " (.parseString minidom (.tostring ET ~tree))))
|
||||
(setv ~g!output-xml (.decode (.tostring ET ~tree) "utf-8")))
|
||||
(setv ~g!output-path (.dirname path (.abspath path __file__))
|
||||
~g!fpath (+ ~g!output-path "/" ~filename))
|
||||
(with [f (open ~g!fpath "w")] (.write f ~g!output-xml))))
|
73
hy_odoo/xml_base.hy
Normal file
73
hy_odoo/xml_base.hy
Normal file
@ -0,0 +1,73 @@
|
||||
;; -*- 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 [if-python2]])
|
||||
(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)) [(unicode k) (unicode v)])
|
||||
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))
|
||||
(when (instance? unicode children) (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 ".py")
|
||||
(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) ".py" "_views.xml")
|
||||
output-path (.join "/" output-path))
|
||||
(with [output-file (open output-path "w")] (.write output-file output-xml))))
|
Loading…
Reference in New Issue
Block a user