diff --git a/.copier-answers.yml b/.copier-answers.yml index d4b6ba0151..99080f40d4 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,8 +1,7 @@ # Do NOT update manually; changes here will be overwritten by Copier -_commit: v1.29 +_commit: v1.44 _src_path: git+https://github.com/OCA/oca-addons-repo-template additional_ruff_rules: [] -ci: GitHub convert_readme_fragments_to_markdown: true enable_checklog_odoo: true generate_requirements_txt: true @@ -17,6 +16,7 @@ odoo_test_flavor: Both odoo_version: 18.0 org_name: Odoo Community Association (OCA) org_slug: OCA +postgres_image: '' rebel_module_groups: [] repo_description: queue repo_name: queue diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..e0d56685a9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +test-requirements.txt merge=union diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 5eb021ef15..408069d32d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,3 +1,4 @@ + name: pre-commit on: @@ -9,6 +10,10 @@ on: - "18.0" - "18.0-ocabot-*" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: pre-commit: runs-on: ubuntu-22.04 @@ -16,7 +21,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.12" + cache: 'pip' + cache-dependency-path: '.pre-commit-config.yaml' - name: Get python version run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - uses: actions/cache@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a06488079f..cd982c1e44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,10 @@ on: - "18.0" - "18.0-ocabot-*" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: unreleased-deps: runs-on: ubuntu-latest @@ -42,7 +46,7 @@ jobs: makepot: "true" services: postgres: - image: postgres:12.0 + image: postgres:12 env: POSTGRES_USER: odoo POSTGRES_PASSWORD: odoo @@ -65,6 +69,13 @@ jobs: run: oca_init_test_database - name: Run tests run: oca_run_tests + - name: Upload screenshots from JS tests + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: Screenshots of failed JS tests - ${{ matrix.name }}${{ join(matrix.include) }} + path: /tmp/odoo_tests/${{ env.PGDATABASE }} + if-no-files-found: ignore - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c74099edcf..29c15eaf6a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ exclude: | # You don't usually want a bot to modify your legal texts (LICENSE.*|COPYING.*) default_language_version: - python: python3 + python: python3.12 node: "22.9.0" repos: - repo: local @@ -38,12 +38,17 @@ repos: entry: found a en.po file language: fail files: '[a-zA-Z0-9_]*/i18n/en\.po$' + - id: obsolete dotfiles + name: obsolete dotfiles + entry: found obsolete files; remove them + files: '^(\.travis\.yml|\.t2d\.yml|CONTRIBUTING\.md|\.prettierrc\.yml|\.eslintrc\.yml)$' + language: fail - repo: https://github.com/sbidoul/whool - rev: v1.2 + rev: v1.3 hooks: - id: whool-init - repo: https://github.com/oca/maintainer-tools - rev: bf9ecb9938b6a5deca0ff3d870fbd3f33341fded + rev: 31bdbd8e4cb94be6534f0e82b1cafb84a455f671 hooks: # update the NOT INSTALLABLE ADDONS section above - id: oca-update-pre-commit-excluded-addons @@ -95,6 +100,7 @@ repos: additional_dependencies: - "eslint@9.12.0" - "eslint-plugin-jsdoc@50.3.1" + - "globals@16.0.0" - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: diff --git a/.pylintrc b/.pylintrc index 7c62b6d2ed..914fd235d9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -4,6 +4,9 @@ load-plugins=pylint_odoo score=n +[IMPORTS] +deprecated-modules=pdb,pudb,ipdb,bs4 + [ODOOLINT] readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" manifest-required-authors=Odoo Community Association (OCA) @@ -25,19 +28,26 @@ disable=all enable=anomalous-backslash-in-string, api-one-deprecated, api-one-multi-together, - assignment-from-none, - attribute-deprecated, class-camelcase, - dangerous-default-value, dangerous-view-replace-wo-priority, - development-status-allowed, duplicate-id-csv, - duplicate-key, duplicate-xml-fields, duplicate-xml-record-id, eval-referenced, - eval-used, incoherent-interpreter-exec-perm, + openerp-exception-warning, + redundant-modulename-xml, + relative-import, + rst-syntax-error, + wrong-tabs-instead-of-spaces, + xml-syntax-error, + assignment-from-none, + attribute-deprecated, + dangerous-default-value, + deprecated-module, + development-status-allowed, + duplicate-key, + eval-used, license-allowed, manifest-author-string, manifest-deprecated-key, @@ -48,73 +58,68 @@ enable=anomalous-backslash-in-string, method-inverse, method-required-super, method-search, - openerp-exception-warning, pointless-statement, pointless-string-statement, print-used, redundant-keyword-arg, - redundant-modulename-xml, reimported, - relative-import, return-in-init, - rst-syntax-error, sql-injection, too-few-format-args, translation-field, translation-required, unreachable, use-vim-comment, - wrong-tabs-instead-of-spaces, - xml-syntax-error, - attribute-string-redundant, character-not-valid-in-resource-link, - consider-merging-classes-inherited, - context-overridden, create-user-wo-reset-password, dangerous-filter-wo-user, dangerous-qweb-replace-wo-priority, deprecated-data-xml-node, deprecated-openerp-xml-node, duplicate-po-message-definition, - except-pass, file-not-used, + missing-newline-extrafiles, + old-api7-method-defined, + po-msgstr-variables, + po-syntax-error, + str-format-used, + unnecessary-utf8-coding-comment, + xml-attribute-translatable, + xml-deprecated-qweb-directive, + xml-deprecated-tree-attribute, + attribute-string-redundant, + consider-merging-classes-inherited, + context-overridden, + except-pass, invalid-commit, manifest-maintainers-list, - missing-newline-extrafiles, missing-readme, missing-return, odoo-addons-relative-import, - old-api7-method-defined, - po-msgstr-variables, - po-syntax-error, renamed-field-parameter, resource-not-exist, - str-format-used, test-folder-imported, translation-contains-variable, translation-positional-used, - unnecessary-utf8-coding-comment, website-manifest-key-not-valid-uri, - xml-attribute-translatable, - xml-deprecated-qweb-directive, - xml-deprecated-tree-attribute, external-request-timeout, - # messages that do not cause the lint step to fail - consider-merging-classes-inherited, + missing-manifest-dependency, + too-complex, create-user-wo-reset-password, dangerous-filter-wo-user, - deprecated-module, file-not-used, - invalid-commit, - missing-manifest-dependency, missing-newline-extrafiles, - missing-readme, no-utf8-coding-comment, - odoo-addons-relative-import, old-api7-method-defined, + unnecessary-utf8-coding-comment, + # messages that do not cause the lint step to fail + consider-merging-classes-inherited, + deprecated-module, + invalid-commit, + missing-readme, + odoo-addons-relative-import, redefined-builtin, - too-complex, - unnecessary-utf8-coding-comment + manifest-external-assets [REPORTS] diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory index 018fd61cdd..e4fd970b0d 100644 --- a/.pylintrc-mandatory +++ b/.pylintrc-mandatory @@ -3,6 +3,9 @@ load-plugins=pylint_odoo score=n +[IMPORTS] +deprecated-modules=pdb,pudb,ipdb,bs4 + [ODOOLINT] readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" manifest-required-authors=Odoo Community Association (OCA) @@ -17,19 +20,26 @@ disable=all enable=anomalous-backslash-in-string, api-one-deprecated, api-one-multi-together, - assignment-from-none, - attribute-deprecated, class-camelcase, - dangerous-default-value, dangerous-view-replace-wo-priority, - development-status-allowed, duplicate-id-csv, - duplicate-key, duplicate-xml-fields, duplicate-xml-record-id, eval-referenced, - eval-used, incoherent-interpreter-exec-perm, + openerp-exception-warning, + redundant-modulename-xml, + relative-import, + rst-syntax-error, + wrong-tabs-instead-of-spaces, + xml-syntax-error, + assignment-from-none, + attribute-deprecated, + dangerous-default-value, + deprecated-module, + development-status-allowed, + duplicate-key, + eval-used, license-allowed, manifest-author-string, manifest-deprecated-key, @@ -40,56 +50,50 @@ enable=anomalous-backslash-in-string, method-inverse, method-required-super, method-search, - openerp-exception-warning, pointless-statement, pointless-string-statement, print-used, redundant-keyword-arg, - redundant-modulename-xml, reimported, - relative-import, return-in-init, - rst-syntax-error, sql-injection, too-few-format-args, translation-field, translation-required, unreachable, use-vim-comment, - wrong-tabs-instead-of-spaces, - xml-syntax-error, - attribute-string-redundant, character-not-valid-in-resource-link, - consider-merging-classes-inherited, - context-overridden, create-user-wo-reset-password, dangerous-filter-wo-user, dangerous-qweb-replace-wo-priority, deprecated-data-xml-node, deprecated-openerp-xml-node, duplicate-po-message-definition, - except-pass, file-not-used, + missing-newline-extrafiles, + old-api7-method-defined, + po-msgstr-variables, + po-syntax-error, + str-format-used, + unnecessary-utf8-coding-comment, + xml-attribute-translatable, + xml-deprecated-qweb-directive, + xml-deprecated-tree-attribute, + attribute-string-redundant, + consider-merging-classes-inherited, + context-overridden, + except-pass, invalid-commit, manifest-maintainers-list, - missing-newline-extrafiles, missing-readme, missing-return, odoo-addons-relative-import, - old-api7-method-defined, - po-msgstr-variables, - po-syntax-error, renamed-field-parameter, resource-not-exist, - str-format-used, test-folder-imported, translation-contains-variable, translation-positional-used, - unnecessary-utf8-coding-comment, website-manifest-key-not-valid-uri, - xml-attribute-translatable, - xml-deprecated-qweb-directive, - xml-deprecated-tree-attribute, external-request-timeout [REPORTS] diff --git a/README.md b/README.md index fb36f7eddb..3d44e9cde8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ +[![Support the OCA](https://odoo-community.org/readme-banner-image)](https://odoo-community.org/get-involved?utm_source=repo-readme) + +# queue [![Runboat](https://img.shields.io/badge/runboat-Try%20me-875A7B.png)](https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0) [![Pre-commit Status](https://github.com/OCA/queue/actions/workflows/pre-commit.yml/badge.svg?branch=18.0)](https://github.com/OCA/queue/actions/workflows/pre-commit.yml?query=branch%3A18.0) [![Build Status](https://github.com/OCA/queue/actions/workflows/test.yml/badge.svg?branch=18.0)](https://github.com/OCA/queue/actions/workflows/test.yml?query=branch%3A18.0) @@ -7,8 +10,6 @@ -# queue - queue @@ -22,10 +23,11 @@ Available addons addon | version | maintainers | summary --- | --- | --- | --- [base_import_async](base_import_async/) | 18.0.1.0.0 | | Import CSV files in the background -[queue_job](queue_job/) | 18.0.3.1.1 | guewen sbidoul | Job Queue -[queue_job_batch](queue_job_batch/) | 18.0.1.0.0 | | Job Queue Batch +[queue_job](queue_job/) | 18.0.3.1.3 | guewen sbidoul | Job Queue +[queue_job_batch](queue_job_batch/) | 18.0.1.1.1 | | Job Queue Batch [queue_job_cron](queue_job_cron/) | 18.0.1.1.1 | | Scheduled Actions as Queue Jobs [queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 18.0.1.0.1 | ivantodorovich | Run jobs without a dedicated JobRunner +[queue_job_profiler](queue_job_profiler/) | 18.0.1.0.1 | simahawk | Job Queue Profiler [queue_job_subscribe](queue_job_subscribe/) | 18.0.1.0.0 | | Control which users are subscribed to queue job notifications [test_queue_job](test_queue_job/) | 18.0.2.0.5 | sbidoul | Queue Job Tests [test_queue_job_batch](test_queue_job_batch/) | 18.0.1.0.0 | | Test Job Queue Batch diff --git a/checklog-odoo.cfg b/checklog-odoo.cfg index 0b55b7bf66..58d43aa666 100644 --- a/checklog-odoo.cfg +++ b/checklog-odoo.cfg @@ -1,3 +1,5 @@ [checklog-odoo] ignore= WARNING.* 0 failed, 0 error\(s\).* + WARNING .* Killing chrome descendants-or-self .* + WARNING.* Missing widget: res_partner_many2one for field of type many2one.* diff --git a/eslint.config.cjs b/eslint.config.cjs index 0d5731f89a..dd0cbe0aef 100644 --- a/eslint.config.cjs +++ b/eslint.config.cjs @@ -1,3 +1,4 @@ +var globals = require('globals'); jsdoc = require("eslint-plugin-jsdoc"); const config = [{ @@ -16,6 +17,8 @@ const config = [{ openerp: "readonly", owl: "readonly", luxon: "readonly", + QUnit: "readonly", + ...globals.browser, }, ecmaVersion: 2024, @@ -191,7 +194,7 @@ const config = [{ }, }, { - files: ["**/*.esm.js"], + files: ["**/*.esm.js", "**/*test.js"], languageOptions: { ecmaVersion: 2024, diff --git a/queue_job/README.rst b/queue_job/README.rst index cfe02b7d37..b27a1675ad 100644 --- a/queue_job/README.rst +++ b/queue_job/README.rst @@ -11,7 +11,7 @@ Job Queue !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:a5cdcff42c2f45fbb57753e7693373b08e3497d0bc07212dac0f3ab56217eaf2 + !! source digest: sha256:85d54819989d21ee1d0349ecbcc7eeafe118baa9521524b1ce50fc3fdb890050 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py index 9463b8cc07..fea6a34476 100644 --- a/queue_job/__manifest__.py +++ b/queue_job/__manifest__.py @@ -2,7 +2,7 @@ { "name": "Job Queue", - "version": "18.0.3.1.1", + "version": "18.0.3.1.3", "author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/queue", "license": "LGPL-3", diff --git a/queue_job/i18n/fr.po b/queue_job/i18n/fr.po new file mode 100644 index 0000000000..ce1a944129 --- /dev/null +++ b/queue_job/i18n/fr.po @@ -0,0 +1,949 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * queue_job +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-06-11 14:32+0000\n" +"Last-Translator: Yann Papouin \n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.15.2\n" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "" +"
\n" +" If the max. retries is 0, the number of retries is infinite." +msgstr "" +"
\n" +" Si le max de " +"tentatives est à 0, le nombre de tentatives sera illimité." + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/controllers/main.py:0 +msgid "Access Denied" +msgstr "Accès refusé" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction +msgid "Action Needed" +msgstr "Nécessite une action" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids +msgid "Activities" +msgstr "Activités" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "Décoration d’activité en exception" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state +msgid "Activity State" +msgstr "Statut de l'activité" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon +msgid "Activity Type Icon" +msgstr "Icône de type d’activité" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__allow_commit +msgid "Allow Commit" +msgstr "Autoriser « commit »" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job_function__allow_commit +msgid "" +"Allows the job to commit transactions during execution. Under the hood, this" +" executes the job in a new database cursor, which incurs an overhead as it " +"requires an extra connection to the database. " +msgstr "" +"Permet à la tâche de valider les transactions (commit) pendant son " +"exécution. En arrière-plan, cela exécute la tâche dans un nouveau curseur de " +"base de données, ce qui entraîne une surcharge, car cela nécessite une " +"connexion supplémentaire à la base de données. " + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__args +msgid "Args" +msgstr "Args" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count +msgid "Attachment Count" +msgstr "Nombre de pièces jointes" + +#. module: queue_job +#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server +msgid "AutoVacuum Job Queue" +msgstr "Vidange automatique de la file d'attente des tâches" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_base +msgid "Base" +msgstr "Base" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done +msgid "Cancel" +msgstr "Annuler" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled +msgid "Cancel all selected jobs" +msgstr "Annuler toutes les tâches sélectionnées" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Cancel job" +msgstr "Annuler la tâche" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_set_jobs_cancelled +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled +msgid "Cancel jobs" +msgstr "Annuler les tâches" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__cancelled +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Cancelled" +msgstr "Annulé" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Cancelled by {}" +msgstr "Annulé par {}" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_channel.py:0 +msgid "Cannot change the root channel" +msgstr "Impossible de changer le canal racine" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_channel.py:0 +msgid "Cannot remove the root channel" +msgstr "Impossible de supprimer le canal racine" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__channel_id +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_function_search +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Channel" +msgstr "Canal" + +#. module: queue_job +#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq +msgid "Channel complete name must be unique" +msgstr "Le nom complet du canal doit être unique" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_queue_job_channel +#: model:ir.ui.menu,name:queue_job.menu_queue_job_channel +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_channel_form +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_channel_search +msgid "Channels" +msgstr "Canaux" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id +msgid "Company" +msgstr "Société" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name +msgid "Complete Method Name" +msgstr "Nom complet de la méthode" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__complete_name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__channel +msgid "Complete Name" +msgstr "Nom complet" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created +msgid "Created Date" +msgstr "Date de création" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Created date" +msgstr "Date de création" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry +msgid "Current try" +msgstr "Tentative courante" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Current try / max. retries" +msgstr "Tentative courante / tentatives max." + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled +msgid "Date Cancelled" +msgstr "Date d'annulation" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done +msgid "Date Done" +msgstr "Date d'achèvement" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependencies +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Dependencies" +msgstr "Dépendances" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph +msgid "Dependency Graph" +msgstr "Graphe de dépendance" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__name +msgid "Description" +msgstr "Description" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name +msgid "Display Name" +msgstr "Intitulé" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__done +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Done" +msgstr "Terminé" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued +msgid "Enqueue Time" +msgstr "Date de mise en file d'attente" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__enqueued +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Enqueued" +msgstr "Mis en file d'attente" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_name +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Exception" +msgstr "Exception" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info +msgid "Exception Info" +msgstr "Informations sur l'exception" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Exception Information" +msgstr "Informations sur l'exception" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message +msgid "Exception Message" +msgstr "Message d’exception" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Exception message" +msgstr "Message d’exception" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Exception:" +msgstr "Exception :" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta +msgid "Execute only after" +msgstr "Exécuter seulement après" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time +msgid "Execution Time (avg)" +msgstr "Temps d’exécution (moyenne)" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__failed +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Failed" +msgstr "Echoué" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype +msgid "Field Type" +msgstr "Type du champ" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_ir_model_fields +msgid "Fields" +msgstr "Champs" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids +msgid "Followers" +msgstr "Abonnés" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids +msgid "Followers (Partners)" +msgstr "Abonnés (Partenaires)" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "Icône Font Awesome ex : fa-tasks" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Graph" +msgstr "Graphe" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Graph Jobs" +msgstr "Tâches du graphe" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count +msgid "Graph Jobs Count" +msgstr "Nombre de tâches du graphe" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid +msgid "Graph UUID" +msgstr "Graphe UUID" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_function_search +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Group By" +msgstr "Regrouper par" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message +msgid "Has Message" +msgstr "A un message" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__id +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id +msgid "ID" +msgstr "ID" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_icon +msgid "Icon" +msgstr "Icône" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "Icône pour indiquer une activité d'exception." + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key +msgid "Identity Key" +msgstr "Clé d'identification" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Si coché, de nouveaux messages requièrent votre attention." + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si coché, certains messages ont une erreur de livraison." + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_function.py:0 +msgid "Invalid job function: {}" +msgstr "Fonction de tâche invalide : {}" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower +msgid "Is Follower" +msgstr "Est abonné" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_job_channel +msgid "Job Channels" +msgstr "Canaux de tâches" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__job_function_id +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Job Function" +msgstr "Fonction de tâche" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_queue_job_function +#: model:ir.model,name:queue_job.model_queue_job_function +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__job_function_ids +#: model:ir.ui.menu,name:queue_job.menu_queue_job_function +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_function_form +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_function_search +msgid "Job Functions" +msgstr "Fonctions de tâche" + +#. module: queue_job +#: model:ir.module.category,name:queue_job.module_category_queue_job +#: model:ir.ui.menu,name:queue_job.menu_queue_job_root +msgid "Job Queue" +msgstr "File d'attente des tâches" + +#. module: queue_job +#: model:res.groups,name:queue_job.group_queue_job_manager +msgid "Job Queue Manager" +msgstr "Gestionnaire de file d'attente des tâches" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized +msgid "Job Serialized" +msgstr "Sérialisation de tâche" + +#. module: queue_job +#: model:mail.message.subtype,name:queue_job.mt_job_failed +msgid "Job failed" +msgstr "Tâche échouée" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__job_ids +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__job_ids +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__job_ids +#: model:ir.ui.menu,name:queue_job.menu_queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_graph +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_pivot +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Jobs" +msgstr "Tâches" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Jobs for graph %s" +msgstr "Tâches du graphe %s" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__kwargs +msgid "Kwargs" +msgstr "Kwargs" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Last 24 hours" +msgstr "Dernières 24 heures" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Last 30 days" +msgstr "Les 30 derniers jours" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Last 7 days" +msgstr "Les 7 derniers jours" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid +msgid "Last Updated by" +msgstr "Modifié par" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date +#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date +#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date +msgid "Last Updated on" +msgstr "Modifié le" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Manually set to done by {}" +msgstr "Défini manuellement comme terminé par {}" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries +msgid "Max. retries" +msgstr "Nombre max. de tentatives" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error +msgid "Message Delivery error" +msgstr "Erreur de livraison du message" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids +msgid "Messages" +msgstr "Messages" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method +msgid "Method" +msgstr "Méthode" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name +msgid "Method Name" +msgstr "Nom de méthode" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__model_name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__model_id +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Model" +msgstr "Modèle" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_function.py:0 +msgid "Model {} not found" +msgstr "Modèle {} non trouvé" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "Mon échéance d'activité" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__name +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__name +msgid "Name" +msgstr "Nom" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Échéance de la prochaine activité" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary +msgid "Next Activity Summary" +msgstr "Résumé de la prochaine activité" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id +msgid "Next Activity Type" +msgstr "Type de la prochaine activité" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "No action available for this job" +msgstr "Aucune action disponible pour cette tâche" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Not allowed to change field(s): {}" +msgstr "Non autorisé à changer le ou les champs : {}" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter +msgid "Number of Actions" +msgstr "Nombre d'actions" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter +msgid "Number of errors" +msgstr "Nombre d'erreurs" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "Nombre de messages nécessitant une action" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Nombre de messages avec erreur de livraison" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id +msgid "Parent Channel" +msgstr "Canal parent" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_channel.py:0 +msgid "Parent channel required." +msgstr "Canal parent requis." + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job_function__edit_retry_pattern +msgid "" +"Pattern expressing from the count of retries on retryable errors, the number of of seconds to postpone the next execution. Setting the number of seconds to a 2-element tuple or list will randomize the retry interval between the 2 values.\n" +"Example: {1: 10, 5: 20, 10: 30, 15: 300}.\n" +"Example: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\n" +"See the module description for details." +msgstr "" +"Modèle permettant de définir, à partir du nombre de tentatives pour les " +"erreurs pouvant faire l'objet d'une nouvelle tentative, le nombre de " +"secondes pendant lesquelles l'exécution suivante sera reportée. En " +"définissant ce nombre de secondes sous la forme d'un tuple ou d'une liste à " +"deux éléments, l'intervalle entre les tentatives sera aléatoire et compris " +"entre ces deux valeurs.\n" +"Exemple : {1 : 10, 5 : 20, 10 : 30, 15 : 300}.\n" +"Exemple : {1 : (1, 10), 5 : (11, 20), 10 : (21, 30), 15 : (100, 300)}.\n" +"Voir la description du module pour plus de détails." + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__pending +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Pending" +msgstr "En attente" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__priority +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Priority" +msgstr "Priorité" + +#. module: queue_job +#: model:ir.ui.menu,name:queue_job.menu_queue +msgid "Queue" +msgstr "File d'attente" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id +msgid "Queue Job" +msgstr "File d'attente des tâches" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_job_lock +msgid "Queue Job Lock" +msgstr "Verrour de la file d'attente des tâches" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__records +msgid "Record(s)" +msgstr "Enregistrement(s)" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Related" +msgstr "En relation" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action +msgid "Related Action" +msgstr "Action rattachée" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action +msgid "Related Action (serialized)" +msgstr "Action rattaché (sérialisé)" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Related Record" +msgstr "Enregistrement lié" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "Related Records" +msgstr "Enregistrements liés" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree +msgid "Remaining days to execute" +msgstr "Jours restants avant l'exécution" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval +msgid "Removal Interval" +msgstr "Intervalle de suppression" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job +msgid "Requeue" +msgstr "Remise en file d'attente" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Requeue Job" +msgstr "Remettre la tâche en file d'attente" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_requeue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job +msgid "Requeue Jobs" +msgstr "Remettre les tâches en file d'attente" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id +msgid "Responsible User" +msgstr "Responsable" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__result +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Result" +msgstr "Résultat" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Results" +msgstr "Résultats" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern +msgid "Retry Pattern" +msgstr "Modèle des tentatives" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern +msgid "Retry Pattern (serialized)" +msgstr "Modèle des tentatives (sérialisé)" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_jobs_to_done +msgid "Set all selected jobs to done" +msgstr "Définir toutes les tâches sélectionnées comme terminées" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done +msgid "Set jobs done" +msgstr "Définir les tâches comme terminées" + +#. module: queue_job +#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done +msgid "Set jobs to done" +msgstr "Définir les tâches comme terminées" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Set to 'Done'" +msgstr "Mettre en \"Terminé\"" + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done +msgid "Set to done" +msgstr "Définir comme terminé" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__graph_uuid +msgid "Single shared identifier of a Graph. Empty for a single job." +msgstr "Identifiant unique et partagé d'un graphe. Vide pour une tâche unique." + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job.py:0 +msgid "" +"Something bad happened during the execution of the job. More details in the " +"'Exception Information' section." +msgstr "" +"Quelque chose s'est mal passé pendant l'exécution de la tâche. Plus de " +"détails dans la section \"Information sur l'exception\"." + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started +msgid "Start Date" +msgstr "Date de début" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__started +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Started" +msgstr "Commencé" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__state +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "State" +msgstr "État" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Statut basé sur les activités\n" +"En retard : la date d'échéance est déjà dépassée\n" +"Aujourd'hui : la date d'activité est aujourd'hui\n" +"Planifiée : activités futures." + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string +msgid "Task" +msgstr "Tâche" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job_function__edit_related_action +msgid "" +"The action when the button *Related Action* is used on a job. The default action is to open the view of the record related to the job. Configured as a dictionary with optional keys: enable, func_name, kwargs.\n" +"See the module description for details." +msgstr "" +"L’action lorsque le bouton « Action rattachée » est utilisé dans une tâche. " +"L’action par défaut est d’ouvrir une vue de l’enregistrement rattaché à la " +"tâche. Configuré avec un dictionnaire avec les clés optionnelles : enable, " +"func_name, kwargs.\n" +"Voir la description du module pour plus de détails." + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__max_retries +msgid "" +"The job will fail if the number of tries reach the max. retries.\n" +"Retries are infinite when empty." +msgstr "" +"La tâche échouera si le nombre d'essai atteint le max. de tentatives.\n" +"Quand vide, le nombre de tentatives est illimité." + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled +msgid "The selected jobs will be cancelled." +msgstr "Les tâches sélectionnées seront définies comme annulée." + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job +msgid "The selected jobs will be requeued." +msgstr "Les tâches sélectionnées seront remises en file d'attente." + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done +msgid "The selected jobs will be set to done." +msgstr "Les tâches sélectionnées seront définies comme terminées." + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form +msgid "Time (s)" +msgstr "Heure(s)" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__exec_time +msgid "Time required to execute this job in seconds. Average when grouped." +msgstr "" +"Temps requis pour exécuter cette tâche en seconde. Moyenne lors des " +"regroupements." + +#. module: queue_job +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Tried many times" +msgstr "Tentée plusieurs fois" + +#. module: queue_job +#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "Type d'activité d'exception enregistrée." + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__uuid +msgid "UUID" +msgstr "UUID" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_function.py:0 +msgid "" +"Unexpected format of Related Action for {}.\n" +"Example of valid format:\n" +"{{\"enable\": True, \"func_name\": \"related_action_foo\", \"kwargs\" {{\"limit\": 10}}}}" +msgstr "" +"Format inattendu pour l’action rattachée de {}.\n" +"Exemple de format valide :\n" +"{{\"enable\" : True, \"func_name\" : \"related_action_foo\", \"kwargs\" {" +"{\"limit\" : 10}}}}" + +#. module: queue_job +#. odoo-python +#: code:addons/queue_job/models/queue_job_function.py:0 +msgid "" +"Unexpected format of Retry Pattern for {}.\n" +"Example of valid formats:\n" +"{{1: 300, 5: 600, 10: 1200, 15: 3000}}\n" +"{{1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}}" +msgstr "" +"Format inattendu du modèle de nouvelle tentative pour {}.\n" +"Exemples de formats valides :\n" +"{{1 : 300, 5 : 600, 10 : 1200, 15 : 3000}}\n" +"{{1 : (1, 10), 5 : (11, 20), 10 : (21, 30), 15 : (100, 300)}}" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id +msgid "User ID" +msgstr "ID utilisateur" + +#. module: queue_job +#: model:ir.model.fields.selection,name:queue_job.selection__queue_job__state__wait_dependencies +#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search +msgid "Wait Dependencies" +msgstr "En attente des dépendances" + +#. module: queue_job +#: model:ir.model,name:queue_job.model_queue_requeue_job +msgid "Wizard to requeue a selection of jobs" +msgstr "Assistant pour mettre en file d'attente une sélection de tâches" + +#. module: queue_job +#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid +msgid "Worker Pid" +msgstr "Identifiant du processus de travail" diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html index 30983ba0c7..c5920c7ac7 100644 --- a/queue_job/static/description/index.html +++ b/queue_job/static/description/index.html @@ -372,7 +372,7 @@

Job Queue

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:a5cdcff42c2f45fbb57753e7693373b08e3497d0bc07212dac0f3ab56217eaf2 +!! source digest: sha256:85d54819989d21ee1d0349ecbcc7eeafe118baa9521524b1ce50fc3fdb890050 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Mature License: LGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

This addon adds an integrated Job Queue to Odoo.

diff --git a/queue_job/tests/common.py b/queue_job/tests/common.py index ca88c31b47..8773d51cc6 100644 --- a/queue_job/tests/common.py +++ b/queue_job/tests/common.py @@ -1,21 +1,24 @@ # Copyright 2019 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import doctest -import logging import typing from contextlib import contextmanager from itertools import groupby from operator import attrgetter from unittest import TestCase, mock -from odoo.tests.case import TestCase as _TestCase -from odoo.tests.common import MetaCase +from odoo.tests.common import SETATTR_SOURCES from odoo.addons.queue_job.delay import Graph # pylint: disable=odoo-addons-relative-import from odoo.addons.queue_job.job import Job +# Register queue_job patcher as a known path to prevent verbose warning +# in tests when methods are patched. +SETATTR_SOURCES["_patch_method"] = tuple( + list(SETATTR_SOURCES.get("_patch_method", [])) + ["/queue_job/models/base.py"], +) + @contextmanager def trap_jobs(): @@ -211,7 +214,7 @@ def assert_enqueued_job(self, method, args=None, kwargs=None, properties=None): if expected_call not in actual_calls: raise AssertionError( - "Job {} was not enqueued.\n" "Actual enqueued jobs:\n{}".format( + "Job {} was not enqueued.\nActual enqueued jobs:\n{}".format( self._format_job_call(expected_call), "\n".join( f" * {self._format_job_call(call)}" for call in actual_calls @@ -411,51 +414,3 @@ def test_export(self): delayable = mock.MagicMock(name="DelayableBinding") delayable_cls.return_value = delayable yield delayable_cls, delayable - - -class OdooDocTestCase(doctest.DocTestCase, _TestCase, MetaCase("DummyCase", (), {})): - """ - We need a custom DocTestCase class in order to: - - define test_tags to run as part of standard tests - - output a more meaningful test name than default "DocTestCase.runTest" - """ - - def __init__( - self, doctest, optionflags=0, setUp=None, tearDown=None, checker=None, seq=0 - ): - super().__init__( - doctest._dt_test, - optionflags=optionflags, - setUp=setUp, - tearDown=tearDown, - checker=checker, - ) - self.test_sequence = seq - - def setUp(self): - """Log an extra statement which test is started.""" - super().setUp() - logging.getLogger(__name__).info("Running tests for %s", self._dt_test.name) - - -def load_doctests(module): - """ - Generates a tests loading method for the doctests of the given module - https://docs.python.org/3/library/unittest.html#load-tests-protocol - """ - - def load_tests(loader, tests, ignore): - """ - Apply the 'test_tags' attribute to each DocTestCase found by the DocTestSuite. - Also extend the DocTestCase class trivially to fit the class teardown - that Odoo backported for its own test classes from Python 3.8. - """ - - for idx, test in enumerate(doctest.DocTestSuite(module)): - odoo_test = OdooDocTestCase(test, seq=idx) - odoo_test.test_tags = {"standard", "at_install", "queue_job", "doctest"} - tests.addTest(odoo_test) - - return tests - - return load_tests diff --git a/queue_job/tests/test_runner_channels.py b/queue_job/tests/test_runner_channels.py index d323d00683..b00dd26261 100644 --- a/queue_job/tests/test_runner_channels.py +++ b/queue_job/tests/test_runner_channels.py @@ -1,10 +1,17 @@ # Copyright 2015-2016 Camptocamp SA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) +import doctest + +from odoo.tests import BaseCase, tagged # pylint: disable=odoo-addons-relative-import # we are testing, we want to test as we were an external consumer of the API from odoo.addons.queue_job.jobrunner import channels -from .common import load_doctests -load_tests = load_doctests(channels) +@tagged("doctest") +class TestDoctest(BaseCase): + def test_doctest(self): + doctest.testmod( + channels, exclude_empty=True, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE + ) diff --git a/queue_job/tests/test_runner_runner.py b/queue_job/tests/test_runner_runner.py index 131ce6322d..1adfa790dd 100644 --- a/queue_job/tests/test_runner_runner.py +++ b/queue_job/tests/test_runner_runner.py @@ -1,17 +1,21 @@ # Copyright 2015-2016 Camptocamp SA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -# pylint: disable=odoo-addons-relative-import -# we are testing, we want to test as we were an external consumer of the API +import doctest import os from odoo.tests import BaseCase, tagged +# pylint: disable=odoo-addons-relative-import +# we are testing, we want to test as we were an external consumer of the API from odoo.addons.queue_job.jobrunner import runner -from .common import load_doctests -load_tests = load_doctests(runner) +@tagged("doctest") +class TestDoctest(BaseCase): + def test_doctest(self): + doctest.testmod( + runner, exclude_empty=True, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE + ) @tagged("-at_install", "post_install") diff --git a/queue_job_batch/README.rst b/queue_job_batch/README.rst index d0b9d22c38..57dc08f67b 100644 --- a/queue_job_batch/README.rst +++ b/queue_job_batch/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + =============== Job Queue Batch =============== @@ -7,13 +11,13 @@ Job Queue Batch !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b6e605728ea05be0bf98dbe6e94c197bab21882e965286ef214f719e252b444e + !! source digest: sha256:ca835258ad181cc54a5ba0eea86590c59e71b3aa70582a7ce115b83caa952991 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github diff --git a/queue_job_batch/__manifest__.py b/queue_job_batch/__manifest__.py index 669ba10927..7b9a0abc54 100644 --- a/queue_job_batch/__manifest__.py +++ b/queue_job_batch/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Job Queue Batch", - "version": "18.0.1.0.0", + "version": "18.0.1.1.1", "author": "Creu Blanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/queue", "license": "AGPL-3", diff --git a/queue_job_batch/models/queue_job.py b/queue_job_batch/models/queue_job.py index ddc3efe879..524da23ef7 100644 --- a/queue_job_batch/models/queue_job.py +++ b/queue_job_batch/models/queue_job.py @@ -9,7 +9,7 @@ class QueueJob(models.Model): _inherit = "queue.job" - job_batch_id = fields.Many2one("queue.job.batch") + job_batch_id = fields.Many2one("queue.job.batch", index="btree_not_null") @api.model_create_multi def create(self, vals_list): diff --git a/queue_job_batch/models/queue_job_batch.py b/queue_job_batch/models/queue_job_batch.py index 998f7a5ac2..a0d084040d 100644 --- a/queue_job_batch/models/queue_job_batch.py +++ b/queue_job_batch/models/queue_job_batch.py @@ -62,9 +62,11 @@ class QueueJobBatch(models.Model): compute="_compute_job_count", ) - def _get_state(self): - self.ensure_one() - job_states = set(self.job_ids.grouped("state").keys()) + def _get_state(self, job_states): + """Determine the batch state from a set of job states. + + :param job_states: set of state strings for all jobs in this batch + """ if all(state in ("done", "cancelled", "failed") for state in job_states): return "finished" elif {"done", "started"} & job_states: @@ -74,8 +76,20 @@ def _get_state(self): return "pending" def check_state(self): + grouped = self.env["queue.job"].read_group( + [("job_batch_id", "in", self.ids)], + ["job_batch_id", "state"], + ["job_batch_id", "state"], + lazy=False, + ) + states_by_batch = {} + for g in grouped: + batch_id = g["job_batch_id"][0] + states_by_batch.setdefault(batch_id, set()).add(g["state"]) + for rec in self: - if (state := rec._get_state()) != rec.state: + job_states = states_by_batch.get(rec.id, set()) + if (state := rec._get_state(job_states)) != rec.state: rec.state = state def set_read(self): @@ -101,13 +115,29 @@ def get_new_batch(self, name, **kwargs): @api.depends("job_ids.state") def _compute_job_count(self): + grouped = self.env["queue.job"].read_group( + [("job_batch_id", "in", self.ids)], + ["job_batch_id", "state"], + ["job_batch_id", "state"], + lazy=False, + ) + counts = {} + for g in grouped: + batch_id = g["job_batch_id"][0] + counts.setdefault(batch_id, {}) + counts[batch_id][g["state"]] = g["__count"] + for rec in self: - jobs_by_state = rec.job_ids.grouped("state") - rec.job_count = len(rec.job_ids) - rec.failed_job_count = len(jobs_by_state.get("failed", [])) - rec.finished_job_count = len(jobs_by_state.get("done", [])) - rec.completeness = rec.finished_job_count / max(1, rec.job_count) - rec.failed_percentage = rec.failed_job_count / max(1, rec.job_count) + by_state = counts.get(rec.id, {}) + total = sum(by_state.values()) + done = by_state.get("done", 0) + failed = by_state.get("failed", 0) + + rec.job_count = total + rec.failed_job_count = failed + rec.finished_job_count = done + rec.completeness = done / max(1, total) + rec.failed_percentage = failed / max(1, total) @api.model def _to_store_fnames(self): diff --git a/queue_job_batch/security/security.xml b/queue_job_batch/security/security.xml index 9443708ae0..a033a049c7 100644 --- a/queue_job_batch/security/security.xml +++ b/queue_job_batch/security/security.xml @@ -20,7 +20,7 @@ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + >[('company_id', 'in', company_ids + [False])] diff --git a/queue_job_batch/static/description/index.html b/queue_job_batch/static/description/index.html index 455c0ecf78..3c8cb8fe0b 100644 --- a/queue_job_batch/static/description/index.html +++ b/queue_job_batch/static/description/index.html @@ -3,7 +3,7 @@ -Job Queue Batch +README.rst -
-

Job Queue Batch

+
+ + +Odoo Community Association + +
+

Job Queue Batch

-

Beta License: AGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

This addon adds an a grouper for queue jobs.

It allows to show your jobs in a batched form in order to know better the results.

Example:

-from odoo import models, fields, api
+from odoo import models, fields, api
 
 
-class MyModel(models.Model):
+class MyModel(models.Model):
     _name = 'my.model'
 
-    def my_method(self, a, k=None):
+    def my_method(self, a, k=None):
         _logger.info('executed with a: %s and k: %s', a, k)
 
 
-class MyOtherModel(models.Model):
+class MyOtherModel(models.Model):
     _name = 'my.other.model'
 
     @api.multi
-    def button_do_stuff(self):
+    def button_do_stuff(self):
         batch = self.env['queue.job.batch'].get_new_batch('Group')
         model = self.env['my.model'].with_context(job_batch=batch)
         for i in range(1, 100):
@@ -415,13 +420,13 @@ 

Job Queue Batch

-

Usage

+

Usage

You can manage your batch jobs from the Systray. A new button will be shown with your currently executing job batches and the recently finished job groups.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -429,15 +434,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Creu Blanca
-

Contributors

+

Contributors

-

Other credits

+

Other credits

The migration of this module from 12.0 to 14.0 was financially supported by Camptocamp

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -475,5 +480,6 @@

Maintainers

+
diff --git a/queue_job_cron/i18n/fr.po b/queue_job_cron/i18n/fr.po new file mode 100644 index 0000000000..d8202cc2a8 --- /dev/null +++ b/queue_job_cron/i18n/fr.po @@ -0,0 +1,56 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * queue_job_cron +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-06-11 16:46+0000\n" +"Last-Translator: Yann Papouin \n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.15.2\n" + +#. module: queue_job_cron +#: model:ir.model.fields,help:queue_job_cron.field_ir_cron__no_parallel_queue_job_run +msgid "" +"Avoid parallel run. If the cron job is already running, the new one will be " +"skipped. By default, odoo never runs the same cron job in parallel. This " +"option is therefore set to True by default when job is run as a queue job." +msgstr "" +"Évitez l'exécution en parallèle. Si la tâche cron est déjà en cours " +"d'exécution, la nouvelle sera ignorée. Par défaut, Odoo n'exécute jamais la " +"même tâche cron en parallèle. Cette option est donc définie sur « True » par " +"défaut lorsque la tâche est exécutée en tant que tâche de file d'attente." + +#. module: queue_job_cron +#: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__channel_id +msgid "Channel" +msgstr "Canal" + +#. module: queue_job_cron +#: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__no_parallel_queue_job_run +msgid "No Parallel Queue Job Run" +msgstr "Aucune exécution de tâche en file d'attente parallèle" + +#. module: queue_job_cron +#: model:ir.model.fields,field_description:queue_job_cron.field_ir_cron__run_as_queue_job +msgid "Run As Queue Job" +msgstr "Exécuté en tant que tâche sur file d'attente" + +#. module: queue_job_cron +#: model:ir.model,name:queue_job_cron.model_ir_cron +msgid "Scheduled Actions" +msgstr "Actions planifiées" + +#. module: queue_job_cron +#: model:ir.model.fields,help:queue_job_cron.field_ir_cron__run_as_queue_job +msgid "Specify if this cron should be ran as a queue job" +msgstr "" +"Spécifie si cette tâche cron doit être exécutée en tant que tâche en file " +"d'attente" diff --git a/queue_job_profiler/README.rst b/queue_job_profiler/README.rst new file mode 100644 index 0000000000..6339df4de4 --- /dev/null +++ b/queue_job_profiler/README.rst @@ -0,0 +1,109 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================== +Job Queue Profiler +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:81110cbf029c5da24b53e16e05ed53c784d8df608158c1506c7464c972af5fbc + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github + :target: https://github.com/OCA/queue/tree/18.0/queue_job_profiler + :alt: OCA/queue +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/queue-18-0/queue-18-0-queue_job_profiler + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This addon adds profiling controls to queue job functions and wraps +queue job execution in an Odoo profiler session when enabled. + +When profiling is enabled for a job function and the executing user +matches one of the configured profiling users (or no users are set), the +queue job runner records SQL and async stack traces via +``odoo.tools.profiler.Profiler`` and saves the results into +``ir_profile``. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +1. Open the Queue Job Functions menu. +2. Open the job function you want to profile. +3. In the Profiler group, enable Profiling and set the Profiling users + (optional) and Profiling until. +4. Run the job with the selected user (the queue job runner usually + executes as the superuser). +5. Inspect the generated entries in ``ir_profile`` to review the + captured profiling data. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Camptocamp + +Contributors +------------ + +- `Camptocamp `__: + + - Simone Orsi + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-simahawk| image:: https://github.com/simahawk.png?size=40px + :target: https://github.com/simahawk + :alt: simahawk + +Current `maintainer `__: + +|maintainer-simahawk| + +This module is part of the `OCA/queue `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/queue_job_profiler/__init__.py b/queue_job_profiler/__init__.py new file mode 100644 index 0000000000..91c5580fed --- /dev/null +++ b/queue_job_profiler/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/queue_job_profiler/__manifest__.py b/queue_job_profiler/__manifest__.py new file mode 100644 index 0000000000..724dad205a --- /dev/null +++ b/queue_job_profiler/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2026 Camptocamp SA (https://www.camptocamp.com). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl + +{ + "name": "Job Queue Profiler", + "version": "18.0.1.0.1", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["simahawk"], + "website": "https://github.com/OCA/queue", + "license": "AGPL-3", + "category": "Generic Modules", + "depends": [ + "queue_job", + ], + "data": [ + "views/queue_job_function_views.xml", + "views/queue_job_views.xml", + ], +} diff --git a/queue_job_profiler/controllers/__init__.py b/queue_job_profiler/controllers/__init__.py new file mode 100644 index 0000000000..12a7e529b6 --- /dev/null +++ b/queue_job_profiler/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/queue_job_profiler/controllers/main.py b/queue_job_profiler/controllers/main.py new file mode 100644 index 0000000000..9717e6d388 --- /dev/null +++ b/queue_job_profiler/controllers/main.py @@ -0,0 +1,39 @@ +# Copyright 2026 Camptocamp SA (https://www.camptocamp.com). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tools.profiler import Profiler + +from odoo.addons.queue_job.controllers.main import RunJobController as BaseController + + +class RunJobController(BaseController): + @classmethod + def _try_perform_job(cls, env, job): + if cls._profiler_is_enabled(env, job): + return cls._profiler_perform_job(env, job) + return super()._try_perform_job(env, job) + + @classmethod + def _profiler_is_enabled(cls, env, job): + func_id = job.job_config.job_function_id + if not func_id: + return False + job_function = env["queue.job.function"].browse(func_id) + return job_function.is_profiling_enabled() + + @classmethod + def _profiler_perform_job(cls, env, job): + with cls._profiler_get(env, job): + result = super()._try_perform_job(env, job) + return result + + @classmethod + def _profiler_get(cls, env, job): + func_id = job.job_config.job_function_id + if not func_id: + return None + job_function = env["queue.job.function"].browse(func_id) + return Profiler( + description=job_function._profile_make_name(job), + profile_session=f"{env.user.name} (uid={env.user.id})", + ) diff --git a/queue_job_profiler/i18n/it.po b/queue_job_profiler/i18n/it.po new file mode 100644 index 0000000000..51bb700240 --- /dev/null +++ b/queue_job_profiler/i18n/it.po @@ -0,0 +1,90 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * queue_job_profiler +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-06-03 09:46+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.15.2\n" + +#. module: queue_job_profiler +#. odoo-python +#: code:addons/queue_job_profiler/models/queue_job_function.py:0 +msgid "A profiling until date must be set when profiling is enabled." +msgstr "" +"Quando la profilazione è abilitata deve essere impostata una data limite." + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_enabled +msgid "Indicates whether profiling is enabled for this job function." +msgstr "" +"Indica quando la profilazione è abilitata per questa funzione lavorativa." + +#. module: queue_job_profiler +#: model:ir.model,name:queue_job_profiler.model_queue_job_function +msgid "Job Functions" +msgstr "Funzioni lavorative" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job__job_is_profiled +msgid "Profiled" +msgstr "Profilato" + +#. module: queue_job_profiler +#: model_terms:ir.ui.view,arch_db:queue_job_profiler.queue_job_function_form_profiler +msgid "Profiler" +msgstr "Profilatore" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_enabled +msgid "Profiling enabled" +msgstr "Profilazione abilitata" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_until +msgid "Profiling until" +msgstr "Profilare fino a" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_user_ids +msgid "Profiling users" +msgstr "Utenti profilazione" + +#. module: queue_job_profiler +#: model:ir.model,name:queue_job_profiler.model_queue_job +msgid "Queue Job" +msgstr "Lavoro in coda" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_until +msgid "" +"The date and time until which profiling is enabled for this job function." +msgstr "" +"Data e ora entro cui la profilazione è abilitata per questa funzione " +"lavorativa." + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_user_ids +msgid "The users allowed to perform profiling for this job function." +msgstr "" +"Gli utenti abilitati ad applicare la profilazione per questa funzione " +"lavorativa." + +#. module: queue_job_profiler +#: model_terms:ir.ui.view,arch_db:queue_job_profiler.queue_job_form_profiler +msgid "View profiler" +msgstr "Visualizza profilatore" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job__job_is_profiled +msgid "Whether this job has been profiled or not." +msgstr "Se questo incarico è stato profilato o meno." diff --git a/queue_job_profiler/i18n/queue_job_profiler.pot b/queue_job_profiler/i18n/queue_job_profiler.pot new file mode 100644 index 0000000000..0f33941cd9 --- /dev/null +++ b/queue_job_profiler/i18n/queue_job_profiler.pot @@ -0,0 +1,81 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * queue_job_profiler +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: queue_job_profiler +#. odoo-python +#: code:addons/queue_job_profiler/models/queue_job_function.py:0 +msgid "A profiling until date must be set when profiling is enabled." +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_enabled +msgid "Indicates whether profiling is enabled for this job function." +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model,name:queue_job_profiler.model_queue_job_function +msgid "Job Functions" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job__job_is_profiled +msgid "Profiled" +msgstr "" + +#. module: queue_job_profiler +#: model_terms:ir.ui.view,arch_db:queue_job_profiler.queue_job_function_form_profiler +msgid "Profiler" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_enabled +msgid "Profiling enabled" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_until +msgid "Profiling until" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,field_description:queue_job_profiler.field_queue_job_function__profiling_user_ids +msgid "Profiling users" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model,name:queue_job_profiler.model_queue_job +msgid "Queue Job" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_until +msgid "" +"The date and time until which profiling is enabled for this job function." +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job_function__profiling_user_ids +msgid "The users allowed to perform profiling for this job function." +msgstr "" + +#. module: queue_job_profiler +#: model_terms:ir.ui.view,arch_db:queue_job_profiler.queue_job_form_profiler +msgid "View profiler" +msgstr "" + +#. module: queue_job_profiler +#: model:ir.model.fields,help:queue_job_profiler.field_queue_job__job_is_profiled +msgid "Whether this job has been profiled or not." +msgstr "" diff --git a/queue_job_profiler/models/__init__.py b/queue_job_profiler/models/__init__.py new file mode 100644 index 0000000000..2cf4503488 --- /dev/null +++ b/queue_job_profiler/models/__init__.py @@ -0,0 +1,2 @@ +from . import queue_job_function +from . import queue_job diff --git a/queue_job_profiler/models/queue_job.py b/queue_job_profiler/models/queue_job.py new file mode 100644 index 0000000000..1f4cba5750 --- /dev/null +++ b/queue_job_profiler/models/queue_job.py @@ -0,0 +1,51 @@ +# Copyright 2026 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class QueueJob(models.Model): + _inherit = "queue.job" + + job_is_profiled = fields.Boolean( + string="Profiled", + default=False, + help="Whether this job has been profiled or not.", + compute="_compute_job_is_profiled", + compute_sudo=True, + ) + + @api.depends("job_function_id") + def _compute_job_is_profiled(self): + for job in self: + if not job.job_function_id: + # cannot be profiled if it has no function + job.job_is_profiled = False + continue + profile_name = job.job_function_id._profile_make_name(job) + job.job_is_profiled = bool(job._profiler_get_record(profile_name)) + + def _profiler_get_record(self, profile_name): + # don't care about perf as this is loaded only on the job form view + IrProfile = self.env["ir.profile"].sudo() + return IrProfile.search( + [("name", "=", profile_name)], + limit=1, + ) + + def action_view_profile(self): + self.ensure_one() + if not self.job_function_id: + return {"type": "ir.actions.act_window_close"} + profile_name = self.job_function_id._profile_make_name(self) + profile = self._profiler_get_record(profile_name) + if not profile: + return {"type": "ir.actions.act_window_close"} + return { + "type": "ir.actions.act_window", + "name": "Profile", + "res_model": "ir.profile", + "view_mode": "form", + "res_id": profile.id, + "target": "current", + } diff --git a/queue_job_profiler/models/queue_job_function.py b/queue_job_profiler/models/queue_job_function.py new file mode 100644 index 0000000000..00550e15cb --- /dev/null +++ b/queue_job_profiler/models/queue_job_function.py @@ -0,0 +1,47 @@ +# Copyright 2026 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, exceptions, fields, models + + +class QueueJobFunction(models.Model): + _inherit = "queue.job.function" + + profiling_enabled = fields.Boolean( + string="Profiling enabled", + help="Indicates whether profiling is enabled for this job function.", + ) + profiling_user_ids = fields.Many2many( + "res.users", + string="Profiling users", + help="The users allowed to perform profiling for this job function.", + ) + profiling_until = fields.Datetime( + string="Profiling until", + help="The date and time until which profiling is enabled " + "for this job function.", + ) + + def is_profiling_enabled(self): + self.ensure_one() + return ( + self.profiling_enabled + and (self.profiling_until and self.profiling_until >= fields.Datetime.now()) + and ( + not self.profiling_user_ids or self.env.user in self.profiling_user_ids + ) + ) + + @api.constrains("profiling_enabled") + def _check_profiling_setup(self): + for record in self: + if record.profiling_enabled and not record.profiling_until: + raise exceptions.ValidationError( + self.env._( + "A profiling until date must be set when profiling is enabled." + ) + ) + + def _profile_make_name(self, job): + self.ensure_one() + return f"queue.job {job.uuid} - {self.name}" diff --git a/queue_job_profiler/pyproject.toml b/queue_job_profiler/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/queue_job_profiler/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/queue_job_profiler/readme/CONTRIBUTORS.md b/queue_job_profiler/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..fc27d9f599 --- /dev/null +++ b/queue_job_profiler/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Camptocamp](https://camptocamp.com): + - Simone Orsi \<\> diff --git a/queue_job_profiler/readme/DESCRIPTION.md b/queue_job_profiler/readme/DESCRIPTION.md new file mode 100644 index 0000000000..06cb1376de --- /dev/null +++ b/queue_job_profiler/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This addon adds profiling controls to queue job functions and wraps +queue job execution in an Odoo profiler session when enabled. + +When profiling is enabled for a job function and the executing user +matches one of the configured profiling users (or no users are set), +the queue job runner records +SQL and async stack traces via `odoo.tools.profiler.Profiler` and saves +the results into `ir_profile`. \ No newline at end of file diff --git a/queue_job_profiler/readme/USAGE.md b/queue_job_profiler/readme/USAGE.md new file mode 100644 index 0000000000..a58ee8eeff --- /dev/null +++ b/queue_job_profiler/readme/USAGE.md @@ -0,0 +1,8 @@ +1. Open the Queue Job Functions menu. +2. Open the job function you want to profile. +3. In the Profiler group, enable Profiling and set the Profiling users + (optional) and Profiling until. +4. Run the job with the selected user (the queue job runner usually executes + as the superuser). +5. Inspect the generated entries in `ir_profile` to review the captured + profiling data. \ No newline at end of file diff --git a/queue_job_profiler/static/description/icon.png b/queue_job_profiler/static/description/icon.png new file mode 100644 index 0000000000..1dcc49c24f Binary files /dev/null and b/queue_job_profiler/static/description/icon.png differ diff --git a/queue_job_profiler/static/description/index.html b/queue_job_profiler/static/description/index.html new file mode 100644 index 0000000000..958d29acc3 --- /dev/null +++ b/queue_job_profiler/static/description/index.html @@ -0,0 +1,454 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Job Queue Profiler

+ +

Beta License: AGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

+

This addon adds profiling controls to queue job functions and wraps +queue job execution in an Odoo profiler session when enabled.

+

When profiling is enabled for a job function and the executing user +matches one of the configured profiling users (or no users are set), the +queue job runner records SQL and async stack traces via +odoo.tools.profiler.Profiler and saves the results into +ir_profile.

+

Table of contents

+ +
+

Usage

+
    +
  1. Open the Queue Job Functions menu.
  2. +
  3. Open the job function you want to profile.
  4. +
  5. In the Profiler group, enable Profiling and set the Profiling users +(optional) and Profiling until.
  6. +
  7. Run the job with the selected user (the queue job runner usually +executes as the superuser).
  8. +
  9. Inspect the generated entries in ir_profile to review the +captured profiling data.
  10. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

simahawk

+

This module is part of the OCA/queue project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/queue_job_profiler/tests/__init__.py b/queue_job_profiler/tests/__init__.py new file mode 100644 index 0000000000..f4cae97b5a --- /dev/null +++ b/queue_job_profiler/tests/__init__.py @@ -0,0 +1 @@ +from . import test_job_profiler diff --git a/queue_job_profiler/tests/test_job_profiler.py b/queue_job_profiler/tests/test_job_profiler.py new file mode 100644 index 0000000000..d1047795cb --- /dev/null +++ b/queue_job_profiler/tests/test_job_profiler.py @@ -0,0 +1,150 @@ +# Copyright 2026 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from datetime import timedelta +from unittest.mock import patch + +from freezegun import freeze_time + +from odoo import exceptions, fields +from odoo.tests import common, new_test_user + +from odoo.addons.queue_job_profiler.controllers.main import RunJobController + + +class TestJobFunction(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.user_func1 = cls.env["queue.job.function"].create( + {"model_id": cls.env["ir.model"]._get_id("res.users"), "method": "read"} + ) + cls.user_func2 = cls.env["queue.job.function"].create( + {"model_id": cls.env["ir.model"]._get_id("res.users"), "method": "read"} + ) + cls.user1 = new_test_user(cls.env, login="user1", groups="base.group_user") + cls.user2 = new_test_user(cls.env, login="user2", groups="base.group_user") + + def test_func_constraint(self): + with self.assertRaisesRegex( + exceptions.ValidationError, + "A profiling until date must be set when profiling is enabled.", + ): + self.user_func1.profiling_enabled = True + + def _enable_profiling(self, job_function, users=None, delta=timedelta(hours=1)): + job_function.profiling_user_ids = users + job_function.profiling_until = fields.Datetime.now() + delta + job_function.profiling_enabled = True + + def test_func_profiling_enabled(self): + # By default, profiling should be disabled for all users + for user in (self.env.user, self.user1, self.user2): + self.assertFalse(self.user_func1.with_user(user).is_profiling_enabled()) + # Enable for all users (no profiling users set) + self._enable_profiling(self.user_func1) + for user in (self.env.user, self.user1, self.user2): + self.assertTrue(self.user_func1.with_user(user).is_profiling_enabled()) + # Enable it for user1 and user2 + self._enable_profiling(self.user_func1, users=self.user1 + self.user2) + self.assertTrue(self.user_func1.with_user(self.user1).is_profiling_enabled()) + self.assertTrue(self.user_func1.with_user(self.user2).is_profiling_enabled()) + self.assertFalse( + self.user_func1.with_user(self.env.user).is_profiling_enabled() + ) + # Check for another job function and user + self._enable_profiling(self.user_func2, users=self.user2) + self.assertTrue(self.user_func2.with_user(self.user2).is_profiling_enabled()) + for user in (self.env.user, self.user1): + self.assertFalse(self.user_func2.with_user(user).is_profiling_enabled()) + with freeze_time(fields.Datetime.now() + timedelta(days=1)): + # After the profiling_until date, profiling should be disabled for all users + for user in (self.env.user, self.user1, self.user2): + self.assertFalse(self.user_func1.with_user(user).is_profiling_enabled()) + self.assertFalse(self.user_func2.with_user(user).is_profiling_enabled()) + + def test_job_without_function(self): + # if a job has no function, it cannot be profiled + self.user_func1.sudo().unlink() + self.user_func2.sudo().unlink() + job1 = self.env.user.with_delay().read(["id"]) + job1.store() + job_rec1 = job1.db_record() + self.assertFalse(job_rec1.job_function_id) + self.assertFalse(job_rec1.job_is_profiled) + with self.assertRaises(ValueError): + self.env["queue.job.function"]._profile_make_name(job_rec1) + + def test_job_is_profiled(self): + job1 = self.env.user.with_delay().read(["id"]) + job1.store() + job_rec1 = job1.db_record() + job2 = self.env.user.with_delay().read(["id"]) + job2.store() + job_rec2 = job2.db_record() + jobs = job_rec1 | job_rec2 + self.assertEqual(jobs.mapped("job_is_profiled"), [False, False]) + profile_name1 = self.user_func1._profile_make_name(job_rec1) + profile_name2 = self.user_func2._profile_make_name(job_rec2) + prof1 = self.env["ir.profile"].create({"name": profile_name1}) + jobs.invalidate_recordset() + self.assertEqual(jobs.mapped("job_is_profiled"), [True, False]) + prof2 = self.env["ir.profile"].create({"name": profile_name2}) + jobs.invalidate_recordset() + self.assertEqual(jobs.mapped("job_is_profiled"), [True, True]) + self.assertEqual(job_rec1._profiler_get_record(profile_name1), prof1) + self.assertEqual(job_rec2._profiler_get_record(profile_name2), prof2) + + def test_job_action_view_profile(self): + job = self.env.user.with_delay().read(["id"]) + job.store() + job_rec = job.db_record() + self.assertEqual( + job_rec.action_view_profile(), {"type": "ir.actions.act_window_close"} + ) + profile_name = self.user_func1._profile_make_name(job_rec) + prof = self.env["ir.profile"].create({"name": profile_name}) + action = job_rec.action_view_profile() + self.assertEqual(action["type"], "ir.actions.act_window") + self.assertEqual(action["res_model"], "ir.profile") + self.assertEqual(action["res_id"], prof.id) + + def _run_controller(self, job, user=None): + env = self.env(user=user) if user else self.env + RunJobController._try_perform_job(env, job) + + def test_controller(self): + job = self.env.user.with_delay().read(["id"]) + self._enable_profiling(self.user_func1, [self.user1.id]) + # TODO @simahawk: I'd like to look for new `ir_profile` records + # but somehow looks very hard to find them + # since `Profiler` creates them in a separated db connection. + # All my attempts to mock the cr or de connection of `db_connect` + # failed poorly, so I prefer to not waste too much time aroudn this. + # I'm falling back to patching the controller method + # that should be called when profiling is enabled. + # The profiler init is tested in another method below. + # Not ideal, but better than no test :) + with patch.object(RunJobController, "_profiler_get") as mock_profiler_get: + self._run_controller(job) + mock_profiler_get.assert_not_called() + with patch.object(RunJobController, "_profiler_get") as mock_profiler_get: + self._run_controller(job, user=self.user1) + mock_profiler_get.assert_called_once() + with freeze_time(fields.Datetime.now() + timedelta(days=1)): + with patch.object(RunJobController, "_profiler_get") as mock_profiler_get: + self._run_controller(job) + mock_profiler_get.assert_not_called() + + def test_controller_profiler_get(self): + # as we mocked it before, make sure it works + controller = RunJobController() + job = self.env.user.with_delay().read(["id"]) + profiler = controller._profiler_get(self.env, job) + self.assertEqual( + profiler.description, f"queue.job {job.uuid} - {job.job_function_name}" + ) + self.assertEqual( + profiler.profile_session, f"{self.env.user.name} (uid={self.env.user.id})" + ) diff --git a/queue_job_profiler/views/queue_job_function_views.xml b/queue_job_profiler/views/queue_job_function_views.xml new file mode 100644 index 0000000000..b351560088 --- /dev/null +++ b/queue_job_profiler/views/queue_job_function_views.xml @@ -0,0 +1,24 @@ + + + + queue.job.function.form.profiler + queue.job.function + + + + + + + + + + + + diff --git a/queue_job_profiler/views/queue_job_views.xml b/queue_job_profiler/views/queue_job_views.xml new file mode 100644 index 0000000000..7e379a3795 --- /dev/null +++ b/queue_job_profiler/views/queue_job_views.xml @@ -0,0 +1,25 @@ + + + + queue.job.form.profiler + queue.job + + + + + + + + diff --git a/queue_job_subscribe/i18n/sl.po b/queue_job_subscribe/i18n/sl.po index 65fe1eaf3a..aa6e2902ba 100644 --- a/queue_job_subscribe/i18n/sl.po +++ b/queue_job_subscribe/i18n/sl.po @@ -10,15 +10,16 @@ msgstr "" "Project-Id-Version: Odoo Server 9.0c\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-04-05 00:42+0000\n" -"PO-Revision-Date: 2017-04-05 00:42+0000\n" -"Last-Translator: Matjaž Mozetič , 2017\n" +"PO-Revision-Date: 2026-06-16 20:46+0000\n" +"Last-Translator: Matjaz Mozetic \n" "Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " -"n%100==4 ? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 5.15.2\n" #. module: queue_job_subscribe #: model_terms:ir.ui.view,arch_db:queue_job_subscribe.view_user_connector_form @@ -36,19 +37,18 @@ msgstr "" #. module: queue_job_subscribe #: model:ir.model.fields,field_description:queue_job_subscribe.field_res_users__subscribe_job -#, fuzzy msgid "Job Notifications" -msgstr "Obvestila o delu" +msgstr "Obvestila o nalogah" #. module: queue_job_subscribe #: model:ir.model,name:queue_job_subscribe.model_queue_job msgid "Queue Job" -msgstr "Delo čakalne vrste" +msgstr "Naloga v čakalni vrsti" #. module: queue_job_subscribe #: model:ir.model,name:queue_job_subscribe.model_res_users msgid "User" -msgstr "" +msgstr "Uporabnik" #~ msgid "Users" #~ msgstr "Uporabniki" diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000000..bd6c2a4f6f --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", ":disableDependencyDashboard"], + "baseBranchPatterns": ["19.0", "18.0", "17.0", "16.0", "15.0"], + "enabledManagers": ["copier"] +} diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index 8ad6da4a41..a7f0370299 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,12 +1,13 @@ [project] name = "odoo-addons-oca-queue" -version = "18.0.20250415.0" +version = "18.0.20260526.0" dependencies = [ "odoo-addon-base_import_async==18.0.*", "odoo-addon-queue_job==18.0.*", "odoo-addon-queue_job_batch==18.0.*", "odoo-addon-queue_job_cron==18.0.*", "odoo-addon-queue_job_cron_jobrunner==18.0.*", + "odoo-addon-queue_job_profiler==18.0.*", "odoo-addon-queue_job_subscribe==18.0.*", "odoo-addon-test_queue_job==18.0.*", "odoo-addon-test_queue_job_batch==18.0.*",