diff --git a/bin/init-odoo-jupyter b/bin/init-odoo-jupyter new file mode 100755 index 00000000..0e48eb62 --- /dev/null +++ b/bin/init-odoo-jupyter @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +""" +Initialize a Jupyter Lab directory with Odoo bootstrap files. + +Creates: + - /.ipython/profile_default/startup/00-odoo.py + - /main.ipynb +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +STARTUP_TEMPLATE = r''' +import os +import atexit + +import odoo +from odoo import api +from odoo.modules.registry import Registry +from odoo.tools import config + + +db_name = os.environ["DB_NAME"] +config["db_name"] = db_name +config["db_host"] = os.environ.get("PGHOST", "localhost") +config["db_port"] = os.environ.get("PGPORT", "5432") +config["db_user"] = os.environ.get("PGUSER", "odoo") +config["db_password"] = os.environ.get("PGPASSWORD", "odoo") + +registry = Registry(db_name) +cr = registry.cursor() +uid = 2 +ctx = api.Environment(cr, uid, {})["res.users"].context_get() +env = api.Environment(cr, uid, ctx) +self = env.user + +ip = get_ipython() +ip.push({ + "odoo": odoo, + "env": env, + "cr": cr, + "uid": uid, + "self": self, + "api": api, +}) + + +@atexit.register +def _cleanup(): + try: + cr.rollback() + cr.close() + except Exception: + pass +''' + + +def create_notebook(path: Path) -> None: + if path.exists(): + print(f"Notebook already exists, skipping: {path}") + return + + notebook = { + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Odoo Notebook\n", + "\n", + "Available automatically:\n", + "\n", + "- `env`\n", + "- `cr`\n", + "- `odoo`\n", + "- `self`\n", + "- `uid`\n", + "- `api`\n", + ], + }, + { + "cell_type": "code", + "execution_count": None, + "metadata": {}, + "outputs": [], + "source": ["env.user.name"], + }, + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3", + }, + "language_info": { + "name": "python", + }, + }, + "nbformat": 4, + "nbformat_minor": 5, + } + + path.write_text(json.dumps(notebook, indent=2)) + print(f"Created notebook: {path}") + + +def create_startup_script(ipython_dir: Path) -> None: + startup_dir = ipython_dir / "profile_default" / "startup" + startup_dir.mkdir(parents=True, exist_ok=True) + + startup_file = startup_dir / "00-odoo.py" + startup_file.write_text(STARTUP_TEMPLATE) + print(f"Created startup script: {startup_file}") + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Initialize a directory for Odoo Jupyter Lab." + ) + parser.add_argument("path", help="Directory to initialize (e.g. tmp)") + args = parser.parse_args() + + notebook_dir = Path(args.path).resolve() + notebook_dir.mkdir(parents=True, exist_ok=True) + + ipython_dir = notebook_dir / ".ipython" + + print() + print("Initializing Odoo Jupyter directory") + print(f"Path: {notebook_dir}") + print() + + create_startup_script(ipython_dir) + create_notebook(notebook_dir / "main.ipynb") + + print() + print("Done. Run 'task start-odoo-jupyter [database]' to launch.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/bin/run-jupyter-notebook b/bin/run-jupyter-notebook index a3a01970..37250ef9 100755 --- a/bin/run-jupyter-notebook +++ b/bin/run-jupyter-notebook @@ -10,75 +10,6 @@ import sys from pathlib import Path -STARTUP_TEMPLATE = r''' -import os -import atexit - -import odoo -from odoo import api -from odoo.modules.registry import Registry -from odoo.tools import config - - -db_name = os.environ["DB_NAME"] - -config["db_name"] = db_name -config["db_host"] = os.environ.get("PGHOST", "localhost") -config["db_port"] = os.environ.get("PGPORT", "5432") -config["db_user"] = os.environ.get("PGUSER", "odoo") -config["db_password"] = os.environ.get("PGPASSWORD", "odoo") - - -registry = Registry(db_name) -cr = registry.cursor() - -uid = 2 - -ctx = api.Environment(cr, uid, {})["res.users"].context_get() - -env = api.Environment(cr, uid, ctx) - -self = env.user - - -ip = get_ipython() - -ip.push({ - "odoo": odoo, - "env": env, - "cr": cr, - "uid": uid, - "self": self, - "api": api, -}) - - -@atexit.register -def _cleanup(): - try: - cr.rollback() - cr.close() - except Exception: - pass - - -print("=" * 60) -print(f"Odoo ready: {db_name}") -print(f"User: {env.user.name}") -print(f"Models: {len(env.registry.models)}") -print("=" * 60) -''' - - -def create_startup_script(ipython_dir: Path) -> None: - startup_dir = ipython_dir / "profile_default" / "startup" - startup_dir.mkdir(parents=True, exist_ok=True) - - startup_file = startup_dir / "00-odoo.py" - - startup_file.write_text(STARTUP_TEMPLATE) - - def main() -> None: parser = argparse.ArgumentParser( description="Execute Jupyter notebook with papermill" @@ -113,17 +44,21 @@ def main() -> None: ipython_dir = project_root / ".ipython" - create_startup_script(ipython_dir) + startup_script = ipython_dir / "profile_default" / "startup" / "00-odoo.py" + if not startup_script.exists(): + print( + f"ERROR: Directory not initialized. Run 'task init-odoo-jupyter {project_root}' first.", + file=sys.stderr, + ) + sys.exit(1) env = os.environ.copy() env["DB_NAME"] = db_name - env.setdefault("PGHOST", "localhost") env.setdefault("PGPORT", "5432") env.setdefault("PGUSER", "odoo") env.setdefault("PGPASSWORD", "odoo") - env["IPYTHONDIR"] = str(ipython_dir) output_path = notebook_path @@ -136,9 +71,7 @@ def main() -> None: ] print() - print("=" * 60) print("Executing notebook") - print("=" * 60) print(f"Notebook : {notebook_path}") print(f"Database : {db_name}") print() @@ -162,9 +95,7 @@ def main() -> None: sys.exit(1) print() - print("=" * 60) print("Notebook executed successfully") - print("=" * 60) print(output_path) print() diff --git a/bin/start-odoo-jupyter b/bin/start-odoo-jupyter index 8a30def6..be9f5781 100755 --- a/bin/start-odoo-jupyter +++ b/bin/start-odoo-jupyter @@ -2,192 +2,64 @@ """ Start Jupyter Lab with a preloaded Odoo environment. -The notebook itself contains NO bootstrap code. -Everything is initialized in the kernel startup process. +Expects the directory to already be initialized via init-odoo-jupyter. """ from __future__ import annotations import argparse -import json import os +import secrets import subprocess from pathlib import Path -STARTUP_TEMPLATE = r''' -import os -import atexit - -import odoo -from odoo import api -from odoo.modules.registry import Registry -from odoo.tools import config - - -db_name = os.environ["DB_NAME"] - -config["db_name"] = db_name -config["db_host"] = os.environ.get("PGHOST", "localhost") -config["db_port"] = os.environ.get("PGPORT", "5432") -config["db_user"] = os.environ.get("PGUSER", "odoo") -config["db_password"] = os.environ.get("PGPASSWORD", "odoo") - - -registry = Registry(db_name) -cr = registry.cursor() - -uid = 2 - -ctx = api.Environment(cr, uid, {})["res.users"].context_get() - -env = api.Environment(cr, uid, ctx) - -self = env.user - - -ip = get_ipython() - -ip.push({ - "odoo": odoo, - "env": env, - "cr": cr, - "uid": uid, - "self": self, - "api": api, -}) - - -@atexit.register -def _cleanup(): - try: - cr.rollback() - cr.close() - except Exception: - pass - - -print("=" * 60) -print(f"Odoo ready: {db_name}") -print(f"User: {env.user.name}") -print(f"Models: {len(env.registry.models)}") -print("=" * 60) -''' - - -def create_notebook(path: Path) -> None: - """ - Create a clean notebook with zero bootstrap code. - """ - - if path.exists(): - return - - notebook = { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Odoo Notebook\n", - "\n", - "Available automatically:\n", - "\n", - "- `env`\n", - "- `cr`\n", - "- `odoo`\n", - "- `self`\n", - "- `uid`\n", - "- `api`\n", - ], - }, - { - "cell_type": "code", - "execution_count": None, - "metadata": {}, - "outputs": [], - "source": [ - "env.user.name" - ], - }, - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3", - }, - "language_info": { - "name": "python", - }, - }, - "nbformat": 4, - "nbformat_minor": 5, - } - - path.write_text(json.dumps(notebook, indent=2)) - - -def create_startup_script(ipython_dir: Path) -> None: - """ - Create IPython startup hook. - """ - - startup_dir = ipython_dir / "profile_default" / "startup" - startup_dir.mkdir(parents=True, exist_ok=True) - - startup_file = startup_dir / "00-odoo.py" - - startup_file.write_text(STARTUP_TEMPLATE) - - def main() -> None: - parser = argparse.ArgumentParser() - + parser = argparse.ArgumentParser( + description="Start Jupyter Lab with Odoo kernel bootstrap." + ) + parser.add_argument("path", help="Directory previously initialized by init-odoo-jupyter") parser.add_argument( "database", nargs="?", default=os.environ.get("DB_NAME", "odoo"), ) - + parser.add_argument( + "token", + nargs="?", + default=None, + ) args = parser.parse_args() - db_name = args.database - - script_dir = Path(__file__).resolve().parent - project_root = script_dir.parent - - notebook_dir = project_root / "tmp" - notebook_dir.mkdir(exist_ok=True) - + notebook_dir = Path(args.path).resolve() ipython_dir = notebook_dir / ".ipython" - env = os.environ.copy() + if not (notebook_dir / "main.ipynb").exists(): + print(f"Error: {notebook_dir} does not look initialized.") + print("Run 'task init-odoo-jupyter ' first.") + raise SystemExit(1) - env["DB_NAME"] = db_name + token = args.token or secrets.token_hex(16) + + env = os.environ.copy() + env["DB_NAME"] = args.database env.setdefault("PGHOST", "localhost") env.setdefault("PGPORT", "5432") env.setdefault("PGUSER", "odoo") env.setdefault("PGPASSWORD", "odoo") - env["IPYTHONDIR"] = str(ipython_dir) - create_startup_script(ipython_dir) - - create_notebook(notebook_dir / "main.ipynb") - print() - print("=" * 60) print("Starting Jupyter Lab with Odoo") - print("=" * 60) - print(f"Database : {db_name}") + print(f"Database : {args.database}") print(f"Notebook : {notebook_dir}") print() cmd = [ "jupyter-lab", f"--ServerApp.root_dir={notebook_dir}", - "--ServerApp.open_browser=True", + f"--ServerApp.token={token}", + "--ServerApp.open_browser=False", "--ServerApp.port=8888", ] diff --git a/requirements.txt b/requirements.txt index 24fe8e06..5b626855 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,5 +45,4 @@ rl-renderPM # Jupyter Notebook jupyter -ipykernel papermill \ No newline at end of file diff --git a/snippets/mint_system.helpdesk.helpdesk_tickets_view_tree.sort_create_date.xml b/snippets/mint_system.helpdesk.helpdesk_tickets_view_tree.sort_create_date.xml new file mode 100644 index 00000000..3ff0eca0 --- /dev/null +++ b/snippets/mint_system.helpdesk.helpdesk_tickets_view_tree.sort_create_date.xml @@ -0,0 +1,5 @@ + + + create_date asc, priority desc, sla_deadline, id + + diff --git a/task b/task index 53e4ab12..1f5d8e90 100755 --- a/task +++ b/task @@ -156,7 +156,6 @@ help-table() { rows+=("psql|[db]|Start interactive psql shell.") rows+=("restart|[name]|Restart container.") rows+=("run|[name][cmd]|Run container with command.") - rows+=("run-jupyter-notebook|[path][db]|Run juypter notebook with papermill.") rows+=("shell|[db][code]|Start interactive odoo shell or run code.") rows+=("source||Source the Python virtual env.") rows+=("start|[name][db]|Start service. Use list to show services.") @@ -184,7 +183,11 @@ help-table() { rows+=("prepare-migration-brach|[path][module][version]|Create migration branch for a module.") rows+=("migrate-module|[path]|Migrate module code from to target Odoo version.") rows+=("upgrade-odoo|[env][step]|Run the Odoo upgrade for a target environment.") - + rows+=("===JUPYTER===| | ") + rows+=("init-odoo-jupyter|[path]|Init jypyter lab directory.") + rows+=("start-odoo-jupyter|[path][db]|Start juypter lab in directory.") + rows+=("run-jupyter-notebook|[path][db]|Run juypter notebook with papermill.") + for row in "${rows[@]}"; do IFS='|' read -r cmd opt desc <<< "$row" (( ${#cmd} > max_cmd )) && max_cmd=${#cmd} @@ -724,9 +727,6 @@ build-odoo() { local python_version="3.12" ;; "19.0") - local python_version="3.13" - ;; - *) local python_version="3.12" ;; esac @@ -1285,11 +1285,6 @@ start() { echo 'Open http://localhost:8080 url in your browser.' open-url-with-delay "http://localhost:8080" 2 & $COMPOSE_COMMAND up -d odoo fi - - if [[ "$1" =~ "jupyter" ]]; then - # jupyter lab - ./bin/start-odoo-jupyter "$database" - fi } debug() { @@ -1334,6 +1329,27 @@ run() { $COMPOSE_COMMAND run --rm "$1" "$2" "$3" } +init-odoo-jupyter() { + if [[ -z "$1" ]]; then + echo '$1/path is empty.' + exit + fi + ./bin/init-odoo-jupyter "$1" +} + +start-odoo-jupyter() { + if [[ -z "$1" ]]; then + echo '$1/path is empty.' + exit + fi + local database="$2" + if [[ -z "$database" ]]; then + database="$DB_NAME" + fi + local token="odoo" + ./bin/start-odoo-jupyter "$1" "$database" "$token" +} + run-jupyter-notebook() { if [[ -z "$1" ]]; then echo '$1/path is empty.' diff --git a/versions/19.0/.gitmodules b/versions/19.0/.gitmodules index 980ede58..bb480fc6 100644 --- a/versions/19.0/.gitmodules +++ b/versions/19.0/.gitmodules @@ -3,28 +3,28 @@ url = git@github.com:odoo/odoo.git branch = 19.0 shallow = true - commit = 82124886fdb3485ef9dc089e5c33b7c803030d5e + commit = 32301e8acba31140408bead9677c466baf0e0a45 [submodule "enterprise"] path = enterprise url = git@github.com:odoo/enterprise.git branch = 19.0 shallow = true - commit = 64f813dab727d76286c1ff6c80c08cb6a6737b49 + commit = af3e13b2881c7647aed022f29a187149c95bb255 [submodule "themes"] path = themes url = git@github.com:odoo/design-themes.git branch = 19.0 - commit = 7599a4bd2862e3fcd672de78397d05bcb135df70 + commit = 6e142b7abc66777bb9f6f5d124609b97ac93e7a5 [submodule "addons/server_tools"] path = addons/server_tools url = git@github.com:Mint-System/Odoo-Apps-Server-Tools.git branch = 19.0 - commit = 41841bf3c5508390676ababdf0cc7df8951ba589 + commit = 53e65405dd605804b75ca3a3dfc40ef4970718c1 [submodule "oca/partner-contact"] path = oca/partner-contact url = git@github.com:OCA/partner-contact.git branch = 19.0 - commit = b6b6652548fb79c95c4f08d725a0e83356b34c99 + commit = b54008efb460beec76eb42347238f1618f6df32a [submodule "addons/management_system"] path = addons/management_system url = git@github.com:Mint-System/Odoo-Apps-Management-System.git @@ -34,7 +34,7 @@ path = addons/sale_workflow url = git@github.com:Mint-System/Odoo-Apps-Sale-Workflow.git branch = 19.0 - commit = e4796bc9cd84628aac3616ba3e146a4d418a64be + commit = 2baad67347f539fa9833be1f1ec2be94ccf603b0 [submodule "addons/account_invoicing"] path = addons/account_invoicing url = git@github.com:Mint-System/Odoo-Apps-Account-Invoicing.git @@ -44,22 +44,22 @@ path = addons/purchase_workflow url = git@github.com:Mint-System/Odoo-Apps-Purchase-Workflow.git branch = 19.0 - commit = 7bd3d7a02f3fadf71ae58ad34f2523188d2cf3f8 + commit = 8b4f075f6b0c606e05bf5bd3e9fbb7e560d50ba5 [submodule "addons/stock_logistics_workflow"] path = addons/stock_logistics_workflow url = git@github.com:Mint-System/Odoo-Apps-Stock-Logistics-Workflow.git branch = 19.0 - commit = b250302f012da0ca116855e7ba641c6eb75578ad + commit = 6587b4a8e411ff350de822dc2a5e17484121214d [submodule "oca/sale-workflow"] path = oca/sale-workflow url = git@github.com:OCA/sale-workflow branch = 19.0 - commit = 87113dc142dab74f34d242904c739a14cfa05b3c + commit = d2121f9419e7a1b87382ca06b278b4b0cf4befde [submodule "oca/server-tools"] path = oca/server-tools url = git@github.com:OCA/server-tools.git branch = 19.0 - commit = 793dd13b1fc60a43b091b7e156aea95c8bc2011d + commit = 3f56112d5d507ba6e6cff73a52309e4368008f66 [submodule "addons/spreadsheet"] path = addons/spreadsheet url = git@github.com:Mint-System/Odoo-Apps-Spreadsheet.git @@ -69,17 +69,17 @@ path = addons/product_attribute url = git@github.com:Mint-System/Odoo-Apps-Product-Attribute.git branch = 19.0 - commit = 4cd5253da71c66d757ed292aa7fd18e64eef8375 + commit = 225d3bdffe175a969fa61dbcf4652a0f7c32cfc8 [submodule "oca/mass-mailing"] path = oca/mass-mailing url = git@github.com:OCA/mass-mailing.git branch = 19.0 - commit = 4baf365254fd80be51e99dd446de2d27e601b981 + commit = 9d6b46cf3d7d0bae5b2b338d351b1d611a55d5c2 [submodule "oca/account-analytic"] path = oca/account-analytic url = git@github.com:OCA/account-analytic.git branch = 19.0 - commit = dc6afca735a01ed0c616339969a03fba9cef6102 + commit = 4f78732271e81962c858f333e74b9b6690e6277c [submodule "addons/website"] path = addons/website url = git@github.com:Mint-System/Odoo-Apps-Website @@ -89,4 +89,4 @@ path = oca/hr-attendance url = git@github.com:OCA/hr-attendance.git branch = 19.0 - commit = 729040b76e01f43dda5b3f171b581e5d87b6f590 + commit = 6af354e9e0f21ebec6c9c052c2789578d33d206a diff --git a/versions/19.0/.python-version b/versions/19.0/.python-version index 24ee5b1b..e4fba218 100644 --- a/versions/19.0/.python-version +++ b/versions/19.0/.python-version @@ -1 +1 @@ -3.13 +3.12