API reference¶
Generata automaticamente dai docstring tramite mkdocstrings.
Configurazione¶
odoo_analysis.config
¶
Configuration loading and validation for Odoo instances.
This module is the single source of truth for how instances.json (versioned)
and .env (NOT versioned) are parsed and combined into a list of typed
InstanceConfig objects ready to be consumed by ConnectionManager.
Responsibilities
- Read and JSON-parse
instances.json. - Validate the schema with pydantic, including version-specific rules
(see
_validate_auth_fields). - Resolve secrets from environment variables (
password_env,api_key_env) after loading.envviapython-dotenv. - Detect duplicate instance names.
Out of scope
- Performing any RPC call (that lives in
client.py). - Caching or session management (that lives in
manager.py).
InstanceConfig
¶
Bases: BaseModel
Validated configuration for a single Odoo instance.
Combines metadata from instances.json with secrets resolved from the
environment. Secrets are wrapped in pydantic.SecretStr so they do not
leak into logs or repr() output.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Unique identifier of the instance (used as CLI |
url |
str
|
Full URL of the Odoo instance, including scheme. |
database |
str
|
Name of the Odoo database. |
version |
Literal[16, 17, 18, 19]
|
Supported Odoo major version (16, 17, 18 or 19). |
username |
str | None
|
Odoo user login. Required for v16/17, optional for v18/19. |
password_env |
str | None
|
Env var name holding the password (NOT the password itself). Required for v16/17, optional for v18/19. |
api_key_env |
str | None
|
Env var name holding the API key. Allowed only for v18/19. |
password |
SecretStr | None
|
Resolved password (set by |
api_key |
SecretStr | None
|
Resolved API key (set by |
Source code in src/odoo_analysis/config.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
validate_auth_config()
¶
Ensure auth fields are coherent with the declared Odoo version.
Source code in src/odoo_analysis/config.py
75 76 77 78 79 | |
load_instances(json_path=DEFAULT_INSTANCES_PATH, env_path=DEFAULT_ENV_PATH)
¶
Load and validate Odoo instance configurations.
Reads instances.json, loads .env (if present), validates the schema
with pydantic, resolves secret env vars, and returns a list of
InstanceConfig. Existing environment variables already set in the
process are preserved (override=False).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_path
|
Path
|
Path to |
DEFAULT_INSTANCES_PATH
|
env_path
|
Path
|
Path to the |
DEFAULT_ENV_PATH
|
Returns:
| Type | Description |
|---|---|
list[InstanceConfig]
|
One |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If the file is missing, malformed, the schema is invalid,
an env var referenced by |
Example
from odoo_analysis.config import load_instances
instances = load_instances()
names = [i.name for i in instances]
Source code in src/odoo_analysis/config.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
Client RPC¶
odoo_analysis.client
¶
Low-level wrapper around odooly.Client with version-aware authentication.
OdooClient is the only place in the package that talks to odooly directly.
Higher-level callers (CLI, export scripts, notebooks) MUST go through
ConnectionManager (see manager.py) or instantiate this wrapper, never
odooly.Client directly: this guarantees consistent error handling
(via AuthenticationError) and correct authentication for each Odoo version.
Authentication strategy
- v16/17: classic username + password.
- v18: API key passed as the
passwordfield (legacy auth path). - v19: API key passed as a Bearer token via
user="__api_key__".
Out of scope
- Loading or validating configuration (see
config.py). - Multi-instance orchestration (see
manager.py). - Any domain-specific data extraction (see
tasks.py,excel.py).
OdooClient
¶
Wrapper around odooly.Client with API Key or password authentication.
Lazily connects on connect() (or via the context manager). After a
successful connection, the underlying odooly.Client is reachable via
the client property.
Attributes:
| Name | Type | Description |
|---|---|---|
config |
The |
Example
from odoo_analysis import ConnectionManager manager = ConnectionManager() with manager.get("prod") as oc: ... partners = oc.client.env["res.partner"].search_read([], ["name"])
Source code in src/odoo_analysis/client.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
client
property
¶
Return the underlying odooly.Client.
Returns:
| Type | Description |
|---|---|
Client
|
The connected |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
__enter__()
¶
Context manager entry: connect and return self.
Source code in src/odoo_analysis/client.py
159 160 161 162 | |
__exit__(exc_type, exc, tb)
¶
Context manager exit: drop the session reference.
Source code in src/odoo_analysis/client.py
164 165 166 167 168 169 170 171 | |
__init__(config)
¶
Initialize the client (does NOT open the connection).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
InstanceConfig
|
Validated configuration produced by |
required |
Source code in src/odoo_analysis/client.py
50 51 52 53 54 55 56 57 | |
close()
¶
Drop the cached odooly.Client reference.
odooly does not expose an explicit close primitive; this method
simply forgets the session so a subsequent connect() reconnects
from scratch.
Source code in src/odoo_analysis/client.py
150 151 152 153 154 155 156 157 | |
connect()
¶
Open the RPC session if not already open.
Idempotent: calling connect() twice returns the same client.
Returns:
| Type | Description |
|---|---|
Client
|
The connected |
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
If the connection or login is rejected. |
Source code in src/odoo_analysis/client.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
health_check()
¶
Connect and return a summary of the live session.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with keys |
dict[str, Any]
|
|
dict[str, Any]
|
command to render one OK line per instance. |
Raises:
| Type | Description |
|---|---|
AuthenticationError
|
If the connection or login fails. |
Source code in src/odoo_analysis/client.py
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
Connection manager¶
odoo_analysis.manager
¶
Multi-instance orchestration on top of OdooClient.
ConnectionManager is the recommended entry point for any code that needs to
talk to one or more Odoo instances. It:
- Loads `instances.json` + `.env` once at construction time.
- Lazily creates a single `OdooClient` per instance name (the same name
always returns the same client, so caching at the call site is rarely
needed).
- Iterates over all configured instances for batch operations
(e.g. `odoo-analysis check`).
Reuse rule
Higher-level scripts (CLI commands, notebooks, batch jobs) MUST use
ConnectionManager rather than constructing OdooClient directly: this
keeps configuration, error handling and lifecycle consistent across the
codebase.
ConnectionManager
¶
Manage multiple Odoo instances defined in instances.json.
The manager is cheap to construct (it only parses configuration), but
each call to get(name) returns the same OdooClient for the lifetime
of the manager — connections are opened lazily inside OdooClient.
Example
manager = ConnectionManager() for client in manager.iter_clients(): ... print(client.health_check()["name"]) manager.close_all()
Source code in src/odoo_analysis/manager.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
names
property
¶
List of configured instance names, in declaration order.
__init__(json_path=DEFAULT_INSTANCES_PATH, env_path=DEFAULT_ENV_PATH)
¶
Load configuration and prepare the per-instance client cache.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_path
|
Path
|
Path to |
DEFAULT_INSTANCES_PATH
|
env_path
|
Path
|
Path to |
DEFAULT_ENV_PATH
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If configuration loading fails (see |
Source code in src/odoo_analysis/manager.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
close_all()
¶
Close every cached client and drop the cache.
Subsequent get(name) calls will produce fresh OdooClient instances.
Source code in src/odoo_analysis/manager.py
102 103 104 105 106 107 108 109 | |
get(name)
¶
Return the OdooClient for the given instance name (cached).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Instance name as declared in |
required |
Returns:
| Type | Description |
|---|---|
OdooClient
|
The |
OdooClient
|
the same client object. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If |
Source code in src/odoo_analysis/manager.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
iter_clients()
¶
Yield one OdooClient per configured instance, in declaration order.
Source code in src/odoo_analysis/manager.py
97 98 99 100 | |
Domain helpers — Task¶
odoo_analysis.tasks
¶
Domain helpers to extract project.task records from Odoo.
This module hosts the canonical query used to retrieve tasks across the rest
of the package (CLI tasks and tasks-excel commands, plus any future
export). New consumers SHOULD import fetch_tasks from here instead of
re-implementing the domain or the field list.
Out of scope
- Connection management (see
manager.py). - File rendering (see
excel.py).
fetch_tasks(client, *, assignee=None, open_only=False)
¶
Retrieve project.task records from a connected Odoo client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
Client
|
A connected |
required |
assignee
|
str | None
|
Optional login (email) used to filter tasks by assignee. When None, all assignees are returned. |
None
|
open_only
|
bool
|
If True, exclude tasks whose stage is folded in the kanban
view ( |
False
|
Returns:
| Type | Description |
|---|---|
list[dict]
|
A list of dicts containing the fields listed in |
list[dict]
|
in the order returned by Odoo's |
Example
tasks = fetch_tasks(client, assignee="alice@example.com", open_only=True)
names = [t["name"] for t in tasks]
Source code in src/odoo_analysis/tasks.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
Excel export¶
odoo_analysis.excel
¶
Excel rendering for open project.task records.
This module is the single place where openpyxl is used. It depends on
fetch_tasks for the domain query and is consumed by the CLI command
tasks-excel. New Excel exports for other Odoo models SHOULD live in this
module (or a sibling) and reuse the same styling helpers.
Out of scope
- Connection management (see
manager.py). - Domain/query logic (see
tasks.py).
export_tasks_to_excel(client, instance_name, *, output_dir=None, filename=None, assignee=None)
¶
Export open tasks to an .xlsx file.
Fetches open tasks via fetch_tasks(open_only=True), resolves assignee
names and the latest chatter activity, and writes a styled spreadsheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
Client
|
Connected Odoo client. |
required |
instance_name
|
str
|
Instance name, used in the default filename. |
required |
output_dir
|
Path | None
|
Destination directory. Defaults to the current working directory. |
None
|
filename
|
str | None
|
Output filename. When None, defaults to
|
None
|
assignee
|
str | None
|
Optional login (email) to filter tasks by assignee. |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
The path to the saved |
Example
from odoo_analysis import ConnectionManager, export_tasks_to_excel manager = ConnectionManager() client = manager.get("prod").connect() path = export_tasks_to_excel(client, "prod")
Source code in src/odoo_analysis/excel.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
CLI¶
odoo_analysis.cli
¶
odoo-analysis command-line interface.
Defines the argparse subcommands exposed by the odoo-analysis script
(registered in pyproject.toml [project.scripts]):
- `check`: verify connectivity and authentication for one or all instances.
- `tasks`: print open/all tasks as JSON.
- `tasks-excel`: export open tasks to an `.xlsx` file.
- `project-task-phases`: print task stages per project.
- `project-task-phases-matrix`: export a project × stage matrix to Excel.
Convention
Every subcommand uses ConnectionManager to obtain its OdooClient,
catches OdooAnalysisError to render [ERROR] lines on stderr, and
closes the manager in a finally block. Add new commands following the
same pattern (_cmd_* function + add_parser block in _build_parser).
main(argv=None)
¶
Entry point for the odoo-analysis script.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
argv
|
Sequence[str] | None
|
Optional argument vector (excluding the program name). When None,
argparse reads from |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The exit code returned by the matched subcommand, or 1 on a top-level |
int
|
|
Source code in src/odoo_analysis/cli.py
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | |
Eccezioni¶
odoo_analysis.exceptions
¶
Exception hierarchy for the odoo_analysis package.
All exceptions raised intentionally by the package derive from
OdooAnalysisError, so callers can catch the whole hierarchy with a single
except OdooAnalysisError: clause and degrade gracefully.
Reuse rule
New code in this package MUST raise one of these exceptions (or a subclass
of OdooAnalysisError) instead of generic Exception / ValueError,
so the CLI error-handling layer in cli.py stays consistent.
AuthenticationError
¶
Bases: OdooAnalysisError
Raised when login to an Odoo instance fails.
Typical causes
- Wrong credentials (password or API key).
- Unreachable host or wrong database name.
odooly.Clientcould not establish the session.
Source code in src/odoo_analysis/exceptions.py
40 41 42 43 44 45 46 47 | |
ConfigError
¶
Bases: OdooAnalysisError
Raised when configuration loading or validation fails.
Typical causes
instances.jsonmissing or malformed.- Pydantic schema validation failure (unsupported version, missing required field for the chosen Odoo version).
- Required environment variable referenced by
password_env/api_key_envis missing or empty. - Duplicate instance names in
instances.json.
Source code in src/odoo_analysis/exceptions.py
27 28 29 30 31 32 33 34 35 36 37 | |
OdooAnalysisError
¶
Bases: Exception
Base error for the odoo_analysis package.
Catch this to handle any failure produced intentionally by the package.
Source code in src/odoo_analysis/exceptions.py
20 21 22 23 24 | |