# -*- coding: utf-8 -*-
#
# ixkit - odoo spy
#
#@purpose : append spy (tooltip & more) code on html node which was genereate by owl template
#@author : Artificer@ixkit.com
#@date : 2024-2-15
#@version : 1.0.0
#
#-----------------------------------------------------------------------
import re
import pickle
import sys
import os
import logging
import json
import html
from odoo.tools import config
from lxml import etree
from ..runtime import runtime_options
from ...land.lang.pattern import singleton
from ...land.lang import model_to_print_data,view_as_dict
from ...land.trace import Tracer
from ..session import request as session_request
from .hook_rule import should_hook_current_url
_logger_me = logging.getLogger(__name__)
_logger = Tracer
SPY_TOOLTIP_VIEW = 'spy.TooltipView'
def get_sys_developer_mode():
spy = sys.modules['spy']
debug_data = spy.get('debug')
return debug_data
def is_spy_mode():
do_spy = True
no_spy_case = None
while True:
#runtime_options.config.get("trace")
#case
debug_data = get_sys_developer_mode()
if debug_data is None:
do_spy = False
no_spy_case = 'developer mode without actived!'
break
#case
if not should_hook_current_url():
do_spy = False
no_spy_case = 'not hook url call'
break
#case
nospy = session_request.get_nospy_param()
if not nospy is None:
do_spy = False
no_spy_case = 'no spy'
break
break
return do_spy,no_spy_case
def _str_pos(value, matched):
if not matched in value:
return -1
index = value.index(matched)
return index
"""
xxx
"""
def _insertTracer2Html(html,code):
middle = "
right = html[index:] # class='a'> xxx
index = right.index(">")
index = index + 1;
right_string = right[:index] + code + right[index:]
final_string = left + right_string;
return final_string
def _replace_last_str(source, target, replace_with):
last_occurrence_index = source.rfind(target)
result = source[:last_occurrence_index] + replace_with + source[last_occurrence_index + len(target):]
return result
def _format_template_code(value):
if True:
buf = 'Filter'
buf = value
buf = buf.replace("\"","&?quote") #avoid json dump issue in js
buf = html.escape(buf,True)
return buf
def _call_web_layout(template_code):
keys = ['t-call="web.login_layout"',
't-call="website.layout"',
't-call="website_sale.checkout_layout"',
]
for key in keys:
if _str_pos(template_code, key )>0:
return True
return False
@singleton
class Spy(object):
def __init__(self):
pass
class ConstError(TypeError): pass
def set(self, key,val):
return self.__setattr__(key,val)
def get(self,key):
return self.__dict__.get(key)
def __setattr__(self, key, value):
# self.__dict__.update()
# if key in self.__dict__:
# raise self.ConstError("constant reassignment error!")
self.__dict__[key] = value
def __str__(self):
val = super().__str__()
result = f'{val}\n{self.__dict__}'
return result
def dump(self, obj):
return etree.tostring(obj)
"""
inject spy code, show the template source meta information: name, file path
@template_code String = IrUIView.arc_prev
@view IrUIView
"""
def inject_spy_template(self,template_code, view,ref):
do_spy, no_spy_case = is_spy_mode()
if not do_spy:
_logger_me.info('👀 skip inject_spy_template, case:%s',no_spy_case)
return template_code
trace_level = Tracer.get_trace_level()
if "DEBUG" == trace_level :
view_data = model_to_print_data(view)
_logger.debug("👀->👽 try inject_spy_template,IrUIView:{},ref:{}", view_data,ref)
_logger.debug("👀->👽 try inject_spy_template,template_code:{},", template_code)
result = self._inject_spy_template(template_code,view)
_logger.debug("👀->👽 inject_spy_template result:{}", result)
return result
def _view_to_tip(self,view,template_code):
view_dict = view_as_dict(view)
view_dict["template_code"] = _format_template_code(template_code)
_logger.debug("👀->🔥 view_dict :{}", view_dict)
info = {
"debug": True,
"class": type(view).__name__,
"view": view_dict
}
info_str = json.dumps(info)
result = " data-spy='qweb' data-tooltip-info='{}'".format(info_str)
_logger.debug("👀->🔥 _view_to_tip result:{}", result)
return result
def _create_spy_node(self, view, template_code):
if _call_web_layout(template_code):
return None
# if view.key == 'website.snippets':
# return None
# if view.key == 'web_editor.colorpicker':
# return None
tip_template = "data-tooltip-template='{}'".format(SPY_TOOLTIP_VIEW)
# hardcode it
tip_data = self._view_to_tip(view,template_code)
tip = tip_template + " " + tip_data
tag_id = 'spy->' + view.key
tag_style = ''
if view.key == 'website.layout':
tag_style = "height: 100vh; overflow-y: auto;"
#tag_style = "border-style: dotted;border-color:red;"
# debug_tag = "".format(tag_id,tip, tag_style)
debug_tag = "".format(tag_id,tip, tag_style)
_logger.debug("👀 _create_spy_node,debug_tag:{}", debug_tag, step=False)
node = etree.fromstring(debug_tag)
return node
def _inject_spy_template(self,template_code,view):
spy_node = self._create_spy_node(view,template_code)
if spy_node is None:
return template_code
etree_view = etree.fromstring(template_code)
_logger.debug("👀 _inject_spy_template,etree_view fromstring etree_view:{}", etree_view, step=False)
for node in etree_view:
if not node.get('o-spy') is None:
return template_code
if not node.get('t-name') is None :
etree_view = node
break
for child in etree_view:
spy_node.append(child)
for child in etree_view:
etree_view.remove(child)
etree_view.append(spy_node)
result = etree.tostring(etree_view, encoding='unicode')
_logger.debug("👀 🚀_inject_spy_template, out new template code:{}", result, step=False)
return result
#sys.modules['spy'] = Spy()
Spy().config = config