odoo-addons-parser 0.6__tar.gz → 0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/PKG-INFO +84 -88
- odoo_addons_parser-0.7/README.md +112 -0
- odoo_addons_parser-0.7/odoo_addons_parser/data_csv.py +92 -0
- odoo_addons_parser-0.7/odoo_addons_parser/data_xml.py +417 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/module.py +54 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/common.py +408 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/repo/module_test/__manifest__.py +9 -2
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/data/res_partner.xml +18 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/demo/res_partner.xml +10 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/reports/reports.xml +37 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/reports/templates.xml +14 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/security/ir.model.access.csv +2 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/tests/test.xml +10 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/views/assets.xml +12 -0
- odoo_addons_parser-0.7/odoo_addons_parser/tests/repo/module_test/views/res_partner.xml +52 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/test_module.py +24 -2
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/test_repository.py +6 -3
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser.egg-info/PKG-INFO +84 -88
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser.egg-info/SOURCES.txt +9 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/pyproject.toml +10 -0
- odoo_addons_parser-0.6/README.md +0 -126
- odoo_addons_parser-0.6/odoo_addons_parser/tests/common.py +0 -169
- odoo_addons_parser-0.6/odoo_addons_parser/tests/repo/module_test/views/res_partner.xml +0 -18
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/.github/workflows/pre-commit.yml +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/.github/workflows/publish.yml +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/.github/workflows/test.yml +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/.gitignore +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/.pre-commit-config.yaml +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/LICENSE +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/MANIFEST.in +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/__init__.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/code.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/odoo.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/repository.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/__init__.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/repo/module_test/__init__.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/repo/module_test/models/__init__.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/repo/module_test/models/res_partner.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/repo/module_test/models/res_users.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/test_odoo.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/tests/test_treesitter_utils.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser/treesitter_utils.py +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser.egg-info/dependency_links.txt +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser.egg-info/requires.txt +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/odoo_addons_parser.egg-info/top_level.txt +0 -0
- {odoo_addons_parser-0.6 → odoo_addons_parser-0.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: odoo-addons-parser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7
|
|
4
4
|
Summary: Collect data from Odoo module folders.
|
|
5
5
|
Author: Sébastien Alix
|
|
6
6
|
License: GNU LESSER GENERAL PUBLIC LICENSE
|
|
@@ -173,8 +173,18 @@ Project-URL: homepage, https://github.com/sebalix/odoo-addons-parser
|
|
|
173
173
|
Project-URL: documentation, https://github.com/sebalix/odoo-addons-parser
|
|
174
174
|
Project-URL: repository, https://github.com/sebalix/odoo-addons-parser
|
|
175
175
|
Keywords: odoo,oca,analyze,parser,collect,modules,addons
|
|
176
|
+
Classifier: Intended Audience :: Developers
|
|
176
177
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
|
178
|
+
Classifier: Operating System :: OS Independent
|
|
179
|
+
Classifier: Programming Language :: Python
|
|
177
180
|
Classifier: Programming Language :: Python :: 3
|
|
181
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
182
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
183
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
184
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
185
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
186
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
187
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
178
188
|
Classifier: Framework :: Odoo
|
|
179
189
|
Requires-Python: >=3.9
|
|
180
190
|
Description-Content-Type: text/markdown
|
|
@@ -187,24 +197,40 @@ Requires-Dist: pytest; extra == "test"
|
|
|
187
197
|
Requires-Dist: coverage; extra == "test"
|
|
188
198
|
Dynamic: license-file
|
|
189
199
|
|
|
200
|
+
# odoo-addons-parser
|
|
201
|
+
|
|
202
|
+
[](https://pypi.org/project/odoo-addons-parser)
|
|
203
|
+
[](https://pypi.org/project/odoo-addons-parser)
|
|
190
204
|
[](https://github.com/sebalix/odoo-addons-parser/actions/workflows/pre-commit.yml?query=branch%3Amain)
|
|
191
205
|
[](https://github.com/sebalix/odoo-addons-parser/actions/workflows/test.yml?query=branch%3Amain)
|
|
192
|
-
[](https://pypi.org/project/odoo-addons-parser/)
|
|
193
206
|
|
|
194
|
-
|
|
207
|
+
Python package to collect and analyze data from Odoo module folders without requiring an Odoo runtime. It performs static file analysis to extract useful information from Odoo modules.
|
|
208
|
+
|
|
209
|
+
## Features
|
|
210
|
+
|
|
211
|
+
- **Static Analysis**: Doesn't rely on any Odoo runtime; performs static files analysis
|
|
212
|
+
- **Code Statistics**: Count lines of code (Python, XML, JavaScript, and CSS)
|
|
213
|
+
- **Manifest Parsing**: Extract useful data from manifest files (authors, dependencies, etc.)
|
|
214
|
+
- **Model Extraction**: Extract Odoo models information (with fields and methods)
|
|
215
|
+
- **Data Extraction**: Extract data from XML and CSV files
|
|
216
|
+
- **Flexible Scanning**: Scan individual modules, repositories, or entire Odoo source code
|
|
195
217
|
|
|
196
|
-
|
|
218
|
+
## Installation
|
|
197
219
|
|
|
198
|
-
|
|
220
|
+
```bash
|
|
221
|
+
uv pip install odoo-addons-parser
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Or with pip:
|
|
225
|
+
```bash
|
|
226
|
+
pip install odoo-addons-parser
|
|
227
|
+
```
|
|
199
228
|
|
|
200
|
-
|
|
201
|
-
- count the number of lines of code (Python, XML, JavaScript and CSS by default)
|
|
202
|
-
- read the manifest file (to get useful data like authors or dependencies)
|
|
203
|
-
- extract Odoo models info (fields, methods...)
|
|
229
|
+
## Usage
|
|
204
230
|
|
|
205
|
-
|
|
231
|
+
### ModuleParser
|
|
206
232
|
|
|
207
|
-
|
|
233
|
+
Parse a single Odoo module:
|
|
208
234
|
|
|
209
235
|
```python
|
|
210
236
|
from odoo_addons_parser import ModuleParser
|
|
@@ -213,103 +239,73 @@ from pprint import pprint as pp
|
|
|
213
239
|
mod = ModuleParser("/path/to/OCA/server-tools/server_environment")
|
|
214
240
|
pp(mod.to_dict())
|
|
215
241
|
```
|
|
216
|
-
|
|
242
|
+
|
|
243
|
+
Example output:
|
|
244
|
+
|
|
217
245
|
```python
|
|
218
|
-
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
246
|
+
{
|
|
247
|
+
'code': {'CSS': 0, 'JavaScript': 0, 'Python': 541, 'XML': 21},
|
|
248
|
+
'manifest': {
|
|
249
|
+
'author': 'Camptocamp,Odoo Community Association (OCA)',
|
|
250
|
+
'category': 'Tools',
|
|
251
|
+
'data': ['security/ir.model.access.csv', 'security/res_groups.xml', 'serv_config.xml'],
|
|
252
|
+
'depends': ['base', 'base_sparse_field'],
|
|
253
|
+
'installable': True,
|
|
254
|
+
'license': 'GPL-3 or any later version',
|
|
255
|
+
'name': 'server configuration environment files',
|
|
256
|
+
'summary': 'move some configurations out of the database',
|
|
257
|
+
'version': '14.0.1.0.0',
|
|
258
|
+
'website': 'https://github.com/OCA/server-env'
|
|
259
|
+
},
|
|
260
|
+
'models': ...,
|
|
261
|
+
'name': 'server_environment'
|
|
262
|
+
}
|
|
233
263
|
```
|
|
234
264
|
|
|
235
|
-
|
|
265
|
+
### RepositoryParser
|
|
236
266
|
|
|
237
|
-
|
|
238
|
-
(also named *addons path*):
|
|
267
|
+
Parse a whole repository of addons:
|
|
239
268
|
|
|
240
269
|
```python
|
|
241
270
|
from odoo_addons_parser import RepositoryParser
|
|
271
|
+
|
|
242
272
|
repo = RepositoryParser("/path/to/OCA/server-tools")
|
|
243
273
|
pp(repo.to_dict())
|
|
244
274
|
```
|
|
245
|
-
=>
|
|
246
|
-
```python
|
|
247
|
-
{'data_encryption': {'code': {'CSS': 0,
|
|
248
|
-
'JavaScript': 0,
|
|
249
|
-
'Python': 187,
|
|
250
|
-
'XML': 0},
|
|
251
|
-
'manifest': {'application': False,
|
|
252
|
-
'author': 'Akretion, Odoo Community '
|
|
253
|
-
'Association (OCA)',
|
|
254
|
-
'category': 'Tools',
|
|
255
|
-
'data': ['security/ir.model.access.csv'],
|
|
256
|
-
'depends': ['base'],
|
|
257
|
-
'development_status': 'Alpha',
|
|
258
|
-
'external_dependencies': {'python': ['cryptography']},
|
|
259
|
-
'installable': True,
|
|
260
|
-
'license': 'AGPL-3',
|
|
261
|
-
'name': 'Encryption data',
|
|
262
|
-
'summary': 'Store accounts and credentials '
|
|
263
|
-
'encrypted by environment',
|
|
264
|
-
'version': '14.0.1.0.0',
|
|
265
|
-
'website': 'https://github.com/OCA/server-env'},
|
|
266
|
-
'models': ...,
|
|
267
|
-
'name': 'data_encryption'},
|
|
268
|
-
'mail_environment': {'code': {'CSS': 0,
|
|
269
|
-
'JavaScript': 0,
|
|
270
|
-
'Python': 43,
|
|
271
|
-
'XML': 0},
|
|
272
|
-
'manifest': {'author': 'Camptocamp, Odoo Community '
|
|
273
|
-
'Association (OCA)',
|
|
274
|
-
'category': 'Tools',
|
|
275
|
-
'depends': ['fetchmail',
|
|
276
|
-
'server_environment'],
|
|
277
|
-
'license': 'AGPL-3',
|
|
278
|
-
'name': 'Mail configuration with '
|
|
279
|
-
'server_environment',
|
|
280
|
-
'summary': 'Configure mail servers with '
|
|
281
|
-
'server_environment_files',
|
|
282
|
-
'version': '14.0.1.0.0',
|
|
283
|
-
'website': 'https://github.com/OCA/server-env'},
|
|
284
|
-
'models': ...,
|
|
285
|
-
'name': 'mail_environment'},
|
|
286
|
-
[...]
|
|
287
|
-
```
|
|
288
275
|
|
|
289
|
-
|
|
276
|
+
### OdooParser
|
|
290
277
|
|
|
291
|
-
|
|
292
|
-
(the one we clone from https://github.com/odoo/odoo). It'll parse the content
|
|
293
|
-
of `./odoo/addons`, `./addons` and Odoo base classes such as `BaseModel`,
|
|
294
|
-
`Model` and `TransientModel`:
|
|
278
|
+
Parse the Odoo source code repository:
|
|
295
279
|
|
|
296
280
|
```python
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
["
|
|
302
|
-
|
|
303
|
-
|
|
281
|
+
from odoo_addons_parser import OdooParser
|
|
282
|
+
|
|
283
|
+
odoo = OdooParser("/path/to/odoo/odoo")
|
|
284
|
+
data = odoo.to_dict()
|
|
285
|
+
list(data["__odoo__"]["models"])
|
|
286
|
+
# ["BaseModel", "Model", "TransientModel"]
|
|
287
|
+
"res.partner" in data["base"]["models"]
|
|
288
|
+
# True
|
|
304
289
|
```
|
|
305
290
|
|
|
306
291
|
## Parameters
|
|
307
292
|
|
|
308
|
-
|
|
309
|
-
`scan_models` parameters, available on all classes:
|
|
293
|
+
You can disable specific features using parameters:
|
|
310
294
|
|
|
311
295
|
```python
|
|
296
|
+
# Disable code statistics
|
|
312
297
|
repo = RepositoryParser("path/to/addons_path", code_stats=False)
|
|
298
|
+
|
|
299
|
+
# Disable model scanning
|
|
313
300
|
mod = ModuleParser("path/to/addons_path/module", scan_models=False)
|
|
314
|
-
|
|
301
|
+
|
|
302
|
+
# Disable data scanning
|
|
303
|
+
mod = ModuleParser("path/to/addons_path/module", scan_data=False)
|
|
304
|
+
|
|
305
|
+
# Disable all
|
|
306
|
+
odoo = OdooParser("/path/to/odoo/odoo", code_stats=False, scan_models=False, scan_data=False)
|
|
315
307
|
```
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
This project is licensed under the LGPL-3.0 License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# odoo-addons-parser
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/odoo-addons-parser)
|
|
4
|
+
[](https://pypi.org/project/odoo-addons-parser)
|
|
5
|
+
[](https://github.com/sebalix/odoo-addons-parser/actions/workflows/pre-commit.yml?query=branch%3Amain)
|
|
6
|
+
[](https://github.com/sebalix/odoo-addons-parser/actions/workflows/test.yml?query=branch%3Amain)
|
|
7
|
+
|
|
8
|
+
Python package to collect and analyze data from Odoo module folders without requiring an Odoo runtime. It performs static file analysis to extract useful information from Odoo modules.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Static Analysis**: Doesn't rely on any Odoo runtime; performs static files analysis
|
|
13
|
+
- **Code Statistics**: Count lines of code (Python, XML, JavaScript, and CSS)
|
|
14
|
+
- **Manifest Parsing**: Extract useful data from manifest files (authors, dependencies, etc.)
|
|
15
|
+
- **Model Extraction**: Extract Odoo models information (with fields and methods)
|
|
16
|
+
- **Data Extraction**: Extract data from XML and CSV files
|
|
17
|
+
- **Flexible Scanning**: Scan individual modules, repositories, or entire Odoo source code
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv pip install odoo-addons-parser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or with pip:
|
|
26
|
+
```bash
|
|
27
|
+
pip install odoo-addons-parser
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### ModuleParser
|
|
33
|
+
|
|
34
|
+
Parse a single Odoo module:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from odoo_addons_parser import ModuleParser
|
|
38
|
+
from pprint import pprint as pp
|
|
39
|
+
|
|
40
|
+
mod = ModuleParser("/path/to/OCA/server-tools/server_environment")
|
|
41
|
+
pp(mod.to_dict())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Example output:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
{
|
|
48
|
+
'code': {'CSS': 0, 'JavaScript': 0, 'Python': 541, 'XML': 21},
|
|
49
|
+
'manifest': {
|
|
50
|
+
'author': 'Camptocamp,Odoo Community Association (OCA)',
|
|
51
|
+
'category': 'Tools',
|
|
52
|
+
'data': ['security/ir.model.access.csv', 'security/res_groups.xml', 'serv_config.xml'],
|
|
53
|
+
'depends': ['base', 'base_sparse_field'],
|
|
54
|
+
'installable': True,
|
|
55
|
+
'license': 'GPL-3 or any later version',
|
|
56
|
+
'name': 'server configuration environment files',
|
|
57
|
+
'summary': 'move some configurations out of the database',
|
|
58
|
+
'version': '14.0.1.0.0',
|
|
59
|
+
'website': 'https://github.com/OCA/server-env'
|
|
60
|
+
},
|
|
61
|
+
'models': ...,
|
|
62
|
+
'name': 'server_environment'
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### RepositoryParser
|
|
67
|
+
|
|
68
|
+
Parse a whole repository of addons:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from odoo_addons_parser import RepositoryParser
|
|
72
|
+
|
|
73
|
+
repo = RepositoryParser("/path/to/OCA/server-tools")
|
|
74
|
+
pp(repo.to_dict())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### OdooParser
|
|
78
|
+
|
|
79
|
+
Parse the Odoo source code repository:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from odoo_addons_parser import OdooParser
|
|
83
|
+
|
|
84
|
+
odoo = OdooParser("/path/to/odoo/odoo")
|
|
85
|
+
data = odoo.to_dict()
|
|
86
|
+
list(data["__odoo__"]["models"])
|
|
87
|
+
# ["BaseModel", "Model", "TransientModel"]
|
|
88
|
+
"res.partner" in data["base"]["models"]
|
|
89
|
+
# True
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Parameters
|
|
93
|
+
|
|
94
|
+
You can disable specific features using parameters:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# Disable code statistics
|
|
98
|
+
repo = RepositoryParser("path/to/addons_path", code_stats=False)
|
|
99
|
+
|
|
100
|
+
# Disable model scanning
|
|
101
|
+
mod = ModuleParser("path/to/addons_path/module", scan_models=False)
|
|
102
|
+
|
|
103
|
+
# Disable data scanning
|
|
104
|
+
mod = ModuleParser("path/to/addons_path/module", scan_data=False)
|
|
105
|
+
|
|
106
|
+
# Disable all
|
|
107
|
+
odoo = OdooParser("/path/to/odoo/odoo", code_stats=False, scan_models=False, scan_data=False)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
This project is licensed under the LGPL-3.0 License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright 2023 Sebastien Alix <https://github.com/sebalix>
|
|
2
|
+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
|
3
|
+
|
|
4
|
+
import csv
|
|
5
|
+
import logging
|
|
6
|
+
import pathlib
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
_logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CsvParseError(Exception):
|
|
13
|
+
"""Exception raised for CSV parsing errors."""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CsvFile:
|
|
19
|
+
"""Parse and extract data from Odoo CSV files."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self, module_path: pathlib.Path, file_path: pathlib.Path, loaded: bool = False
|
|
23
|
+
):
|
|
24
|
+
self.module_path = module_path
|
|
25
|
+
self.module_name = self.module_path.name
|
|
26
|
+
self.file_path = file_path
|
|
27
|
+
self.relative_file_path = self.file_path.relative_to(self.module_path)
|
|
28
|
+
self.loaded = loaded
|
|
29
|
+
self.model_name = self._extract_model_name()
|
|
30
|
+
self.records = self._parse_csv()
|
|
31
|
+
|
|
32
|
+
def _extract_model_name(self) -> str:
|
|
33
|
+
"""Extract model name from filename.
|
|
34
|
+
|
|
35
|
+
E.g. 'res.country.csv' -> 'res.country'.
|
|
36
|
+
"""
|
|
37
|
+
if self.file_path.name.endswith(".csv"):
|
|
38
|
+
return self.file_path.stem
|
|
39
|
+
raise CsvParseError(f"Invalid CSV filename: {self.file_path.name}")
|
|
40
|
+
|
|
41
|
+
def _parse_csv(self) -> List[Dict[str, Any]]:
|
|
42
|
+
"""Parse CSV file and return list of records."""
|
|
43
|
+
records = []
|
|
44
|
+
try:
|
|
45
|
+
with open(self.file_path, "r", encoding="utf-8") as file_:
|
|
46
|
+
reader = csv.DictReader(file_)
|
|
47
|
+
for row_num, row in enumerate(reader, start=2):
|
|
48
|
+
try:
|
|
49
|
+
record = self._process_row(row, row_num)
|
|
50
|
+
if record:
|
|
51
|
+
records.append(record)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
_logger.warning(
|
|
54
|
+
f"Error processing row {row_num} in {self.file_path}: {e}"
|
|
55
|
+
)
|
|
56
|
+
continue
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise CsvParseError(f"Error reading CSV file {self.file_path}: {e}")
|
|
59
|
+
return records
|
|
60
|
+
|
|
61
|
+
def _process_row(
|
|
62
|
+
self, row: Dict[str, str], row_num: int
|
|
63
|
+
) -> Optional[Dict[str, Any]]:
|
|
64
|
+
"""Process a single CSV row into a structured record."""
|
|
65
|
+
if not row:
|
|
66
|
+
return None
|
|
67
|
+
# Extract data from row
|
|
68
|
+
data = {}
|
|
69
|
+
for field_name, value in row.items():
|
|
70
|
+
if ":" in field_name:
|
|
71
|
+
# Handle field references like "country_id:id"
|
|
72
|
+
base_field = field_name.split(":")[0]
|
|
73
|
+
data[base_field] = value
|
|
74
|
+
else:
|
|
75
|
+
data[field_name] = value
|
|
76
|
+
# Validate required fields
|
|
77
|
+
if "id" not in data:
|
|
78
|
+
_logger.warning(f"Row {row_num} missing 'id' field in {self.file_path}")
|
|
79
|
+
return None
|
|
80
|
+
# Create structured record
|
|
81
|
+
record = {
|
|
82
|
+
"id": data["id"],
|
|
83
|
+
"model": self.model_name,
|
|
84
|
+
"file_path": str(self.relative_file_path),
|
|
85
|
+
"data": data,
|
|
86
|
+
"loaded": self.loaded,
|
|
87
|
+
}
|
|
88
|
+
return record
|
|
89
|
+
|
|
90
|
+
def to_dict(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
91
|
+
"""Convert parsed CSV data to dictionary format."""
|
|
92
|
+
return {self.model_name: self.records}
|