Skip to content

Commit 6ea6bc9

Browse files
committed
[ADD] tools/fetch-release-notes-video-id.py
Script to extract the video ID from the release-notes page and update the `src/util/report.py` file. closes #282 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 4598c14 commit 6ea6bc9

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

tools/fetch-release-notes-video-id.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env -S uv run --script --quiet
2+
3+
# /// script
4+
# requires-python = ">=3.12"
5+
# dependencies = [
6+
# "httpx",
7+
# "libcst",
8+
# "lxml",
9+
# ]
10+
# ///
11+
12+
import re
13+
import sys
14+
from pathlib import Path
15+
16+
import httpx
17+
import libcst as cst
18+
from lxml import etree
19+
20+
if len(sys.argv) != 2:
21+
sys.exit(f"Usage: {sys.argv[0]} VERSION")
22+
23+
version = sys.argv[1]
24+
25+
VERSION_RE = re.compile(r"^(?:saas[~-])?([0-9]+)(?:\.([0-9]+))?$")
26+
27+
if (match := VERSION_RE.match(version)) is None:
28+
sys.exit(f"Invalid version: {version!r}")
29+
30+
major, minor = match.groups(default="0")
31+
32+
version_url = major if minor == "0" else f"{major}-{minor}"
33+
full_version = f"{major}.0" if minor == "0" else f"saas~{major}.{minor}"
34+
35+
html = httpx.get(f"https://www.odoo.com/odoo-{version_url}-release-notes")
36+
if html.status_code != 200:
37+
sys.exit(f"Cannot fetch release notes page for version {version}")
38+
39+
root = etree.fromstring(html.text, parser=etree.HTMLParser())
40+
iframe = root.xpath("//main//iframe[contains(@src, 'youtube.com') or contains(@src, 'youtube-nocookie.com')]")
41+
if not iframe:
42+
sys.exit(f"Cannot find youtube video in {html.url}")
43+
44+
yt_link = httpx.URL(iframe[0].attrib["src"])
45+
video_id = yt_link.path.removeprefix("/embed/")
46+
47+
48+
report_py = Path(__file__).parent.parent / "src" / "util" / "report.py"
49+
50+
source_tree = cst.parse_module(report_py.read_bytes())
51+
52+
53+
class Transformer(cst.CSTTransformer):
54+
def __init__(self):
55+
self.video_dict = None
56+
self.key_found = False
57+
super().__init__()
58+
59+
def visit_Assign(self, node):
60+
match node:
61+
case cst.Assign(
62+
targets=[cst.AssignTarget(target=cst.Name(value="ODOO_SHOWCASE_VIDEOS"))],
63+
value=video_dict,
64+
):
65+
self.video_dict = video_dict
66+
return True
67+
return False
68+
69+
def visit_Dict(self, node):
70+
return node is self.video_dict
71+
72+
def leave_DictElement(self, original_node, updated_node):
73+
if original_node.key.raw_value == full_version:
74+
self.key_found = True
75+
if original_node.value.raw_value != video_id:
76+
updated_node = updated_node.with_changes(value=cst.SimpleString(f'"{video_id}"'))
77+
return updated_node
78+
79+
def leave_Dict(self, original_node, updated_node):
80+
if original_node is self.video_dict:
81+
if self.key_found:
82+
elements = updated_node.elements
83+
else:
84+
new_elem = updated_node.elements[0].with_changes(
85+
key=cst.SimpleString(f'"{full_version}"'), value=cst.SimpleString(f'"{video_id}"')
86+
)
87+
elements = [new_elem, *updated_node.elements]
88+
89+
elements = sorted(elements, reverse=True, key=lambda e: VERSION_RE.match(e.key.raw_value).groups("0"))
90+
updated_node = updated_node.with_changes(elements=elements)
91+
return updated_node
92+
93+
94+
modified_tree = source_tree.visit(Transformer())
95+
96+
report_py.write_text(modified_tree.code)

0 commit comments

Comments
 (0)