[REF][IMP][WIP]Pull improvements from purepy yaltik_dsl

This commit is contained in:
Fabien BOURGEOIS 2021-02-22 00:43:17 +01:00
parent 9ac4e9f24e
commit 9a3dfacc31
6 changed files with 193 additions and 146 deletions

View File

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

View File

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

View File

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

View File

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