update
All checks were successful
Setup Native Action / native (3.12.7) (push) Has been skipped
Setup Native Action / docker (3.12.7) (push) Has been skipped

This commit is contained in:
KaySar12 2025-03-10 14:17:20 +07:00
parent 659ee738ea
commit dd595904a2
3 changed files with 174 additions and 2 deletions

View File

@ -87,7 +87,7 @@ cleanup_addons:
install_modules:
${PYTHON} odoo-bin --config=${CONFIG} -d ${DATABASE} -i ${MODULES} --xmlrpc-port=${PORT}
upgrade_modules:
${PYTHON} odoo-bin upgrade_code --addons-path ${UPGRADE_DIR} --from ${OLD_VER} --to ${NEW_VER}
${PYTHON} odoo-bin upgrade_code --addons-path=${UPGRADE_DIR} --from ${OLD_VER} --to ${NEW_VER} --dry-run
##### Docker Deployment #########
run_test_docker:

View File

@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
import re
from bs4 import BeautifulSoup as bs
from ast import literal_eval
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Constants
NEW_ATTRS = {'required', 'invisible', 'readonly', 'column_invisible'}
def upgrade(file_manager):
"""Upgrade XML files by converting 'attrs' and 'states' into new attributes."""
# Filter files to only XML files
files = [file for file in file_manager if file.path.suffix == '.xml']
if not files:
return
# Regex patterns with re.VERBOSE for readability
percent_d_re = re.compile(r"""
% # Start with percent
\( # Opening parenthesis
'? # Optional single quote
"? # Optional double quote
(?P<key>[\w\.\d_]+) # Capture key (word chars, dots, digits, underscores)
'? # Optional single quote
"? # Optional double quote
\) # Closing parenthesis
d # Ends with 'd'
""", re.VERBOSE)
# Helper functions
def normalize_domain(domain):
if len(domain) == 1:
return domain
result = []
expected = 1
op_arity = {'!': 1, '&': 2, '|': 2}
for token in domain:
if expected == 0:
result[0:0] = ['&']
expected = 1
if isinstance(token, (list, tuple)):
expected -= 1
token = tuple(token)
else:
expected += op_arity.get(token, 0) - 1
result.append(token)
return result
def stringify_leaf(leaf):
operator = str(leaf[1])
if operator == '=':
operator = '=='
elif 'like' in operator:
operator = 'not in' if 'not' in operator else 'in'
return f"{leaf[2] if isinstance(leaf[2], str) else str(leaf[2])} {operator} {leaf[0]}"
right = str(leaf[2]) if isinstance(leaf[2], (list, tuple, set, int, float, bool)) or leaf[2] in ('True', 'False', '1', '0') else f"'{leaf[2]}'"
return f"{leaf[0]} {operator} {right}"
def stringify_attr(stack):
if stack in (True, False, 'True', 'False', 1, 0, '1', '0'):
return str(stack)
last_paren_idx = max(i for i, item in enumerate(stack[::-1]) if item not in ('|', '!'))
stack = normalize_domain(stack)[::-1]
result = []
for idx, item in enumerate(stack):
if item == '!':
expr = result.pop()
result.append(f"(not ({expr}))")
elif item in ('&', '|'):
try:
left, right = result.pop(), result.pop()
op = 'and' if item == '&' else 'or'
form = f"({left} {op} {right})" if idx > last_paren_idx else f"{left} {op} {right}"
result.append(form)
except IndexError:
result.append(f"{left} {op}")
else:
result.append(stringify_leaf(item))
return result[0]
def get_new_attrs(attrs):
try:
# Use ast.literal_eval for safe evaluation of Python literals
attrs_dict = literal_eval(attrs.strip())
if not isinstance(attrs_dict, dict):
logger.warning(f"Invalid attrs format, expected dict, got {attrs_dict}")
return {}
return {attr: stringify_attr(attrs_dict[attr]) for attr in NEW_ATTRS if attr in attrs_dict}
except (ValueError, SyntaxError) as e:
logger.error(f"Failed to parse attrs '{attrs}': {e}")
return {}
except Exception as e:
logger.error(f"Unexpected error parsing attrs '{attrs}': {e}")
return {}
# Process each file
for fileno, file in enumerate(files, start=1):
content = file.content
# Handle percent-d replacements
percent_d_results = {}
for i, match in enumerate(percent_d_re.findall(content), 1):
placeholder = f"'REPLACEME{i}'"
percent_d_results[i] = match[0] # Full match
content = content.replace(match[0], placeholder)
# Parse XML with BeautifulSoup
soup = bs(content, 'xml')
tags_with_attrs = soup.select('[attrs]')
attr_tags = soup.select('attribute[name="attrs"]')
tags_with_states = soup.select('[states]')
state_tags = soup.select('attribute[name="states"]')
if not (tags_with_attrs or attr_tags or tags_with_states or state_tags):
continue
# Process tags with attrs
for tag in tags_with_attrs:
new_attrs = get_new_attrs(tag['attrs'])
if new_attrs: # Only proceed if parsing succeeded
del tag['attrs']
for attr, value in new_attrs.items():
tag[attr] = value
else:
logger.warning(f"Skipping tag in {file.path} due to invalid attrs: {tag.get('attrs')}")
# Process <attribute name="attrs">
for attr_tag in attr_tags:
new_attrs = get_new_attrs(attr_tag.text)
if new_attrs:
for attr, value in new_attrs.items():
new_tag = soup.new_tag('attribute', name=attr)
new_tag.string = value
attr_tag.insert_after(new_tag)
attr_tag.decompose()
else:
logger.warning(f"Skipping attribute tag in {file.path} due to invalid attrs: {attr_tag.text}")
# Process tags with states
for tag in tags_with_states:
base_invisible = tag.get('invisible', '')
if base_invisible and not base_invisible.endswith(('or', 'and')):
base_invisible += ' or '
states = [f"'{s.strip()}'" for s in tag['states'].split(',')]
tag['invisible'] = f"{base_invisible}state not in [{','.join(states)}]"
del tag['states']
# Process <attribute name="states">
for state_tag in state_tags:
states = [f"'{s.strip()}'" for s in state_tag.text.split(',')]
parent = state_tag.parent
inv_tag = next((t for t in parent.findAll('attribute') if t['name'] == 'invisible'), None)
if not inv_tag:
inv_tag = soup.new_tag('attribute', name='invisible')
current = inv_tag.text or ''
new_text = f"state not in [{','.join(states)}]"
inv_tag.string = f"{current} or {new_text}" if current else new_text
state_tag.insert_after(inv_tag)
state_tag.decompose()
# Write back with percent-d restored
output = str(soup)
for i, original in percent_d_results.items():
output = output.replace(f"'REPLACEME{i}'", original)
file.content = output
file_manager.print_progress(fileno, len(files))

View File

@ -228,7 +228,7 @@ print('################## Run Debug ##################')
print('################################################')
if nofilesfound:
print('No XML Files with "attrs" or "states" found in dir " %s "' % root_dir)
print('No XML Files with "attrs" or "states" found in dir "%s"' % root_dir)
print('Succeeded on files')
for file in ok_files: