[WIP] add estimate-workload command

This commit is contained in:
Sylvain LE GAL 2022-05-31 22:07:12 +02:00
parent 3554f92564
commit 2a0b46ca60
10 changed files with 278 additions and 25 deletions

View File

@ -52,6 +52,7 @@ pytest:
tests/cli_05_execute_script_python_test.py
tests/cli_06_execute_script_sql_test.py
tests/cli_07_upgrade_test.py
tests/cli_08_estimate_workload_test.py
# Disabled test on gitlab-ci :
# The following tests should work locally but doesn't on gitlab-ci

View File

@ -10,6 +10,7 @@ from loguru import logger
import odoo_openupgrade_wizard
from odoo_openupgrade_wizard.cli_docker_build import docker_build
from odoo_openupgrade_wizard.cli_estimate_workload import estimate_workload
from odoo_openupgrade_wizard.cli_execute_script_python import (
execute_script_python,
)
@ -109,10 +110,9 @@ def main(ctx, env_folder, filestore_folder, log_level):
elif ctx.invoked_subcommand != "init":
raise
logger.debug("context %s: " % ctx.obj)
main.add_command(docker_build)
main.add_command(estimate_workload)
main.add_command(execute_script_python)
main.add_command(execute_script_sql)
main.add_command(generate_module_analysis)

View File

@ -0,0 +1,37 @@
from datetime import datetime
from pathlib import Path
import click
from odoo_openupgrade_wizard import templates
from odoo_openupgrade_wizard.tools_odoo_module import Analysis
from odoo_openupgrade_wizard.tools_system import (
ensure_file_exists_from_template,
)
@click.command()
@click.option(
"--analysis-file-path",
type=click.Path(
dir_okay=False,
),
default="./analysis.html",
)
@click.pass_context
def estimate_workload(ctx, analysis_file_path):
# Analyse
analysis = Analysis(ctx)
# Make some clean to display properly
analysis.modules = sorted(analysis.modules)
# Render html file
# TODO, make
ensure_file_exists_from_template(
Path(analysis_file_path),
templates.ANALYSIS_TEMPLATE,
ctx=ctx,
analysis=analysis,
current_date=datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
)

View File

@ -1,5 +1,3 @@
import csv
import click
from loguru import logger
@ -7,7 +5,11 @@ from odoo_openupgrade_wizard.cli_options import (
database_option,
get_migration_step_from_options,
)
from odoo_openupgrade_wizard.tools_odoo import kill_odoo, run_odoo
from odoo_openupgrade_wizard.tools_odoo import (
get_odoo_modules_from_csv,
kill_odoo,
run_odoo,
)
from odoo_openupgrade_wizard.tools_odoo_instance import OdooInstance
from odoo_openupgrade_wizard.tools_postgres import ensure_database
@ -20,23 +22,7 @@ def install_from_csv(ctx, database):
ensure_database(ctx, database, state="present")
# Get modules list from the CSV file
csv_path = ctx.obj["module_file_path"]
logger.info("Reading '%s' file ..." % csv_path)
module_names = []
csvfile = open(csv_path, "r")
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')
for row in spamreader:
# Try to guess that a line is not correct
if not row:
continue
if not row[0]:
continue
if " " in row[0]:
continue
if any([x.isupper() for x in row[0]]):
continue
module_names.append(row[0])
module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
module_names.sort()
logger.info("Found %d modules." % (len(module_names)))
logger.debug(module_names)

View File

@ -131,3 +131,46 @@ base,Base
account,Account Module
web_responsive,Web Responsive Module
"""
ANALYSIS_TEMPLATE = """
<html>
<body>
<h1>Migration Analysis</h1>
<table border="1" width="100%">
<thead>
<tr>
<th>Initial Release</th>
<th>Final Release</th>
<th>Project Name</th>
<th>Analysis Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ ctx.obj["config"]["odoo_versions"][0]["release"] }}</td>
<td>{{ ctx.obj["config"]["odoo_versions"][-1]["release"] }}</td>
<td>{{ ctx.obj["config"]["project_name"] }}</td>
<td>{{ current_date }}</td>
</tr>
</tbody>
</table>
<table border="1" width="100%">
<thead>
<tr>
<th> - </th>
</tr>
</thead>
<tbody>
{%- for odoo_module in analysis.modules -%}
<tr>
<td>{{odoo_module.name}} ({{odoo_module.module_type}})
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
"""

View File

@ -1,4 +1,5 @@
import configparser
import csv
import os
import sys
import traceback
@ -41,7 +42,7 @@ def get_odoo_addons_path(ctx, root_path: Path, migration_step: dict) -> str:
else:
addons_path.append(path)
return ",".join([str(x) for x in addons_path])
return addons_path
def get_odoo_env_path(ctx, odoo_version: dict) -> Path:
@ -128,7 +129,14 @@ def generate_odoo_config_file(ctx, migration_step, log_file):
parser.read(custom_odoo_config_file)
# compute addons_path
addons_path = get_odoo_addons_path(ctx, Path("/odoo_env"), migration_step)
addons_path = ",".join(
[
str(x)
for x in get_odoo_addons_path(
ctx, Path("/odoo_env"), migration_step
)
]
)
# compute server wides modules
server_wide_modules = parser.get(
@ -297,3 +305,22 @@ def execute_click_odoo_python_files(
raise e
finally:
kill_odoo(ctx, migration_step)
def get_odoo_modules_from_csv(module_file_path: Path) -> list:
logger.info("Reading '%s' file ..." % module_file_path)
module_names = []
csvfile = open(module_file_path, "r")
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')
for row in spamreader:
# Try to guess that a line is not correct
if not row:
continue
if not row[0]:
continue
if " " in row[0]:
continue
if any([x.isupper() for x in row[0]]):
continue
module_names.append(row[0])
return module_names

View File

@ -0,0 +1,102 @@
from functools import total_ordering
from git import Repo
from loguru import logger
from odoo_openupgrade_wizard.tools_odoo import (
get_odoo_addons_path,
get_odoo_env_path,
get_odoo_modules_from_csv,
)
class Analysis(object):
modules = []
def __init__(self, ctx):
module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
initial_release = ctx.obj["config"]["odoo_versions"][0]["release"]
# Instanciate a new odoo_module
for module_name in module_names:
repository_name = OdooModule.find_repository(
ctx, module_name, initial_release
)
if (
repository_name
and "%s.%s" % (repository_name, module_name)
not in self.modules
):
logger.debug(
"Discovering module '%s' in %s for release %s"
% (module_name, repository_name, initial_release)
)
self.modules.append(
OdooModule(ctx, module_name, repository_name)
)
@total_ordering
class OdooModule(object):
active = True
name = False
repository = False
module_type = False
unique_name = False
@classmethod
def find_repository(cls, ctx, module_name, current_release):
# Try to find the repository that contains the module
main_path = get_odoo_env_path(ctx, {"release": current_release})
addons_path = get_odoo_addons_path(
ctx, main_path, {"release": current_release, "action": "update"}
)
for addon_path in addons_path:
if (addon_path / module_name).exists():
if str(addon_path).endswith("odoo/odoo/addons"):
path = addon_path.parent.parent
elif str(addon_path).endswith("odoo/addons"):
path = addon_path.parent
else:
path = addon_path
repo = Repo(str(path))
repository_name = repo.remotes[0].url.replace(
"https://github.com/", ""
)
return repository_name
return False
def __init__(self, ctx, module_name, repository_name):
self.name = module_name
self.repository = repository_name
if repository_name == "odoo/odoo":
self.module_type = "odoo"
elif repository_name.startswith("OCA"):
self.module_type = "OCA"
else:
self.module_type = "custom"
self.unique_name = "%s.%s" % (repository_name, module_name)
def __eq__(self, other):
if isinstance(other, str):
return self.unique_name == other
elif isinstance(other, OdooModule):
return self.unique_name == other.unique_name
def __lt__(self, other):
if self.module_type != other.module_type:
if self.module_type == "odoo":
return True
elif self.module_type == "OCA" and self.module_type == "custom":
return True
else:
return False
else:
return self.name < other.name

45
poetry.lock generated
View File

@ -208,6 +208,29 @@ colorama = "*"
kaptan = "*"
requests = "*"
[[package]]
name = "gitdb"
version = "4.0.9"
description = "Git Object Database"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
version = "3.1.20"
description = "Python Git Library"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
gitdb = ">=4.0.1,<5"
typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""}
[[package]]
name = "idna"
version = "3.3"
@ -597,6 +620,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "smmap"
version = "5.0.0"
description = "A pure Python implementation of a sliding window memory map manager"
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "toml"
version = "0.10.2"
@ -750,7 +781,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
content-hash = "126762024024c25cf869fbd4a2c4e2dd5b58a9d1a198586f5071ab65a68e1f17"
content-hash = "c86a563043f2c105d46b393c93b6d10d67e35917d5dfbd0dd83daf42e62e3dcd"
[metadata.files]
aiocontextvars = [
@ -868,6 +899,14 @@ git-aggregator = [
{file = "git-aggregator-2.1.0.tar.gz", hash = "sha256:efdc4d3f360fd63ef5b14e7064ce5edb14ea404c6a4047715cfc5b9384ff49cc"},
{file = "git_aggregator-2.1.0-py3-none-any.whl", hash = "sha256:59986c0ff7a1641849504dc4d86491872d9f65b46a076aac4bf21cd550ff61df"},
]
gitdb = [
{file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
{file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
]
gitpython = [
{file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"},
{file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"},
]
idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
@ -1173,6 +1212,10 @@ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
smmap = [
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},

View File

@ -39,6 +39,7 @@ single-source = "^0.3"
git-aggregator = "^2.1"
docker = "^5.0"
pyyaml = "5.4.1"
GitPython = "^3.1"
[tool.poetry.dev-dependencies]
pytest = [

View File

@ -0,0 +1,13 @@
from . import cli_runner_invoke, move_to_test_folder
def test_cli_estimate_workload():
move_to_test_folder()
cli_runner_invoke(
[
"--log-level=DEBUG",
"estimate-workload",
]
)
# TODO, write test