Skip to content

Commit 9ae95aa

Browse files
Merge pull request #250 from GlodoUK/cloc-14.0
[14.0][FEAT] cloc Implementation
2 parents 3d6b567 + a1225bc commit 9ae95aa

5 files changed

Lines changed: 104 additions & 16 deletions

File tree

glodo_client/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"version": "14.0.1.0.1",
88
"depends": ["base"],
99
"external_dependencies": {
10-
"python": ["cryptography"],
10+
"python": ["cryptography", "manifestoo_core"],
1111
},
1212
"license": "Other proprietary",
1313
"post_load": "_post_load",

glodo_client/controllers/main.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
from odoo.http import Controller, request, route
1919
from odoo.modules.registry import Registry
2020
from odoo.service.db import list_dbs
21-
from odoo.tools import cloc
2221

22+
from ..utils.cloc import CustomCloc
2323
from ..utils.crypto import (
2424
get_client_config,
2525
glodo_authenticated,
@@ -79,7 +79,7 @@ def info(self, **kwargs):
7979
8080
Response includes:
8181
- Instance-level information (Odoo version, etc.)
82-
- List of databases with CLOC and module info
82+
- List of databases with module info and CLOC breakdown
8383
"""
8484
# payload available via request.glodo_payload if needed
8585

@@ -140,27 +140,22 @@ def _get_database_info(self, db_name: str) -> dict:
140140
for m in modules
141141
]
142142

143+
try:
144+
cl = CustomCloc()
145+
cl.count_env(env)
146+
cloc_data = cl.summary()
147+
except Exception as e:
148+
cloc_data = {"error": str(e)}
149+
143150
db_info = {
144151
"name": db_name,
145152
"user_count": user_count,
146153
"expiration_date": expiration_date or None,
147154
"expiration_reason": expiration_reason or None,
148155
"installed_modules": module_list,
149-
"cloc": {},
156+
"cloc": cloc_data,
150157
}
151158

152-
# Try to get CLOC
153-
try:
154-
cl = cloc.Cloc()
155-
cl.count_customization(env)
156-
157-
db_info["cloc"] = {
158-
"output": cl.report(),
159-
"returncode": cl.code,
160-
}
161-
except Exception as e:
162-
db_info["cloc"] = {"error": str(e)}
163-
164159
return db_info
165160

166161
except Exception as e:

glodo_client/utils/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from . import cloc
12
from . import crypto

glodo_client/utils/cloc.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import ast
2+
import os
3+
import pathlib
4+
5+
from manifestoo_core.core_addons import is_core_addon
6+
from manifestoo_core.odoo_series import OdooSeries, UnsupportedOdooSeries
7+
8+
from odoo import release
9+
from odoo.modules import get_module_path
10+
from odoo.modules.module import MANIFEST_NAMES
11+
from odoo.tools.cloc import DEFAULT_EXCLUDE, MAX_FILE_SIZE, Cloc
12+
13+
DEFAULT_EXCLUDE_INCLUDE_TESTS = [
14+
p for p in DEFAULT_EXCLUDE if not p.startswith(("tests/", "static/tests/"))
15+
]
16+
17+
18+
class CustomCloc(Cloc):
19+
def summary(self):
20+
return {
21+
"code": dict(self.code),
22+
"errors": {m: dict(files) for m, files in self.errors.items()},
23+
}
24+
25+
def count_modules(self, env):
26+
try:
27+
major, minor = release.version_info[:2]
28+
series = OdooSeries(f"{major}.{minor}")
29+
except (ValueError, UnsupportedOdooSeries):
30+
series = None
31+
32+
domain = [("state", "=", "installed")]
33+
if env["ir.module.module"]._fields.get("imported"):
34+
domain.append(("imported", "=", False))
35+
module_list = env["ir.module.module"].search(domain).mapped("name")
36+
37+
for module_name in module_list:
38+
if series and is_core_addon(module_name, series):
39+
continue
40+
module_path = os.path.realpath(get_module_path(module_name))
41+
if module_path:
42+
self.count_path(module_path)
43+
44+
# Exact copy of odoo.tools.cloc.Cloc.count_path with DEFAULT_EXCLUDE → DEFAULT_EXCLUDE_INCLUDE_TESTS
45+
46+
# fmt: off
47+
# pylint: disable=broad-except,except-pass
48+
# ruff: noqa: E501, B007
49+
def count_path(self, path, exclude=None):
50+
path = path.rstrip('/')
51+
exclude_list = []
52+
for i in MANIFEST_NAMES:
53+
manifest_path = os.path.join(path, i)
54+
try:
55+
with open(manifest_path, 'rb') as manifest:
56+
exclude_list.extend(DEFAULT_EXCLUDE_INCLUDE_TESTS)
57+
d = ast.literal_eval(manifest.read().decode('latin1'))
58+
for j in ['cloc_exclude', 'demo', 'demo_xml']:
59+
exclude_list.extend(d.get(j, []))
60+
break
61+
except Exception:
62+
pass
63+
if not exclude:
64+
exclude = set()
65+
for i in filter(None, exclude_list):
66+
exclude.update(str(p) for p in pathlib.Path(path).glob(i))
67+
68+
module_name = os.path.basename(path)
69+
self.book(module_name)
70+
for root, dirs, files in os.walk(path):
71+
for file_name in files:
72+
file_path = os.path.join(root, file_name)
73+
74+
if file_path in exclude:
75+
continue
76+
77+
ext = os.path.splitext(file_path)[1].lower()
78+
if ext in ['.py', '.js', '.xml']:
79+
if os.path.getsize(file_path) > MAX_FILE_SIZE:
80+
self.book(module_name, file_path, (-1, "Max file size exceeded"))
81+
continue
82+
83+
with open(file_path, 'rb') as f:
84+
content = f.read().decode('latin1')
85+
if ext == '.py':
86+
self.book(module_name, file_path, self.parse_py(content))
87+
elif ext == '.js':
88+
self.book(module_name, file_path, self.parse_js(content))
89+
elif ext == '.xml':
90+
self.book(module_name, file_path, self.parse_xml(content))
91+
# fmt: on

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
cryptography
33
ftpparser
44
lxml
5+
manifestoo_core
56
pandas
67
phonenumbers
78
pycurl

0 commit comments

Comments
 (0)