strx-copy-installed-modules 0.1.1__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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: strx-copy-installed-modules
3
+ Version: 0.1.1
4
+ Home-page: https://github.com/straconxsa/strx_tools/
5
+ Project-URL: Bug Tracker, https://github.com/straconxsa/strx_tools/issues
6
+ Requires-Dist: psycopg2-binary
7
+ Dynamic: home-page
8
+ Dynamic: project-url
9
+ Dynamic: requires-dist
@@ -0,0 +1,30 @@
1
+ # STRX Copy Installed Modules
2
+
3
+ Herramienta para copiar m贸dulos instalados desde un directorio de origen (como initium16) a un directorio de destino bas谩ndose en el estado del m贸dulo en la base de datos de Odoo.
4
+
5
+ ## 馃殌 Instalaci贸n
6
+
7
+ ```bash
8
+ cd /odoo20/strx_tools/strx_copy_installed_modules
9
+ pip install -e .
10
+ ```
11
+
12
+ ## 馃幆 Uso
13
+
14
+ ```bash
15
+ strx-copy-installed-modules --db_name NOMBRE_DB --destination /ruta/destino --initium_path /ruta/origen
16
+ ```
17
+
18
+ ### Opciones principales:
19
+
20
+ - `--db_name`: Nombre de la base de datos Odoo.
21
+ - `--destination`: Carpeta donde se copiar谩n los m贸dulos.
22
+ - `--initium_path`: Carpeta donde buscar los m贸dulos (por defecto `/odoo16/initium16`).
23
+ - `--preserve_structure`: Mantiene la estructura de carpetas original.
24
+ - `--db_user`: Usuario de la base de datos.
25
+ - `--db_passwd`: Contrase帽a de la base de datos.
26
+ - `--db_host`: Host de la base de datos (por defecto `localhost`).
27
+ - `--db_port`: Puerto de la base de datos (por defecto `5432`).
28
+
29
+ ---
30
+ **Desarrollado por Initium Services ITS SAS**
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="strx-copy-installed-modules",
5
+ version="0.1.1",
6
+ url="https://github.com/straconxsa/strx_tools/",
7
+ project_urls={
8
+ "Bug Tracker": "https://github.com/straconxsa/strx_tools/issues",
9
+ },
10
+ package_dir={"": "src"},
11
+ packages=find_packages(where="src"),
12
+ install_requires=[
13
+ "psycopg2-binary",
14
+ ],
15
+ entry_points={
16
+ "console_scripts": [
17
+ "strx-copy-installed-modules=strx_copy_installed_modules.main:main",
18
+ ],
19
+ },
20
+ )
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script to copy installed modules from initium16 directory.
4
+
5
+ This script connects to an Odoo database, queries ir_module_module for modules
6
+ with state='installed', and copies those modules from the initium16 directory
7
+ to a specified destination directory.
8
+ """
9
+
10
+ import os
11
+ import shutil
12
+ import psycopg2
13
+ import logging
14
+ import getpass
15
+ import configparser
16
+ import argparse
17
+ from pathlib import Path
18
+ from typing import List, Set, Optional
19
+
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
23
+ )
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def get_installed_modules(
28
+ db_user: str,
29
+ db_passwd: str,
30
+ db_host: str,
31
+ db_port: str,
32
+ db_name: str
33
+ ) -> Set[str]:
34
+ """
35
+ Get list of installed modules from ir_module_module table.
36
+
37
+ Args:
38
+ db_user: Database user
39
+ db_passwd: Database password
40
+ db_host: Database host
41
+ db_port: Database port
42
+ db_name: Database name
43
+
44
+ Returns:
45
+ Set of module technical names with state='installed'
46
+ """
47
+ try:
48
+ conn = psycopg2.connect(
49
+ user=db_user,
50
+ password=db_passwd,
51
+ host=db_host,
52
+ port=db_port,
53
+ database=db_name
54
+ )
55
+ cursor = conn.cursor()
56
+ cursor.execute(
57
+ "SELECT name FROM ir_module_module WHERE state = 'installed'"
58
+ )
59
+ modules = {row[0] for row in cursor.fetchall()}
60
+ cursor.close()
61
+ conn.close()
62
+ logger.info(f"Found {len(modules)} installed modules in database")
63
+ return modules
64
+ except psycopg2.Error as e:
65
+ logger.error(f"Error connecting to database: {e}")
66
+ raise
67
+
68
+
69
+ def find_module_in_initium(module_name: str, initium_path: str) -> Optional[Path]:
70
+ """
71
+ Find module directory in initium16 path.
72
+
73
+ Args:
74
+ module_name: Technical name of the module
75
+ initium_path: Path to initium16 directory
76
+
77
+ Returns:
78
+ Path to module directory if found, None otherwise
79
+ """
80
+ initium_path = Path(initium_path)
81
+ if not initium_path.exists():
82
+ logger.error(f"Initium path does not exist: {initium_path}")
83
+ return None
84
+
85
+ # Search in all subdirectories of initium16
86
+ for root, dirs, files in os.walk(initium_path):
87
+ # Skip extra directory
88
+ if 'extra' in root.split(os.sep):
89
+ continue
90
+
91
+ # Check if current directory is the module
92
+ current_dir = Path(root)
93
+ if current_dir.name == module_name:
94
+ # Verify it's a valid Odoo module (has __manifest__.py)
95
+ manifest_file = current_dir / '__manifest__.py'
96
+ if manifest_file.exists():
97
+ logger.debug(f"Found module {module_name} at {current_dir}")
98
+ return current_dir
99
+
100
+ logger.debug(f"Module {module_name} not found in {initium_path}")
101
+ return None
102
+
103
+
104
+ def copy_module(module_path: Path, destination_path: Path) -> bool:
105
+ """
106
+ Copy module directory to destination.
107
+
108
+ Args:
109
+ module_path: Source path of the module
110
+ destination_path: Destination path where module will be copied
111
+
112
+ Returns:
113
+ True if copy was successful, False otherwise
114
+ """
115
+ try:
116
+ if not module_path.exists():
117
+ logger.error(f"Module path does not exist: {module_path}")
118
+ return False
119
+
120
+ # Create destination directory if it doesn't exist
121
+ destination_path.parent.mkdir(parents=True, exist_ok=True)
122
+
123
+ # If destination already exists, remove it first
124
+ if destination_path.exists():
125
+ logger.warning(
126
+ f"Destination {destination_path} already exists. Removing it."
127
+ )
128
+ shutil.rmtree(destination_path)
129
+
130
+ # Copy the entire module directory
131
+ shutil.copytree(module_path, destination_path)
132
+ logger.info(f"Copied {module_path.name} to {destination_path}")
133
+ return True
134
+ except Exception as e:
135
+ logger.error(f"Error copying module {module_path}: {e}")
136
+ return False
137
+
138
+
139
+ def copy_installed_modules(
140
+ db_user: str,
141
+ db_passwd: str,
142
+ db_host: str,
143
+ db_port: str,
144
+ db_name: str,
145
+ initium_path: str,
146
+ destination_path: str,
147
+ preserve_structure: bool = False
148
+ ) -> dict:
149
+ """
150
+ Main function to copy installed modules from initium16.
151
+
152
+ Args:
153
+ db_user: Database user
154
+ db_passwd: Database password
155
+ db_host: Database host
156
+ db_port: Database port
157
+ db_name: Database name
158
+ initium_path: Path to initium16 directory
159
+ destination_path: Path where modules will be copied
160
+ preserve_structure: If True, preserve directory structure from initium16
161
+
162
+ Returns:
163
+ Dictionary with statistics about the copy operation
164
+ """
165
+ stats = {
166
+ 'total_installed': 0,
167
+ 'found_in_initium': 0,
168
+ 'copied': 0,
169
+ 'failed': 0,
170
+ 'not_found': []
171
+ }
172
+
173
+ # Get installed modules from database
174
+ logger.info("Querying database for installed modules...")
175
+ installed_modules = get_installed_modules(
176
+ db_user, db_passwd, db_host, db_port, db_name
177
+ )
178
+ stats['total_installed'] = len(installed_modules)
179
+
180
+ # Find and copy modules
181
+ destination_base = Path(destination_path)
182
+ destination_base.mkdir(parents=True, exist_ok=True)
183
+
184
+ for module_name in sorted(installed_modules):
185
+ logger.info(f"Processing module: {module_name}")
186
+ module_path = find_module_in_initium(module_name, initium_path)
187
+
188
+ if not module_path:
189
+ stats['not_found'].append(module_name)
190
+ logger.warning(f"Module {module_name} not found in {initium_path}")
191
+ continue
192
+
193
+ stats['found_in_initium'] += 1
194
+
195
+ # Determine destination path
196
+ if preserve_structure:
197
+ # Preserve relative structure from initium16
198
+ relative_path = module_path.relative_to(Path(initium_path))
199
+ dest_path = destination_base / relative_path
200
+ else:
201
+ # Copy all modules to flat structure
202
+ dest_path = destination_base / module_name
203
+
204
+ # Copy module
205
+ if copy_module(module_path, dest_path):
206
+ stats['copied'] += 1
207
+ else:
208
+ stats['failed'] += 1
209
+
210
+ return stats
211
+
212
+
213
+ def print_statistics(stats: dict):
214
+ """
215
+ Print copy operation statistics.
216
+
217
+ Args:
218
+ stats: Dictionary with statistics
219
+ """
220
+ print("\n" + "=" * 60)
221
+ print("COPY OPERATION STATISTICS")
222
+ print("=" * 60)
223
+ print(f"Total installed modules in database: {stats['total_installed']}")
224
+ print(f"Modules found in initium16: {stats['found_in_initium']}")
225
+ print(f"Modules successfully copied: {stats['copied']}")
226
+ print(f"Modules failed to copy: {stats['failed']}")
227
+ print(f"Modules not found in initium16: {len(stats['not_found'])}")
228
+ print("=" * 60)
229
+
230
+ if stats['not_found']:
231
+ print("\nModules not found in initium16:")
232
+ for module in sorted(stats['not_found']):
233
+ print(f" - {module}")
234
+
235
+
236
+ def main():
237
+ """Main entry point for the script."""
238
+ parser = argparse.ArgumentParser(
239
+ description="Copy installed modules from initium16 directory"
240
+ )
241
+ parser.add_argument(
242
+ "--db_name",
243
+ required=True,
244
+ help="Database name"
245
+ )
246
+ parser.add_argument(
247
+ "--db_user",
248
+ help="Database user"
249
+ )
250
+ parser.add_argument(
251
+ "--db_passwd",
252
+ help="Database password"
253
+ )
254
+ parser.add_argument(
255
+ "--db_host",
256
+ default="localhost",
257
+ help="Database host (default: localhost)"
258
+ )
259
+ parser.add_argument(
260
+ "--db_port",
261
+ default="5432",
262
+ help="Database port (default: 5432)"
263
+ )
264
+ parser.add_argument(
265
+ "--initium_path",
266
+ default="/odoo16/initium16",
267
+ help="Path to initium16 directory (default: /odoo16/initium16)"
268
+ )
269
+ parser.add_argument(
270
+ "--destination",
271
+ required=True,
272
+ help="Destination directory where modules will be copied"
273
+ )
274
+ parser.add_argument(
275
+ "--preserve_structure",
276
+ action="store_true",
277
+ help="Preserve directory structure from initium16"
278
+ )
279
+ parser.add_argument(
280
+ "--config",
281
+ default="odoo_config.conf",
282
+ help="Configuration file path (default: odoo_config.conf)"
283
+ )
284
+ parser.add_argument(
285
+ "--verbose",
286
+ "-v",
287
+ action="store_true",
288
+ help="Enable verbose logging"
289
+ )
290
+
291
+ args = parser.parse_args()
292
+
293
+ # Set logging level
294
+ if args.verbose:
295
+ logging.getLogger().setLevel(logging.DEBUG)
296
+
297
+ # Load configuration from file if exists
298
+ db_config = {}
299
+ config_file = (
300
+ args.config
301
+ if os.path.isabs(args.config)
302
+ else os.path.join(os.path.dirname(__file__), args.config)
303
+ )
304
+
305
+ if os.path.exists(config_file):
306
+ logger.info(f"Loading configuration from {config_file}")
307
+ config = configparser.ConfigParser()
308
+ config.read(config_file)
309
+ if 'database' in config:
310
+ db_config = config['database']
311
+
312
+ # Get database credentials
313
+ db_user = args.db_user or db_config.get('db_user')
314
+ db_passwd = args.db_passwd or db_config.get('db_passwd')
315
+ db_host = args.db_host or db_config.get('db_host', 'localhost')
316
+ db_port = args.db_port or db_config.get('db_port', '5432')
317
+
318
+ # Prompt for password if not provided
319
+ if not db_passwd:
320
+ db_passwd = getpass.getpass("Enter database password: ")
321
+
322
+ # Validate required parameters
323
+ if not db_user:
324
+ logger.error("Database user is required. Provide --db_user or set in config file.")
325
+ return 1
326
+
327
+ if not args.db_name:
328
+ logger.error("Database name is required. Provide --db_name.")
329
+ return 1
330
+
331
+ if not args.destination:
332
+ logger.error("Destination path is required. Provide --destination.")
333
+ return 1
334
+
335
+ # Validate paths
336
+ if not os.path.exists(args.initium_path):
337
+ logger.error(f"Initium path does not exist: {args.initium_path}")
338
+ return 1
339
+
340
+ # Execute copy operation
341
+ try:
342
+ stats = copy_installed_modules(
343
+ db_user=db_user,
344
+ db_passwd=db_passwd,
345
+ db_host=db_host,
346
+ db_port=db_port,
347
+ db_name=args.db_name,
348
+ initium_path=args.initium_path,
349
+ destination_path=args.destination,
350
+ preserve_structure=args.preserve_structure
351
+ )
352
+
353
+ print_statistics(stats)
354
+ return 0
355
+
356
+ except Exception as e:
357
+ logger.error(f"Error during copy operation: {e}")
358
+ logger.exception(e)
359
+ return 1
360
+
361
+
362
+ if __name__ == "__main__":
363
+ exit(main())
364
+
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: strx-copy-installed-modules
3
+ Version: 0.1.1
4
+ Home-page: https://github.com/straconxsa/strx_tools/
5
+ Project-URL: Bug Tracker, https://github.com/straconxsa/strx_tools/issues
6
+ Requires-Dist: psycopg2-binary
7
+ Dynamic: home-page
8
+ Dynamic: project-url
9
+ Dynamic: requires-dist
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ src/strx_copy_installed_modules/__init__.py
4
+ src/strx_copy_installed_modules/main.py
5
+ src/strx_copy_installed_modules.egg-info/PKG-INFO
6
+ src/strx_copy_installed_modules.egg-info/SOURCES.txt
7
+ src/strx_copy_installed_modules.egg-info/dependency_links.txt
8
+ src/strx_copy_installed_modules.egg-info/entry_points.txt
9
+ src/strx_copy_installed_modules.egg-info/requires.txt
10
+ src/strx_copy_installed_modules.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ strx-copy-installed-modules = strx_copy_installed_modules.main:main