Skip to content

Commit 6b6bffc

Browse files
Bruno Zanottilef-adhoc
authored andcommitted
[IMP] add name_get replace script
1 parent 723f224 commit 6b6bffc

3 files changed

Lines changed: 147 additions & 38 deletions

File tree

odoo_module_migrate/migration_scripts/migrate_160_170.py

Lines changed: 129 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@
88
import ast
99
from typing import Any
1010
from ..tools import get_files
11+
import re
1112

1213
empty_list = ast.parse("[]").body[0].value
1314

1415

16+
### Abstract visitor class ###
17+
18+
1519
class AbstractVisitor(ast.NodeVisitor):
16-
def __init__(self) -> None:
20+
def __init__(self, all_code=None) -> None:
1721
# ((line, line_end, col_offset, end_col_offset), replace_by) NO OVERLAPS
22+
self.all_code = all_code
1823
self.change_todo = []
1924

2025
def post_process(self, all_code: str, file: str) -> str:
@@ -46,6 +51,73 @@ def add_change(self, old_node: ast.AST, new_node: ast.AST | str):
4651
self.change_todo.append((position, ast.unparse(new_node)))
4752

4853

54+
def apply_visitor_scripts(logger, filename):
55+
with open(filename, mode="rt") as file:
56+
new_all = all_code = file.read()
57+
if ".read_group(" in all_code or "._read_group(" in all_code:
58+
for Step in Steps_visitor:
59+
visitor = Step()
60+
try:
61+
visitor.visit(ast.parse(new_all))
62+
except Exception:
63+
logger.error(
64+
f"ERROR in {filename} at step {visitor.__class__}: \n{new_all}"
65+
)
66+
raise
67+
new_all = visitor.post_process(new_all, filename)
68+
if new_all == all_code:
69+
logger.warning(
70+
"read_group detected but not changed in file %s" % filename
71+
)
72+
else:
73+
logger.info("Script read_group replace applied in file %s" % filename)
74+
with open(filename, mode="wt") as file:
75+
file.write(new_all)
76+
77+
if "def name_get(" in all_code:
78+
visitor = VisitorNameGet(all_code)
79+
try:
80+
visitor.visit(ast.parse(new_all))
81+
except Exception:
82+
logger.error(
83+
f"ERROR in {filename} at step {visitor.__class__}: \n{new_all}"
84+
)
85+
raise
86+
new_all = visitor.post_process(new_all, filename)
87+
if new_all == all_code:
88+
logger.warning(
89+
"name_get detected but not changed in file: %s" % filename
90+
)
91+
else:
92+
logger.warning(
93+
"Script name_get replace applied in file %s. "
94+
"You probably need to add manually some dependencies to the new computed method."
95+
% filename
96+
)
97+
with open(filename, mode="wt") as file:
98+
file.write(new_all)
99+
100+
101+
def _reformat_files(
102+
logger, module_path, module_name, manifest_path, migration_steps, tools
103+
):
104+
"""Reformat read_group method in py files."""
105+
106+
reformat_file_ext = ".py"
107+
file_paths = _get_files(module_path, reformat_file_ext)
108+
logger.debug(f"{reformat_file_ext} files found:\n" f"{list(map(str, file_paths))}")
109+
110+
reformatted_files = list()
111+
for file_path in file_paths:
112+
reformatted_file = apply_visitor_scripts(logger, file_path)
113+
if reformatted_file:
114+
reformatted_files.append(reformatted_file)
115+
logger.debug("Reformatted files:\n" f"{list(reformatted_files)}")
116+
117+
118+
### Refactor _read_group script ###
119+
120+
49121
class VisitorToPrivateReadGroup(AbstractVisitor):
50122
def post_process(self, all_code: str, file: str) -> str:
51123
all_lines = all_code.split("\n")
@@ -214,29 +286,7 @@ def visit_Call(self, node: ast.Call) -> Any:
214286
]
215287

216288

217-
def replace_read_group_signature(logger, filename):
218-
with open(filename, mode="rt") as file:
219-
new_all = all_code = file.read()
220-
if ".read_group(" in all_code or "._read_group(" in all_code:
221-
for Step in Steps_visitor:
222-
visitor = Step()
223-
try:
224-
visitor.visit(ast.parse(new_all))
225-
except Exception:
226-
logger.info(
227-
f"ERROR in {filename} at step {visitor.__class__}: \n{new_all}"
228-
)
229-
raise
230-
new_all = visitor.post_process(new_all, filename)
231-
if new_all == all_code:
232-
logger.info("read_group detected but not changed in file %s" % filename)
233-
234-
if new_all != all_code:
235-
logger.info("Script read_group replace applied in file %s" % filename)
236-
with open(filename, mode="wt") as file:
237-
file.write(new_all)
238-
239-
289+
### Warning open_form_view ###
240290

241291

242292
def _check_open_form_view(logger, file_path: Path):
@@ -266,23 +316,64 @@ def _check_open_form(
266316
_check_open_form_view(logger, file_path)
267317

268318

269-
def _reformat_read_group(
270-
logger, module_path, module_name, manifest_path, migration_steps, tools
271-
):
272-
"""Reformat read_group method in py files."""
319+
### name_get script ###
273320

274-
reformat_file_ext = (".py")
275-
file_paths = get_files(module_path, reformat_file_ext)
276-
logger.debug(f"{reformat_file_ext} files found:\n" f"{list(map(str, file_paths))}")
321+
list_comprehension_re = re.compile(
322+
r"""def name_get\(self\):(.*)
323+
return \[\s*\(.*id,\s*(.+)\s*\)\s*for\s*(\w+)\s*in\s*self\s*\]""",
324+
re.S,
325+
)
326+
list_comprehension_into = r"""@api.depends('')
327+
def _compute_display_name(self):\g<1>
328+
for \g<3> in self:
329+
\g<3>.display_name = \g<2>"""
277330

278-
reformatted_files = list()
279-
for file_path in file_paths:
280-
reformatted_file = replace_read_group_signature(logger, file_path)
281-
if reformatted_file:
282-
reformatted_files.append(reformatted_file)
283-
logger.debug("Reformatted files:\n" f"{list(reformatted_files)}")
331+
list_append_re = re.compile(
332+
r"""def name_get\(self\):.+
333+
return ([A-Za-z_]+)""",
334+
re.S,
335+
)
336+
337+
338+
class VisitorNameGet(AbstractVisitor):
339+
def post_process(self, all_code: str, file: str) -> str:
340+
for (old_str, new_str) in self.change_todo:
341+
all_code = all_code.replace(old_str, new_str)
342+
return all_code
343+
344+
def add_change(self, old_str, new_str):
345+
self.change_todo.append((old_str, new_str))
346+
347+
def visit_FunctionDef(self, node: ast.FunctionDef) -> Any:
348+
if node.name == "name_get":
349+
# Replace fields by aggregate and orderby by order
350+
code = ast.get_source_segment(self.all_code, node)
351+
new_code = list_comprehension_re.sub(list_comprehension_into, code, 1)
352+
353+
if new_code != code:
354+
self.add_change(code, new_code)
355+
elif groups := list_append_re.fullmatch(new_code):
356+
variable_to_remove = groups.group(1)
357+
new_code = new_code.replace(f"\n {variable_to_remove} = []", "")
358+
new_code = new_code.replace(
359+
"def name_get(self):", "def _compute_display_name(self):"
360+
)
361+
new_code = new_code.replace(
362+
"super().name_get()", "super()._compute_display_name()"
363+
)
364+
new_code = new_code.replace(
365+
f"\n return {variable_to_remove}", ""
366+
)
367+
new_code = re.sub(
368+
variable_to_remove + r".append\(\((.+).id, (.*),?\)\)",
369+
r"\g<1>.display_name = \g<2>",
370+
new_code,
371+
)
372+
self.add_change(code, new_code)
373+
else:
374+
print(f"Cannot find a way to convert:\n{code}")
284375

285376

286377
class MigrationScript(BaseMigrationScript):
287378

288-
_GLOBAL_FUNCTIONS = [_check_open_form, _reformat_read_group]
379+
_GLOBAL_FUNCTIONS = [_check_open_form, _reformat_files]

tests/data_result/module_160_170/models/res_partner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ResPartner(models.Model):
77
test_field_1 = fields.Boolean()
88
task_ids = fields.One2many('project.task')
99
task_count = fields.Integer(compute='_compute_task_count', string='# Tasks')
10+
last_name = fields.Char()
1011

1112
def _compute_task_count(self):
1213
# retrieve all children partners and prefetch 'parent_id' on them
@@ -21,3 +22,10 @@ def _compute_task_count(self):
2122
group_dependent = self.env['project.task']._read_group([
2223
('depend_on_ids', 'in', task_data.ids),
2324
], ['depend_on_ids'], ['__count'])
25+
26+
def _compute_display_name(self):
27+
if self.test_field_1:
28+
return super()._compute_display_name()
29+
for partner in self:
30+
name = partner.name + ' ' + partner.last_name
31+
partner.display_name = name

tests/data_template/module_160/models/res_partner.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class ResPartner(models.Model):
77
test_field_1 = fields.Boolean()
88
task_ids = fields.One2many('project.task')
99
task_count = fields.Integer(compute='_compute_task_count', string='# Tasks')
10+
last_name = fields.Char()
1011

1112
def _compute_task_count(self):
1213
# retrieve all children partners and prefetch 'parent_id' on them
@@ -21,3 +22,12 @@ def _compute_task_count(self):
2122
group_dependent = self.env['project.task']._read_group([
2223
('depend_on_ids', 'in', task_data.ids),
2324
], ['depend_on_ids'], ['depend_on_ids'])
25+
26+
def name_get(self):
27+
result = []
28+
if self.test_field_1:
29+
return super().name_get()
30+
for partner in self:
31+
name = partner.name + ' ' + partner.last_name
32+
result.append((partner.id, name))
33+
return result

0 commit comments

Comments
 (0)