diff --git a/addons/queue_job/README.rst b/addons/queue_job/README.rst deleted file mode 100644 index f22fd7b..0000000 --- a/addons/queue_job/README.rst +++ /dev/null @@ -1,707 +0,0 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - -========= -Job Queue -========= - -.. - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b92d06dbbf161572f2bf02e0c6a59282cea11cc5e903378094bead986f0125de - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png - :target: https://odoo-community.org/page/development-status - :alt: Mature -.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png - :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html - :alt: License: LGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fqueue-lightgray.png?logo=github - :target: https://github.com/OCA/queue/tree/16.0/queue_job - :alt: OCA/queue -.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/queue-16-0/queue-16-0-queue_job - :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=16.0 - :alt: Try me on Runboat - -|badge1| |badge2| |badge3| |badge4| |badge5| - -This addon adds an integrated Job Queue to Odoo. - -It allows to postpone method calls executed asynchronously. - -Jobs are executed in the background by a ``Jobrunner``, in their own transaction. - -Example: - -.. code-block:: python - - from odoo import models, fields, api - - class MyModel(models.Model): - _name = 'my.model' - - def my_method(self, a, k=None): - _logger.info('executed with a: %s and k: %s', a, k) - - - class MyOtherModel(models.Model): - _name = 'my.other.model' - - def button_do_stuff(self): - self.env['my.model'].with_delay().my_method('a', k=2) - - -In the snippet of code above, when we call ``button_do_stuff``, a job **capturing -the method and arguments** will be postponed. It will be executed as soon as the -Jobrunner has a free bucket, which can be instantaneous if no other job is -running. - - -Features: - -* Views for jobs, jobs are stored in PostgreSQL -* Jobrunner: execute the jobs, highly efficient thanks to PostgreSQL's NOTIFY -* Channels: give a capacity for the root channel and its sub-channels and - segregate jobs in them. Allow for instance to restrict heavy jobs to be - executed one at a time while little ones are executed 4 at a times. -* Retries: Ability to retry jobs by raising a type of exception -* Retry Pattern: the 3 first tries, retry after 10 seconds, the 5 next tries, - retry after 1 minutes, ... -* Job properties: priorities, estimated time of arrival (ETA), custom - description, number of retries -* Related Actions: link an action on the job view, such as open the record - concerned by the job - -**Table of contents** - -.. contents:: - :local: - -Installation -============ - -Be sure to have the ``requests`` library. - -Configuration -============= - -* Using environment variables and command line: - - * Adjust environment variables (optional): - - - ``ODOO_QUEUE_JOB_CHANNELS=root:4`` or any other channels configuration. - The default is ``root:1`` - - - if ``xmlrpc_port`` is not set: ``ODOO_QUEUE_JOB_PORT=8069`` - - * Start Odoo with ``--load=web,queue_job`` - and ``--workers`` greater than 1. [1]_ - -* Keep in mind that the number of workers should be greater than the number of - channels. ``queue_job`` will reuse normal Odoo workers to process jobs. It - will not spawn its own workers. - -* Using the Odoo configuration file: - -.. code-block:: ini - - [options] - (...) - workers = 6 - server_wide_modules = web,queue_job - - (...) - [queue_job] - channels = root:2 - -* Environment variables have priority over the configuration file. - -* Confirm the runner is starting correctly by checking the odoo log file: - -.. code-block:: - - ...INFO...queue_job.jobrunner.runner: starting - ...INFO...queue_job.jobrunner.runner: initializing database connections - ...INFO...queue_job.jobrunner.runner: queue job runner ready for db - ...INFO...queue_job.jobrunner.runner: database connections ready - -* Create jobs (eg using ``base_import_async``) and observe they - start immediately and in parallel. - -* Tip: to enable debug logging for the queue job, use - ``--log-handler=odoo.addons.queue_job:DEBUG`` - -.. [1] It works with the threaded Odoo server too, although this way - of running Odoo is obviously not for production purposes. - -* Jobs that remain in `enqueued` or `started` state (because, for instance, their worker has been killed) will be automatically re-queued. - -Usage -===== - -To use this module, you need to: - -#. Go to ``Job Queue`` menu - -Developers -~~~~~~~~~~ - -Delaying jobs -------------- - -The fast way to enqueue a job for a method is to use ``with_delay()`` on a record -or model: - - -.. code-block:: python - - def button_done(self): - self.with_delay().print_confirmation_document(self.state) - self.write({"state": "done"}) - return True - -Here, the method ``print_confirmation_document()`` will be executed asynchronously -as a job. ``with_delay()`` can take several parameters to define more precisely how -the job is executed (priority, ...). - -All the arguments passed to the method being delayed are stored in the job and -passed to the method when it is executed asynchronously, including ``self``, so -the current record is maintained during the job execution (warning: the context -is not kept). - -Dependencies can be expressed between jobs. To start a graph of jobs, use ``delayable()`` -on a record or model. The following is the equivalent of ``with_delay()`` but using the -long form: - -.. code-block:: python - - def button_done(self): - delayable = self.delayable() - delayable.print_confirmation_document(self.state) - delayable.delay() - self.write({"state": "done"}) - return True - -Methods of Delayable objects return itself, so it can be used as a builder pattern, -which in some cases allow to build the jobs dynamically: - -.. code-block:: python - - def button_generate_simple_with_delayable(self): - self.ensure_one() - # Introduction of a delayable object, using a builder pattern - # allowing to chain jobs or set properties. The delay() method - # on the delayable object actually stores the delayable objects - # in the queue_job table - ( - self.delayable() - .generate_thumbnail((50, 50)) - .set(priority=30) - .set(description=_("generate xxx")) - .delay() - ) - -The simplest way to define a dependency is to use ``.on_done(job)`` on a Delayable: - -.. code-block:: python - - def button_chain_done(self): - self.ensure_one() - job1 = self.browse(1).delayable().generate_thumbnail((50, 50)) - job2 = self.browse(1).delayable().generate_thumbnail((50, 50)) - job3 = self.browse(1).delayable().generate_thumbnail((50, 50)) - # job 3 is executed when job 2 is done which is executed when job 1 is done - job1.on_done(job2.on_done(job3)).delay() - -Delayables can be chained to form more complex graphs using the ``chain()`` and -``group()`` primitives. -A chain represents a sequence of jobs to execute in order, a group represents -jobs which can be executed in parallel. Using ``chain()`` has the same effect as -using several nested ``on_done()`` but is more readable. Both can be combined to -form a graph, for instance we can group [A] of jobs, which blocks another group -[B] of jobs. When and only when all the jobs of the group [A] are executed, the -jobs of the group [B] are executed. The code would look like: - -.. code-block:: python - - from odoo.addons.queue_job.delay import group, chain - - def button_done(self): - group_a = group(self.delayable().method_foo(), self.delayable().method_bar()) - group_b = group(self.delayable().method_baz(1), self.delayable().method_baz(2)) - chain(group_a, group_b).delay() - self.write({"state": "done"}) - return True - -When a failure happens in a graph of jobs, the execution of the jobs that depend on the -failed job stops. They remain in a state ``wait_dependencies`` until their "parent" job is -successful. This can happen in two ways: either the parent job retries and is successful -on a second try, either the parent job is manually "set to done" by a user. In these two -cases, the dependency is resolved and the graph will continue to be processed. Alternatively, -the failed job and all its dependent jobs can be canceled by a user. The other jobs of the -graph that do not depend on the failed job continue their execution in any case. - -Note: ``delay()`` must be called on the delayable, chain, or group which is at the top -of the graph. In the example above, if it was called on ``group_a``, then ``group_b`` -would never be delayed (but a warning would be shown). - -It is also possible to split a job into several jobs, each one processing a part of the -work. This can be useful to avoid very long jobs, parallelize some task and get more specific -errors. Usage is as follows: - -.. code-block:: python - - def button_split_delayable(self): - ( - self # Can be a big recordset, let's say 1000 records - .delayable() - .generate_thumbnail((50, 50)) - .set(priority=30) - .set(description=_("generate xxx")) - .split(50) # Split the job in 20 jobs of 50 records each - .delay() - ) - -The ``split()`` method takes a ``chain`` boolean keyword argument. If set to -True, the jobs will be chained, meaning that the next job will only start when the previous -one is done: - -.. code-block:: python - - def button_increment_var(self): - ( - self - .delayable() - .increment_counter() - .split(1, chain=True) # Will exceute the jobs one after the other - .delay() - ) - - -Enqueing Job Options --------------------- - -* priority: default is 10, the closest it is to 0, the faster it will be - executed -* eta: Estimated Time of Arrival of the job. It will not be executed before this - date/time -* max_retries: default is 5, maximum number of retries before giving up and set - the job state to 'failed'. A value of 0 means infinite retries. -* description: human description of the job. If not set, description is computed - from the function doc or method name -* channel: the complete name of the channel to use to process the function. If - specified it overrides the one defined on the function -* identity_key: key uniquely identifying the job, if specified and a job with - the same key has not yet been run, the new job will not be created - -Configure default options for jobs ----------------------------------- - -In earlier versions, jobs could be configured using the ``@job`` decorator. -This is now obsolete, they can be configured using optional ``queue.job.function`` -and ``queue.job.channel`` XML records. - -Example of channel: - -.. code-block:: XML - - - sale - - - -Example of job function: - -.. code-block:: XML - - - - action_done - - - - - -The general form for the ``name`` is: ``.method``. - -The channel, related action and retry pattern options are optional, they are -documented below. - -When writing modules, if 2+ modules add a job function or channel with the same -name (and parent for channels), they'll be merged in the same record, even if -they have different xmlids. On uninstall, the merged record is deleted when all -the modules using it are uninstalled. - - -**Job function: model** - -If the function is defined in an abstract model, you can not write -```` -but you have to define a function for each model that inherits from the abstract model. - - -**Job function: channel** - -The channel where the job will be delayed. The default channel is ``root``. - -**Job function: related action** - -The *Related Action* appears as a button on the Job's view. -The button will execute the defined action. - -The default one is to open the view of the record related to the job (form view -when there is a single record, list view for several records). -In many cases, the default related action is enough and doesn't need -customization, but it can be customized by providing a dictionary on the job -function: - -.. code-block:: python - - { - "enable": False, - "func_name": "related_action_partner", - "kwargs": {"name": "Partner"}, - } - -* ``enable``: when ``False``, the button has no effect (default: ``True``) -* ``func_name``: name of the method on ``queue.job`` that returns an action -* ``kwargs``: extra arguments to pass to the related action method - -Example of related action code: - -.. code-block:: python - - class QueueJob(models.Model): - _inherit = 'queue.job' - - def related_action_partner(self, name): - self.ensure_one() - model = self.model_name - partner = self.records - action = { - 'name': name, - 'type': 'ir.actions.act_window', - 'res_model': model, - 'view_type': 'form', - 'view_mode': 'form', - 'res_id': partner.id, - } - return action - - -**Job function: retry pattern** - -When a job fails with a retryable error type, it is automatically -retried later. By default, the retry is always 10 minutes later. - -A retry pattern can be configured on the job function. What a pattern represents -is "from X tries, postpone to Y seconds". It is expressed as a dictionary where -keys are tries and values are seconds to postpone as integers: - - -.. code-block:: python - - { - 1: 10, - 5: 20, - 10: 30, - 15: 300, - } - -Based on this configuration, we can tell that: - -* 5 first retries are postponed 10 seconds later -* retries 5 to 10 postponed 20 seconds later -* retries 10 to 15 postponed 30 seconds later -* all subsequent retries postponed 5 minutes later - -**Job Context** - -The context of the recordset of the job, or any recordset passed in arguments of -a job, is transferred to the job according to an allow-list. - -The default allow-list is `("tz", "lang", "allowed_company_ids", "force_company", "active_test")`. It can -be customized in ``Base._job_prepare_context_before_enqueue_keys``. -**Bypass jobs on running Odoo** - -When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately. - -To do so you can set `QUEUE_JOB__NO_DELAY=1` in your environment. - -**Bypass jobs in tests** - -When writing tests on job-related methods is always tricky to deal with -delayed recordsets. To make your testing life easier -you can set `queue_job__no_delay=True` in the context. - -Tip: you can do this at test case level like this - -.. code-block:: python - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.env = cls.env(context=dict( - cls.env.context, - queue_job__no_delay=True, # no jobs thanks - )) - -Then all your tests execute the job methods synchronously -without delaying any jobs. - -Testing -------- - -**Asserting enqueued jobs** - -The recommended way to test jobs, rather than running them directly and synchronously is to -split the tests in two parts: - - * one test where the job is mocked (trap jobs with ``trap_jobs()`` and the test - only verifies that the job has been delayed with the expected arguments - * one test that only calls the method of the job synchronously, to validate the - proper behavior of this method only - -Proceeding this way means that you can prove that jobs will be enqueued properly -at runtime, and it ensures your code does not have a different behavior in tests -and in production (because running your jobs synchronously may have a different -behavior as they are in the same transaction / in the middle of the method). -Additionally, it gives more control on the arguments you want to pass when -calling the job's method (synchronously, this time, in the second type of -tests), and it makes tests smaller. - -The best way to run such assertions on the enqueued jobs is to use -``odoo.addons.queue_job.tests.common.trap_jobs()``. - -Inside this context manager, instead of being added in the database's queue, -jobs are pushed in an in-memory list. The context manager then provides useful -helpers to verify that jobs have been enqueued with the expected arguments. It -even can run the jobs of its list synchronously! Details in -``odoo.addons.queue_job.tests.common.JobsTester``. - -A very small example (more details in ``tests/common.py``): - -.. code-block:: python - - # code - def my_job_method(self, name, count): - self.write({"name": " ".join([name] * count) - - def method_to_test(self): - count = self.env["other.model"].search_count([]) - self.with_delay(priority=15).my_job_method("Hi!", count=count) - return count - - # tests - from odoo.addons.queue_job.tests.common import trap_jobs - - # first test only check the expected behavior of the method and the proper - # enqueuing of jobs - def test_method_to_test(self): - with trap_jobs() as trap: - result = self.env["model"].method_to_test() - expected_count = 12 - - trap.assert_jobs_count(1, only=self.env["model"].my_job_method) - trap.assert_enqueued_job( - self.env["model"].my_job_method, - args=("Hi!",), - kwargs=dict(count=expected_count), - properties=dict(priority=15) - ) - self.assertEqual(result, expected_count) - - - # second test to validate the behavior of the job unitarily - def test_my_job_method(self): - record = self.env["model"].browse(1) - record.my_job_method("Hi!", count=12) - self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!") - -If you prefer, you can still test the whole thing in a single test, by calling -``jobs_tester.perform_enqueued_jobs()`` in your test. - -.. code-block:: python - - def test_method_to_test(self): - with trap_jobs() as trap: - result = self.env["model"].method_to_test() - expected_count = 12 - - trap.assert_jobs_count(1, only=self.env["model"].my_job_method) - trap.assert_enqueued_job( - self.env["model"].my_job_method, - args=("Hi!",), - kwargs=dict(count=expected_count), - properties=dict(priority=15) - ) - self.assertEqual(result, expected_count) - - trap.perform_enqueued_jobs() - - record = self.env["model"].browse(1) - record.my_job_method("Hi!", count=12) - self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!") - -**Execute jobs synchronously when running Odoo** - -When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately. - -To do so you can set ``QUEUE_JOB__NO_DELAY=1`` in your environment. - -.. WARNING:: Do not do this in production - -**Execute jobs synchronously in tests** - -You should use ``trap_jobs``, really, but if for any reason you could not use it, -and still need to have job methods executed synchronously in your tests, you can -do so by setting ``queue_job__no_delay=True`` in the context. - -Tip: you can do this at test case level like this - -.. code-block:: python - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.env = cls.env(context=dict( - cls.env.context, - queue_job__no_delay=True, # no jobs thanks - )) - -Then all your tests execute the job methods synchronously without delaying any -jobs. - -In tests you'll have to mute the logger like: - - @mute_logger('odoo.addons.queue_job.models.base') - -.. NOTE:: in graphs of jobs, the ``queue_job__no_delay`` context key must be in at - least one job's env of the graph for the whole graph to be executed synchronously - - -Tips and tricks ---------------- - -* **Idempotency** (https://www.restapitutorial.com/lessons/idempotency.html): The queue_job should be idempotent so they can be retried several times without impact on the data. -* **The job should test at the very beginning its relevance**: the moment the job will be executed is unknown by design. So the first task of a job should be to check if the related work is still relevant at the moment of the execution. - -Patterns --------- -Through the time, two main patterns emerged: - -1. For data exposed to users, a model should store the data and the model should be the creator of the job. The job is kept hidden from the users -2. For technical data, that are not exposed to the users, it is generally alright to create directly jobs with data passed as arguments to the job, without intermediary models. - -Known issues / Roadmap -====================== - -* After creating a new database or installing ``queue_job`` on an - existing database, Odoo must be restarted for the runner to detect it. - -* When Odoo shuts down normally, it waits for running jobs to finish. - However, when the Odoo server crashes or is otherwise force-stopped, - running jobs are interrupted while the runner has no chance to know - they have been aborted. In such situations, jobs may remain in - ``started`` or ``enqueued`` state after the Odoo server is halted. - Since the runner has no way to know if they are actually running or - not, and does not know for sure if it is safe to restart the jobs, - it does not attempt to restart them automatically. Such stale jobs - therefore fill the running queue and prevent other jobs to start. - You must therefore requeue them manually, either from the Jobs view, - or by running the following SQL statement *before starting Odoo*: - -.. code-block:: sql - - update queue_job set state='pending' where state in ('started', 'enqueued') - -Changelog -========= - -.. [ The change log. The goal of this file is to help readers - understand changes between version. The primary audience is - end users and integrators. Purely technical changes such as - code refactoring must not be mentioned here. - - This file may contain ONE level of section titles, underlined - with the ~ (tilde) character. Other section markers are - forbidden and will likely break the structure of the README.rst - or other documents where this fragment is included. ] - -Next -~~~~ - -* [ADD] Run jobrunner as a worker process instead of a thread in the main - process (when running with --workers > 0) -* [REF] ``@job`` and ``@related_action`` deprecated, any method can be delayed, - and configured using ``queue.job.function`` records -* [MIGRATION] from 13.0 branched at rev. e24ff4b - -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 -* ACSONE SA/NV - -Contributors -~~~~~~~~~~~~ - -* Guewen Baconnier -* Stéphane Bidoul -* Matthieu Dietrich -* Jos De Graeve -* David Lefever -* Laurent Mignon -* Laetitia Gangloff -* Cédric Pigeon -* Tatiana Deribina -* Souheil Bejaoui -* Eric Antones -* 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-guewen| image:: https://github.com/guewen.png?size=40px - :target: https://github.com/guewen - :alt: guewen - -Current `maintainer `__: - -|maintainer-guewen| - -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/addons/queue_job/__init__.py b/addons/queue_job/__init__.py deleted file mode 100644 index 6ca18c3..0000000 --- a/addons/queue_job/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from . import controllers -from . import fields -from . import models -from . import wizards -from . import jobrunner -from .post_init_hook import post_init_hook -from .post_load import post_load - -# shortcuts -from .job import identity_exact diff --git a/addons/queue_job/__manifest__.py b/addons/queue_job/__manifest__.py deleted file mode 100644 index f32b20e..0000000 --- a/addons/queue_job/__manifest__.py +++ /dev/null @@ -1,35 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -{ - "name": "Job Queue", - "version": "16.0.2.12.0", - "author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)", - "website": "https://github.com/OCA/queue", - "license": "LGPL-3", - "category": "Generic Modules", - "depends": ["mail", "base_sparse_field", "web"], - "external_dependencies": {"python": ["requests"]}, - "data": [ - "security/security.xml", - "security/ir.model.access.csv", - "views/queue_job_views.xml", - "views/queue_job_channel_views.xml", - "views/queue_job_function_views.xml", - "wizards/queue_jobs_to_done_views.xml", - "wizards/queue_jobs_to_cancelled_views.xml", - "wizards/queue_requeue_job_views.xml", - "views/queue_job_menus.xml", - "data/queue_data.xml", - "data/queue_job_function_data.xml", - ], - "assets": { - "web.assets_backend": [ - "/queue_job/static/src/views/**/*", - ], - }, - "installable": True, - "development_status": "Mature", - "maintainers": ["guewen"], - "post_init_hook": "post_init_hook", - "post_load": "post_load", -} diff --git a/addons/queue_job/controllers/__init__.py b/addons/queue_job/controllers/__init__.py deleted file mode 100644 index 12a7e52..0000000 --- a/addons/queue_job/controllers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import main diff --git a/addons/queue_job/controllers/main.py b/addons/queue_job/controllers/main.py deleted file mode 100644 index 4addf1b..0000000 --- a/addons/queue_job/controllers/main.py +++ /dev/null @@ -1,320 +0,0 @@ -# Copyright (c) 2015-2016 ACSONE SA/NV () -# Copyright 2013-2016 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import logging -import random -import time -import traceback -from io import StringIO - -from psycopg2 import OperationalError, errorcodes -from werkzeug.exceptions import BadRequest, Forbidden - -from odoo import SUPERUSER_ID, _, api, http, registry, tools -from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY - -from ..delay import chain, group -from ..exception import FailedJobError, NothingToDoJob, RetryableJobError -from ..job import ENQUEUED, Job - -_logger = logging.getLogger(__name__) - -PG_RETRY = 5 # seconds - -DEPENDS_MAX_TRIES_ON_CONCURRENCY_FAILURE = 5 - - -class RunJobController(http.Controller): - def _try_perform_job(self, env, job): - """Try to perform the job.""" - job.set_started() - job.store() - env.cr.commit() - job.lock() - - _logger.debug("%s started", job) - - job.perform() - # Triggers any stored computed fields before calling 'set_done' - # so that will be part of the 'exec_time' - env.flush_all() - job.set_done() - job.store() - env.flush_all() - env.cr.commit() - _logger.debug("%s done", job) - - def _enqueue_dependent_jobs(self, env, job): - tries = 0 - while True: - try: - job.enqueue_waiting() - except OperationalError as err: - # Automatically retry the typical transaction serialization - # errors - if err.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY: - raise - if tries >= DEPENDS_MAX_TRIES_ON_CONCURRENCY_FAILURE: - _logger.info( - "%s, maximum number of tries reached to update dependencies", - errorcodes.lookup(err.pgcode), - ) - raise - wait_time = random.uniform(0.0, 2**tries) - tries += 1 - _logger.info( - "%s, retry %d/%d in %.04f sec...", - errorcodes.lookup(err.pgcode), - tries, - DEPENDS_MAX_TRIES_ON_CONCURRENCY_FAILURE, - wait_time, - ) - time.sleep(wait_time) - else: - break - - @http.route( - "/queue_job/runjob", - type="http", - auth="none", - save_session=False, - readonly=False, - ) - def runjob(self, db, job_uuid, **kw): - http.request.session.db = db - env = http.request.env(user=SUPERUSER_ID) - - def retry_postpone(job, message, seconds=None): - job.env.clear() - with registry(job.env.cr.dbname).cursor() as new_cr: - job.env = api.Environment(new_cr, SUPERUSER_ID, {}) - job.postpone(result=message, seconds=seconds) - job.set_pending(reset_retry=False) - job.store() - - # ensure the job to run is in the correct state and lock the record - env.cr.execute( - "SELECT state FROM queue_job WHERE uuid=%s AND state=%s FOR UPDATE", - (job_uuid, ENQUEUED), - ) - if not env.cr.fetchone(): - _logger.warning( - "was requested to run job %s, but it does not exist, " - "or is not in state %s", - job_uuid, - ENQUEUED, - ) - return "" - - job = Job.load(env, job_uuid) - assert job and job.state == ENQUEUED - - try: - try: - self._try_perform_job(env, job) - except OperationalError as err: - # Automatically retry the typical transaction serialization - # errors - if err.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY: - raise - - _logger.debug("%s OperationalError, postponed", job) - raise RetryableJobError( - tools.ustr(err.pgerror, errors="replace"), seconds=PG_RETRY - ) from err - - except NothingToDoJob as err: - if str(err): - msg = str(err) - else: - msg = _("Job interrupted and set to Done: nothing to do.") - job.set_done(msg) - job.store() - env.cr.commit() - - except RetryableJobError as err: - # delay the job later, requeue - retry_postpone(job, str(err), seconds=err.seconds) - _logger.debug("%s postponed", job) - # Do not trigger the error up because we don't want an exception - # traceback in the logs we should have the traceback when all - # retries are exhausted - env.cr.rollback() - return "" - - except (FailedJobError, Exception) as orig_exception: - buff = StringIO() - traceback.print_exc(file=buff) - traceback_txt = buff.getvalue() - _logger.error(traceback_txt) - job.env.clear() - with registry(job.env.cr.dbname).cursor() as new_cr: - job.env = job.env(cr=new_cr) - vals = self._get_failure_values(job, traceback_txt, orig_exception) - job.set_failed(**vals) - job.store() - buff.close() - raise - - _logger.debug("%s enqueue depends started", job) - self._enqueue_dependent_jobs(env, job) - _logger.debug("%s enqueue depends done", job) - - return "" - - def _get_failure_values(self, job, traceback_txt, orig_exception): - """Collect relevant data from exception.""" - exception_name = orig_exception.__class__.__name__ - if hasattr(orig_exception, "__module__"): - exception_name = orig_exception.__module__ + "." + exception_name - exc_message = ( - orig_exception.args[0] if orig_exception.args else str(orig_exception) - ) - return { - "exc_info": traceback_txt, - "exc_name": exception_name, - "exc_message": exc_message, - } - - # flake8: noqa: C901 - @http.route("/queue_job/create_test_job", type="http", auth="user") - def create_test_job( - self, - priority=None, - max_retries=None, - channel=None, - description="Test job", - size=1, - failure_rate=0, - ): - """Create test jobs - - Examples of urls: - - * http://127.0.0.1:8069/queue_job/create_test_job: single job - * http://127.0.0.1:8069/queue_job/create_test_job?size=10: a graph of 10 jobs - * http://127.0.0.1:8069/queue_job/create_test_job?size=10&failure_rate=0.5: - a graph of 10 jobs, half will fail - - """ - if not http.request.env.user.has_group("base.group_erp_manager"): - raise Forbidden(_("Access Denied")) - - if failure_rate is not None: - try: - failure_rate = float(failure_rate) - except (ValueError, TypeError): - failure_rate = 0 - - if not (0 <= failure_rate <= 1): - raise BadRequest("failure_rate must be between 0 and 1") - - if size is not None: - try: - size = int(size) - except (ValueError, TypeError): - size = 1 - - if priority is not None: - try: - priority = int(priority) - except ValueError: - priority = None - - if max_retries is not None: - try: - max_retries = int(max_retries) - except ValueError: - max_retries = None - - if size == 1: - return self._create_single_test_job( - priority=priority, - max_retries=max_retries, - channel=channel, - description=description, - failure_rate=failure_rate, - ) - - if size > 1: - return self._create_graph_test_jobs( - size, - priority=priority, - max_retries=max_retries, - channel=channel, - description=description, - failure_rate=failure_rate, - ) - return "" - - def _create_single_test_job( - self, - priority=None, - max_retries=None, - channel=None, - description="Test job", - size=1, - failure_rate=0, - ): - delayed = ( - http.request.env["queue.job"] - .with_delay( - priority=priority, - max_retries=max_retries, - channel=channel, - description=description, - ) - ._test_job(failure_rate=failure_rate) - ) - return "job uuid: %s" % (delayed.db_record().uuid,) - - TEST_GRAPH_MAX_PER_GROUP = 5 - - def _create_graph_test_jobs( - self, - size, - priority=None, - max_retries=None, - channel=None, - description="Test job", - failure_rate=0, - ): - model = http.request.env["queue.job"] - current_count = 0 - - possible_grouping_methods = (chain, group) - - tails = [] # we can connect new graph chains/groups to tails - root_delayable = None - while current_count < size: - jobs_count = min( - size - current_count, random.randint(1, self.TEST_GRAPH_MAX_PER_GROUP) - ) - - jobs = [] - for __ in range(jobs_count): - current_count += 1 - jobs.append( - model.delayable( - priority=priority, - max_retries=max_retries, - channel=channel, - description="%s #%d" % (description, current_count), - )._test_job(failure_rate=failure_rate) - ) - - grouping = random.choice(possible_grouping_methods) - delayable = grouping(*jobs) - if not root_delayable: - root_delayable = delayable - else: - tail_delayable = random.choice(tails) - tail_delayable.on_done(delayable) - tails.append(delayable) - - root_delayable.delay() - - return "graph uuid: %s" % ( - list(root_delayable._head())[0]._generated_job.graph_uuid, - ) diff --git a/addons/queue_job/data/queue_data.xml b/addons/queue_job/data/queue_data.xml deleted file mode 100644 index a2680cc..0000000 --- a/addons/queue_job/data/queue_data.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - Job failed - queue.job - - - - AutoVacuum Job Queue - - - - 1 - days - -1 - - code - model.autovacuum() - - - - - root - - - diff --git a/addons/queue_job/data/queue_job_function_data.xml b/addons/queue_job/data/queue_job_function_data.xml deleted file mode 100644 index 0105dbc..0000000 --- a/addons/queue_job/data/queue_job_function_data.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - _test_job - - diff --git a/addons/queue_job/delay.py b/addons/queue_job/delay.py deleted file mode 100644 index 726e850..0000000 --- a/addons/queue_job/delay.py +++ /dev/null @@ -1,666 +0,0 @@ -# Copyright 2019 Camptocamp -# Copyright 2019 Guewen Baconnier -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) - -import itertools -import logging -import uuid -from collections import defaultdict, deque - -from .job import Job -from .utils import must_run_without_delay - -_logger = logging.getLogger(__name__) - - -def group(*delayables): - """Return a group of delayable to form a graph - - A group means that jobs can be executed concurrently. - A job or a group of jobs depending on a group can be executed only after - all the jobs of the group are done. - - Shortcut to :class:`~odoo.addons.queue_job.delay.DelayableGroup`. - - Example:: - - g1 = group(delayable1, delayable2) - g2 = group(delayable3, delayable4) - g1.on_done(g2) - g1.delay() - """ - return DelayableGroup(*delayables) - - -def chain(*delayables): - """Return a chain of delayable to form a graph - - A chain means that jobs must be executed sequentially. - A job or a group of jobs depending on a group can be executed only after - the last job of the chain is done. - - Shortcut to :class:`~odoo.addons.queue_job.delay.DelayableChain`. - - Example:: - - chain1 = chain(delayable1, delayable2, delayable3) - chain2 = chain(delayable4, delayable5, delayable6) - chain1.on_done(chain2) - chain1.delay() - """ - return DelayableChain(*delayables) - - -class Graph: - """Acyclic directed graph holding vertices of any hashable type - - This graph is not specifically designed to hold :class:`~Delayable` - instances, although ultimately it is used for this purpose. - """ - - __slots__ = "_graph" - - def __init__(self, graph=None): - if graph: - self._graph = graph - else: - self._graph = {} - - def add_vertex(self, vertex): - """Add a vertex - - Has no effect if called several times with the same vertex - """ - self._graph.setdefault(vertex, set()) - - def add_edge(self, parent, child): - """Add an edge between a parent and a child vertex - - Has no effect if called several times with the same pair of vertices - """ - self.add_vertex(child) - self._graph.setdefault(parent, set()).add(child) - - def vertices(self): - """Return the vertices (nodes) of the graph""" - return set(self._graph) - - def edges(self): - """Return the edges (links) of the graph""" - links = [] - for vertex, neighbours in self._graph.items(): - for neighbour in neighbours: - links.append((vertex, neighbour)) - return links - - # from - # https://codereview.stackexchange.com/questions/55767/finding-all-paths-from-a-given-graph - def paths(self, vertex): - """Generate the maximal cycle-free paths in graph starting at vertex. - - >>> g = {1: [2, 3], 2: [3, 4], 3: [1], 4: []} - >>> sorted(self.paths(1)) - [[1, 2, 3], [1, 2, 4], [1, 3]] - >>> sorted(self.paths(3)) - [[3, 1, 2, 4]] - """ - path = [vertex] # path traversed so far - seen = {vertex} # set of vertices in path - - def search(): - dead_end = True - for neighbour in self._graph[path[-1]]: - if neighbour not in seen: - dead_end = False - seen.add(neighbour) - path.append(neighbour) - yield from search() - path.pop() - seen.remove(neighbour) - if dead_end: - yield list(path) - - yield from search() - - def topological_sort(self): - """Yields a proposed order of nodes to respect dependencies - - The order is not unique, the result may vary, but it is guaranteed - that a node depending on another is not yielded before. - It assumes the graph has no cycle. - """ - depends_per_node = defaultdict(int) - for __, tail in self.edges(): - depends_per_node[tail] += 1 - - # the queue contains only elements for which all dependencies - # are resolved - queue = deque(self.root_vertices()) - while queue: - vertex = queue.popleft() - yield vertex - for node in self._graph[vertex]: - depends_per_node[node] -= 1 - if not depends_per_node[node]: - queue.append(node) - - def root_vertices(self): - """Returns the root vertices - - meaning they do not depend on any other job. - """ - dependency_vertices = set() - for dependencies in self._graph.values(): - dependency_vertices.update(dependencies) - return set(self._graph.keys()) - dependency_vertices - - def __repr__(self): - paths = [path for vertex in self.root_vertices() for path in self.paths(vertex)] - lines = [] - for path in paths: - lines.append(" → ".join(repr(vertex) for vertex in path)) - return "\n".join(lines) - - -class DelayableGraph(Graph): - """Directed Graph for :class:`~Delayable` dependencies - - It connects together the :class:`~Delayable`, :class:`~DelayableGroup` and - :class:`~DelayableChain` graphs, and creates then enqueued the jobs. - """ - - def _merge_graph(self, graph): - """Merge a graph in the current graph - - It takes each vertex, which can be :class:`~Delayable`, - :class:`~DelayableChain` or :class:`~DelayableGroup`, and updates the - current graph with the edges between Delayable objects (connecting - heads and tails of the groups and chains), so that at the end, the - graph contains only Delayable objects and their links. - """ - for vertex, neighbours in graph._graph.items(): - tails = vertex._tail() - for tail in tails: - # connect the tails with the heads of each node - heads = {head for n in neighbours for head in n._head()} - self._graph.setdefault(tail, set()).update(heads) - - def _connect_graphs(self): - """Visit the vertices' graphs and connect them, return the whole graph - - Build a new graph, walk the vertices and their related vertices, merge - their graph in the new one, until we have visited all the vertices - """ - graph = DelayableGraph() - graph._merge_graph(self) - - seen = set() - visit_stack = deque([self]) - while visit_stack: - current = visit_stack.popleft() - if current in seen: - continue - - vertices = current.vertices() - for vertex in vertices: - vertex_graph = vertex._graph - graph._merge_graph(vertex_graph) - visit_stack.append(vertex_graph) - - seen.add(current) - - return graph - - def _has_to_execute_directly(self, vertices): - """Used for tests to run tests directly instead of storing them - - In tests, prefer to use - :func:`odoo.addons.queue_job.tests.common.trap_jobs`. - """ - envs = {vertex.recordset.env for vertex in vertices} - for env in envs: - if must_run_without_delay(env): - return True - return False - - @staticmethod - def _ensure_same_graph_uuid(jobs): - """Set the same graph uuid on all jobs of the same graph""" - jobs_count = len(jobs) - if jobs_count == 0: - raise ValueError("Expecting jobs") - elif jobs_count == 1: - if jobs[0].graph_uuid: - raise ValueError( - "Job %s is a single job, it should not" - " have a graph uuid" % (jobs[0],) - ) - else: - graph_uuids = {job.graph_uuid for job in jobs if job.graph_uuid} - if len(graph_uuids) > 1: - raise ValueError("Jobs cannot have dependencies between several graphs") - elif len(graph_uuids) == 1: - graph_uuid = graph_uuids.pop() - else: - graph_uuid = str(uuid.uuid4()) - for job in jobs: - job.graph_uuid = graph_uuid - - def delay(self): - """Build the whole graph, creates jobs and delay them""" - graph = self._connect_graphs() - - vertices = graph.vertices() - - for vertex in vertices: - vertex._build_job() - - self._ensure_same_graph_uuid([vertex._generated_job for vertex in vertices]) - - if self._has_to_execute_directly(vertices): - self._execute_graph_direct(graph) - return - - for vertex, neighbour in graph.edges(): - neighbour._generated_job.add_depends({vertex._generated_job}) - - # If all the jobs of the graph have another job with the same identity, - # we do not create them. Maybe we should check that the found jobs are - # part of the same graph, but not sure it's really required... - # Also, maybe we want to check only the root jobs. - existing_mapping = {} - for vertex in vertices: - if not vertex.identity_key: - continue - generated_job = vertex._generated_job - existing = generated_job.job_record_with_same_identity_key() - if not existing: - # at least one does not exist yet, we'll delay the whole graph - existing_mapping.clear() - break - existing_mapping[vertex] = existing - - # We'll replace the generated jobs by the existing ones, so callers - # can retrieve the existing job in "_generated_job". - # existing_mapping contains something only if *all* the job with an - # identity have an existing one. - for vertex, existing in existing_mapping.items(): - vertex._generated_job = existing - return - - for vertex in vertices: - vertex._generated_job.store() - - def _execute_graph_direct(self, graph): - for delayable in graph.topological_sort(): - delayable._execute_direct() - - -class DelayableChain: - """Chain of delayables to form a graph - - Delayables can be other :class:`~Delayable`, :class:`~DelayableChain` or - :class:`~DelayableGroup` objects. - - A chain means that jobs must be executed sequentially. - A job or a group of jobs depending on a group can be executed only after - the last job of the chain is done. - - Chains can be connected to other Delayable, DelayableChain or - DelayableGroup objects by using :meth:`~done`. - - A Chain is enqueued by calling :meth:`~delay`, which delays the whole - graph. - Important: :meth:`~delay` must be called on the top-level - delayable/chain/group object of the graph. - """ - - __slots__ = ("_graph", "__head", "__tail") - - def __init__(self, *delayables): - self._graph = DelayableGraph() - iter_delayables = iter(delayables) - head = next(iter_delayables) - self.__head = head - self._graph.add_vertex(head) - for neighbour in iter_delayables: - self._graph.add_edge(head, neighbour) - head = neighbour - self.__tail = head - - def _head(self): - return self.__head._tail() - - def _tail(self): - return self.__tail._head() - - def __repr__(self): - inner_graph = "\n\t".join(repr(self._graph).split("\n")) - return "DelayableChain(\n\t{}\n)".format(inner_graph) - - def on_done(self, *delayables): - """Connects the current chain to other delayables/chains/groups - - The delayables/chains/groups passed in the parameters will be executed - when the current Chain is done. - """ - for delayable in delayables: - self._graph.add_edge(self.__tail, delayable) - return self - - def delay(self): - """Delay the whole graph""" - self._graph.delay() - - -class DelayableGroup: - """Group of delayables to form a graph - - Delayables can be other :class:`~Delayable`, :class:`~DelayableChain` or - :class:`~DelayableGroup` objects. - - A group means that jobs must be executed sequentially. - A job or a group of jobs depending on a group can be executed only after - the all the jobs of the group are done. - - Groups can be connected to other Delayable, DelayableChain or - DelayableGroup objects by using :meth:`~done`. - - A group is enqueued by calling :meth:`~delay`, which delays the whole - graph. - Important: :meth:`~delay` must be called on the top-level - delayable/chain/group object of the graph. - """ - - __slots__ = ("_graph", "_delayables") - - def __init__(self, *delayables): - self._graph = DelayableGraph() - self._delayables = set(delayables) - for delayable in delayables: - self._graph.add_vertex(delayable) - - def _head(self): - return itertools.chain.from_iterable(node._head() for node in self._delayables) - - def _tail(self): - return itertools.chain.from_iterable(node._tail() for node in self._delayables) - - def __repr__(self): - inner_graph = "\n\t".join(repr(self._graph).split("\n")) - return "DelayableGroup(\n\t{}\n)".format(inner_graph) - - def on_done(self, *delayables): - """Connects the current group to other delayables/chains/groups - - The delayables/chains/groups passed in the parameters will be executed - when the current Group is done. - """ - for parent in self._delayables: - for child in delayables: - self._graph.add_edge(parent, child) - return self - - def delay(self): - """Delay the whole graph""" - self._graph.delay() - - -class Delayable: - """Unit of a graph, one Delayable will lead to an enqueued job - - Delayables can have dependencies on each others, as well as dependencies on - :class:`~DelayableGroup` or :class:`~DelayableChain` objects. - - This class will generally not be used directly, it is used internally - by :meth:`~odoo.addons.queue_job.models.base.Base.delayable`. Look - in the base model for more details. - - Delayables can be connected to other Delayable, DelayableChain or - DelayableGroup objects by using :meth:`~done`. - - Properties of the future job can be set using the :meth:`~set` method, - which always return ``self``:: - - delayable.set(priority=15).set({"max_retries": 5, "eta": 15}).delay() - - It can be used for example to set properties dynamically. - - A Delayable is enqueued by calling :meth:`delay()`, which delays the whole - graph. - Important: :meth:`delay()` must be called on the top-level - delayable/chain/group object of the graph. - """ - - _properties = ( - "priority", - "eta", - "max_retries", - "description", - "channel", - "identity_key", - ) - __slots__ = _properties + ( - "recordset", - "_graph", - "_job_method", - "_job_args", - "_job_kwargs", - "_generated_job", - ) - - def __init__( - self, - recordset, - priority=None, - eta=None, - max_retries=None, - description=None, - channel=None, - identity_key=None, - ): - self._graph = DelayableGraph() - self._graph.add_vertex(self) - - self.recordset = recordset - - self.priority = priority - self.eta = eta - self.max_retries = max_retries - self.description = description - self.channel = channel - self.identity_key = identity_key - - self._job_method = None - self._job_args = () - self._job_kwargs = {} - - self._generated_job = None - - def _head(self): - return [self] - - def _tail(self): - return [self] - - def __repr__(self): - return "Delayable({}.{}({}, {}))".format( - self.recordset, - self._job_method.__name__ if self._job_method else "", - self._job_args, - self._job_kwargs, - ) - - def __del__(self): - if not self._generated_job: - _logger.warning("Delayable %s was prepared but never delayed", self) - - def _set_from_dict(self, properties): - for key, value in properties.items(): - if key not in self._properties: - raise ValueError("No property %s" % (key,)) - setattr(self, key, value) - - def set(self, *args, **kwargs): - """Set job properties and return self - - The values can be either a dictionary and/or keywork args - """ - if args: - # args must be a dict - self._set_from_dict(*args) - self._set_from_dict(kwargs) - return self - - def on_done(self, *delayables): - """Connects the current Delayable to other delayables/chains/groups - - The delayables/chains/groups passed in the parameters will be executed - when the current Delayable is done. - """ - for child in delayables: - self._graph.add_edge(self, child) - return self - - def delay(self): - """Delay the whole graph""" - self._graph.delay() - - def split(self, size, chain=False): - """Split the Delayables. - - Use `DelayableGroup` or `DelayableChain` - if `chain` is True containing batches of size `size` - """ - if not self._job_method: - raise ValueError("No method set on the Delayable") - - total_records = len(self.recordset) - - delayables = [] - for index in range(0, total_records, size): - recordset = self.recordset[index : index + size] - delayable = Delayable( - recordset, - priority=self.priority, - eta=self.eta, - max_retries=self.max_retries, - description=self.description, - channel=self.channel, - identity_key=self.identity_key, - ) - # Update the __self__ - delayable._job_method = getattr(recordset, self._job_method.__name__) - delayable._job_args = self._job_args - delayable._job_kwargs = self._job_kwargs - - delayables.append(delayable) - - description = self.description or ( - self._job_method.__doc__.splitlines()[0].strip() - if self._job_method.__doc__ - else "{}.{}".format(self.recordset._name, self._job_method.__name__) - ) - for index, delayable in enumerate(delayables): - delayable.set( - description="%s (split %s/%s)" - % (description, index + 1, len(delayables)) - ) - - # Prevent warning on deletion - self._generated_job = True - - return (DelayableChain if chain else DelayableGroup)(*delayables) - - def _build_job(self): - if self._generated_job: - return self._generated_job - self._generated_job = Job( - self._job_method, - args=self._job_args, - kwargs=self._job_kwargs, - priority=self.priority, - max_retries=self.max_retries, - eta=self.eta, - description=self.description, - channel=self.channel, - identity_key=self.identity_key, - ) - return self._generated_job - - def _store_args(self, *args, **kwargs): - self._job_args = args - self._job_kwargs = kwargs - return self - - def __getattr__(self, name): - if name in self.__slots__: - return super().__getattr__(name) - if name in self.recordset: - raise AttributeError( - "only methods can be delayed (%s called on %s)" % (name, self.recordset) - ) - recordset_method = getattr(self.recordset, name) - self._job_method = recordset_method - return self._store_args - - def _execute_direct(self): - assert self._generated_job - self._generated_job.perform() - - -class DelayableRecordset: - """Allow to delay a method for a recordset (shortcut way) - - Usage:: - - delayable = DelayableRecordset(recordset, priority=20) - delayable.method(args, kwargs) - - The method call will be processed asynchronously in the job queue, with - the passed arguments. - - This class will generally not be used directly, it is used internally - by :meth:`~odoo.addons.queue_job.models.base.Base.with_delay` - """ - - __slots__ = ("delayable",) - - def __init__( - self, - recordset, - priority=None, - eta=None, - max_retries=None, - description=None, - channel=None, - identity_key=None, - ): - self.delayable = Delayable( - recordset, - priority=priority, - eta=eta, - max_retries=max_retries, - description=description, - channel=channel, - identity_key=identity_key, - ) - - @property - def recordset(self): - return self.delayable.recordset - - def __getattr__(self, name): - def _delay_delayable(*args, **kwargs): - getattr(self.delayable, name)(*args, **kwargs).delay() - return self.delayable._generated_job - - return _delay_delayable - - def __str__(self): - return "DelayableRecordset(%s%s)" % ( - self.delayable.recordset._name, - getattr(self.delayable.recordset, "_ids", ""), - ) - - __repr__ = __str__ diff --git a/addons/queue_job/exception.py b/addons/queue_job/exception.py deleted file mode 100644 index 093344e..0000000 --- a/addons/queue_job/exception.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2012-2016 Camptocamp -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - - -class BaseQueueJobError(Exception): - """Base queue job error""" - - -class JobError(BaseQueueJobError): - """A job had an error""" - - -class NoSuchJobError(JobError): - """The job does not exist.""" - - -class FailedJobError(JobError): - """A job had an error having to be resolved.""" - - -class RetryableJobError(JobError): - """A job had an error but can be retried. - - The job will be retried after the given number of seconds. If seconds is - empty, it will be retried according to the ``retry_pattern`` of the job or - by :const:`odoo.addons.queue_job.job.RETRY_INTERVAL` if nothing is defined. - - If ``ignore_retry`` is True, the retry counter will not be increased. - """ - - def __init__(self, msg, seconds=None, ignore_retry=False): - super().__init__(msg) - self.seconds = seconds - self.ignore_retry = ignore_retry - - -# TODO: remove support of NothingToDo: too dangerous -class NothingToDoJob(JobError): - """The Job has nothing to do.""" - - -class ChannelNotFound(BaseQueueJobError): - """A channel could not be found""" diff --git a/addons/queue_job/fields.py b/addons/queue_job/fields.py deleted file mode 100644 index 2c78c18..0000000 --- a/addons/queue_job/fields.py +++ /dev/null @@ -1,123 +0,0 @@ -# copyright 2016 Camptocamp -# license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import json -from datetime import date, datetime - -import dateutil -import lxml - -from odoo import fields, models -from odoo.tools.func import lazy - - -class JobSerialized(fields.Field): - """Provide the storage for job fields stored as json - - A base_type must be set, it must be dict, list or tuple. - When the field is not set, the json will be the corresponding - json string ("{}" or "[]"). - - Support for some custom types has been added to the json decoder/encoder - (see JobEncoder and JobDecoder). - """ - - type = "job_serialized" - column_type = ("text", "text") - - _base_type = None - - # these are the default values when we convert an empty value - _default_json_mapping = { - dict: "{}", - list: "[]", - tuple: "[]", - models.BaseModel: lambda env: json.dumps( - {"_type": "odoo_recordset", "model": "base", "ids": [], "uid": env.uid} - ), - } - - def __init__(self, string=fields.Default, base_type=fields.Default, **kwargs): - super().__init__(string=string, _base_type=base_type, **kwargs) - - def _setup_attrs(self, model, name): # pylint: disable=missing-return - super()._setup_attrs(model, name) - if self._base_type not in self._default_json_mapping: - raise ValueError("%s is not a supported base type" % (self._base_type)) - - def _base_type_default_json(self, env): - default_json = self._default_json_mapping.get(self._base_type) - if not isinstance(default_json, str): - default_json = default_json(env) - return default_json - - def convert_to_column(self, value, record, values=None, validate=True): - return self.convert_to_cache(value, record, validate=validate) - - def convert_to_cache(self, value, record, validate=True): - # cache format: json.dumps(value) or None - if isinstance(value, self._base_type): - return json.dumps(value, cls=JobEncoder) - else: - return value or None - - def convert_to_record(self, value, record): - default = self._base_type_default_json(record.env) - return json.loads(value or default, cls=JobDecoder, env=record.env) - - -class JobEncoder(json.JSONEncoder): - """Encode Odoo recordsets so that we can later recompose them""" - - def _get_record_context(self, obj): - return obj._job_prepare_context_before_enqueue() - - def default(self, obj): - if isinstance(obj, models.BaseModel): - return { - "_type": "odoo_recordset", - "model": obj._name, - "ids": obj.ids, - "uid": obj.env.uid, - "su": obj.env.su, - "context": self._get_record_context(obj), - } - elif isinstance(obj, datetime): - return {"_type": "datetime_isoformat", "value": obj.isoformat()} - elif isinstance(obj, date): - return {"_type": "date_isoformat", "value": obj.isoformat()} - elif isinstance(obj, lxml.etree._Element): - return { - "_type": "etree_element", - "value": lxml.etree.tostring(obj, encoding=str), - } - elif isinstance(obj, lazy): - return obj._value - return json.JSONEncoder.default(self, obj) - - -class JobDecoder(json.JSONDecoder): - """Decode json, recomposing recordsets""" - - def __init__(self, *args, **kwargs): - env = kwargs.pop("env") - super().__init__(object_hook=self.object_hook, *args, **kwargs) - assert env - self.env = env - - def object_hook(self, obj): - if "_type" not in obj: - return obj - type_ = obj["_type"] - if type_ == "odoo_recordset": - model = self.env(user=obj.get("uid"), su=obj.get("su"))[obj["model"]] - if obj.get("context"): - model = model.with_context(**obj.get("context")) - return model.browse(obj["ids"]) - elif type_ == "datetime_isoformat": - return dateutil.parser.parse(obj["value"]) - elif type_ == "date_isoformat": - return dateutil.parser.parse(obj["value"]).date() - elif type_ == "etree_element": - return lxml.etree.fromstring(obj["value"]) - return obj diff --git a/addons/queue_job/i18n/ca.po b/addons/queue_job/i18n/ca.po deleted file mode 100644 index 1a4ae1c..0000000 --- a/addons/queue_job/i18n/ca.po +++ /dev/null @@ -1,964 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2025-07-29 07:25+0000\n" -"Last-Translator: Enric Tobella \n" -"Language-Team: none\n" -"Language: ca\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.10.4\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 el máx. " -"reintents es 0, el nombre de reintents es infinit." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "Accés denegat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "Acció requerida" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "Activitats" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Decoració de l'activitat d'exepció" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "Estat de l'activitat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "Icona del tipus d'activitat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "Arguments" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "Nombre d'adjunts" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "Buidat automàtic de la cua de Treballs" - -#. 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 "Cancel·lar" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "Cancel·lar tots els treballs seleccionats" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "Cancel·lar treball" - -#. 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 "Cancel·lar treballs" - -#. 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 "Cancel·lat" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "Cancel·lat per %s" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "No es pot cambiar el canal arrel" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "No es pot eliminar el canal arrel" - -#. 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 "El nom complet del canal ha de ser únic" - -#. 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 "Canals" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "Companyia" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "Nom complet del métode" - -#. 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 "Data de creació" - -#. 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 "Creat per" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "Data de creació" - -#. 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 "Creat el" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "Intent actual" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Intent actual / reintents màx." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "Data de cancel·lació" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "Data de realització" - -#. 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 "Dependències" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "Gràfic de dependència" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "Descripció" - -#. 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 "Nom a mostrar" - -#. 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 "Realitzat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "Encua el treball" - -#. 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 "Encuat" - -#. 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 "Exepció" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "Gràfic dels treballs" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Nombre de gràfics de treballs" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "UUID del gràfic" - -#. 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 "Agrupar per" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "Té missatges" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_icon -msgid "Icon" -msgstr "Icona" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Icona per indicar una activitat d'excepció." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "Clau identificadora" - -#. 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 està marcat, nous missatges requereixen la teva atenció." - -#. 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 està marcat, alguns missatges tenen error d'enviament." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "Funció de treball invàlida: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "Es seguidor" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "Canals de treball" - -#. 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 "Funció de treball" - -#. 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 "Funcions de treball" - -#. 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 "Cua de treballs" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "Gestor de la Cua de treballs" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "Treball seralitzat" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "Treball fallit" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "Treball interromput i marcat com a realitzat: res a realitzar." - -#. 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 "Treballs" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "Gràfic de treball %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 "Últimes 24 hores" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "Últims 30 dies" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "Últims 7 dies" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Última modificació el" - -#. 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 "Última actualització per" - -#. 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 "Última actualització el" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Adjunt principal" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "Marcat manualment com realitzat per %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "Màx. reintents" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Error d'enviament del missatge" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "Missatges" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "Mètode" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "Nom del mètode" - -#. 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 "Model" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "Model {} no trobat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Data límit de la meva activitat" - -#. 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 "Data límit de la següent activitat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Resum de la següent activitat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Tipus de la següent activitat" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "No hi ha accions disponibles per aquest treball" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "No està permés cambiar els camps: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "Nombre d'accions" - -#. 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'errors" - -#. 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 missatges que requereixen accions" - -#. 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 missatges amb error d'enviament" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "Canal pare" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "Canal pare obligatori." - -#. 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 "" - -#. 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 "Pendent" - -#. 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 "Prioritat" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "Cua" - -#. 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 "Cua de treballs" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "Els treballs en cua es creen mitjançant la funció 'with_delay()'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Registre" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "Registre(s)" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "Relacionat" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "Acció relacionada" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "Acció relacionada (en serie)" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "Registre relacionat" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "Registres relacionats" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "Dies restants per executar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "Interval d'eliminació" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "Reencua" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "Reencua el treball" - -#. 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 "Reencua el treball" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Usuari 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 "Resultat" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "Resultats" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "Patró de reintents" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "Patró de reintents (serialitzat)" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "Marcar tots els treballs seleccionats com realitzats" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "Marcar treballs com realitzats" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "Marcar treballs com realitzats" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "Marcar com 'Realitzat'" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Marcar com realitzat" - -#. 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 "Identificador únic del gràfic. Buit per a un únic treball." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Ha sorgit un problema durant l'execució del treball %s. Més detalls a la " -"secció 'Informació sobre l'exepció'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "Data d'inici" - -#. 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 "Iniciat" - -#. 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 "Estat" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "Tasca" - -#. 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 "" - -#. 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 "" -"El treball fallarà si arriba al nombre de reintents màxim.\n" -"Els reintents són infinits quan es deixa buit." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "Els treballs seleccionats seran cancel·lats." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "Els treballs seleccionats es tornaran a posar en cua." - -#. 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 "Els treballs seleccionats es marcaran com realitzats." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "Temps (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 necessari per executar el treball en segons. Mitjana quan s'agrupa." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "Tipus d'acitivitat de l'exepció en el registre." - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id -msgid "User ID" -msgstr "ID de l'usuari" - -#. 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 "Esperant dependències" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Assistent per tornar a posar en cua una selecció de treballs" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "PID del treballador" diff --git a/addons/queue_job/i18n/de.po b/addons/queue_job/i18n/de.po deleted file mode 100644 index 2717a70..0000000 --- a/addons/queue_job/i18n/de.po +++ /dev/null @@ -1,1029 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2025-09-12 16:42+0000\n" -"Last-Translator: davidbeckercbl \n" -"Language-Team: none\n" -"Language: de\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.10.4\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" -" Wie die " -"maximale Anzahl an Versuchen auf 0 gesetzt, so wird es unendlich oft " -"wiederholt." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "Zugriff verweigert" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "Aktion notwendig" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "Aktivitäten" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Aussehen von Aktivitätsfehlern" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "Aktivitätsstatus" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "Icon für Aktivitätstyp" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "Argumente" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "Anzahl der Anhänge" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "AutoVacuum für Job-Warteschlange" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_base -msgid "Base" -msgstr "Basis" - -#. 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 "Abbrechen" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "Alle ausgewählten Jobs abbrechen" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "Job abbrechen" - -#. 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 "Jobs abbrechen" - -#. 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 "Abgebrochen" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "Abgebrochen durch %s" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "Der Root-Kanal kann nicht geändert werden" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "Der Root-Kanal kann nicht entfernt werden" - -#. 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 "Kanal" - -#. module: queue_job -#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq -msgid "Channel complete name must be unique" -msgstr "Der vollständige Name des Kanals muss eindeutig sein" - -#. 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 "Kanäle" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "Unternehmen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "Vollständiger Methodenname" - -#. 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 "Vollständiger Name" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "Erstellt am" - -#. 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 "Erstellt von" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "Erstellt am" - -#. 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 "Erstellt am" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "Aktueller Versuch" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Aktueller Versuch / max. Anzahl der Wiederholung" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "Datum Abbruch" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "Erledigt am" - -#. 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 "Abhängigkeiten" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "Abhängigkeitsgraf" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "Beschreibung" - -#. 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 "Anzeigename" - -#. 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 "Erledigt" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "Zeit der Einreihung" - -#. 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 "Eingereiht" - -#. 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 "Ausnahme" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "Ausnahmeinformation" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "Ausnahmeinformation" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "Ausnahmenachricht" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "Ausnahmenachricht" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "Ausnahme:" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "Erst ausführen nach" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "Ausführungszeit (durchschnittlich)" - -#. 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 "Fehlgeschlagen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "Feldart" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "Felder" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "Abonnenten" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "Abonnenten (Partner)" - -#. 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 "Font Awesome Icon z.B. fa-tasks" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "Graph" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "Graph Jobs" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Anzahl Graph Jobs" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "Graph 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 "Gruppieren nach" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "Hat Nachricht" - -#. 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 "Icon" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Dies ist das Icon zur Kennzeichnung eines Aktivitätsfehlers." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "Identitätsschlüssel" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction -msgid "If checked, new messages require your attention." -msgstr "" -"Wenn das Häkchen gesetzt ist, erfordern neue Nachrichten Ihre Aufmerksamkeit." - -#. 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 "" -"Wenn das Häkchen gesetzt ist, gibt es einige Nachrichten mit einem " -"Übertragungsfehler." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "Ungültige Job-Funktion: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "Ist Abonnent" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "Job-Kanäle" - -#. 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 "Job-Funktion" - -#. 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 "Job-Funktionen" - -#. 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 "Job-Warteschlange" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "Job-Warteschlangenverwalter" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "Job angeordnet" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "Job ist fehlgeschlagen" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "Job unterbrochen und als Erledigt markiert: Es ist nicht zu tun." - -#. 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 "Jobs" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "Jobs für Graph %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 "Letzte 24 Stunden" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "Letzte 30 Tage" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "Letzte 7 Tage" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Zuletzt geändert am" - -#. 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 "Zuletzt aktualisiert von" - -#. 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 "Zuletzt aktualisiert am" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Haupt-Anhang" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "Manuell als erledigt markiert von: %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "max. Anzahl von Wiederholungen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Nachrichtenübertragungsfehler" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "Nachrichten" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "Methode" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "Methodenname" - -#. 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 "Modell" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "Modell {} nicht gefunden" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Meine Aktivitätsfrist" - -#. 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 "Bezeichnung" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Fälligkeit der nächsten Aktivität" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Zusammenfassung der nächsten Aktivität" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Typ der nächsten Aktivität" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "Für diesen Job ist keine Aktion verfügbar" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "Es ist nicht erlaubt, folgende(s) Feld(er) zu ändern: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "Anzahl der Aktionen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "Anzahl der Fehler" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "Anzahl der Meldungen, die eine Aktion erfordern" - -#. 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 "Das ist die Anzahl von Nachrichten mit Übermittlungsfehler" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "Übergeordneter Kanal" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "Es ist ein übergeordneter Kanal notwendig." - -#. 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 "" -"Muster, das aus der Anzahl der Wiederholungsversuche bei wiederholbaren " -"Fehlern die Anzahl der Sekunden ableitet, um die nächste Ausführung zu " -"verzögern. Wird die Anzahl der Sekunden als 2-Element-Tupel oder Liste " -"angegeben, erfolgt eine Zufallsauswahl des Wiederholungsintervalls innerhalb " -"der beiden Werte.\n" -"Beispiel: {1: 10, 5: 20, 10: 30, 15: 300}.\n" -"Beispiel: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\n" -"Weitere Einzelheiten sind der Modulbeschreibung zu entnehmen." - -#. 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 "Ausstehend" - -#. 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ät" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "Warteschlange" - -#. 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 "Job einreihen" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "Job einreihen Sperre" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "Eingereihte Jobs müssen mit 'with_delay()' erstellt worden sein." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Datensatz" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "Datensatz/Datensätze" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "Zugehörige Aktion anzeigen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "Zugehörige Aktion" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "Zugehörige Aktion (serialisiert)" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "Zugehöriger Datensatz" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "Zugehörige Datensätze" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "Verbleibende Tage bis zur Ausführung" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "Entfernungsintervall" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "Erneut einreihen" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "Job erneut einreihen" - -#. 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 "Jobs erneut einreihen" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Verantwortlicher Benutzer" - -#. 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 "Ergebnis" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "Ergebnisse" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "Wiederholungsmuster" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "Wiederholungsmuster (serialisiert)" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "Alle ausgewählten Jobs als Erledigt markieren" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "Jobs als Erledigt markieren" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "Jobs als Erledigt markieren" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "Als Erledigt markieren" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Als Erledigt markieren" - -#. 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 "" -"Einfache geteilte Identität eines Graphen. Leer für einen einzelnen Job." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Etwas Schlechtes ist bei der Ausführung des Jobs %s passiert. Mehr Details " -"finden Sie im Abschnitt 'Ausnahmeninformation'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "Gestartet am" - -#. 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 "Gestartet" - -#. 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 "Status" - -#. 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 "" -"Der Status hängt von den Aktivitäten ab.\n" -"Überfällig: Das Fälligkeitsdatum der Aktivität ist überschritten.\n" -"Heute: Die Aktivität findet heute statt.\n" -"Geplant: Die Aktivitäten findet in der Zukunft statt." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "Aufgabe" - -#. 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 "" -"Die Aktion, die ausgeführt wird, wenn die Schaltfläche *Zugehörige Aktion* " -"bei einem Job verwendet wird. Die Standardaktion besteht darin, die Ansicht " -"des mit dem Job verknüpften Datensatzes zu öffnen. Konfiguriert als " -"Dictionary mit optionalen Schlüsseln: enable, func_name, kwargs.\n" -"Weitere Einzelheiten sind der Modulbeschreibung zu entnehmen." - -#. 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 "" -"Der Job wird fehlschlagen, wenn die Anzahl der Versuche gleich der maximalen " -"Anzahl der Wiederholungen ist.\n" -"Wenn Letzteres nicht gesetzt ist, werden unendlich viele Versuche " -"unternommen." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "Die markierten Jobs werden abgebrochen." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "Die ausgewählten Jobs werden erneut eingereiht." - -#. 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 "Die ausgewählten Jobs werden als Erledigt markiert." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "Zeit (in Sekunden)" - -#. 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 "" -"Benötigte Zeit zur Ausführung dieses Jobs in Sekunden. Bei Gruppierung wird " -"der Durchschnitt ermittelt." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "Typ der Ausnahmeaktivität im Datensatz." - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" -"Unerwartetes Format der zugehörigen Aktion für {}.\n" -"Beispiel für ein gültiges Format:\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 -#, python-format -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 "" -"Unerwartetes Format des Wiederholungsmusters für {}.\n" -"Beispiele für gültige Formate:\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 "Benutzer" - -#. 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 "Warten Abhängigkeiten" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Assistent zur erneuten Einreihung einer Job-Auswahl" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "Arbeiter Prozess-Id" - -#, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "Wenn beide Parameter 0 sind, werden ALLE Jobs neu eingereiht!" - -#, python-format -#~ msgid "" -#~ "Something bad happened during the execution of the job. More details in " -#~ "the 'Exception Information' section." -#~ msgstr "" -#~ "Bei der Ausführung des Jobs ist etwas Ungewöhnliches passiert. Beachten " -#~ "Sie die Details im Abschnitt \"Exception-Information\"." - -#~ msgid "SMS Delivery error" -#~ msgstr "Fehler bei der SMS Nachrichtenübermittlung" - -#~ msgid "Number of messages which requires an action" -#~ msgstr "Das ist die Anzahl von Nachrichten, die eine Aktion benötigen" - -#~ msgid "" -#~ " If the max. retries is 0, the number " -#~ "of retries is infinite." -#~ msgstr "" -#~ "Wenn die maximale Anzahl der " -#~ "Wiederholung auf 0 gesetzt ist, wird dies als unendlich interpretiert." - -#~ msgid "Override Channel" -#~ msgstr "Kanal überschreiben" - -#~ msgid "Number of unread messages" -#~ msgstr "Das ist die Anzahl von ungelesenen Nachrichten" diff --git a/addons/queue_job/i18n/es.po b/addons/queue_job/i18n/es.po deleted file mode 100644 index 0580170..0000000 --- a/addons/queue_job/i18n/es.po +++ /dev/null @@ -1,1033 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 15.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-12-01 15:33+0000\n" -"Last-Translator: Ivorra78 \n" -"Language-Team: none\n" -"Language: es\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 4.17\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 el máx. " -"reintentos es 0, el número de reintentos es infinito." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "Acceso denegado" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "Acción requerida" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "Actividades" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Decoración de Actividad de Excepción" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "Estado de la actividad" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "Icono de tipo de actividad" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "Argumentos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "Nº de archivos adjuntos" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "Vaciado automático de la cola de trabajos" - -#. 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 "Cancelar" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "Cancelar todos los trabajos seleccionados" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "Cancelar trabajo" - -#. 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 "Cancelar trabajos" - -#. 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 "Cancelada" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "Cancelado por %s" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "No se puede cambiar el canal raíz" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "No se puede eliminar el canal raíz" - -#. 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 "El nombre completo del canal debe ser único" - -#. 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 "Canales" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "Empresa" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "Nombre completo del método" - -#. 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 "Nombre completo" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "Fecha de creación" - -#. 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 "Creado por" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "" - -#. 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 "Creado el" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "Intento actual" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Intento actual / reintentos máx" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "Fecha de cancelación" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "Fecha de realización" - -#. 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 "Dependencias" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "Gráfico de dependencias" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "Descripción" - -#. 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 "Nombre mostrado" - -#. 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 "Hecho" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "Hora en que se puso en cola" - -#. 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 "En la cola" - -#. 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 "Excepción" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "Información de la excepción" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "Información de la excepción" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "Mensaje de la excepción" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "Mensaje de la excepción" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "Excepción:" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "Ejecutar solo después de" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "Duración media de ejecución" - -#. 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 "Fallido" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "Tipo de campo" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "Campos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "Seguidores" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "Seguidores (Socios)" - -#. 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 "Icono de Font Awesome ej. fa-tasks" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "Gráfico" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "Gráfico de trabajos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Nº de gráfico de trabajos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "UUID del Gráfico" - -#. 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 "Agrupar por" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "Tiene un mensaje" - -#. 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 "Icono" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Icono para indicar una actividad de excepción." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "Clave identificadora" - -#. 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 se encuentra seleccionado, hay nuevos mensajes que requieren tu atención." - -#. 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 se encuentra seleccionado, algunos mensajes tienen error de envío." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "Función del trabajo no válida: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "Es un seguidor" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "Canales de trabajos" - -#. 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 "Función del trabajo" - -#. 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 "Funciones de los trabajos" - -#. 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 "Cola de trabajos" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "Gestor de la cola de trabajos" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "Trabajo en serie" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "Trabajo fallido" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "Trabajo interrumpido y marcado como hecho: nada que hacer." - -#. 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 "Trabajos" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "Trabajos para gráfico %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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Última modificación el" - -#. 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 "Última actualización por" - -#. 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 "Última actualización el" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Adjuntos principales" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "Marcado como hecho a mano por %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "Reintentos máx" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Error de Envío de Mensaje" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "Mensajes" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "Método" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "Nombre del método" - -#. 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 "Modelo" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "No se ha encontrado el modelo {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Mi fecha límite de actividad" - -#. 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 "Nombre" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Fecha límite de siguiente actividad" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Resumen de la siguiente actividad" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Siguiente tipo de actividad" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "No hay ninguna acción disponible para este trabajo" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "No se permite cambiar los campos: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "Número de acciones" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "Numero de errores" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "Número de mensajes que requieren una acción" - -#. 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 "Número de mensajes con error de envío" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "Canal padre" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "Se requiere un canal padre." - -#. 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 "" -"Patrón que expresa cuántos segundos se pospondrá la próxima ejecución, " -"basado en el número de reintentos de los errores reintentables. Si se usa " -"una tupla o lista de 2 elementos para expresar el número de segundos, se " -"escogerá un número aleatorio entre ambos valores.\n" -"Ejemplo: {1: 10, 5: 20, 10: 30, 15: 300}.\n" -"Ejemplo: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\n" -"Vea la descripción del módulo para más detalles." - -#. 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 "Pendiente" - -#. 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 "Prioridad" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "Cola" - -#. 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 "Cola de trabajos" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "Los trabajos en cola deben crearse llamando a 'with_delay()'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Registro" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "Registro(s)" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "Relacionado" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "Acción relacionada" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "Acción relacionada (en serie)" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "Registro relacionado" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "Registros relacionados" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "Días restantes para ejecutar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "Intervalo de eliminación" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "Volver a poner en la cola" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "Volver a poner el trabajo en la cola" - -#. 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 "Volver a poner los trabajos en la cola" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Usuario 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 "Resultado" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "Resultados" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "Patrón de reintentos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "Patrón de reintentos (en serie)" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "Marcar como hechos todos los trabajos seleccionados" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "Marcar trabajos como hechos" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "Marcar trabajos como hechos" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "Marcar como 'Hecho'" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Marcar como hecho" - -#. 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 "" -"Identificador único compartido de un gráfico. Vacío para un solo trabajo." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Algo malo ocurrió durante la ejecución del trabajo %s. Más detalles en la " -"sección 'Información sobre excepciones'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "Fecha de inicio" - -#. 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 "Iniciado" - -#. 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 "Estado" - -#. 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 "" -"Estado basado en actividades\n" -"Vencida: la fecha tope ya ha pasado\n" -"Hoy: La fecha tope es hoy\n" -"Planificada: futuras actividades." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "Tarea" - -#. 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 "" -"Acción cuando se usa el botón *Acción relacionada* en un trabajo. La acción " -"por defecto es abrir la vista del registro relacionado con el trabajo. Se " -"configura como un diccionario con estas claves opcionales: enable, " -"func_name, kwargs.\n" -"Vea la descripción del módulo para más detalles." - -#. 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 "" -"El trabajo fallará si alcanza el máx. de re intentos.\n" -"Los reintentos son infinitos si se deja vacío." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "Los trabajos seleccionados serán cancelados." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "Los trabajos seleccionados volverán a ponerse en la cola." - -#. 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 "Los trabajos seleccionados se marcarán como hechos." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "Tiempo (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 "" -"Tiempo requerido para ejecutar este trabajo en segundos. Promedio cuando se " -"agrupa." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "Tipo de actividad de excepción registrada." - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" -"Formato inesperado en la acción relacionada con {}.\n" -"Ejemplo de un formato válido:\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 -#, python-format -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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id -msgid "User ID" -msgstr "ID de usuario" - -#. 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 "Esperando dependencias" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Asistente para volver a poner en cola una selección de trabajos" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "Pid del trabajador" - -#, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "" -#~ "Si ambos parámetros son 0, ¡TODOS los trabajos se volverán a poner en la " -#~ "cola!" - -#~ msgid "Jobs Garbage Collector" -#~ msgstr "Recolector de basura de trabajos" - -#, python-format -#~ msgid "" -#~ "Unexpected format of Retry Pattern for {}.\n" -#~ "Example of valid format:\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" -#~ msgstr "" -#~ "Formato inesperado en el patrón de reintentos de {}.\n" -#~ "Ejemplo de un formato válido:\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" - -#, python-format -#~ msgid "" -#~ "Something bad happened during the execution of the job. More details in " -#~ "the 'Exception Information' section." -#~ msgstr "" -#~ "Algo malo pasó durante la ejecución del trabajo. Más detalles en la " -#~ "sección 'Información de la excepción'." - -#~ msgid "SMS Delivery error" -#~ msgstr "Error de entrega del SMS" - -#~ msgid "Number of messages which requires an action" -#~ msgstr "Número de mensajes que requieren una acción" - -#~ msgid "" -#~ " If the max. retries is 0, the number " -#~ "of retries is infinite." -#~ msgstr "" -#~ " Si los reintentos máximos son 0, el " -#~ "número de reintentos es infinito." - -#~ msgid "Override Channel" -#~ msgstr "Sobreescribir canal" - -#~ msgid "Number of unread messages" -#~ msgstr "Número de mensajes no leidos" diff --git a/addons/queue_job/i18n/fr.po b/addons/queue_job/i18n/fr.po deleted file mode 100644 index 0a03d54..0000000 --- a/addons/queue_job/i18n/fr.po +++ /dev/null @@ -1,1006 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-04-17 09:07+0000\n" -"Last-Translator: Vincent Hatakeyama \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 4.17\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 maximum " -"d'essais est défini à 0, le nombre d'essais sera infini." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -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 "Action nécessaire" - -#. 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 "Activité exception décoration" - -#. 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__args -msgid "Args" -msgstr "Arguments" - -#. 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 -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "Nettoyage 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ée" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "Annulée par %s" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "Le canal racine ne peut pas être modifié" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "Le canal racine ne peut être supprimé" - -#. 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 "" - -#. 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 actuelle" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Tentative actuelle / tentatives maximales" - -#. 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 de fin" - -#. 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 "Nom affiché" - -#. 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 "Heure 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 "Dans la 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 "Information sur l’exception" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "Information 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 uniquement 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 "En erreur" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "Type de 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 par 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 "Graphe des tâches" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Nombre de graphe de tâches" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "Identifiant unique du graphe" - -#. 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 "Identifiant" - -#. 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 indiquant 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’identité" - -#. 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 demandent 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 -#, python-format -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 un abonné" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "Canaux de tâche" - -#. 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 de tâche" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "Gestionnaire de file d’attente de tâche" - -#. 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 en échec" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "Tâche interrompue et mise à Terminé : il n’y a rien à faire." - -#. 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 -#, python-format -msgid "Jobs for graph %s" -msgstr "Tâches pour le graphe %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__kwargs -msgid "Kwargs" -msgstr "Arguments par clé" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 24 hours" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Dernière modification le" - -#. 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 "Dernière mise à jour 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 "Dernière mise à jour le" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Attachement principal" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "Mis en terminé manuellement par %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "Tentatives maximales" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Erreur d’envoi 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 la 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 -#, python-format -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 "Échéance de mon 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 "Date limite de l’activité à venir" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Résumé de l’activité suivante" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Type de l'activité suivante" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "Pas d’action disponible pour cette tâche" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -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 des erreurs d’envoi" - -#. 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 -#, python-format -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 indiquant le nombre de secondes de délai de la prochaine exécution à " -"partir du nombre de tentative d’essai sur les erreurs pouvant être tentées à " -"nouveau. Indiquer un couple ou une liste pour le nombre de seconde va rendre " -"aléatoire la sélection de l’intervalle parmi les 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 les 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 "Tâche en file d’attente" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "" -"Les tâches de file d’attente doivent être créé en appelant « with_delay() »." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Enregistrement" - -#. 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 "Rattaché" - -#. 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 -#, python-format -msgid "Related Record" -msgstr "Enregistrement rattaché" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "Enregistrements rattaché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 d’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 "Remettre dans la 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 dans la 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 dans la file d’attente" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Utilisateur 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 "Mettre toutes les tâches sélectionnées en terminé" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "Mettre les tâches terminées" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "Mettre les tâches en terminé" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "Mis en terminé" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Mis en 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 partagé d’un graphe. Vide pour une tâche unique." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Un problème a été rencontré lors de l’exécution de la tâche %s. La section " -"« Information d’exception » contient plus de détails." - -#. 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 "Démarré" - -#. 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 passera en échec si le nombre de tentative atteint le nombre de " -"tentatives maximales.\n" -"Les tentatives sont infinies si vide." - -#. 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 annulées." - -#. 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 sont 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 mise à l’état terminé." - -#. 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 "" - -#. 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 -#, python-format -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 -#, python-format -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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id -msgid "User ID" -msgstr "Identifiant 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 "Dépendances en attente" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Assistant de remise de tâches en file d’attente" - -#. 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" - -#, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "" -#~ "Si les deux paramètres sont à 0, toutes les tâches seront remises en file " -#~ "d’attente !" - -#~ msgid "Jobs Garbage Collector" -#~ msgstr "Ramasse-miette des tâches" - -#, python-format -#~ msgid "" -#~ "Unexpected format of Retry Pattern for {}.\n" -#~ "Example of valid format:\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" -#~ msgstr "" -#~ "Format inattendu pour le modèle des tentatives de {}.\n" -#~ "Exemple de format valide :\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" diff --git a/addons/queue_job/i18n/it.po b/addons/queue_job/i18n/it.po deleted file mode 100644 index 5baf580..0000000 --- a/addons/queue_job/i18n/it.po +++ /dev/null @@ -1,1005 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2026-01-05 19:43+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.10.4\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" -" Se il massimo " -"di tentativi è 0, il numero di tentativi è infinito." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "Accesso negato" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "Azione richiesta" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "Attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Decorazione eccezione attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "Stato attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "Icona tipo attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "Argomenti" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "Conteggio allegati" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "Auto pulizia coda lavoro" - -#. 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 "Annulla" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "Annulla tutti i lavori selezionati" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "Annulla lavoro" - -#. 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 "Annulla lavori" - -#. 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 "Annullata" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "Annullata da %s" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "Non si può cambiare il canale radice" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "Non si può rimuovere il canale radice" - -#. 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 "Canale" - -#. module: queue_job -#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq -msgid "Channel complete name must be unique" -msgstr "Il nome completo del canale deve essere univoco" - -#. 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 "Canali" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "Azienda" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "Nome completo metodo" - -#. 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 "Nome completo" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "Data creazione" - -#. 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 "Creato da" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "Data creazione" - -#. 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 "Creato il" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "Tentativo attuale" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Tentativo attuale / massimo tentativi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "Data annullamento" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "Data completamento" - -#. 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 "Dipendenze" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "Grafico dipendenza" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "Descrizione" - -#. 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 "Nome visualizzato" - -#. 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 "Completata" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "Ora accodamento" - -#. 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 "In coda" - -#. 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 "Eccezione" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "Informazioni eccezione" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "Informazioni eccezione" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "Messaggio eccezione" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "Messaggio eccezione" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "Eccezione:" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "Eseguire solo dopo" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "Tempo esecuzione (medio)" - -#. 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 "Fallito" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "Tipo campo" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "Campi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "Seguito da" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "Seguito da (partner)" - -#. 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 "Icona Font Awesome es. fa-tasks" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "Grafico" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "Grafico lavori" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Grafico conteggio lavori" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "Grafico 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 "Raggruppa per" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "Ha un messaggio" - -#. 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 "Icona" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "Icona per indicare un'attività eccezione." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "Chiave identità" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction -msgid "If checked, new messages require your attention." -msgstr "Se selezionata, nuovi messaggi richiedono attenzione." - -#. 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 "Se selezionata, alcuni messaggi hanno un errore di consegna." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "Funzione lavoro non valida: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "Segue" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "Canali lavoro" - -#. 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 "Funzione lavoro" - -#. 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 "Funzioni lavoro" - -#. 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 "Coda lavoro" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "Gestore coda lavoro" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "Lavoro serializzato" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "Lavro fallito" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "Lavoro interrotto e impostato a completato: nulla da fare." - -#. 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 "Lavori" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "Lavori per grafico %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 "Ultime 24 ore" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "Ultimi 30 giorni" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "Ultimi 7 giorni" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Ultima modifica il" - -#. 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 "Ultimo aggiornamento di" - -#. 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 "Ultimo aggiornamento il" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Allegato principale" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "Impostato manualmente a completato da %s" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "Massimo tentativi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Errore di consegna messaggio" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "Messaggi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "Metodo" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "Nome metodo" - -#. 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 "Modello" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "Modello {} non trovato" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Scadenza mia attività" - -#. 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 "Nome" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Scadenza prossima attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Riepilogo prossima attività" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Tipo prossima attività" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "Nessuna azione disponibile per questo lavoro" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "Non autorizzato a modificare i campi: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "Numero di azioni" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "Numero di errori" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "Numero di messaggi che richiedono un'azione" - -#. 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 "Numero di messaggi con errore di consegna" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "Canale padre" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "Richiesto canale padre." - -#. 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 "" -"Schema derivante dal conteggio dei tentativi degli errori ripetibili, numero " -"di secondi per ritardare l'esecuzione successiva. Impostando il numero di " -"secondi ad una tupla di due elementi o un elenco renderà causale " -"l'intervallo tra i tentativi tra i due valori.\n" -"Esempio: {1: 10, 5: 20, 10: 30, 15: 300}.\n" -"Esempio: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\n" -"Vedere la descrizione del modulo per i dettagli." - -#. 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 "In attesa" - -#. 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 "Coda" - -#. 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 "Lavoro in coda" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "Blocco coda lavoro" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "Il lavoro in coda deve essere creato chiamando 'with_delay()'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Record" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "Record" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "Collegato" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "Azione collegata" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "Azione collegata (serializzata)" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "Record collegato" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "Record collegati" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "Giorni rimanenti all'esecuzione" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "Intervallo rimozione" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "Rimetti in coda" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "Riaccoda lavoro" - -#. 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 "Riaccoda lavori" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Utente responsabile" - -#. 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 "Risultato" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "Risultati" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "Riprova schema" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "Riprova schema (serializzato)" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "Imposta a completati tutti i lavori selezionati" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "Imposta i lavori a completato" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "Imposta i lavori a completato" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "Imposta come 'Completato'" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Imposta a completato" - -#. 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 "" -"Singolo identificatore condiviso di un grafico. Vuoto per un lavoro singolo." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Qualcosa è andato male durante l'esecuzione del lavoro %s. Maggiori dettagli " -"nella sezione 'informazioni eccezione'." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "Data inizio" - -#. 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 "Iniziato" - -#. 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 "Stato" - -#. 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 "" -"Stato in base alle attività\n" -"Scaduto: la data richiesta è trascorsa\n" -"Oggi: la data attività è oggi\n" -"Pianificato: attività future." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "Lavoro" - -#. 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'azione quando si usa il pulsante 'Azione collegata' in un lavoro. L'azione " -"predefinita è di aprire la vista del record collegato al lavoro. Configrata " -"come dizionari con chiavi opzionali: enable, func_name, kwargs.\n" -"Vedere la descrizione del modulo per i dettagli." - -#. 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 "" -"Il lavoro fallirà se il numero di tentativi raggiunge il massimo.\n" -"I tentativi sono infiniti quando vuoto." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "I lavori selezionati verranno annullati." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "I lavori selezionati verranno riaccodati." - -#. 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 "I lavori selezionati verranno impostati a completato." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "Ora (e)" - -#. 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 "" -"Tempo in secondi richiesto per eseguire il lavoro. Medio quando raggruppati." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "Provato molte volte" - -#. 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 "Tipo di attività eccezione sul record." - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" -"Formato inaspettato di azione colegata per {}.\n" -"Esempio di formato valido:\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 -#, python-format -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 "" -"Formatto inatteso dello schema del nuovo tentativo per {}.\n" -"Esempio di formati validi:\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 utente" - -#. 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 "Attesa dipendenze" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Procedura guidata per riaccodare una selezione di lavori" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "PID worker" - -#, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "Se entrambi i parametri sono 0, tutti i lavori verranno riaccodati!" - -#~ msgid "Jobs Garbage Collector" -#~ msgstr "Garbage collector lavori" - -#, python-format -#~ msgid "" -#~ "Unexpected format of Retry Pattern for {}.\n" -#~ "Example of valid format:\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" -#~ msgstr "" -#~ "Formato inaspettato di schema tentativo per {}.\n" -#~ "Esempio di formato valido:\n" -#~ "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" diff --git a/addons/queue_job/i18n/queue_job.pot b/addons/queue_job/i18n/queue_job.pot deleted file mode 100644 index 3a3462a..0000000 --- a/addons/queue_job/i18n/queue_job.pot +++ /dev/null @@ -1,946 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.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 -#: 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_base -msgid "Base" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq -msgid "Channel complete name must be unique" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_icon -msgid "Icon" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction -msgid "If checked, new messages require your attention." -msgstr "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__kwargs -msgid "Kwargs" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 24 hours" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "" - -#. 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 "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__uuid -msgid "UUID" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", \"kwargs\" {{\"limit\": 10}}}}" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id -msgid "User ID" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "" diff --git a/addons/queue_job/i18n/tr.po b/addons/queue_job/i18n/tr.po deleted file mode 100644 index ba8e6bf..0000000 --- a/addons/queue_job/i18n/tr.po +++ /dev/null @@ -1,995 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2025-06-13 15:27+0000\n" -"Last-Translator: Betül Öğmen \n" -"Language-Team: none\n" -"Language: tr\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.10.4\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" -" Eğer maks. " -"deneme sayısı 0 ise, deneme sayısı sonsuz olur." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "Erişim Engellendi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "Eylem Gerekli" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "Aktiviteler" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "Aktivite İstisna Dekorasyonu" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "Aktivite Durumu" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "Aktivite Türü Simgesi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "Argümanlar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "Ek Sayısı" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "Otomatik Temizleme İş Kuyruğu" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_base -msgid "Base" -msgstr "Temel" - -#. 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 "İptal" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "Tüm seçili işleri iptal et" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "İşi iptal et" - -#. 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 "İşleri iptal et" - -#. 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 "İptal Edildi" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "%s tarafından iptal edildi" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "Kök kanal değiştirilemez" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "Kök kanal silinemez" - -#. 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 "Kanal" - -#. module: queue_job -#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq -msgid "Channel complete name must be unique" -msgstr "Kanal tam adı benzersiz olmalıdır" - -#. 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 "Kanallar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "Şirket" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "Tam Metot Adı" - -#. 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 "Tam Adı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "Oluşturulma Tarihi" - -#. 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 "Oluşturan" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "Oluşturulma tarihi" - -#. 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 "Oluşturuldu" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "Şu anki deneme" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "Şu anki deneme / maks. deneme" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "İptal Edilme Tarihi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "Tamamlanma Tarihi" - -#. 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 "Bağımlılıklar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "Bağımlılık Grafiği" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "Açıklama" - -#. 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 "Görünüm Adı" - -#. 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 "Tamamlandı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "Sıraya Alınma Zamanı" - -#. 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 "Sıraya Alındı" - -#. 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 "İstisna" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "İstisna Bilgisi" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "İstisna Bilgisi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "İstisna Mesajı" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "İstisna mesajı" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "İstisna:" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "Bundan sonra çalıştır" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "Çalıştırma Zamanı (ort)" - -#. 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 "Başarısız" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "Alan Tipi" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "Alanlar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "Takipçiler" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "Takipçiler (İş Ortakları)" - -#. 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 "Font awesome simgeleri ör. fa-tasks" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "Grafik" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "Grafiğin İşleri" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "Grafiğin İş Sayısı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "Grafik 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 "Gruplandı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "Mesajı Var" - -#. 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 "Simge" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "İstisna etkinliğini gösteren simge." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "Benzersiz Anahtar" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction -msgid "If checked, new messages require your attention." -msgstr "İşaretlenirse, sizi bekleyen mesajlar var." - -#. 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 "İşaretlenirse, bazı mesajlar teslimat hatası içerir." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "Geçersiz iş fonksiyonu: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "Takipçi" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "İş Kanalları" - -#. 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 "İş Fonksiyonu" - -#. 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 "İş Fonksiyonları" - -#. 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 "İş Kuyruğu" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "İş Kuyruğu Yöneticisi" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -msgid "Job Serialized" -msgstr "Serileştirilmiş İş" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "İş başarısız oldu" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "İş yarıda kesildi ve bitti olarak ayarlandı: yapılacak bir şey yok." - -#. 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 "İşler" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "%s grafiğinin işleri" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__kwargs -msgid "Kwargs" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 24 hours" -msgstr "Son 24 saat" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "Son 30 gün" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "Son 7 gün" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "Son Değiştirme" - -#. 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 "Son Güncelleyen" - -#. 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 "Son Güncelleme" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "Ana Ek" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "%s tarafından tamamlandı olarak ayarlandı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "Maks. deneme" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "Mesaj Teslimat hatası" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "Mesajlar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -msgid "Method" -msgstr "Yöntem" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "Yöntem Adı" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "Model {} bulunamadı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "Aktivite Son Tarihim" - -#. 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 "Ad" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "Sonraki Aktivite Son Tarihi" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "Sonraki Aktivite Özeti" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "Sonraki Aktivite Türü" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "Bu iş için uygun bir eylem yok" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "Alan(lar)ı değiştirme izni yok: {}" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "Eylem Sayısı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "Hata sayısı" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "Eylem gerektiren mesaj sayısı" - -#. 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 "Teslimat hatası içeren mesaj sayısı" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "Üst Kanal" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "Üst kanal gerekli." - -#. 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 "" -"Yeniden denenebilir hatalarda yeniden deneme sayısından, bir sonraki " -"çalışmanın ertelenmesi için saniye sayısını ifade eden desen. Saniye " -"sayısını 2 elemanlı bir demet veya liste olarak ayarlama, yeniden deneme " -"aralığını 2 değer arasında rastgele hale getirir.\n" -"Örneğin: {1: 10, 5: 20, 10: 30, 15: 300}.\n" -"Örneğin: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\n" -"Ayrıntılar için modül açıklamasına bakınız." - -#. 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 "Beklemede" - -#. 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 "Öncelik" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "Sıra" - -#. 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 "Kuyruk İşi" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "Kuyruk İş Kilidi" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "Kuyruk işleri 'with_delay()' çağrılarak oluşturulmalıdır." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "Kayıt" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -msgid "Record(s)" -msgstr "Kayıt(lar)" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "İlgili" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -msgid "Related Action" -msgstr "İlgili Eylem" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "İlgili Eylem (serileştirilmiş)" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "İlgili Kayıt" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "İlgili Kayıtlar" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "Çalıştırılacak kalan günler" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "Kaldırma Aralığı" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "Yeniden sıraya al" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "İşi yeniden sıraya al" - -#. 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 "İşleri Tekrar Sıraya Al" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "Sorumlu Kullanıcı" - -#. 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 "Sonuç" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "Sonuçlar" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "Tekrar Şablonu" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "Tekrar Şablonu (serileştirilmiş)" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "Seçili tüm işleri tamamlandı olarak işaretle" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "İşleri tamamlandı olarak işaretle" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "İşleri tamamlandı olarak işaretle" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "'Tamamlandı' olarak işaretle" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "Tamamlandı olarak işaretle" - -#. 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 "Bir grafiğin paylaşılan tanımlayıcısı. Tek işler için değeri boştur." - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" -"Bu işi yaparken bir şeyler ters gitti %s. 'İstisna Bilgisi' kısmında daha " -"fazla detaya erişebilirsiniz." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "Başlama Tarihi" - -#. 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 "Başladı" - -#. 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 "Durum" - -#. 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 "" -"Aktivitelere dayalı durum\n" -"Geçikmiş: Son tarih zaten geçti\n" -"Bugün: Aktivite tarihi bugün\n" -"Planlandı: Gelecekteki aktiviteler." - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "Görev" - -#. 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 "" -"İşteki *İlgili Eylem* düğmesi kullanıldığında gerçekleşen eylem. Varsayılan " -"eylem, işle ilişkili kaydın görünümünü açmaktır. İsteğe bağlı anahtarlar ile " -"yapılandırılmış bir sözlük: etkinleştir, func_name, kwargs.\n" -"Ayrıntılar için modül açıklamasına bakınız." - -#. 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 "" -"Eğer tekrar sayısı maks. deneme sayısına ulaşırsa iş başarısız olacaktır.\n" -"Boş olduğunda sonsuz tekrar yapar." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "Seçilen işler iptal edilecektir." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "Seçilen işler tekrar kuyruğa alınacaktır." - -#. 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 "Seçilen işler tamamlandı olarak ayarlanacaktır." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "Süre (sn)" - -#. 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 "" -"Saniye cinsinden bu işi yapmak için gereken süre. Gruplandığında ortalaması " -"alınır." - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "Kayıttaki istisna aktivitesinin türü." - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" -"İlgili eylem için beklenmeyen biçim {}.\n" -"Doğru biçim örneği:\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 -#, python-format -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 "" -"{} için beklenmeyen tekrarlama şablonu.\n" -"Geçerli kullanım örnekleri:\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 "Kullanıcı ID" - -#. 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 "Bağımlılıklar Bekleniyor" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "Seçilen işleri tekrar sıraya alan sihirbaz" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "Çalışan Pid" - -#, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "Her iki parametre de 0 ise, TÜM işler tekrar sıraya alınacaktır!" - -#~ msgid "Jobs Garbage Collector" -#~ msgstr "İş Çöp Toplayıcısı" diff --git a/addons/queue_job/i18n/zh_CN.po b/addons/queue_job/i18n/zh_CN.po deleted file mode 100644 index f75cca8..0000000 --- a/addons/queue_job/i18n/zh_CN.po +++ /dev/null @@ -1,996 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * queue_job -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2022-08-13 08:07+0000\n" -"Last-Translator: Dong \n" -"Language-Team: none\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.3.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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Access Denied" -msgstr "拒绝访问" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction -msgid "Action Needed" -msgstr "前置操作" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_ids -msgid "Activities" -msgstr "活动" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "活动异常装饰" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_state -msgid "Activity State" -msgstr "活动状态" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__args -msgid "Args" -msgstr "位置参数" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_attachment_count -msgid "Attachment Count" -msgstr "附件数量" - -#. module: queue_job -#: model:ir.actions.server,name:queue_job.ir_cron_autovacuum_queue_jobs_ir_actions_server -#: model:ir.cron,cron_name:queue_job.ir_cron_autovacuum_queue_jobs -msgid "AutoVacuum Job Queue" -msgstr "自动清空作业队列" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_base -msgid "Base" -msgstr "基础" - -#. 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 "取消" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_cancelled -msgid "Cancel all selected jobs" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Cancel job" -msgstr "" - -#. 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 "" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Cancelled by %s" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot change the root channel" -msgstr "无法更改root频道" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Cannot remove the root channel" -msgstr "无法删除root频道" - -#. 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 "频道" - -#. module: queue_job -#: model:ir.model.constraint,message:queue_job.constraint_queue_job_channel_name_uniq -msgid "Channel complete name must be unique" -msgstr "频道完整名称必须是唯一的" - -#. 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 "频道" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__company_id -msgid "Company" -msgstr "公司" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__channel_method_name -msgid "Complete Method Name" -msgstr "" - -#. 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 "完整名称" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_created -msgid "Created Date" -msgstr "创建日期" - -#. 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 "创建者" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Created date" -msgstr "" - -#. 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 "创建时间" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__retry -msgid "Current try" -msgstr "当前尝试" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Current try / max. retries" -msgstr "当前尝试/最大重试次数" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_cancelled -msgid "Date Cancelled" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_done -msgid "Date Done" -msgstr "完成日期" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__dependency_graph -msgid "Dependency Graph" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__name -msgid "Description" -msgstr "说明" - -#. 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 "显示名称" - -#. 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 "完成" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_enqueued -msgid "Enqueue Time" -msgstr "排队时间" - -#. 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 "排队" - -#. 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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_info -msgid "Exception Info" -msgstr "异常信息" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception Information" -msgstr "异常信息" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exc_message -msgid "Exception Message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Exception message" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Exception:" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__eta -msgid "Execute only after" -msgstr "仅在此之后执行" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__exec_time -msgid "Execution Time (avg)" -msgstr "" - -#. 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 "失败" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_ir_model_fields__ttype -msgid "Field Type" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_ir_model_fields -msgid "Fields" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_follower_ids -msgid "Followers" -msgstr "关注者" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_partner_ids -msgid "Followers (Partners)" -msgstr "关注者(业务伙伴)" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Graph" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Graph Jobs" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_jobs_count -msgid "Graph Jobs Count" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__graph_uuid -msgid "Graph UUID" -msgstr "" - -#. 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 "分组" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__has_message -msgid "Has Message" -msgstr "" - -#. 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 "图标" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "指示异常活动的图标。" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__identity_key -msgid "Identity Key" -msgstr "身份密钥" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction -msgid "If checked, new messages require your attention." -msgstr "确认后, 出现提示消息。" - -#. 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 "如果勾选此项, 某些消息将会产生传递错误。" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Invalid job function: {}" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_is_follower -msgid "Is Follower" -msgstr "关注者" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_channel -msgid "Job Channels" -msgstr "作业频道" - -#. 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 "作业函数" - -#. 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 "作业函数" - -#. 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 "作业队列" - -#. module: queue_job -#: model:res.groups,name:queue_job.group_queue_job_manager -msgid "Job Queue Manager" -msgstr "作业队列管理员" - -#. module: queue_job -#: model:ir.model.fields.selection,name:queue_job.selection__ir_model_fields__ttype__job_serialized -#, fuzzy -msgid "Job Serialized" -msgstr "作业失败" - -#. module: queue_job -#: model:mail.message.subtype,name:queue_job.mt_job_failed -msgid "Job failed" -msgstr "作业失败" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/controllers/main.py:0 -#, python-format -msgid "Job interrupted and set to Done: nothing to do." -msgstr "作业中断并设置为已完成:无需执行任何操作。" - -#. 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 "作业" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Jobs for graph %s" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__kwargs -msgid "Kwargs" -msgstr "关键字参数" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 24 hours" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 30 days" -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Last 7 days" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done____last_update -#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job____last_update -msgid "Last Modified on" -msgstr "最后修改日" - -#. 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 "最后更新者" - -#. 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 "最后更新时间" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_main_attachment_id -msgid "Main Attachment" -msgstr "附件" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Manually set to done by %s" -msgstr "由%s手动设置为完成" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__max_retries -msgid "Max. retries" -msgstr "最大重试次数" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error -msgid "Message Delivery error" -msgstr "消息递送错误" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_ids -msgid "Messages" -msgstr "消息" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__method -#, fuzzy -msgid "Method" -msgstr "方法名称" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__method_name -msgid "Method Name" -msgstr "方法名称" - -#. 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 "模型" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -msgid "Model {} not found" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. 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 "名称" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "下一活动截止日期" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_summary -msgid "Next Activity Summary" -msgstr "下一活动摘要" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_type_id -msgid "Next Activity Type" -msgstr "下一活动类型" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "No action available for this job" -msgstr "此作业无法执行任何操作" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Not allowed to change field(s): {}" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_needaction_counter -msgid "Number of Actions" -msgstr "操作次数" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__message_has_error_counter -msgid "Number of errors" -msgstr "错误数量" - -#. module: queue_job -#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "" - -#. 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 "递送错误消息数量" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__parent_id -msgid "Parent Channel" -msgstr "父频道" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_channel.py:0 -#, python-format -msgid "Parent channel required." -msgstr "父频道必填。" - -#. 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 "" - -#. 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 "等待" - -#. 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 "优先级" - -#. module: queue_job -#: model:ir.ui.menu,name:queue_job.menu_queue -msgid "Queue" -msgstr "队列" - -#. 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 "队列作业" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_job_lock -msgid "Queue Job Lock" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Queue jobs must be created by calling 'with_delay()'." -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__record_ids -msgid "Record" -msgstr "记录" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__records -#, fuzzy -msgid "Record(s)" -msgstr "记录" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Related" -msgstr "相关的" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_related_action -#, fuzzy -msgid "Related Action" -msgstr "相关记录" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__related_action -msgid "Related Action (serialized)" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Record" -msgstr "相关记录" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "Related Records" -msgstr "相关记录" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_tree -msgid "Remaining days to execute" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__removal_interval -msgid "Removal Interval" -msgstr "清除间隔" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "Requeue" -msgstr "重新排队" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Requeue Job" -msgstr "重新排队作业" - -#. 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 "重新排队作业" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__activity_user_id -msgid "Responsible User" -msgstr "负责的用户" - -#. 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 "结果" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Results" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__edit_retry_pattern -msgid "Retry Pattern" -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__retry_pattern -msgid "Retry Pattern (serialized)" -msgstr "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_jobs_to_done -msgid "Set all selected jobs to done" -msgstr "将所有选定的作业设置为完成" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set jobs done" -msgstr "设置作业完成" - -#. module: queue_job -#: model:ir.actions.act_window,name:queue_job.action_set_jobs_done -msgid "Set jobs to done" -msgstr "将作业设置为完成" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Set to 'Done'" -msgstr "设置为“完成”" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_done -msgid "Set to done" -msgstr "设置为完成" - -#. 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 "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job.py:0 -#, python-format -msgid "" -"Something bad happened during the execution of job %s. More details in the " -"'Exception Information' section." -msgstr "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__date_started -msgid "Start Date" -msgstr "开始日期" - -#. 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 "开始" - -#. 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 "状态" - -#. 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 "" -"基于活动的状态\n" -"逾期:已经超过截止日期\n" -"现今:活动日期是当天\n" -"计划:未来的活动。" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__func_string -msgid "Task" -msgstr "任务" - -#. 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 "" - -#. 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 "" -"如果尝试次数达到最大重试次数,作业将失败。\n" -"空的时候重试是无限的。" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_set_jobs_cancelled -msgid "The selected jobs will be cancelled." -msgstr "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_requeue_job -msgid "The selected jobs will be requeued." -msgstr "所选作业将重新排队。" - -#. 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 "所选作业将设置为完成。" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_form -msgid "Time (s)" -msgstr "" - -#. 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 "" - -#. module: queue_job -#: model_terms:ir.ui.view,arch_db:queue_job.view_queue_job_search -msgid "Tried many times" -msgstr "" - -#. 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 "记录的异常活动的类型。" - -#. 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 -#, python-format -msgid "" -"Unexpected format of Related Action for {}.\n" -"Example of valid format:\n" -"{{\"enable\": True, \"func_name\": \"related_action_foo\", " -"\"kwargs\" {{\"limit\": 10}}}}" -msgstr "" - -#. module: queue_job -#. odoo-python -#: code:addons/queue_job/models/queue_job_function.py:0 -#, python-format -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 "" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__user_id -msgid "User ID" -msgstr "用户" - -#. 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 "" - -#. module: queue_job -#: model:ir.model,name:queue_job.model_queue_requeue_job -msgid "Wizard to requeue a selection of jobs" -msgstr "重新排队向导所选的作业" - -#. module: queue_job -#: model:ir.model.fields,field_description:queue_job.field_queue_job__worker_pid -msgid "Worker Pid" -msgstr "" - -#, fuzzy, python-format -#~ msgid "If both parameters are 0, ALL jobs will be requeued!" -#~ msgstr "所选作业将重新排队。" - -#, python-format -#~ msgid "" -#~ "Something bad happened during the execution of the job. More details in " -#~ "the 'Exception Information' section." -#~ msgstr "" -#~ "在执行作业期间发生了一些不好的事情。有关详细信息,请参见“异常信息”部分。" - -#~ msgid "SMS Delivery error" -#~ msgstr "短信传递错误" - -#~ msgid "Number of messages which requires an action" -#~ msgstr "需要操作消息数量" - -#~ msgid "" -#~ " If the max. retries is 0, the number " -#~ "of retries is infinite." -#~ msgstr "" -#~ "如果最大重试次数是0,则重试次数是无限" -#~ "的。" - -#~ msgid "Override Channel" -#~ msgstr "覆盖频道" - -#~ msgid "Number of unread messages" -#~ msgstr "未读消息数量" diff --git a/addons/queue_job/job.py b/addons/queue_job/job.py deleted file mode 100644 index 790e07d..0000000 --- a/addons/queue_job/job.py +++ /dev/null @@ -1,891 +0,0 @@ -# Copyright 2013-2020 Camptocamp -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import hashlib -import inspect -import logging -import os -import sys -import uuid -import weakref -from datetime import datetime, timedelta -from random import randint - -import odoo - -from .exception import FailedJobError, NoSuchJobError, RetryableJobError - -WAIT_DEPENDENCIES = "wait_dependencies" -PENDING = "pending" -ENQUEUED = "enqueued" -CANCELLED = "cancelled" -DONE = "done" -STARTED = "started" -FAILED = "failed" - -STATES = [ - (WAIT_DEPENDENCIES, "Wait Dependencies"), - (PENDING, "Pending"), - (ENQUEUED, "Enqueued"), - (STARTED, "Started"), - (DONE, "Done"), - (CANCELLED, "Cancelled"), - (FAILED, "Failed"), -] - -DEFAULT_PRIORITY = 10 # used by the PriorityQueue to sort the jobs -DEFAULT_MAX_RETRIES = 5 -RETRY_INTERVAL = 10 * 60 # seconds - -_logger = logging.getLogger(__name__) - - -# TODO remove in 15.0 or 16.0, used to keep compatibility as the -# class has been moved in 'delay'. -def DelayableRecordset(*args, **kwargs): - # prevent circular import - from .delay import DelayableRecordset as dr - - _logger.warning( - "DelayableRecordset moved from the queue_job.job" - " to the queue_job.delay python module" - ) - return dr(*args, **kwargs) - - -def identity_exact(job_): - """Identity function using the model, method and all arguments as key - - When used, this identity key will have the effect that when a job should be - created and a pending job with the exact same recordset and arguments, the - second will not be created. - - It should be used with the ``identity_key`` argument: - - .. python:: - - from odoo.addons.queue_job.job import identity_exact - - # [...] - delayable = self.with_delay(identity_key=identity_exact) - delayable.export_record(force=True) - - Alternative identity keys can be built using the various fields of the job. - For example, you could compute a hash using only some arguments of - the job. - - .. python:: - - def identity_example(job_): - hasher = hashlib.sha1() - hasher.update(job_.model_name) - hasher.update(job_.method_name) - hasher.update(str(sorted(job_.recordset.ids))) - hasher.update(str(job_.args[1])) - hasher.update(str(job_.kwargs.get('foo', ''))) - return hasher.hexdigest() - - Usually you will probably always want to include at least the name of the - model and method. - """ - hasher = identity_exact_hasher(job_) - return hasher.hexdigest() - - -def identity_exact_hasher(job_): - """Prepare hasher object for identity_exact.""" - hasher = hashlib.sha1() - hasher.update(job_.model_name.encode("utf-8")) - hasher.update(job_.method_name.encode("utf-8")) - hasher.update(str(sorted(job_.recordset.ids)).encode("utf-8")) - hasher.update(str(job_.args).encode("utf-8")) - hasher.update(str(sorted(job_.kwargs.items())).encode("utf-8")) - return hasher - - -class Job: - """A Job is a task to execute. It is the in-memory representation of a job. - - Jobs are stored in the ``queue.job`` Odoo Model, but they are handled - through this class. - - .. attribute:: uuid - - Id (UUID) of the job. - - .. attribute:: graph_uuid - - Shared UUID of the job's graph. Empty if the job is a single job. - - .. attribute:: state - - State of the job, can pending, enqueued, started, done or failed. - The start state is pending and the final state is done. - - .. attribute:: retry - - The current try, starts at 0 and each time the job is executed, - it increases by 1. - - .. attribute:: max_retries - - The maximum number of retries allowed before the job is - considered as failed. - - .. attribute:: args - - Arguments passed to the function when executed. - - .. attribute:: kwargs - - Keyword arguments passed to the function when executed. - - .. attribute:: description - - Human description of the job. - - .. attribute:: func - - The python function itself. - - .. attribute:: model_name - - Odoo model on which the job will run. - - .. attribute:: priority - - Priority of the job, 0 being the higher priority. - - .. attribute:: date_created - - Date and time when the job was created. - - .. attribute:: date_enqueued - - Date and time when the job was enqueued. - - .. attribute:: date_started - - Date and time when the job was started. - - .. attribute:: date_done - - Date and time when the job was done. - - .. attribute:: result - - A description of the result (for humans). - - .. attribute:: exc_name - - Exception error name when the job failed. - - .. attribute:: exc_message - - Exception error message when the job failed. - - .. attribute:: exc_info - - Exception information (traceback) when the job failed. - - .. attribute:: user_id - - Odoo user id which created the job - - .. attribute:: eta - - Estimated Time of Arrival of the job. It will not be executed - before this date/time. - - .. attribute:: recordset - - Model recordset when we are on a delayed Model method - - .. attribute::channel - - The complete name of the channel to use to process the job. If - provided it overrides the one defined on the job's function. - - .. attribute::identity_key - - A key referencing the job, multiple job with the same key will not - be added to a channel if the existing job with the same key is not yet - started or executed. - - """ - - @classmethod - def load(cls, env, job_uuid): - """Read a single job from the Database - - Raise an error if the job is not found. - """ - stored = cls.db_records_from_uuids(env, [job_uuid]) - if not stored: - raise NoSuchJobError( - "Job %s does no longer exist in the storage." % job_uuid - ) - return cls._load_from_db_record(stored) - - @classmethod - def load_many(cls, env, job_uuids): - """Read jobs in batch from the Database - - Jobs not found are ignored. - """ - recordset = cls.db_records_from_uuids(env, job_uuids) - return {cls._load_from_db_record(record) for record in recordset} - - def add_lock_record(self): - """ - Create row in db to be locked while the job is being performed. - """ - self.env.cr.execute( - """ - INSERT INTO - queue_job_lock (id, queue_job_id) - SELECT - id, id - FROM - queue_job - WHERE - uuid = %s - ON CONFLICT(id) - DO NOTHING; - """, - [self.uuid], - ) - - def lock(self): - """ - Lock row of job that is being performed - - If a job cannot be locked, - it means that the job wasn't started, - a RetryableJobError is thrown. - """ - self.env.cr.execute( - """ - SELECT - * - FROM - queue_job_lock - WHERE - queue_job_id in ( - SELECT - id - FROM - queue_job - WHERE - uuid = %s - AND state='started' - ) - FOR UPDATE; - """, - [self.uuid], - ) - - # 1 job should be locked - if 1 != len(self.env.cr.fetchall()): - raise RetryableJobError( - f"Trying to lock job that wasn't started, uuid: {self.uuid}" - ) - - @classmethod - def _load_from_db_record(cls, job_db_record): - stored = job_db_record - - args = stored.args - kwargs = stored.kwargs - method_name = stored.method_name - - recordset = stored.records - method = getattr(recordset, method_name) - - eta = None - if stored.eta: - eta = stored.eta - - job_ = cls( - method, - args=args, - kwargs=kwargs, - priority=stored.priority, - eta=eta, - job_uuid=stored.uuid, - description=stored.name, - channel=stored.channel, - identity_key=stored.identity_key, - ) - - if stored.date_created: - job_.date_created = stored.date_created - - if stored.date_enqueued: - job_.date_enqueued = stored.date_enqueued - - if stored.date_started: - job_.date_started = stored.date_started - - if stored.date_done: - job_.date_done = stored.date_done - - if stored.date_cancelled: - job_.date_cancelled = stored.date_cancelled - - job_.state = stored.state - job_.graph_uuid = stored.graph_uuid if stored.graph_uuid else None - job_.result = stored.result if stored.result else None - job_.exc_info = stored.exc_info if stored.exc_info else None - job_.retry = stored.retry - job_.max_retries = stored.max_retries - if stored.company_id: - job_.company_id = stored.company_id.id - job_.identity_key = stored.identity_key - job_.worker_pid = stored.worker_pid - - job_.__depends_on_uuids.update(stored.dependencies.get("depends_on", [])) - job_.__reverse_depends_on_uuids.update( - stored.dependencies.get("reverse_depends_on", []) - ) - return job_ - - def job_record_with_same_identity_key(self): - """Check if a job to be executed with the same key exists.""" - existing = ( - self.env["queue.job"] - .sudo() - .search( - [ - ("identity_key", "=", self.identity_key), - ("state", "in", [WAIT_DEPENDENCIES, PENDING, ENQUEUED]), - ], - limit=1, - ) - ) - return existing - - @staticmethod - def db_record_from_uuid(env, job_uuid): - # TODO remove in 15.0 or 16.0 - _logger.debug("deprecated, use 'db_records_from_uuids") - return Job.db_records_from_uuids(env, [job_uuid]) - - @staticmethod - def db_records_from_uuids(env, job_uuids): - model = env["queue.job"].sudo() - record = model.search([("uuid", "in", tuple(job_uuids))]) - return record.with_env(env).sudo() - - def __init__( - self, - func, - args=None, - kwargs=None, - priority=None, - eta=None, - job_uuid=None, - max_retries=None, - description=None, - channel=None, - identity_key=None, - ): - """Create a Job - - :param func: function to execute - :type func: function - :param args: arguments for func - :type args: tuple - :param kwargs: keyworkd arguments for func - :type kwargs: dict - :param priority: priority of the job, - the smaller is the higher priority - :type priority: int - :param eta: the job can be executed only after this datetime - (or now + timedelta) - :type eta: datetime or timedelta - :param job_uuid: UUID of the job - :param max_retries: maximum number of retries before giving up and set - the job state to 'failed'. A value of 0 means infinite retries. - :param description: human description of the job. If None, description - is computed from the function doc or name - :param channel: The complete channel name to use to process the job. - :param identity_key: A hash to uniquely identify a job, or a function - that returns this hash (the function takes the job - as argument) - """ - if args is None: - args = () - if isinstance(args, list): - args = tuple(args) - assert isinstance(args, tuple), "%s: args are not a tuple" % args - if kwargs is None: - kwargs = {} - - assert isinstance(kwargs, dict), "%s: kwargs are not a dict" % kwargs - - if not _is_model_method(func): - raise TypeError("Job accepts only methods of Models") - - recordset = func.__self__ - env = recordset.env - self.method_name = func.__name__ - self.recordset = recordset - - self.env = env - self.job_model = self.env["queue.job"] - self.job_model_name = "queue.job" - - self.job_config = ( - self.env["queue.job.function"].sudo().job_config(self.job_function_name) - ) - - self.state = PENDING - - self.retry = 0 - if max_retries is None: - self.max_retries = DEFAULT_MAX_RETRIES - else: - self.max_retries = max_retries - - self._uuid = job_uuid - self.graph_uuid = None - - self.args = args - self.kwargs = kwargs - - self.__depends_on_uuids = set() - self.__reverse_depends_on_uuids = set() - self._depends_on = set() - self._reverse_depends_on = weakref.WeakSet() - - self.priority = priority - if self.priority is None: - self.priority = DEFAULT_PRIORITY - - self.date_created = datetime.now() - self._description = description - - if isinstance(identity_key, str): - self._identity_key = identity_key - self._identity_key_func = None - else: - # we'll compute the key on the fly when called - # from the function - self._identity_key = None - self._identity_key_func = identity_key - - self.date_enqueued = None - self.date_started = None - self.date_done = None - self.date_cancelled = None - - self.result = None - self.exc_name = None - self.exc_message = None - self.exc_info = None - - if "company_id" in env.context: - company_id = env.context["company_id"] - else: - company_id = env.company.id - self.company_id = company_id - self._eta = None - self.eta = eta - self.channel = channel - self.worker_pid = None - - def add_depends(self, jobs): - if self in jobs: - raise ValueError("job cannot depend on itself") - self.__depends_on_uuids |= {j.uuid for j in jobs} - self._depends_on.update(jobs) - for parent in jobs: - parent.__reverse_depends_on_uuids.add(self.uuid) - parent._reverse_depends_on.add(self) - if any(j.state != DONE for j in jobs): - self.state = WAIT_DEPENDENCIES - - def perform(self): - """Execute the job. - - The job is executed with the user which has initiated it. - """ - self.retry += 1 - try: - self.result = self.func(*tuple(self.args), **self.kwargs) - except RetryableJobError as err: - if err.ignore_retry: - self.retry -= 1 - raise - elif not self.max_retries: # infinite retries - raise - elif self.retry >= self.max_retries: - type_, value, traceback = sys.exc_info() - # change the exception type but keep the original - # traceback and message: - # http://blog.ianbicking.org/2007/09/12/re-raising-exceptions/ - new_exc = FailedJobError( - "Max. retries (%d) reached: %s" % (self.max_retries, value or type_) - ) - raise new_exc from err - raise - - return self.result - - def _get_common_dependent_jobs_query(self): - return """ - UPDATE queue_job - SET state = %s - FROM ( - SELECT child.id, array_agg(parent.state) as parent_states - FROM queue_job job - JOIN LATERAL - json_array_elements_text( - job.dependencies::json->'reverse_depends_on' - ) child_deps ON true - JOIN queue_job child - ON child.graph_uuid = job.graph_uuid - AND child.uuid = child_deps - JOIN LATERAL - json_array_elements_text( - child.dependencies::json->'depends_on' - ) parent_deps ON true - JOIN queue_job parent - ON parent.graph_uuid = job.graph_uuid - AND parent.uuid = parent_deps - WHERE job.uuid = %s - GROUP BY child.id - ) jobs - WHERE - queue_job.id = jobs.id - AND %s = ALL(jobs.parent_states) - AND state = %s; - """ - - def enqueue_waiting(self): - sql = self._get_common_dependent_jobs_query() - self.env.cr.execute(sql, (PENDING, self.uuid, DONE, WAIT_DEPENDENCIES)) - self.env["queue.job"].invalidate_model(["state"]) - - def cancel_dependent_jobs(self): - sql = self._get_common_dependent_jobs_query() - self.env.cr.execute(sql, (CANCELLED, self.uuid, CANCELLED, WAIT_DEPENDENCIES)) - self.env["queue.job"].invalidate_model(["state"]) - - def store(self): - """Store the Job""" - job_model = self.env["queue.job"] - # The sentinel is used to prevent edition sensitive fields (such as - # method_name) from RPC methods. - edit_sentinel = job_model.EDIT_SENTINEL - - db_record = self.db_record() - if db_record: - db_record.with_context(_job_edit_sentinel=edit_sentinel).write( - self._store_values() - ) - else: - job_model.with_context(_job_edit_sentinel=edit_sentinel).sudo().create( - self._store_values(create=True) - ) - - def _store_values(self, create=False): - vals = { - "state": self.state, - "priority": self.priority, - "retry": self.retry, - "max_retries": self.max_retries, - "exc_name": self.exc_name, - "exc_message": self.exc_message, - "exc_info": self.exc_info, - "company_id": self.company_id, - "result": str(self.result) if self.result else False, - "date_enqueued": False, - "date_started": False, - "date_done": False, - "exec_time": False, - "date_cancelled": False, - "eta": False, - "identity_key": False, - "worker_pid": self.worker_pid, - "graph_uuid": self.graph_uuid, - } - - if self.date_enqueued: - vals["date_enqueued"] = self.date_enqueued - if self.date_started: - vals["date_started"] = self.date_started - if self.date_done: - vals["date_done"] = self.date_done - if self.exec_time: - vals["exec_time"] = self.exec_time - if self.date_cancelled: - vals["date_cancelled"] = self.date_cancelled - if self.eta: - vals["eta"] = self.eta - if self.identity_key: - vals["identity_key"] = self.identity_key - - dependencies = { - "depends_on": [parent.uuid for parent in self.depends_on], - "reverse_depends_on": [ - children.uuid for children in self.reverse_depends_on - ], - } - vals["dependencies"] = dependencies - - if create: - vals.update( - { - "user_id": self.env.uid, - "channel": self.channel, - # The following values must never be modified after the - # creation of the job - "uuid": self.uuid, - "name": self.description, - "func_string": self.func_string, - "date_created": self.date_created, - "model_name": self.recordset._name, - "method_name": self.method_name, - "job_function_id": self.job_config.job_function_id, - "channel_method_name": self.job_function_name, - "records": self.recordset, - "args": self.args, - "kwargs": self.kwargs, - } - ) - - vals_from_model = self._store_values_from_model() - # Sanitize values: make sure you cannot screw core values - vals_from_model = {k: v for k, v in vals_from_model.items() if k not in vals} - vals.update(vals_from_model) - return vals - - def _store_values_from_model(self): - vals = {} - value_handlers_candidates = ( - "_job_store_values_for_" + self.method_name, - "_job_store_values", - ) - for candidate in value_handlers_candidates: - handler = getattr(self.recordset, candidate, None) - if handler is not None: - vals = handler(self) - return vals - - @property - def func_string(self): - model = repr(self.recordset) - args = [repr(arg) for arg in self.args] - kwargs = ["{}={!r}".format(key, val) for key, val in self.kwargs.items()] - all_args = ", ".join(args + kwargs) - return "{}.{}({})".format(model, self.method_name, all_args) - - def __eq__(self, other): - return self.uuid == other.uuid - - def __hash__(self): - return self.uuid.__hash__() - - def db_record(self): - return self.db_records_from_uuids(self.env, [self.uuid]) - - @property - def func(self): - recordset = self.recordset.with_context(job_uuid=self.uuid) - return getattr(recordset, self.method_name) - - @property - def job_function_name(self): - func_model = self.env["queue.job.function"].sudo() - return func_model.job_function_name(self.recordset._name, self.method_name) - - @property - def identity_key(self): - if self._identity_key is None: - if self._identity_key_func: - self._identity_key = self._identity_key_func(self) - return self._identity_key - - @identity_key.setter - def identity_key(self, value): - if isinstance(value, str): - self._identity_key = value - self._identity_key_func = None - else: - # we'll compute the key on the fly when called - # from the function - self._identity_key = None - self._identity_key_func = value - - @property - def depends_on(self): - if not self._depends_on: - self._depends_on = Job.load_many(self.env, self.__depends_on_uuids) - return self._depends_on - - @property - def reverse_depends_on(self): - if not self._reverse_depends_on: - self._reverse_depends_on = Job.load_many( - self.env, self.__reverse_depends_on_uuids - ) - return set(self._reverse_depends_on) - - @property - def description(self): - if self._description: - return self._description - elif self.func.__doc__: - return self.func.__doc__.splitlines()[0].strip() - else: - return "{}.{}".format(self.model_name, self.func.__name__) - - @property - def uuid(self): - """Job ID, this is an UUID""" - if self._uuid is None: - self._uuid = str(uuid.uuid4()) - return self._uuid - - @property - def model_name(self): - return self.recordset._name - - @property - def user_id(self): - return self.recordset.env.uid - - @property - def eta(self): - return self._eta - - @eta.setter - def eta(self, value): - if not value: - self._eta = None - elif isinstance(value, timedelta): - self._eta = datetime.now() + value - elif isinstance(value, int): - self._eta = datetime.now() + timedelta(seconds=value) - else: - self._eta = value - - @property - def channel(self): - return self._channel or self.job_config.channel - - @channel.setter - def channel(self, value): - self._channel = value - - @property - def exec_time(self): - if self.date_done and self.date_started: - return (self.date_done - self.date_started).total_seconds() - return None - - def set_pending(self, result=None, reset_retry=True): - if any(j.state != DONE for j in self.depends_on): - self.state = WAIT_DEPENDENCIES - else: - self.state = PENDING - self.date_enqueued = None - self.date_started = None - self.date_done = None - self.worker_pid = None - self.date_cancelled = None - if reset_retry: - self.retry = 0 - if result is not None: - self.result = result - - def set_enqueued(self): - self.state = ENQUEUED - self.date_enqueued = datetime.now() - self.date_started = None - self.worker_pid = None - - def set_started(self): - self.state = STARTED - self.date_started = datetime.now() - self.worker_pid = os.getpid() - self.add_lock_record() - - def set_done(self, result=None): - self.state = DONE - self.exc_name = None - self.exc_info = None - self.date_done = datetime.now() - if result is not None: - self.result = result - - def set_cancelled(self, result=None): - self.state = CANCELLED - self.date_cancelled = datetime.now() - if result is not None: - self.result = result - - def set_failed(self, **kw): - self.state = FAILED - for k, v in kw.items(): - if v is not None: - setattr(self, k, v) - - def __repr__(self): - return "" % (self.uuid, self.priority) - - def _get_retry_seconds(self, seconds=None): - retry_pattern = self.job_config.retry_pattern - if not seconds and retry_pattern: - # ordered from higher to lower count of retries - patt = sorted(retry_pattern.items(), key=lambda t: t[0]) - seconds = RETRY_INTERVAL - for retry_count, postpone_seconds in patt: - if self.retry >= retry_count: - seconds = postpone_seconds - else: - break - elif not seconds: - seconds = RETRY_INTERVAL - if isinstance(seconds, (list, tuple)): - seconds = randint(seconds[0], seconds[1]) - return seconds - - def postpone(self, result=None, seconds=None): - """Postpone the job - - Write an estimated time arrival to n seconds - later than now. Used when an retryable exception - want to retry a job later. - """ - eta_seconds = self._get_retry_seconds(seconds) - self.eta = timedelta(seconds=eta_seconds) - self.exc_name = None - self.exc_info = None - if result is not None: - self.result = result - - def related_action(self): - record = self.db_record() - if not self.job_config.related_action_enable: - return None - - funcname = self.job_config.related_action_func_name - if not funcname: - funcname = record._default_related_action - if not isinstance(funcname, str): - raise ValueError( - "related_action must be the name of the " - "method on queue.job as string" - ) - action = getattr(record, funcname) - action_kwargs = self.job_config.related_action_kwargs - return action(**action_kwargs) - - -def _is_model_method(func): - return inspect.ismethod(func) and isinstance( - func.__self__.__class__, odoo.models.MetaModel - ) diff --git a/addons/queue_job/jobrunner/__init__.py b/addons/queue_job/jobrunner/__init__.py deleted file mode 100644 index e2561b0..0000000 --- a/addons/queue_job/jobrunner/__init__.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (c) 2015-2016 ACSONE SA/NV () -# Copyright 2016 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import logging -from threading import Thread -import time - -from odoo.service import server -from odoo.tools import config - -try: - from odoo.addons.server_environment import serv_config - - if serv_config.has_section("queue_job"): - queue_job_config = serv_config["queue_job"] - else: - queue_job_config = {} -except ImportError: - queue_job_config = config.misc.get("queue_job", {}) - - -from .runner import QueueJobRunner, _channels - -_logger = logging.getLogger(__name__) - -START_DELAY = 5 - - -# Here we monkey patch the Odoo server to start the job runner thread -# in the main server process (and not in forked workers). This is -# very easy to deploy as we don't need another startup script. - - -class QueueJobRunnerThread(Thread): - def __init__(self): - Thread.__init__(self) - self.daemon = True - self.runner = QueueJobRunner.from_environ_or_config() - - def run(self): - # sleep a bit to let the workers start at ease - time.sleep(START_DELAY) - self.runner.run() - - def stop(self): - self.runner.stop() - - -class WorkerJobRunner(server.Worker): - """Jobrunner workers""" - - def __init__(self, multi): - super().__init__(multi) - self.watchdog_timeout = None - self.runner = QueueJobRunner.from_environ_or_config() - self._recover = False - - def sleep(self): - pass - - def signal_handler(self, sig, frame): # pylint: disable=missing-return - _logger.debug("WorkerJobRunner (%s) received signal %s", self.pid, sig) - super().signal_handler(sig, frame) - self.runner.stop() - - def process_work(self): - if self._recover: - _logger.info("WorkerJobRunner (%s) runner is reinitialized", self.pid) - self.runner = QueueJobRunner.from_environ_or_config() - self._recover = False - _logger.debug("WorkerJobRunner (%s) starting up", self.pid) - time.sleep(START_DELAY) - self.runner.run() - - def signal_time_expired_handler(self, n, stack): - _logger.info( - "Worker (%d) CPU time limit (%s) reached.Stop gracefully and recover", - self.pid, - config["limit_time_cpu"], - ) - self._recover = True - self.runner.stop() - - -runner_thread = None - - -def _is_runner_enabled(): - return not _channels().strip().startswith("root:0") - - -def _start_runner_thread(server_type): - global runner_thread - if not config["stop_after_init"]: - if _is_runner_enabled(): - _logger.info("starting jobrunner thread (in %s)", server_type) - runner_thread = QueueJobRunnerThread() - runner_thread.start() - else: - _logger.info( - "jobrunner thread (in %s) NOT started, " - "because the root channel's capacity is set to 0", - server_type, - ) - - -orig_prefork__init__ = server.PreforkServer.__init__ -orig_prefork_process_spawn = server.PreforkServer.process_spawn -orig_prefork_worker_pop = server.PreforkServer.worker_pop -orig_threaded_start = server.ThreadedServer.start -orig_threaded_stop = server.ThreadedServer.stop - - -def prefork__init__(server, app): - res = orig_prefork__init__(server, app) - server.jobrunner = {} - return res - - -def prefork_process_spawn(server): - orig_prefork_process_spawn(server) - if not hasattr(server, "jobrunner"): - # if 'queue_job' is not in server wide modules, PreforkServer is - # not initialized with a 'jobrunner' attribute, skip this - return - if not server.jobrunner and _is_runner_enabled(): - server.worker_spawn(WorkerJobRunner, server.jobrunner) - - -def prefork_worker_pop(server, pid): - res = orig_prefork_worker_pop(server, pid) - if not hasattr(server, "jobrunner"): - # if 'queue_job' is not in server wide modules, PreforkServer is - # not initialized with a 'jobrunner' attribute, skip this - return res - if pid in server.jobrunner: - server.jobrunner.pop(pid) - return res - - -def threaded_start(server, *args, **kwargs): - res = orig_threaded_start(server, *args, **kwargs) - _start_runner_thread("threaded server") - return res - - -def threaded_stop(server): - global runner_thread - if runner_thread: - runner_thread.stop() - res = orig_threaded_stop(server) - if runner_thread: - runner_thread.join() - runner_thread = None - return res - - -server.PreforkServer.__init__ = prefork__init__ -server.PreforkServer.process_spawn = prefork_process_spawn -server.PreforkServer.worker_pop = prefork_worker_pop -server.ThreadedServer.start = threaded_start -server.ThreadedServer.stop = threaded_stop diff --git a/addons/queue_job/jobrunner/__main__.py b/addons/queue_job/jobrunner/__main__.py deleted file mode 100644 index 8f9628c..0000000 --- a/addons/queue_job/jobrunner/__main__.py +++ /dev/null @@ -1,13 +0,0 @@ -import odoo - -from .runner import QueueJobRunner - - -def main(): - odoo.tools.config.parse_config() - runner = QueueJobRunner.from_environ_or_config() - runner.run() - - -if __name__ == "__main__": - main() diff --git a/addons/queue_job/jobrunner/channels.py b/addons/queue_job/jobrunner/channels.py deleted file mode 100644 index 7fcabbd..0000000 --- a/addons/queue_job/jobrunner/channels.py +++ /dev/null @@ -1,1082 +0,0 @@ -# Copyright (c) 2015-2016 ACSONE SA/NV () -# Copyright 2015-2016 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) -import logging -from collections import namedtuple -from functools import total_ordering -from heapq import heappop, heappush -from weakref import WeakValueDictionary - -from ..exception import ChannelNotFound -from ..job import CANCELLED, DONE, ENQUEUED, FAILED, PENDING, STARTED, WAIT_DEPENDENCIES - -NOT_DONE = (WAIT_DEPENDENCIES, PENDING, ENQUEUED, STARTED, FAILED) -JobSortingKey = namedtuple("SortingKey", "eta priority date_created seq") - -_logger = logging.getLogger(__name__) - - -class PriorityQueue: - """A priority queue that supports removing arbitrary objects. - - Adding an object already in the queue is a no op. - Popping an empty queue returns None. - - >>> q = PriorityQueue() - >>> q.add(2) - >>> q.add(3) - >>> q.add(3) - >>> q.add(1) - >>> q[0] - 1 - >>> len(q) - 3 - >>> q.pop() - 1 - >>> q.remove(2) - >>> len(q) - 1 - >>> q[0] - 3 - >>> q.pop() - 3 - >>> q.pop() - >>> q.add(2) - >>> q.remove(2) - >>> q.add(2) - >>> q.pop() - 2 - """ - - def __init__(self): - self._heap = [] - self._known = set() # all objects in the heap (including removed) - self._removed = set() # all objects that have been removed - - def __len__(self): - return len(self._known) - len(self._removed) - - def __getitem__(self, i): - if i != 0: - raise IndexError() - while True: - if not self._heap: - raise IndexError() - o = self._heap[0] - if o in self._removed: - o2 = heappop(self._heap) - assert o2 == o - self._removed.remove(o) - self._known.remove(o) - else: - return o - - def __contains__(self, o): - return o in self._known and o not in self._removed - - def add(self, o): - if o is None: - raise ValueError() - self._removed.discard(o) - if o in self._known: - return - self._known.add(o) - heappush(self._heap, o) - - def remove(self, o): - if o is None: - raise ValueError() - if o not in self._known: - return - self._removed.add(o) - - def pop(self): - while True: - try: - o = heappop(self._heap) - except IndexError: - # queue is empty - return None - self._known.remove(o) - if o in self._removed: - self._removed.remove(o) - else: - return o - - -@total_ordering -class ChannelJob: - """A channel job is attached to a channel and holds the properties of a - job that are necessary to prioritise them. - - Channel jobs are comparable according to the following rules: - * jobs with an eta cannot be compared with jobs without - * then jobs with a smaller eta come first - * then jobs with a smaller priority come first - * then jobs with a smaller creation time come first - * then jobs with a smaller sequence come first - - Here are some examples. - - j1 comes before j2 because it has an earlier date_created - - >>> j1 = ChannelJob(None, None, 1, - ... seq=0, date_created=1, priority=9, eta=None) - >>> j1 - - >>> j2 = ChannelJob(None, None, 2, - ... seq=0, date_created=2, priority=9, eta=None) - >>> j1 < j2 - True - - j3 comes first because it has lower priority, - despite having a creation date after j1 and j2 - - >>> j3 = ChannelJob(None, None, 3, - ... seq=0, date_created=3, priority=2, eta=None) - >>> j3 < j1 - True - - j4 and j5 have an eta, they cannot be compared with j3 - - >>> j4 = ChannelJob(None, None, 4, - ... seq=0, date_created=4, priority=9, eta=9) - >>> j5 = ChannelJob(None, None, 5, - ... seq=0, date_created=5, priority=9, eta=9) - >>> j4 < j5 - True - >>> j4 < j3 - Traceback (most recent call last): - ... - TypeError: '<' not supported between instances of 'int' and 'NoneType' - - j6 has same date_created and priority as j5 but a smaller eta - - >>> j6 = ChannelJob(None, None, 6, - ... seq=0, date_created=5, priority=9, eta=2) - >>> j6 < j4 < j5 - True - - Here is the complete suite: - - >>> j6 < j4 < j5 and j3 < j1 < j2 - True - - j0 has the same properties as j1 but they are not considered - equal as they are different instances - - >>> j0 = ChannelJob(None, None, 1, - ... seq=0, date_created=1, priority=9, eta=None) - >>> j0 == j1 - False - >>> j0 == j0 - True - - Comparison excluding eta: - - >>> j1.sorting_key_ignoring_eta() < j2.sorting_key_ignoring_eta() - True - - """ - - __slots__ = ("db_name", "channel", "uuid", "_sorting_key", "__weakref__") - - def __init__(self, db_name, channel, uuid, seq, date_created, priority, eta): - self.db_name = db_name - self.channel = channel - self.uuid = uuid - self._sorting_key = JobSortingKey(eta, priority, date_created, seq) - - def __repr__(self): - return "" % self.uuid - - def __eq__(self, other): - return id(self) == id(other) - - def __hash__(self): - return id(self) - - def set_no_eta(self): - self._sorting_key = JobSortingKey(None, *self._sorting_key[1:]) - - @property - def seq(self): - return self._sorting_key.seq - - @property - def date_created(self): - return self._sorting_key.date_created - - @property - def priority(self): - return self._sorting_key.priority - - @property - def eta(self): - return self._sorting_key.eta - - def sorting_key(self): - # DEPRECATED - return self._sorting_key - - def sorting_key_ignoring_eta(self): - return self._sorting_key[1:] - - def __lt__(self, other): - # Do not compare job where ETA is set with job where it is not - # If one job 'eta' is set, and the other is None, it raises TypeError - return self._sorting_key < other._sorting_key - - -class ChannelQueue: - """A channel queue is a priority queue for jobs. - - Jobs with an eta are set aside until their eta is past due, at - which point they start competing normally with other jobs. - - >>> q = ChannelQueue() - >>> j1 = ChannelJob(None, None, 1, - ... seq=0, date_created=1, priority=1, eta=10) - >>> j2 = ChannelJob(None, None, 2, - ... seq=0, date_created=2, priority=1, eta=None) - >>> j3 = ChannelJob(None, None, 3, - ... seq=0, date_created=3, priority=1, eta=None) - >>> q.add(j1) - >>> q.add(j2) - >>> q.add(j3) - - Wakeup time is the eta of job 1. - - >>> q.get_wakeup_time() - 10 - - We have not reached the eta of job 1, so we get job 2. - - >>> q.pop(now=1) - - - Wakeup time is still the eta of job 1, and we get job 1 when we are past - it's eta. - - >>> q.get_wakeup_time() - 10 - >>> q.pop(now=11) - - - Now there is no wakeup time anymore, because no job have an eta. - - >>> q.get_wakeup_time() - 0 - >>> q.pop(now=12) - - >>> q.get_wakeup_time() - 0 - >>> q.pop(now=13) - - Observe that job with past eta still run after jobs with higher priority. - - >>> j4 = ChannelJob(None, None, 4, - ... seq=0, date_created=4, priority=10, eta=20) - >>> j5 = ChannelJob(None, None, 5, - ... seq=0, date_created=5, priority=1, eta=None) - >>> q.add(j4) - >>> q.add(j5) - >>> q.get_wakeup_time() - 20 - >>> q.pop(21) - - >>> q.get_wakeup_time() - 0 - >>> q.pop(22) - - - Test a sequential queue. - - >>> sq = ChannelQueue(sequential=True) - >>> j6 = ChannelJob(None, None, 6, - ... seq=0, date_created=6, priority=1, eta=None) - >>> j7 = ChannelJob(None, None, 7, - ... seq=0, date_created=7, priority=1, eta=20) - >>> j8 = ChannelJob(None, None, 8, - ... seq=0, date_created=8, priority=1, eta=None) - >>> sq.add(j6) - >>> sq.add(j7) - >>> sq.add(j8) - >>> sq.pop(10) - - >>> sq.pop(15) - >>> sq.pop(20) - - >>> sq.pop(30) - - """ - - def __init__(self, sequential=False): - self._queue = PriorityQueue() - self._eta_queue = PriorityQueue() - self.sequential = sequential - - def __len__(self): - return len(self._eta_queue) + len(self._queue) - - def __contains__(self, o): - return o in self._eta_queue or o in self._queue - - def add(self, job): - if job.eta: - self._eta_queue.add(job) - else: - self._queue.add(job) - - def remove(self, job): - self._eta_queue.remove(job) - self._queue.remove(job) - - def pop(self, now): - while self._eta_queue and self._eta_queue[0].eta <= now: - eta_job = self._eta_queue.pop() - eta_job.set_no_eta() - self._queue.add(eta_job) - if self.sequential and self._eta_queue and self._queue: - eta_job = self._eta_queue[0] - job = self._queue[0] - - if eta_job.sorting_key_ignoring_eta() < job.sorting_key_ignoring_eta(): - # eta ignored, the job with eta has higher priority - # than the job without eta; since it's a sequential - # queue we wait until eta - return None - return self._queue.pop() - - def get_wakeup_time(self, wakeup_time=0): - if self._eta_queue: - if not wakeup_time: - wakeup_time = self._eta_queue[0].eta - else: - wakeup_time = min(wakeup_time, self._eta_queue[0].eta) - return wakeup_time - - -class Channel: - """A channel for jobs, with a maximum capacity. - - When jobs are created by queue_job modules, they may be associated - to a job channel. Jobs with no channel are inserted into the root channel. - - Job channels are joined in a hierarchy down to the root channel. - When a job channel has available capacity, jobs are dequeued, marked - as running in the channel and are inserted into the queue of the - parent channel where they wait for available capacity and so on. - - Job channels can be visualized as water channels with a given flow - limit (= capacity). Channels are joined together in a downstream channel - and the flow limit of the downstream channel limits upstream channels.:: - - ---------------------+ - | - | - Ch. A C:4,Q:12,R:4 +----------------------- - - ---------------------+ Ch. root C:5,Q:0,R:4 - | - ---------------------+ - Ch. B C:1,Q:0,R:0 - ---------------------+----------------------- - - The above diagram illustrates two channels joining in the root channel. - The root channel has a capacity of 5, and 4 running jobs coming from - Channel A. Channel A has a capacity of 4, all in use (passed down to the - root channel), and 12 jobs enqueued. Channel B has a capacity of 1, - none in use. This means that whenever a new job comes in channel B, - there will be available room for it to run in the root channel. - - Note that from the point of view of a channel, 'running' means enqueued - in the downstream channel. Only jobs marked running in the root channel - are actually sent to Odoo for execution. - - Should a downstream channel have less capacity than its upstream channels, - jobs going downstream will be enqueued in the downstream channel, - and compete normally according to their properties (priority, etc). - - Using this technique, it is possible to enforce sequence in a channel - with a capacity of 1. It is also possible to dedicate a channel with a - limited capacity for application-autocreated subchannels - without risking to overflow the system. - """ - - def __init__(self, name, parent, capacity=None, sequential=False, throttle=0): - self.name = name - self.parent = parent - if self.parent: - self.parent.children[name] = self - self.children = {} - self._queue = ChannelQueue() - self._running = set() - self._failed = set() - self._pause_until = 0 # utc seconds since the epoch - self.capacity = capacity - self.throttle = throttle # seconds - self.sequential = sequential - - @property - def sequential(self): - return self._queue.sequential - - @sequential.setter - def sequential(self, val): - self._queue.sequential = val - - def configure(self, config): - """Configure a channel from a dictionary. - - Supported keys are: - - * capacity - * sequential - * throttle - """ - assert self.fullname.endswith(config["name"]) - self.capacity = config.get("capacity", None) - self.sequential = bool(config.get("sequential", False)) - self.throttle = int(config.get("throttle", 0)) - if self.sequential and self.capacity != 1: - raise ValueError("A sequential channel must have a capacity of 1") - - @property - def fullname(self): - """The full name of the channel, in dot separated notation.""" - if self.parent: - return self.parent.fullname + "." + self.name - else: - return self.name - - def get_subchannel_by_name(self, subchannel_name): - return self.children.get(subchannel_name) - - def __str__(self): - capacity = "∞" if self.capacity is None else str(self.capacity) - return "%s(C:%s,Q:%d,R:%d,F:%d)" % ( - self.fullname, - capacity, - len(self._queue), - len(self._running), - len(self._failed), - ) - - def remove(self, job): - """Remove a job from the channel.""" - self._queue.remove(job) - self._running.discard(job) - self._failed.discard(job) - if self.parent: - self.parent.remove(job) - - def set_done(self, job): - """Mark a job as done. - - This removes it from the channel queue. - """ - self.remove(job) - _logger.debug("job %s marked done in channel %s", job.uuid, self) - - def set_pending(self, job): - """Mark a job as pending. - - This puts the job in the channel queue and remove it - from parent channels queues. - """ - if job not in self._queue: - self._queue.add(job) - self._running.discard(job) - self._failed.discard(job) - if self.parent: - self.parent.remove(job) - _logger.debug("job %s marked pending in channel %s", job.uuid, self) - - def set_running(self, job): - """Mark a job as running. - - This also marks the job as running in parent channels. - """ - if job not in self._running: - self._queue.remove(job) - self._running.add(job) - self._failed.discard(job) - if self.parent: - self.parent.set_running(job) - _logger.debug("job %s marked running in channel %s", job.uuid, self) - - def set_failed(self, job): - """Mark the job as failed.""" - if job not in self._failed: - self._queue.remove(job) - self._running.discard(job) - self._failed.add(job) - if self.parent: - self.parent.remove(job) - _logger.debug("job %s marked failed in channel %s", job.uuid, self) - - def has_capacity(self): - if self.sequential and self._failed: - # a sequential queue blocks on failed jobs - return False - if not self.capacity: - # unlimited capacity - return True - return len(self._running) < self.capacity - - def get_jobs_to_run(self, now): - """Get jobs that are ready to run in channel. - - This works by enqueuing jobs that are ready to run in children - channels, then yielding jobs from the channel queue until - ``capacity`` jobs are marked running in the channel. - - If the ``throttle`` option is set on the channel, then it yields - no job until at least throttle seconds have elapsed since the previous - yield. - - :param now: the current datetime in seconds - - :return: iterator of - :class:`odoo.addons.queue_job.jobrunner.ChannelJob` - """ - # enqueue jobs of children channels - for child in self.children.values(): - for job in child.get_jobs_to_run(now): - self._queue.add(job) - # is this channel paused? - if self.throttle and self._pause_until: - if now < self._pause_until: - if self.has_capacity(): - _logger.debug( - "channel %s paused until %s because " - "of throttle delay between jobs", - self, - self._pause_until, - ) - return - else: - # unpause, this is important to avoid perpetual wakeup - # while the channel is at full capacity - self._pause_until = 0 - _logger.debug("channel %s unpaused at %s", self, now) - # yield jobs that are ready to run, while we have capacity - while self.has_capacity(): - job = self._queue.pop(now) - if not job: - return - self._running.add(job) - _logger.debug("job %s marked running in channel %s", job.uuid, self) - yield job - if self.throttle: - self._pause_until = now + self.throttle - _logger.debug("pausing channel %s until %s", self, self._pause_until) - return - - def get_wakeup_time(self, wakeup_time=0): - if not self.has_capacity(): - # this channel is full, do not request timed wakeup, as - # a notification will wakeup the runner when a job finishes - return wakeup_time - if self._pause_until: - # this channel is paused, request wakeup at the end of the pause - if not wakeup_time: - wakeup_time = self._pause_until - else: - wakeup_time = min(wakeup_time, self._pause_until) - # since this channel is paused, no need to look at the - # wakeup time of children nor eta jobs, as such jobs would not - # run anyway because they would end up in this paused channel - return wakeup_time - wakeup_time = self._queue.get_wakeup_time(wakeup_time) - for child in self.children.values(): - wakeup_time = child.get_wakeup_time(wakeup_time) - return wakeup_time - - -def split_strip(s, sep, maxsplit=-1): - """Split string and strip each component. - - >>> split_strip("foo: bar baz\\n: fred:", ":") - ['foo', 'bar baz', 'fred', ''] - """ - return [x.strip() for x in s.split(sep, maxsplit)] - - -class ChannelManager: - """High level interface for channels - - This class handles: - - * configuration of channels - * high level api to create and remove jobs (notify, remove_job, remove_db) - * get jobs to run - - Here is how the runner will use it. - - Let's create a channel manager and configure it. - - >>> from pprint import pprint as pp - >>> cm = ChannelManager() - >>> cm.simple_configure('root:4,A:4,B:1') - >>> db = 'db' - - Add a few jobs in channel A with priority 10 - - >>> cm.notify(db, 'A', 'A1', 1, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A2', 2, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A3', 3, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A4', 4, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A5', 5, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A6', 6, 0, 10, None, 'pending') - - Add a few jobs in channel B with priority 5 - - >>> cm.notify(db, 'B', 'B1', 1, 0, 5, None, 'pending') - >>> cm.notify(db, 'B', 'B2', 2, 0, 5, None, 'pending') - - We must now run one job from queue B which has a capacity of 1 - and 3 jobs from queue A so the root channel capacity of 4 is filled. - - >>> pp(list(cm.get_jobs_to_run(now=100))) - [, , , ] - - Job A2 is done. Next job to run is A5, even if we have - higher priority job in channel B, because channel B has a capacity of 1. - - >>> cm.notify(db, 'A', 'A2', 2, 0, 10, None, 'done') - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - - Job B1 is done. Next job to run is B2 because it has higher priority. - - >>> cm.notify(db, 'B', 'B1', 1, 0, 5, None, 'done') - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - - Let's say A1 is done and A6 gets a higher priority. A6 will run next. - - >>> cm.notify(db, 'A', 'A1', 1, 0, 10, None, 'done') - >>> cm.notify(db, 'A', 'A6', 6, 0, 5, None, 'pending') - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - - Let's test the throttling mechanism. Configure a 2 seconds delay - on channel A, end enqueue two jobs. - - >>> cm = ChannelManager() - >>> cm.simple_configure('root:4,A:4:throttle=2') - >>> cm.notify(db, 'A', 'A1', 1, 0, 10, None, 'pending') - >>> cm.notify(db, 'A', 'A2', 2, 0, 10, None, 'pending') - - We have only one job to run, because of the throttle. - - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - >>> cm.get_wakeup_time() - 102 - - We have no job to run, because of the throttle. - - >>> pp(list(cm.get_jobs_to_run(now=101))) - [] - >>> cm.get_wakeup_time() - 102 - - 2 seconds later, we can run the other job (even though the first one - is still running, because we have enough capacity). - - >>> pp(list(cm.get_jobs_to_run(now=102))) - [] - >>> cm.get_wakeup_time() - 104 - - Let's test throttling in combination with a queue reaching full capacity. - - >>> cm = ChannelManager() - >>> cm.simple_configure('root:4,T:2:throttle=2') - >>> cm.notify(db, 'T', 'T1', 1, 0, 10, None, 'pending') - >>> cm.notify(db, 'T', 'T2', 2, 0, 10, None, 'pending') - >>> cm.notify(db, 'T', 'T3', 3, 0, 10, None, 'pending') - - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - >>> pp(list(cm.get_jobs_to_run(now=102))) - [] - - Channel is now full, so no job to run even though throttling - delay is over. - - >>> pp(list(cm.get_jobs_to_run(now=103))) - [] - >>> cm.get_wakeup_time() # no wakeup time, since queue is full - 0 - >>> pp(list(cm.get_jobs_to_run(now=104))) - [] - >>> cm.get_wakeup_time() # queue is still full - 0 - - >>> cm.notify(db, 'T', 'T1', 1, 0, 10, None, 'done') - >>> pp(list(cm.get_jobs_to_run(now=105))) - [] - >>> cm.get_wakeup_time() # queue is full - 0 - >>> cm.notify(db, 'T', 'T2', 1, 0, 10, None, 'done') - >>> cm.get_wakeup_time() - 107 - - Test wakeup time behaviour in presence of eta. - - >>> cm = ChannelManager() - >>> cm.simple_configure('root:4,E:1') - >>> cm.notify(db, 'E', 'E1', 1, 0, 10, None, 'pending') - >>> cm.notify(db, 'E', 'E2', 2, 0, 10, None, 'pending') - >>> cm.notify(db, 'E', 'E3', 3, 0, 10, None, 'pending') - - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - >>> pp(list(cm.get_jobs_to_run(now=101))) - [] - >>> cm.notify(db, 'E', 'E1', 1, 0, 10, 105, 'pending') - >>> cm.get_wakeup_time() # wakeup at eta - 105 - >>> pp(list(cm.get_jobs_to_run(now=102))) # but there is capacity - [] - >>> pp(list(cm.get_jobs_to_run(now=106))) # no capacity anymore - [] - >>> cm.get_wakeup_time() # no timed wakeup because no capacity - 0 - >>> cm.notify(db, 'E', 'E2', 1, 0, 10, None, 'done') - >>> cm.get_wakeup_time() - 105 - >>> pp(list(cm.get_jobs_to_run(now=107))) # no capacity anymore - [] - >>> cm.get_wakeup_time() - 0 - - Test wakeup time behaviour in a sequential queue. - - >>> cm = ChannelManager() - >>> cm.simple_configure('root:4,S:1:sequential') - >>> cm.notify(db, 'S', 'S1', 1, 0, 10, None, 'pending') - >>> cm.notify(db, 'S', 'S2', 2, 0, 10, None, 'pending') - >>> cm.notify(db, 'S', 'S3', 3, 0, 10, None, 'pending') - - >>> pp(list(cm.get_jobs_to_run(now=100))) - [] - >>> cm.notify(db, 'S', 'S1', 1, 0, 10, None, 'failed') - >>> pp(list(cm.get_jobs_to_run(now=101))) - [] - >>> cm.notify(db, 'S', 'S2', 2, 0, 10, 105, 'pending') - >>> pp(list(cm.get_jobs_to_run(now=102))) - [] - - No wakeup time because due to eta, because the sequential queue - is waiting for a failed job. - - >>> cm.get_wakeup_time() - 0 - >>> cm.notify(db, 'S', 'S1', 1, 0, 10, None, 'pending') - >>> cm.get_wakeup_time() - 105 - >>> pp(list(cm.get_jobs_to_run(now=102))) - [] - >>> pp(list(cm.get_jobs_to_run(now=103))) - [] - >>> cm.notify(db, 'S', 'S1', 1, 0, 10, None, 'done') - - At this stage, we have S2 with an eta of 105 and since the - queue is sequential, we wait for it. - - >>> pp(list(cm.get_jobs_to_run(now=103))) - [] - >>> pp(list(cm.get_jobs_to_run(now=105))) - [] - >>> cm.notify(db, 'S', 'S2', 2, 0, 10, 105, 'done') - >>> pp(list(cm.get_jobs_to_run(now=105))) - [] - >>> cm.notify(db, 'S', 'S3', 3, 0, 10, None, 'done') - >>> pp(list(cm.get_jobs_to_run(now=105))) - [] - """ - - def __init__(self): - self._jobs_by_uuid = WeakValueDictionary() - self._root_channel = Channel(name="root", parent=None, capacity=1) - self._channels_by_name = WeakValueDictionary(root=self._root_channel) - - @classmethod - def parse_simple_config(cls, config_string): - """Parse a simple channels configuration string. - - The general form is as follow: - channel(.subchannel)*(:capacity(:key(=value)?)*)? [, ...] - - If capacity is absent, it defaults to 1. - If a key is present without value, it gets True as value. - When declaring subchannels, the root channel may be omitted - (ie sub:4 is the same as root.sub:4). - - Returns a list of channel configuration dictionaries. - - >>> from pprint import pprint as pp - >>> pp(ChannelManager.parse_simple_config('root:4')) - [{'capacity': 4, 'name': 'root'}] - >>> pp(ChannelManager.parse_simple_config('root:4,root.sub:2')) - [{'capacity': 4, 'name': 'root'}, {'capacity': 2, 'name': 'root.sub'}] - >>> pp(ChannelManager.parse_simple_config('root:4,root.sub:2:' - ... 'sequential:k=v')) - [{'capacity': 4, 'name': 'root'}, - {'capacity': 2, 'k': 'v', 'name': 'root.sub', 'sequential': True}] - >>> pp(ChannelManager.parse_simple_config('root')) - [{'capacity': 1, 'name': 'root'}] - >>> pp(ChannelManager.parse_simple_config('sub:2')) - [{'capacity': 2, 'name': 'sub'}] - - It ignores whitespace around values, and drops empty entries which - would be generated by trailing commas, or commented lines on the Odoo - config file. - - >>> pp(ChannelManager.parse_simple_config(''' - ... root : 4, - ... , - ... foo bar:1: k=va lue, - ... ''')) - [{'capacity': 4, 'name': 'root'}, - {'capacity': 1, 'k': 'va lue', 'name': 'foo bar'}] - - It's also possible to replace commas with line breaks, which is more - readable if the channel configuration comes from the odoo config file. - - >>> pp(ChannelManager.parse_simple_config(''' - ... root : 4 - ... foo bar:1: k=va lue - ... baz - ... ''')) - [{'capacity': 4, 'name': 'root'}, - {'capacity': 1, 'k': 'va lue', 'name': 'foo bar'}, - {'capacity': 1, 'name': 'baz'}] - """ - res = [] - config_string = config_string.replace("\n", ",") - for channel_config_string in split_strip(config_string, ","): - if not channel_config_string: - # ignore empty entries (commented lines, trailing commas) - continue - config = {} - config_items = split_strip(channel_config_string, ":") - name = config_items[0] - if not name: - raise ValueError( - "Invalid channel config %s: missing channel name" % config_string - ) - config["name"] = name - if len(config_items) > 1: - capacity = config_items[1] - try: - config["capacity"] = int(capacity) - except Exception as ex: - raise ValueError( - "Invalid channel config %s: " - "invalid capacity %s" % (config_string, capacity) - ) from ex - for config_item in config_items[2:]: - kv = split_strip(config_item, "=") - if len(kv) == 1: - k, v = kv[0], True - elif len(kv) == 2: - k, v = kv - else: - raise ValueError( - "Invalid channel config %s: " - "incorrect config item %s" % (config_string, config_item) - ) - if k in config: - raise ValueError( - "Invalid channel config %s: " - "duplicate key %s" % (config_string, k) - ) - config[k] = v - else: - config["capacity"] = 1 - res.append(config) - return res - - def simple_configure(self, config_string): - """Configure the channel manager from a simple configuration string - - >>> cm = ChannelManager() - >>> c = cm.get_channel_by_name('root') - >>> c.capacity - 1 - >>> cm.simple_configure('root:4,autosub.sub:2,seq:1:sequential') - >>> cm.get_channel_by_name('root').capacity - 4 - >>> cm.get_channel_by_name('root').sequential - False - >>> cm.get_channel_by_name('root.autosub').capacity - >>> cm.get_channel_by_name('root.autosub.sub').capacity - 2 - >>> cm.get_channel_by_name('root.autosub.sub').sequential - False - >>> cm.get_channel_by_name('autosub.sub').capacity - 2 - >>> cm.get_channel_by_name('seq').capacity - 1 - >>> cm.get_channel_by_name('seq').sequential - True - """ - for config in ChannelManager.parse_simple_config(config_string): - self.get_channel_from_config(config) - - def get_channel_from_config(self, config): - """Return a Channel object from a parsed configuration. - - If the channel does not exist it is created. - The configuration is applied on the channel before returning it. - If some of the parent channels are missing when creating a subchannel, - the parent channels are auto created with an infinite capacity - (except for the root channel, which defaults to a capacity of 1 - when not configured explicity). - """ - channel = self.get_channel_by_name(config["name"], autocreate=True) - channel.configure(config) - _logger.info("Configured channel: %s", channel) - return channel - - def get_channel_by_name( - self, channel_name, autocreate=False, parent_fallback=False - ): - """Return a Channel object by its name. - - If it does not exist and autocreate is True, it is created - with a default configuration and inserted in the Channels structure. - If autocreate is False and the channel does not exist, an exception - is raised. - - >>> cm = ChannelManager() - >>> c = cm.get_channel_by_name('root', autocreate=False) - >>> c.name - 'root' - >>> c.fullname - 'root' - >>> c = cm.get_channel_by_name('root.sub', autocreate=True) - >>> c.name - 'sub' - >>> c.fullname - 'root.sub' - >>> c = cm.get_channel_by_name('sub', autocreate=True) - >>> c.name - 'sub' - >>> c.fullname - 'root.sub' - >>> c = cm.get_channel_by_name('autosub.sub', autocreate=True) - >>> c.name - 'sub' - >>> c.fullname - 'root.autosub.sub' - >>> c = cm.get_channel_by_name(None) - >>> c.fullname - 'root' - >>> c = cm.get_channel_by_name('root.sub') - >>> c.fullname - 'root.sub' - >>> c = cm.get_channel_by_name('sub') - >>> c.fullname - 'root.sub' - >>> c = cm.get_channel_by_name('root.sub.not.configured', parent_fallback=True) - >>> c.fullname - 'root.sub.sub.not.configured' - """ - if not channel_name or channel_name == self._root_channel.name: - return self._root_channel - if not channel_name.startswith(self._root_channel.name + "."): - channel_name = self._root_channel.name + "." + channel_name - if channel_name in self._channels_by_name: - return self._channels_by_name[channel_name] - if not autocreate and not parent_fallback: - raise ChannelNotFound("Channel %s not found" % channel_name) - parent = self._root_channel - if parent_fallback: - # Look for first direct parent w/ config. - # Eg: `root.edi.foo.baz` will falback on `root.edi.foo` - # or `root.edi` or `root` in sequence - parent_name = channel_name - while True: - parent_name = parent_name.rsplit(".", 1)[:-1][0] - if parent_name == self._root_channel.name: - break - if parent_name in self._channels_by_name: - parent = self._channels_by_name[parent_name] - _logger.debug( - "%s has no specific configuration: using %s", - channel_name, - parent_name, - ) - break - for subchannel_name in channel_name.split(".")[1:]: - subchannel = parent.get_subchannel_by_name(subchannel_name) - if not subchannel: - subchannel = Channel(subchannel_name, parent, capacity=None) - self._channels_by_name[subchannel.fullname] = subchannel - parent = subchannel - return parent - - def notify( - self, db_name, channel_name, uuid, seq, date_created, priority, eta, state - ): - channel = self.get_channel_by_name(channel_name, parent_fallback=True) - job = self._jobs_by_uuid.get(uuid) - if job: - # db_name is invariant - assert job.db_name == db_name - # date_created is invariant - assert job.date_created == date_created - # if one of the job properties that influence - # scheduling order has changed, we remove the job - # from the queues and create a new job object - if ( - seq != job.seq - or priority != job.priority - or eta != job.eta - or channel != job.channel - ): - _logger.debug("job %s properties changed, rescheduling it", uuid) - self.remove_job(uuid) - job = None - if not job: - job = ChannelJob(db_name, channel, uuid, seq, date_created, priority, eta) - self._jobs_by_uuid[uuid] = job - # state transitions - if not state or state in (DONE, CANCELLED): - job.channel.set_done(job) - elif state == PENDING: - job.channel.set_pending(job) - elif state in (ENQUEUED, STARTED): - job.channel.set_running(job) - elif state == FAILED: - job.channel.set_failed(job) - elif state == WAIT_DEPENDENCIES: - # wait until all parent jobs are done - pass - else: - _logger.error("unexpected state %s for job %s", state, job) - - def remove_job(self, uuid): - job = self._jobs_by_uuid.get(uuid) - if job: - job.channel.remove(job) - del self._jobs_by_uuid[job.uuid] - - def remove_db(self, db_name): - for job in list(self._jobs_by_uuid.values()): - if job.db_name == db_name: - job.channel.remove(job) - del self._jobs_by_uuid[job.uuid] - - def get_jobs_to_run(self, now): - return self._root_channel.get_jobs_to_run(now) - - def get_wakeup_time(self): - return self._root_channel.get_wakeup_time() diff --git a/addons/queue_job/jobrunner/runner.py b/addons/queue_job/jobrunner/runner.py deleted file mode 100644 index 846682a..0000000 --- a/addons/queue_job/jobrunner/runner.py +++ /dev/null @@ -1,629 +0,0 @@ -# Copyright (c) 2015-2016 ACSONE SA/NV () -# Copyright 2015-2016 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) -""" -What is the job runner? ------------------------ -The job runner is the main process managing the dispatch of delayed jobs to -available Odoo workers - -How does it work? ------------------ - -* It starts as a thread in the Odoo main process or as a new worker -* It receives postgres NOTIFY messages each time jobs are - added or updated in the queue_job table. -* It maintains an in-memory priority queue of jobs that - is populated from the queue_job tables in all databases. -* It does not run jobs itself, but asks Odoo to run them through an - anonymous ``/queue_job/runjob`` HTTP request. [1]_ - -How to use it? --------------- - -* Optionally adjust your configuration through environment variables: - - - ``ODOO_QUEUE_JOB_CHANNELS=root:4`` (or any other channels - configuration), default ``root:1``. - - ``ODOO_QUEUE_JOB_SCHEME=https``, default ``http``. - - ``ODOO_QUEUE_JOB_HOST=load-balancer``, default ``http_interface`` - or ``localhost`` if unset. - - ``ODOO_QUEUE_JOB_PORT=443``, default ``http_port`` or 8069 if unset. - - ``ODOO_QUEUE_JOB_HTTP_AUTH_USER=jobrunner``, default empty. - - ``ODOO_QUEUE_JOB_HTTP_AUTH_PASSWORD=s3cr3t``, default empty. - - ``ODOO_QUEUE_JOB_JOBRUNNER_DB_HOST=master-db``, default ``db_host`` - or ``False`` if unset. - - ``ODOO_QUEUE_JOB_JOBRUNNER_DB_PORT=5432``, default ``db_port`` - or ``False`` if unset. - - ``ODOO_QUEUE_JOB_JOBRUNNER_DB_USER=userdb``, default ``db_user`` - or ``False`` if unset. - - ``ODOO_QUEUE_JOB_JOBRUNNER_DB_PASSWORD=passdb``, default ``db_password`` - or ``False`` if unset. - -* Alternatively, configure the channels through the Odoo configuration - file, like: - -.. code-block:: ini - - [queue_job] - channels = root:4 - scheme = https - host = load-balancer - port = 443 - http_auth_user = jobrunner - http_auth_password = s3cr3t - jobrunner_db_host = master-db - jobrunner_db_port = 5432 - jobrunner_db_user = userdb - jobrunner_db_password = passdb - -* Or, if using ``anybox.recipe.odoo``, add this to your buildout configuration: - -.. code-block:: ini - - [odoo] - recipe = anybox.recipe.odoo - (...) - queue_job.channels = root:4 - queue_job.scheme = https - queue_job.host = load-balancer - queue_job.port = 443 - queue_job.http_auth_user = jobrunner - queue_job.http_auth_password = s3cr3t - -* Start Odoo with ``--load=web,web_kanban,queue_job`` - and ``--workers`` greater than 1 [2]_, or set the ``server_wide_modules`` - option in The Odoo configuration file: - -.. code-block:: ini - - [options] - (...) - workers = 4 - server_wide_modules = web,web_kanban,queue_job - (...) - -* Or, if using ``anybox.recipe.odoo``: - -.. code-block:: ini - - [odoo] - recipe = anybox.recipe.odoo - (...) - options.workers = 4 - options.server_wide_modules = web,web_kanban,queue_job - -* Confirm the runner is starting correctly by checking the odoo log file: - -.. code-block:: none - - ...INFO...queue_job.jobrunner.runner: starting - ...INFO...queue_job.jobrunner.runner: initializing database connections - ...INFO...queue_job.jobrunner.runner: queue job runner ready for db - ...INFO...queue_job.jobrunner.runner: database connections ready - -* Create jobs (eg using base_import_async) and observe they - start immediately and in parallel. - -* Tip: to enable debug logging for the queue job, use - ``--log-handler=odoo.addons.queue_job:DEBUG`` - -Caveat ------- - -* After creating a new database or installing queue_job on an - existing database, Odoo must be restarted for the runner to detect it. - -.. rubric:: Footnotes - -.. [1] From a security standpoint, it is safe to have an anonymous HTTP - request because this request only accepts to run jobs that are - enqueued. -.. [2] It works with the threaded Odoo server too, although this way - of running Odoo is obviously not for production purposes. -""" - -import datetime -import logging -import os -import selectors -import threading -import time -from contextlib import closing, contextmanager - -import psycopg2 -import requests -from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT - -import odoo -from odoo.tools import config - -from . import queue_job_config -from .channels import ENQUEUED, NOT_DONE, ChannelManager - -SELECT_TIMEOUT = 60 -ERROR_RECOVERY_DELAY = 5 -PG_ADVISORY_LOCK_ID = 2293787760715711918 - -_logger = logging.getLogger(__name__) - -select = selectors.DefaultSelector - - -class MasterElectionLost(Exception): - pass - - -# Unfortunately, it is not possible to extend the Odoo -# server command line arguments, so we resort to environment variables -# to configure the runner (channels mostly). -# -# On the other hand, the odoo configuration file can be extended at will, -# so we check it in addition to the environment variables. - - -def _channels(): - return ( - os.environ.get("ODOO_QUEUE_JOB_CHANNELS") - or queue_job_config.get("channels") - or "root:1" - ) - - -def _datetime_to_epoch(dt): - # important: this must return the same as postgresql - # EXTRACT(EPOCH FROM TIMESTAMP dt) - return (dt - datetime.datetime(1970, 1, 1)).total_seconds() - - -def _odoo_now(): - dt = datetime.datetime.utcnow() - return _datetime_to_epoch(dt) - - -def _connection_info_for(db_name): - db_or_uri, connection_info = odoo.sql_db.connection_info_for(db_name) - - for p in ("host", "port", "user", "password"): - cfg = os.environ.get( - "ODOO_QUEUE_JOB_JOBRUNNER_DB_%s" % p.upper() - ) or queue_job_config.get("jobrunner_db_" + p) - - if cfg: - connection_info[p] = cfg - - return connection_info - - -def _async_http_get(scheme, host, port, user, password, db_name, job_uuid): - # TODO: better way to HTTP GET asynchronously (grequest, ...)? - # if this was python3 I would be doing this with - # asyncio, aiohttp and aiopg - def urlopen(): - url = "{}://{}:{}/queue_job/runjob?db={}&job_uuid={}".format( - scheme, host, port, db_name, job_uuid - ) - # pylint: disable=except-pass - try: - auth = None - if user: - auth = (user, password) - # we are not interested in the result, so we set a short timeout - # but not too short so we trap and log hard configuration errors - response = requests.get(url, timeout=1, auth=auth) - - # raise_for_status will result in either nothing, a Client Error - # for HTTP Response codes between 400 and 500 or a Server Error - # for codes between 500 and 600 - response.raise_for_status() - except requests.Timeout: - # A timeout is a normal behaviour, it shouldn't be logged as an exception - pass - except Exception: - _logger.exception("exception in GET %s", url) - - thread = threading.Thread(target=urlopen) - thread.daemon = True - thread.start() - - -class Database: - def __init__(self, db_name): - self.db_name = db_name - connection_info = _connection_info_for(db_name) - self.conn = psycopg2.connect(**connection_info) - try: - self.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) - self.has_queue_job = self._has_queue_job() - if self.has_queue_job: - self._acquire_master_lock() - self._initialize() - except BaseException: - self.close() - raise - - def close(self): - # pylint: disable=except-pass - # if close fail for any reason, it's either because it's already closed - # and we don't care, or for any reason but anyway it will be closed on - # del - try: - self.conn.close() - except Exception: - pass - self.conn = None - - def _acquire_master_lock(self): - """Acquire the master runner lock or raise MasterElectionLost""" - with closing(self.conn.cursor()) as cr: - cr.execute("SELECT pg_try_advisory_lock(%s)", (PG_ADVISORY_LOCK_ID,)) - if not cr.fetchone()[0]: - msg = f"could not acquire master runner lock on {self.db_name}" - raise MasterElectionLost(msg) - - def _has_queue_job(self): - with closing(self.conn.cursor()) as cr: - cr.execute( - "SELECT 1 FROM pg_tables WHERE tablename=%s", ("ir_module_module",) - ) - if not cr.fetchone(): - _logger.debug("%s doesn't seem to be an odoo db", self.db_name) - return False - cr.execute( - "SELECT 1 FROM ir_module_module WHERE name=%s AND state=%s", - ("queue_job", "installed"), - ) - if not cr.fetchone(): - _logger.debug("queue_job is not installed for db %s", self.db_name) - return False - cr.execute( - """SELECT COUNT(1) - FROM information_schema.triggers - WHERE event_object_table = %s - AND trigger_name = %s""", - ("queue_job", "queue_job_notify"), - ) - if cr.fetchone()[0] != 3: # INSERT, DELETE, UPDATE - _logger.error( - "queue_job_notify trigger is missing in db %s", self.db_name - ) - return False - return True - - def _initialize(self): - with closing(self.conn.cursor()) as cr: - cr.execute("LISTEN queue_job") - - @contextmanager - def select_jobs(self, where, args): - # pylint: disable=sql-injection - # the checker thinks we are injecting values but we are not, we are - # adding the where conditions, values are added later properly with - # parameters - query = ( - "SELECT channel, uuid, id as seq, date_created, " - "priority, EXTRACT(EPOCH FROM eta), state " - "FROM queue_job WHERE %s" % (where,) - ) - with closing(self.conn.cursor("select_jobs", withhold=True)) as cr: - cr.execute(query, args) - yield cr - - def keep_alive(self): - query = "SELECT 1" - with closing(self.conn.cursor()) as cr: - cr.execute(query) - - def set_job_enqueued(self, uuid): - with closing(self.conn.cursor()) as cr: - cr.execute( - "UPDATE queue_job SET state=%s, " - "date_enqueued=date_trunc('seconds', " - " now() at time zone 'utc') " - "WHERE uuid=%s", - (ENQUEUED, uuid), - ) - - def _query_requeue_dead_jobs(self): - return """ - UPDATE - queue_job - SET - state=( - CASE - WHEN - max_retries IS NOT NULL AND - max_retries != 0 AND -- infinite retries if max_retries is 0 - retry IS NOT NULL AND - retry>max_retries - THEN 'failed' - ELSE 'pending' - END), - retry=(CASE WHEN state='started' THEN COALESCE(retry,0)+1 ELSE retry END), - exc_name=( - CASE - WHEN - max_retries IS NOT NULL AND - max_retries != 0 AND -- infinite retries if max_retries is 0 - retry IS NOT NULL AND - retry>max_retries - THEN 'JobFoundDead' - ELSE exc_name - END), - exc_info=( - CASE - WHEN - max_retries IS NOT NULL AND - max_retries != 0 AND -- infinite retries if max_retries is 0 - retry IS NOT NULL AND - retry>max_retries - THEN 'Job found dead after too many retries' - ELSE exc_info - END) - WHERE - id in ( - SELECT - queue_job_id - FROM - queue_job_lock - WHERE - queue_job_id in ( - SELECT - id - FROM - queue_job - WHERE - state IN ('enqueued','started') - AND date_enqueued < - (now() AT TIME ZONE 'utc' - INTERVAL '10 sec') - ) - FOR UPDATE SKIP LOCKED - ) - RETURNING uuid - """ - - def requeue_dead_jobs(self): - """ - Set started and enqueued jobs but not locked to pending - - A job is locked when it's being executed - When a job is killed, it releases the lock - - If the number of retries exceeds the number of max retries, - the job is set as 'failed' with the error 'JobFoundDead'. - - Adding a buffer on 'date_enqueued' to check - that it has been enqueued for more than 10sec. - This prevents from requeuing jobs before they are actually started. - - When Odoo shuts down normally, it waits for running jobs to finish. - However, when the Odoo server crashes or is otherwise force-stopped, - running jobs are interrupted while the runner has no chance to know - they have been aborted. - """ - - with closing(self.conn.cursor()) as cr: - query = self._query_requeue_dead_jobs() - - cr.execute(query) - - for (uuid,) in cr.fetchall(): - _logger.warning("Re-queued dead job with uuid: %s", uuid) - - -class QueueJobRunner: - def __init__( - self, - scheme="http", - host="localhost", - port=8069, - user=None, - password=None, - channel_config_string=None, - ): - self.scheme = scheme - self.host = host - self.port = port - self.user = user - self.password = password - self.channel_manager = ChannelManager() - if channel_config_string is None: - channel_config_string = _channels() - self.channel_manager.simple_configure(channel_config_string) - self.db_by_name = {} - self._stop = False - self._stop_pipe = os.pipe() - - def __del__(self): - # pylint: disable=except-pass - try: - os.close(self._stop_pipe[0]) - except OSError: - pass - try: - os.close(self._stop_pipe[1]) - except OSError: - pass - - @classmethod - def from_environ_or_config(cls): - scheme = os.environ.get("ODOO_QUEUE_JOB_SCHEME") or queue_job_config.get( - "scheme" - ) - host = ( - os.environ.get("ODOO_QUEUE_JOB_HOST") - or queue_job_config.get("host") - or config["http_interface"] - ) - port = ( - os.environ.get("ODOO_QUEUE_JOB_PORT") - or queue_job_config.get("port") - or config["http_port"] - ) - user = os.environ.get("ODOO_QUEUE_JOB_HTTP_AUTH_USER") or queue_job_config.get( - "http_auth_user" - ) - password = os.environ.get( - "ODOO_QUEUE_JOB_HTTP_AUTH_PASSWORD" - ) or queue_job_config.get("http_auth_password") - runner = cls( - scheme=scheme or "http", - host=host or "localhost", - port=port or 8069, - user=user, - password=password, - ) - return runner - - def get_db_names(self): - if config["db_name"]: - db_names = config["db_name"].split(",") - else: - db_names = odoo.service.db.list_dbs(True) - return db_names - - def close_databases(self, remove_jobs=True): - for db_name, db in self.db_by_name.items(): - try: - if remove_jobs: - self.channel_manager.remove_db(db_name) - db.close() - except Exception: - _logger.warning("error closing database %s", db_name, exc_info=True) - self.db_by_name = {} - - def initialize_databases(self): - for db_name in sorted(self.get_db_names()): - # sorting is important to avoid deadlocks in acquiring the master lock - db = Database(db_name) - if db.has_queue_job: - self.db_by_name[db_name] = db - with db.select_jobs("state in %s", (NOT_DONE,)) as cr: - for job_data in cr: - self.channel_manager.notify(db_name, *job_data) - _logger.info("queue job runner ready for db %s", db_name) - else: - db.close() - - def requeue_dead_jobs(self): - for db in self.db_by_name.values(): - if db.has_queue_job: - db.requeue_dead_jobs() - - def run_jobs(self): - now = _odoo_now() - for job in self.channel_manager.get_jobs_to_run(now): - if self._stop: - break - _logger.info("asking Odoo to run job %s on db %s", job.uuid, job.db_name) - self.db_by_name[job.db_name].set_job_enqueued(job.uuid) - _async_http_get( - self.scheme, - self.host, - self.port, - self.user, - self.password, - job.db_name, - job.uuid, - ) - - def process_notifications(self): - for db in self.db_by_name.values(): - if not db.conn.notifies: - # If there are no activity in the queue_job table it seems that - # tcp keepalives are not sent (in that very specific scenario), - # causing some intermediaries (such as haproxy) to close the - # connection, making the jobrunner to restart on a socket error - db.keep_alive() - while db.conn.notifies: - if self._stop: - break - notification = db.conn.notifies.pop() - uuid = notification.payload - with db.select_jobs("uuid = %s", (uuid,)) as cr: - job_datas = cr.fetchone() - if job_datas: - self.channel_manager.notify(db.db_name, *job_datas) - else: - self.channel_manager.remove_job(uuid) - - def wait_notification(self): - for db in self.db_by_name.values(): - if db.conn.notifies: - # something is going on in the queue, no need to wait - return - # wait for something to happen in the queue_job tables - # we'll select() on database connections and the stop pipe - conns = [db.conn for db in self.db_by_name.values()] - conns.append(self._stop_pipe[0]) - # look if the channels specify a wakeup time - wakeup_time = self.channel_manager.get_wakeup_time() - if not wakeup_time: - # this could very well be no timeout at all, because - # any activity in the job queue will wake us up, but - # let's have a timeout anyway, just to be safe - timeout = SELECT_TIMEOUT - else: - timeout = wakeup_time - _odoo_now() - # wait for a notification or a timeout; - # if timeout is negative (ie wakeup time in the past), - # do not wait; this should rarely happen - # because of how get_wakeup_time is designed; actually - # if timeout remains a large negative number, it is most - # probably a bug - _logger.debug("select() timeout: %.2f sec", timeout) - if timeout > 0: - if conns and not self._stop: - with select() as sel: - for conn in conns: - sel.register(conn, selectors.EVENT_READ) - events = sel.select(timeout=timeout) - for key, _mask in events: - if key.fileobj == self._stop_pipe[0]: - # stop-pipe is not a conn so doesn't need poll() - continue - key.fileobj.poll() - - def stop(self): - _logger.info("graceful stop requested") - self._stop = True - # wakeup the select() in wait_notification - os.write(self._stop_pipe[1], b".") - - def run(self): - _logger.info("starting") - while not self._stop: - # outer loop does exception recovery - try: - _logger.debug("initializing database connections") - # TODO: how to detect new databases or databases - # on which queue_job is installed after server start? - self.initialize_databases() - _logger.info("database connections ready") - # inner loop does the normal processing - while not self._stop: - self.requeue_dead_jobs() - self.process_notifications() - self.run_jobs() - self.wait_notification() - except KeyboardInterrupt: - self.stop() - except InterruptedError: - # Interrupted system call, i.e. KeyboardInterrupt during select - self.stop() - except MasterElectionLost as e: - _logger.debug( - "master election lost: %s, sleeping %ds and retrying", - e, - ERROR_RECOVERY_DELAY, - ) - self.close_databases() - time.sleep(ERROR_RECOVERY_DELAY) - except Exception: - _logger.exception( - "exception: sleeping %ds and retrying", ERROR_RECOVERY_DELAY - ) - self.close_databases() - time.sleep(ERROR_RECOVERY_DELAY) - self.close_databases(remove_jobs=False) - _logger.info("stopped") diff --git a/addons/queue_job/migrations/15.0.1.1.0/post-migration.py b/addons/queue_job/migrations/15.0.1.1.0/post-migration.py deleted file mode 100644 index f6eff72..0000000 --- a/addons/queue_job/migrations/15.0.1.1.0/post-migration.py +++ /dev/null @@ -1,47 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - with api.Environment.manage(): - env = api.Environment(cr, SUPERUSER_ID, {}) - _logger.info("Computing exception name for failed jobs") - _compute_jobs_new_values(env) - - -def _compute_jobs_new_values(env): - for job in env["queue.job"].search( - [("state", "=", "failed"), ("exc_info", "!=", False)] - ): - exception_details = _get_exception_details(job) - if exception_details: - job.update(exception_details) - - -def _get_exception_details(job): - for line in reversed(job.exc_info.splitlines()): - if _find_exception(line): - name, msg = line.split(":", 1) - return { - "exc_name": name.strip(), - "exc_message": msg.strip("()', \""), - } - - -def _find_exception(line): - # Just a list of common errors. - # If you want to target others, add your own migration step for your db. - exceptions = ( - "Error:", # catch all well named exceptions - # other live instance errors found - "requests.exceptions.MissingSchema", - "botocore.errorfactory.NoSuchKey", - ) - for exc in exceptions: - if exc in line: - return exc diff --git a/addons/queue_job/migrations/15.0.1.1.0/pre-migration.py b/addons/queue_job/migrations/15.0.1.1.0/pre-migration.py deleted file mode 100644 index 8ae6cb3..0000000 --- a/addons/queue_job/migrations/15.0.1.1.0/pre-migration.py +++ /dev/null @@ -1,33 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -from odoo.tools.sql import column_exists, table_exists - - -def migrate(cr, version): - if table_exists(cr, "queue_job") and not column_exists( - cr, "queue_job", "exec_time" - ): - # Disable trigger otherwise the update takes ages. - cr.execute( - """ - ALTER TABLE queue_job DISABLE TRIGGER queue_job_notify; - """ - ) - cr.execute( - """ - ALTER TABLE queue_job ADD COLUMN exec_time double precision DEFAULT 0; - """ - ) - cr.execute( - """ - UPDATE - queue_job - SET - exec_time = EXTRACT(EPOCH FROM (date_done - date_started)); - """ - ) - cr.execute( - """ - ALTER TABLE queue_job ENABLE TRIGGER queue_job_notify; - """ - ) diff --git a/addons/queue_job/migrations/16.0.2.11.1/pre-migration.py b/addons/queue_job/migrations/16.0.2.11.1/pre-migration.py deleted file mode 100644 index 931c336..0000000 --- a/addons/queue_job/migrations/16.0.2.11.1/pre-migration.py +++ /dev/null @@ -1,11 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) -from openupgradelib import openupgrade - - -@openupgrade.migrate() -def migrate(env, version): - # Remove cron garbage collector - openupgrade.delete_records_safely_by_xml_id( - env, - ["queue_job.ir_cron_queue_job_garbage_collector"], - ) diff --git a/addons/queue_job/migrations/16.0.2.6.1/pre-migration.py b/addons/queue_job/migrations/16.0.2.6.1/pre-migration.py deleted file mode 100644 index 53d9690..0000000 --- a/addons/queue_job/migrations/16.0.2.6.1/pre-migration.py +++ /dev/null @@ -1,10 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -from odoo.tools.sql import table_exists - - -def migrate(cr, version): - if table_exists(cr, "queue_job"): - # Drop index 'queue_job_identity_key_state_partial_index', - # it will be recreated during the update - cr.execute("DROP INDEX IF EXISTS queue_job_identity_key_state_partial_index;") diff --git a/addons/queue_job/models/__init__.py b/addons/queue_job/models/__init__.py deleted file mode 100644 index 6265dfe..0000000 --- a/addons/queue_job/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from . import base -from . import ir_model_fields -from . import queue_job -from . import queue_job_channel -from . import queue_job_function -from . import queue_job_lock diff --git a/addons/queue_job/models/base.py b/addons/queue_job/models/base.py deleted file mode 100644 index bd481dc..0000000 --- a/addons/queue_job/models/base.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright 2016 Camptocamp -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import functools - -from odoo import api, models - -from ..delay import Delayable, DelayableRecordset -from ..utils import must_run_without_delay - - -class Base(models.AbstractModel): - """The base model, which is implicitly inherited by all models. - - A new :meth:`~with_delay` method is added on all Odoo Models, allowing to - postpone the execution of a job method in an asynchronous process. - """ - - _inherit = "base" - - def with_delay( - self, - priority=None, - eta=None, - max_retries=None, - description=None, - channel=None, - identity_key=None, - ): - """Return a ``DelayableRecordset`` - - It is a shortcut for the longer form as shown below:: - - self.with_delay(priority=20).action_done() - # is equivalent to: - self.delayable().set(priority=20).action_done().delay() - - ``with_delay()`` accepts job properties which specify how the job will - be executed. - - Usage with job properties:: - - env['a.model'].with_delay(priority=30, eta=60*60*5).action_done() - delayable.export_one_thing(the_thing_to_export) - # => the job will be executed with a low priority and not before a - # delay of 5 hours from now - - When using :meth:``with_delay``, the final ``delay()`` is implicit. - See the documentation of :meth:``delayable`` for more details. - - :return: instance of a DelayableRecordset - :rtype: :class:`odoo.addons.queue_job.job.DelayableRecordset` - """ - return DelayableRecordset( - self, - priority=priority, - eta=eta, - max_retries=max_retries, - description=description, - channel=channel, - identity_key=identity_key, - ) - - def delayable( - self, - priority=None, - eta=None, - max_retries=None, - description=None, - channel=None, - identity_key=None, - ): - """Return a ``Delayable`` - - The returned instance allows to enqueue any method of the recordset's - Model. - - Usage:: - - delayable = self.env["res.users"].browse(10).delayable(priority=20) - delayable.do_work(name="test"}).delay() - - In this example, the ``do_work`` method will not be executed directly. - It will be executed in an asynchronous job. - - Method calls on a Delayable generally return themselves, so calls can - be chained together:: - - delayable.set(priority=15).do_work(name="test"}).delay() - - The order of the calls that build the job is not relevant, beside - the call to ``delay()`` that must happen at the very end. This is - equivalent to the example above:: - - delayable.do_work(name="test"}).set(priority=15).delay() - - Very importantly, ``delay()`` must be called on the top-most parent - of a chain of jobs, so if you have this:: - - job1 = record1.delayable().do_work() - job2 = record2.delayable().do_work() - job1.on_done(job2) - - The ``delay()`` call must be made on ``job1``, otherwise ``job2`` will - be delayed, but ``job1`` will never be. When done on ``job1``, the - ``delay()`` call will traverse the graph of jobs and delay all of - them:: - - job1.delay() - - For more details on the graph dependencies, read the documentation of - :module:`~odoo.addons.queue_job.delay`. - - :param priority: Priority of the job, 0 being the higher priority. - Default is 10. - :param eta: Estimated Time of Arrival of the job. It will not be - executed before this date/time. - :param max_retries: maximum number of retries before giving up and set - the job state to 'failed'. A value of 0 means - infinite retries. Default is 5. - :param description: human description of the job. If None, description - is computed from the function doc or name - :param channel: the complete name of the channel to use to process - the function. If specified it overrides the one - defined on the function - :param identity_key: key uniquely identifying the job, if specified - and a job with the same key has not yet been run, - the new job will not be added. It is either a - string, either a function that takes the job as - argument (see :py:func:`..job.identity_exact`). - the new job will not be added. - :return: instance of a Delayable - :rtype: :class:`odoo.addons.queue_job.job.Delayable` - """ - return Delayable( - self, - priority=priority, - eta=eta, - max_retries=max_retries, - description=description, - channel=channel, - identity_key=identity_key, - ) - - def _patch_job_auto_delay(self, method_name, context_key=None): - """Patch a method to be automatically delayed as job method when called - - This patch method has to be called in ``_register_hook`` (example - below). - - When a method is patched, any call to the method will not directly - execute the method's body, but will instead enqueue a job. - - When a ``context_key`` is set when calling ``_patch_job_auto_delay``, - the patched method is automatically delayed only when this key is - ``True`` in the caller's context. It is advised to patch the method - with a ``context_key``, because making the automatic delay *in any - case* can produce nasty and unexpected side effects (e.g. another - module calls the method and expects it to be computed before doing - something else, expecting a result, ...). - - A typical use case is when a method in a module we don't control is - called synchronously in the middle of another method, and we'd like all - the calls to this method become asynchronous. - - The options of the job usually passed to ``with_delay()`` (priority, - description, identity_key, ...) can be returned in a dictionary by a - method named after the name of the method suffixed by ``_job_options`` - which takes the same parameters as the initial method. - - It is still possible to force synchronous execution of the method by - setting a key ``_job_force_sync`` to True in the environment context. - - Example patching the "foo" method to be automatically delayed as job - (the job options method is optional): - - .. code-block:: python - - # original method: - def foo(self, arg1): - print("hello", arg1) - - def large_method(self): - # doing a lot of things - self.foo("world) - # doing a lot of other things - - def button_x(self): - self.with_context(auto_delay_foo=True).large_method() - - # auto delay patch: - def foo_job_options(self, arg1): - return { - "priority": 100, - "description": "Saying hello to {}".format(arg1) - } - - def _register_hook(self): - self._patch_method( - "foo", - self._patch_job_auto_delay("foo", context_key="auto_delay_foo") - ) - return super()._register_hook() - - The result when ``button_x`` is called, is that a new job for ``foo`` - is delayed. - """ - - def auto_delay_wrapper(self, *args, **kwargs): - # when no context_key is set, we delay in any case (warning, can be - # dangerous) - context_delay = self.env.context.get(context_key) if context_key else True - if ( - self.env.context.get("job_uuid") - or not context_delay - or must_run_without_delay(self.env) - ): - # we are in the job execution - return auto_delay_wrapper.origin(self, *args, **kwargs) - else: - # replace the synchronous call by a job on itself - method_name = auto_delay_wrapper.origin.__name__ - job_options_method = getattr( - self, "{}_job_options".format(method_name), None - ) - job_options = {} - if job_options_method: - job_options.update(job_options_method(*args, **kwargs)) - delayed = self.with_delay(**job_options) - return getattr(delayed, method_name)(*args, **kwargs) - - origin = getattr(self, method_name) - return functools.update_wrapper(auto_delay_wrapper, origin) - - @api.model - def _job_store_values(self, job): - """Hook for manipulating job stored values. - - You can define a more specific hook for a job function - by defining a method name with this pattern: - - `_queue_job_store_values_${func_name}` - - NOTE: values will be stored only if they match stored fields on `queue.job`. - - :param job: current queue_job.job.Job instance. - :return: dictionary for setting job values. - """ - return {} - - @api.model - def _job_prepare_context_before_enqueue_keys(self): - """Keys to keep in context of stored jobs - Empty by default for backward compatibility. - """ - return ("tz", "lang", "allowed_company_ids", "force_company", "active_test") - - def _job_prepare_context_before_enqueue(self): - """Return the context to store in the jobs - Can be used to keep only safe keys. - """ - return { - key: value - for key, value in self.env.context.items() - if key in self._job_prepare_context_before_enqueue_keys() - } diff --git a/addons/queue_job/models/ir_model_fields.py b/addons/queue_job/models/ir_model_fields.py deleted file mode 100644 index 5a31fcd..0000000 --- a/addons/queue_job/models/ir_model_fields.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 Camptocamp -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -from odoo import fields, models - - -class IrModelFields(models.Model): - _inherit = "ir.model.fields" - - ttype = fields.Selection( - selection_add=[("job_serialized", "Job Serialized")], - ondelete={"job_serialized": "cascade"}, - ) diff --git a/addons/queue_job/models/queue_job.py b/addons/queue_job/models/queue_job.py deleted file mode 100644 index df33e2c..0000000 --- a/addons/queue_job/models/queue_job.py +++ /dev/null @@ -1,463 +0,0 @@ -# Copyright 2013-2020 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import logging -import random -from datetime import datetime, timedelta - -from odoo import _, api, exceptions, fields, models -from odoo.tools import config, html_escape, index_exists - -from odoo.addons.base_sparse_field.models.fields import Serialized - -from ..delay import Graph -from ..exception import JobError -from ..fields import JobSerialized -from ..job import ( - CANCELLED, - DONE, - FAILED, - PENDING, - STARTED, - STATES, - WAIT_DEPENDENCIES, - Job, -) - -_logger = logging.getLogger(__name__) - - -class QueueJob(models.Model): - """Model storing the jobs to be executed.""" - - _name = "queue.job" - _description = "Queue Job" - _inherit = ["mail.thread", "mail.activity.mixin"] - _log_access = False - - _order = "date_created DESC, date_done DESC" - - _removal_interval = 30 # days - _default_related_action = "related_action_open_record" - - # This must be passed in a context key "_job_edit_sentinel" to write on - # protected fields. It protects against crafting "queue.job" records from - # RPC (e.g. on internal methods). When ``with_delay`` is used, the sentinel - # is set. - EDIT_SENTINEL = object() - _protected_fields = ( - "uuid", - "name", - "date_created", - "model_name", - "method_name", - "func_string", - "channel_method_name", - "job_function_id", - "records", - "args", - "kwargs", - ) - - uuid = fields.Char(string="UUID", readonly=True, index=True, required=True) - graph_uuid = fields.Char( - string="Graph UUID", - readonly=True, - index=True, - help="Single shared identifier of a Graph. Empty for a single job.", - ) - user_id = fields.Many2one(comodel_name="res.users", string="User ID") - company_id = fields.Many2one( - comodel_name="res.company", string="Company", index=True - ) - name = fields.Char(string="Description", readonly=True) - - model_name = fields.Char(string="Model", readonly=True) - method_name = fields.Char(readonly=True) - # record_ids field is only for backward compatibility (e.g. used in related - # actions), can be removed (replaced by "records") in 14.0 - record_ids = JobSerialized(compute="_compute_record_ids", base_type=list) - records = JobSerialized( - string="Record(s)", - readonly=True, - base_type=models.BaseModel, - ) - dependencies = Serialized(readonly=True) - # dependency graph as expected by the field widget - dependency_graph = Serialized(compute="_compute_dependency_graph") - graph_jobs_count = fields.Integer(compute="_compute_graph_jobs_count") - args = JobSerialized(readonly=True, base_type=tuple) - kwargs = JobSerialized(readonly=True, base_type=dict) - func_string = fields.Char(string="Task", readonly=True) - - state = fields.Selection(STATES, readonly=True, required=True, index=True) - priority = fields.Integer(group_operator=False) - exc_name = fields.Char(string="Exception", readonly=True) - exc_message = fields.Char(string="Exception Message", readonly=True, tracking=True) - exc_info = fields.Text(string="Exception Info", readonly=True) - result = fields.Text(readonly=True) - - date_created = fields.Datetime(string="Created Date", readonly=True) - date_started = fields.Datetime(string="Start Date", readonly=True) - date_enqueued = fields.Datetime(string="Enqueue Time", readonly=True) - date_done = fields.Datetime(readonly=True) - exec_time = fields.Float( - string="Execution Time (avg)", - group_operator="avg", - help="Time required to execute this job in seconds. Average when grouped.", - ) - date_cancelled = fields.Datetime(readonly=True) - - eta = fields.Datetime(string="Execute only after") - retry = fields.Integer(string="Current try") - max_retries = fields.Integer( - string="Max. retries", - help="The job will fail if the number of tries reach the " - "max. retries.\n" - "Retries are infinite when empty.", - ) - # FIXME the name of this field is very confusing - channel_method_name = fields.Char(string="Complete Method Name", readonly=True) - job_function_id = fields.Many2one( - comodel_name="queue.job.function", - string="Job Function", - readonly=True, - ) - - channel = fields.Char(index=True) - - identity_key = fields.Char(readonly=True) - worker_pid = fields.Integer(readonly=True) - - def init(self): - index_1 = "queue_job_identity_key_state_partial_index" - index_2 = "queue_job_channel_date_done_date_created_index" - if not index_exists(self._cr, index_1): - # Used by Job.job_record_with_same_identity_key - self._cr.execute( - "CREATE INDEX queue_job_identity_key_state_partial_index " - "ON queue_job (identity_key) WHERE state in ('pending', " - "'enqueued', 'wait_dependencies') AND identity_key IS NOT NULL;" - ) - if not index_exists(self._cr, index_2): - # Used by .autovacuum - self._cr.execute( - "CREATE INDEX queue_job_channel_date_done_date_created_index " - "ON queue_job (channel, date_done, date_created);" - ) - - @api.depends("records") - def _compute_record_ids(self): - for record in self: - record.record_ids = record.records.ids - - @api.depends("dependencies") - def _compute_dependency_graph(self): - jobs_groups = self.env["queue.job"].read_group( - [ - ( - "graph_uuid", - "in", - [uuid for uuid in self.mapped("graph_uuid") if uuid], - ) - ], - ["graph_uuid", "ids:array_agg(id)"], - ["graph_uuid"], - ) - ids_per_graph_uuid = { - group["graph_uuid"]: group["ids"] for group in jobs_groups - } - for record in self: - if not record.graph_uuid: - record.dependency_graph = {} - continue - - graph_jobs = self.browse(ids_per_graph_uuid.get(record.graph_uuid) or []) - if not graph_jobs: - record.dependency_graph = {} - continue - - graph_ids = {graph_job.uuid: graph_job.id for graph_job in graph_jobs} - graph_jobs_by_ids = {graph_job.id: graph_job for graph_job in graph_jobs} - - graph = Graph() - for graph_job in graph_jobs: - graph.add_vertex(graph_job.id) - for parent_uuid in graph_job.dependencies["depends_on"]: - parent_id = graph_ids.get(parent_uuid) - if not parent_id: - continue - graph.add_edge(parent_id, graph_job.id) - for child_uuid in graph_job.dependencies["reverse_depends_on"]: - child_id = graph_ids.get(child_uuid) - if not child_id: - continue - graph.add_edge(graph_job.id, child_id) - - record.dependency_graph = { - # list of ids - "nodes": [ - graph_jobs_by_ids[graph_id]._dependency_graph_vis_node() - for graph_id in graph.vertices() - ], - # list of tuples (from, to) - "edges": graph.edges(), - } - - def _dependency_graph_vis_node(self): - """Return the node as expected by the JobDirectedGraph widget""" - default = ("#D2E5FF", "#2B7CE9") - colors = { - DONE: ("#C2FABC", "#4AD63A"), - FAILED: ("#FB7E81", "#FA0A10"), - STARTED: ("#FFFF00", "#FFA500"), - } - return { - "id": self.id, - "title": "%s
%s" - % ( - html_escape(self.display_name), - html_escape(self.func_string), - ), - "color": colors.get(self.state, default)[0], - "border": colors.get(self.state, default)[1], - "shadow": True, - } - - def _compute_graph_jobs_count(self): - jobs_groups = self.env["queue.job"].read_group( - [ - ( - "graph_uuid", - "in", - [uuid for uuid in self.mapped("graph_uuid") if uuid], - ) - ], - ["graph_uuid"], - ["graph_uuid"], - ) - count_per_graph_uuid = { - group["graph_uuid"]: group["graph_uuid_count"] for group in jobs_groups - } - for record in self: - record.graph_jobs_count = count_per_graph_uuid.get(record.graph_uuid) or 0 - - @api.model_create_multi - def create(self, vals_list): - if self.env.context.get("_job_edit_sentinel") is not self.EDIT_SENTINEL: - # Prevent to create a queue.job record "raw" from RPC. - # ``with_delay()`` must be used. - raise exceptions.AccessError( - _("Queue jobs must be created by calling 'with_delay()'.") - ) - return super( - QueueJob, - self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True), - ).create(vals_list) - - def write(self, vals): - if self.env.context.get("_job_edit_sentinel") is not self.EDIT_SENTINEL: - write_on_protected_fields = [ - fieldname for fieldname in vals if fieldname in self._protected_fields - ] - if write_on_protected_fields: - raise exceptions.AccessError( - _("Not allowed to change field(s): {}").format( - write_on_protected_fields - ) - ) - - different_user_jobs = self.browse() - if vals.get("user_id"): - different_user_jobs = self.filtered( - lambda records: records.env.user.id != vals["user_id"] - ) - - if vals.get("state") == "failed": - self._message_post_on_failure() - - result = super().write(vals) - - for record in different_user_jobs: - # the user is stored in the env of the record, but we still want to - # have a stored user_id field to be able to search/groupby, so - # synchronize the env of records with user_id - super(QueueJob, record).write( - {"records": record.records.with_user(vals["user_id"])} - ) - return result - - def open_related_action(self): - """Open the related action associated to the job""" - self.ensure_one() - job = Job.load(self.env, self.uuid) - action = job.related_action() - if action is None: - raise exceptions.UserError(_("No action available for this job")) - return action - - def open_graph_jobs(self): - """Return action that opens all jobs of the same graph""" - self.ensure_one() - jobs = self.env["queue.job"].search([("graph_uuid", "=", self.graph_uuid)]) - - action = self.env["ir.actions.act_window"]._for_xml_id( - "queue_job.action_queue_job" - ) - action.update( - { - "name": _("Jobs for graph %s") % (self.graph_uuid), - "context": {}, - "domain": [("id", "in", jobs.ids)], - } - ) - return action - - def _change_job_state(self, state, result=None): - """Change the state of the `Job` object - - Changing the state of the Job will automatically change some fields - (date, result, ...). - """ - for record in self: - job_ = Job.load(record.env, record.uuid) - if state == DONE: - job_.set_done(result=result) - job_.store() - record.env["queue.job"].flush_model() - job_.enqueue_waiting() - elif state == PENDING: - job_.set_pending(result=result) - job_.store() - elif state == CANCELLED: - job_.set_cancelled(result=result) - job_.store() - record.env["queue.job"].flush_model() - job_.cancel_dependent_jobs() - else: - raise ValueError("State not supported: %s" % state) - - def button_done(self): - result = _("Manually set to done by %s") % self.env.user.name - self._change_job_state(DONE, result=result) - return True - - def button_cancelled(self): - result = _("Cancelled by %s") % self.env.user.name - self._change_job_state(CANCELLED, result=result) - return True - - def requeue(self): - jobs_to_requeue = self.filtered(lambda job_: job_.state != WAIT_DEPENDENCIES) - jobs_to_requeue._change_job_state(PENDING) - return jobs_to_requeue - - def _message_post_on_failure(self): - # subscribe the users now to avoid to subscribe them - # at every job creation - domain = self._subscribe_users_domain() - base_users = self.env["res.users"].search(domain) - for record in self: - users = base_users | record.user_id - record.message_subscribe(partner_ids=users.mapped("partner_id").ids) - msg = record._message_failed_job() - if msg: - record.message_post(body=msg, subtype_xmlid="queue_job.mt_job_failed") - - def _subscribe_users_domain(self): - """Subscribe all users having the 'Queue Job Manager' group""" - group = self.env.ref("queue_job.group_queue_job_manager") - if not group: - return None - companies = self.mapped("company_id") - domain = [("groups_id", "=", group.id)] - if companies: - domain.append(("company_id", "in", companies.ids)) - return domain - - def _message_failed_job(self): - """Return a message which will be posted on the job when it is failed. - - It can be inherited to allow more precise messages based on the - exception informations. - - If nothing is returned, no message will be posted. - """ - self.ensure_one() - return _( - "Something bad happened during the execution of job %s. " - "More details in the 'Exception Information' section.", - self.uuid, - ) - - def _needaction_domain_get(self): - """Returns the domain to filter records that require an action - - :return: domain or False is no action - """ - return [("state", "=", "failed")] - - def autovacuum(self): - """Delete all jobs done based on the removal interval defined on the - channel - - Called from a cron. - """ - for channel in self.env["queue.job.channel"].search([]): - deadline = datetime.now() - timedelta(days=int(channel.removal_interval)) - while True: - jobs = self.search( - [ - "|", - ("date_done", "<=", deadline), - ("date_cancelled", "<=", deadline), - ("channel", "=", channel.complete_name), - ], - order="date_done, date_created", - limit=1000, - ) - if jobs: - jobs.unlink() - if not config["test_enable"]: - self.env.cr.commit() # pylint: disable=E8102 - else: - break - return True - - def related_action_open_record(self): - """Open a form view with the record(s) of the job. - - For instance, for a job on a ``product.product``, it will open a - ``product.product`` form view with the product record(s) concerned by - the job. If the job concerns more than one record, it opens them in a - list. - - This is the default related action. - - """ - self.ensure_one() - records = self.records.exists() - if not records: - return None - action = { - "name": _("Related Record"), - "type": "ir.actions.act_window", - "view_mode": "form", - "res_model": records._name, - } - if len(records) == 1: - action["res_id"] = records.id - else: - action.update( - { - "name": _("Related Records"), - "view_mode": "tree,form", - "domain": [("id", "in", records.ids)], - } - ) - return action - - def _test_job(self, failure_rate=0): - _logger.info("Running test job.") - if random.random() <= failure_rate: - raise JobError("Job failed") diff --git a/addons/queue_job/models/queue_job_channel.py b/addons/queue_job/models/queue_job_channel.py deleted file mode 100644 index 920b021..0000000 --- a/addons/queue_job/models/queue_job_channel.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2013-2020 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - - -from odoo import _, api, exceptions, fields, models - - -class QueueJobChannel(models.Model): - _name = "queue.job.channel" - _description = "Job Channels" - - name = fields.Char() - complete_name = fields.Char( - compute="_compute_complete_name", store=True, readonly=True, recursive=True - ) - parent_id = fields.Many2one( - comodel_name="queue.job.channel", string="Parent Channel", ondelete="restrict" - ) - job_function_ids = fields.One2many( - comodel_name="queue.job.function", - inverse_name="channel_id", - string="Job Functions", - ) - removal_interval = fields.Integer( - default=lambda self: self.env["queue.job"]._removal_interval, required=True - ) - - _sql_constraints = [ - ("name_uniq", "unique(complete_name)", "Channel complete name must be unique") - ] - - @api.depends("name", "parent_id.complete_name") - def _compute_complete_name(self): - for record in self: - if not record.name: - complete_name = "" # new record - elif record.parent_id: - complete_name = ".".join([record.parent_id.complete_name, record.name]) - else: - complete_name = record.name - record.complete_name = complete_name - - @api.constrains("parent_id", "name") - def parent_required(self): - for record in self: - if record.name != "root" and not record.parent_id: - raise exceptions.ValidationError(_("Parent channel required.")) - - @api.model_create_multi - def create(self, vals_list): - records = self.browse() - if self.env.context.get("install_mode"): - # installing a module that creates a channel: rebinds the channel - # to an existing one (likely we already had the channel created by - # the @job decorator previously) - new_vals_list = [] - for vals in vals_list: - name = vals.get("name") - parent_id = vals.get("parent_id") - if name and parent_id: - existing = self.search( - [("name", "=", name), ("parent_id", "=", parent_id)] - ) - if existing: - if not existing.get_metadata()[0].get("noupdate"): - existing.write(vals) - records |= existing - continue - new_vals_list.append(vals) - vals_list = new_vals_list - records |= super().create(vals_list) - return records - - def write(self, values): - for channel in self: - if ( - not self.env.context.get("install_mode") - and channel.name == "root" - and ("name" in values or "parent_id" in values) - ): - raise exceptions.UserError(_("Cannot change the root channel")) - return super().write(values) - - def unlink(self): - for channel in self: - if channel.name == "root": - raise exceptions.UserError(_("Cannot remove the root channel")) - return super().unlink() - - def name_get(self): - result = [] - for record in self: - result.append((record.id, record.complete_name)) - return result diff --git a/addons/queue_job/models/queue_job_function.py b/addons/queue_job/models/queue_job_function.py deleted file mode 100644 index ad034b4..0000000 --- a/addons/queue_job/models/queue_job_function.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright 2013-2020 Camptocamp SA -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) - -import ast -import logging -import re -from collections import namedtuple - -from odoo import _, api, exceptions, fields, models, tools - -from ..fields import JobSerialized - -_logger = logging.getLogger(__name__) - - -regex_job_function_name = re.compile(r"^<([0-9a-z_\.]+)>\.([0-9a-zA-Z_]+)$") - - -class QueueJobFunction(models.Model): - _name = "queue.job.function" - _description = "Job Functions" - _log_access = False - - JobConfig = namedtuple( - "JobConfig", - "channel " - "retry_pattern " - "related_action_enable " - "related_action_func_name " - "related_action_kwargs " - "job_function_id ", - ) - - def _default_channel(self): - return self.env.ref("queue_job.channel_root") - - name = fields.Char( - compute="_compute_name", - inverse="_inverse_name", - index=True, - store=True, - ) - - # model and method should be required, but the required flag doesn't - # let a chance to _inverse_name to be executed - model_id = fields.Many2one( - comodel_name="ir.model", string="Model", ondelete="cascade" - ) - method = fields.Char() - - channel_id = fields.Many2one( - comodel_name="queue.job.channel", - string="Channel", - required=True, - default=lambda r: r._default_channel(), - ) - channel = fields.Char(related="channel_id.complete_name", store=True, readonly=True) - retry_pattern = JobSerialized(string="Retry Pattern (serialized)", base_type=dict) - edit_retry_pattern = fields.Text( - string="Retry Pattern", - compute="_compute_edit_retry_pattern", - inverse="_inverse_edit_retry_pattern", - help="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.", - ) - related_action = JobSerialized(string="Related Action (serialized)", base_type=dict) - edit_related_action = fields.Text( - string="Related Action", - compute="_compute_edit_related_action", - inverse="_inverse_edit_related_action", - help="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.", - ) - - @api.depends("model_id.model", "method") - def _compute_name(self): - for record in self: - if not (record.model_id and record.method): - record.name = "" - continue - record.name = self.job_function_name(record.model_id.model, record.method) - - def _inverse_name(self): - groups = regex_job_function_name.match(self.name) - if not groups: - raise exceptions.UserError(_("Invalid job function: {}").format(self.name)) - model_name = groups[1] - method = groups[2] - model = ( - self.env["ir.model"].sudo().search([("model", "=", model_name)], limit=1) - ) - if not model: - raise exceptions.UserError(_("Model {} not found").format(model_name)) - self.model_id = model.id - self.method = method - - @api.depends("retry_pattern") - def _compute_edit_retry_pattern(self): - for record in self: - retry_pattern = record._parse_retry_pattern() - record.edit_retry_pattern = str(retry_pattern) - - def _inverse_edit_retry_pattern(self): - try: - edited = (self.edit_retry_pattern or "").strip() - if edited: - self.retry_pattern = ast.literal_eval(edited) - else: - self.retry_pattern = {} - except (ValueError, TypeError, SyntaxError) as ex: - raise exceptions.UserError( - self._retry_pattern_format_error_message() - ) from ex - - @api.depends("related_action") - def _compute_edit_related_action(self): - for record in self: - record.edit_related_action = str(record.related_action) - - def _inverse_edit_related_action(self): - try: - edited = (self.edit_related_action or "").strip() - if edited: - self.related_action = ast.literal_eval(edited) - else: - self.related_action = {} - except (ValueError, TypeError, SyntaxError) as ex: - raise exceptions.UserError( - self._related_action_format_error_message() - ) from ex - - @staticmethod - def job_function_name(model_name, method_name): - return "<{}>.{}".format(model_name, method_name) - - def job_default_config(self): - return self.JobConfig( - channel="root", - retry_pattern={}, - related_action_enable=True, - related_action_func_name=None, - related_action_kwargs={}, - job_function_id=None, - ) - - def _parse_retry_pattern(self): - try: - # as json can't have integers as keys and the field is stored - # as json, convert back to int - retry_pattern = {} - for try_count, postpone_value in self.retry_pattern.items(): - if isinstance(postpone_value, int): - retry_pattern[int(try_count)] = postpone_value - else: - retry_pattern[int(try_count)] = tuple(postpone_value) - except ValueError: - _logger.error( - "Invalid retry pattern for job function %s," - " keys could not be parsed as integers, fallback" - " to the default retry pattern.", - self.name, - ) - retry_pattern = {} - return retry_pattern - - @tools.ormcache("name") - def job_config(self, name): - config = self.search([("name", "=", name)], limit=1) - if not config: - return self.job_default_config() - retry_pattern = config._parse_retry_pattern() - return self.JobConfig( - channel=config.channel, - retry_pattern=retry_pattern, - related_action_enable=config.related_action.get("enable", True), - related_action_func_name=config.related_action.get("func_name"), - related_action_kwargs=config.related_action.get("kwargs", {}), - job_function_id=config.id, - ) - - def _retry_pattern_format_error_message(self): - return _( - "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)}}" - ).format(self.name) - - @api.constrains("retry_pattern") - def _check_retry_pattern(self): - for record in self: - retry_pattern = record.retry_pattern - if not retry_pattern: - continue - - all_values = list(retry_pattern) + list(retry_pattern.values()) - for value in all_values: - try: - self._retry_value_type_check(value) - except ValueError as ex: - raise exceptions.UserError( - record._retry_pattern_format_error_message() - ) from ex - - def _retry_value_type_check(self, value): - if isinstance(value, (tuple, list)): - if len(value) != 2: - raise ValueError - [self._retry_value_type_check(element) for element in value] - return - int(value) - - def _related_action_format_error_message(self): - return _( - "Unexpected format of Related Action for {}.\n" - "Example of valid format:\n" - '{{"enable": True, "func_name": "related_action_foo",' - ' "kwargs" {{"limit": 10}}}}' - ).format(self.name) - - @api.constrains("related_action") - def _check_related_action(self): - valid_keys = ("enable", "func_name", "kwargs") - for record in self: - related_action = record.related_action - if not related_action: - continue - - if any(key not in valid_keys for key in related_action): - raise exceptions.UserError( - record._related_action_format_error_message() - ) - - @api.model_create_multi - def create(self, vals_list): - records = self.browse() - if self.env.context.get("install_mode"): - # installing a module that creates a job function: rebinds the record - # to an existing one (likely we already had the job function created by - # the @job decorator previously) - new_vals_list = [] - for vals in vals_list: - name = vals.get("name") - if name: - existing = self.search([("name", "=", name)], limit=1) - if existing: - if not existing.get_metadata()[0].get("noupdate"): - existing.write(vals) - records |= existing - continue - new_vals_list.append(vals) - vals_list = new_vals_list - records |= super().create(vals_list) - self.clear_caches() - return records - - def write(self, values): - res = super().write(values) - self.clear_caches() - return res - - def unlink(self): - res = super().unlink() - self.clear_caches() - return res diff --git a/addons/queue_job/models/queue_job_lock.py b/addons/queue_job/models/queue_job_lock.py deleted file mode 100644 index b01c7f3..0000000 --- a/addons/queue_job/models/queue_job_lock.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2025 ACSONE SA/NV -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class QueueJobLock(models.Model): - _name = "queue.job.lock" - _description = "Queue Job Lock" - - queue_job_id = fields.Many2one( - comodel_name="queue.job", - required=True, - ondelete="cascade", - index=True, - ) diff --git a/addons/queue_job/post_init_hook.py b/addons/queue_job/post_init_hook.py deleted file mode 100644 index 1e1a469..0000000 --- a/addons/queue_job/post_init_hook.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2020 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import logging - -logger = logging.getLogger(__name__) - - -def post_init_hook(cr, registry): - # this is the trigger that sends notifications when jobs change - logger.info("Create queue_job_notify trigger") - cr.execute( - """ - DROP TRIGGER IF EXISTS queue_job_notify ON queue_job; - CREATE OR REPLACE - FUNCTION queue_job_notify() RETURNS trigger AS $$ - BEGIN - IF TG_OP = 'DELETE' THEN - IF OLD.state != 'done' THEN - PERFORM pg_notify('queue_job', OLD.uuid); - END IF; - ELSE - PERFORM pg_notify('queue_job', NEW.uuid); - END IF; - RETURN NULL; - END; - $$ LANGUAGE plpgsql; - CREATE TRIGGER queue_job_notify - AFTER INSERT OR UPDATE OR DELETE - ON queue_job - FOR EACH ROW EXECUTE PROCEDURE queue_job_notify(); - """ - ) diff --git a/addons/queue_job/post_load.py b/addons/queue_job/post_load.py deleted file mode 100644 index f0c1df8..0000000 --- a/addons/queue_job/post_load.py +++ /dev/null @@ -1,25 +0,0 @@ -import logging - -from odoo import http - -_logger = logging.getLogger(__name__) - - -def post_load(): - _logger.info( - "Apply Request._get_session_and_dbname monkey patch to capture db" - " from request with multiple databases" - ) - _get_session_and_dbname_orig = http.Request._get_session_and_dbname - - def _get_session_and_dbname(self): - session, dbname = _get_session_and_dbname_orig(self) - if ( - not dbname - and self.httprequest.path == "/queue_job/runjob" - and self.httprequest.args.get("db") - ): - dbname = self.httprequest.args["db"] - return session, dbname - - http.Request._get_session_and_dbname = _get_session_and_dbname diff --git a/addons/queue_job/readme/CONFIGURE.rst b/addons/queue_job/readme/CONFIGURE.rst deleted file mode 100644 index 0d24075..0000000 --- a/addons/queue_job/readme/CONFIGURE.rst +++ /dev/null @@ -1,50 +0,0 @@ -* Using environment variables and command line: - - * Adjust environment variables (optional): - - - ``ODOO_QUEUE_JOB_CHANNELS=root:4`` or any other channels configuration. - The default is ``root:1`` - - - if ``xmlrpc_port`` is not set: ``ODOO_QUEUE_JOB_PORT=8069`` - - * Start Odoo with ``--load=web,queue_job`` - and ``--workers`` greater than 1. [1]_ - -* Keep in mind that the number of workers should be greater than the number of - channels. ``queue_job`` will reuse normal Odoo workers to process jobs. It - will not spawn its own workers. - -* Using the Odoo configuration file: - -.. code-block:: ini - - [options] - (...) - workers = 6 - server_wide_modules = web,queue_job - - (...) - [queue_job] - channels = root:2 - -* Environment variables have priority over the configuration file. - -* Confirm the runner is starting correctly by checking the odoo log file: - -.. code-block:: - - ...INFO...queue_job.jobrunner.runner: starting - ...INFO...queue_job.jobrunner.runner: initializing database connections - ...INFO...queue_job.jobrunner.runner: queue job runner ready for db - ...INFO...queue_job.jobrunner.runner: database connections ready - -* Create jobs (eg using ``base_import_async``) and observe they - start immediately and in parallel. - -* Tip: to enable debug logging for the queue job, use - ``--log-handler=odoo.addons.queue_job:DEBUG`` - -.. [1] It works with the threaded Odoo server too, although this way - of running Odoo is obviously not for production purposes. - -* Jobs that remain in `enqueued` or `started` state (because, for instance, their worker has been killed) will be automatically re-queued. diff --git a/addons/queue_job/readme/CONTRIBUTORS.rst b/addons/queue_job/readme/CONTRIBUTORS.rst deleted file mode 100644 index 4b34823..0000000 --- a/addons/queue_job/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,12 +0,0 @@ -* Guewen Baconnier -* Stéphane Bidoul -* Matthieu Dietrich -* Jos De Graeve -* David Lefever -* Laurent Mignon -* Laetitia Gangloff -* Cédric Pigeon -* Tatiana Deribina -* Souheil Bejaoui -* Eric Antones -* Simone Orsi diff --git a/addons/queue_job/readme/DESCRIPTION.rst b/addons/queue_job/readme/DESCRIPTION.rst deleted file mode 100644 index 263f863..0000000 --- a/addons/queue_job/readme/DESCRIPTION.rst +++ /dev/null @@ -1,46 +0,0 @@ -This addon adds an integrated Job Queue to Odoo. - -It allows to postpone method calls executed asynchronously. - -Jobs are executed in the background by a ``Jobrunner``, in their own transaction. - -Example: - -.. code-block:: python - - from odoo import models, fields, api - - class MyModel(models.Model): - _name = 'my.model' - - def my_method(self, a, k=None): - _logger.info('executed with a: %s and k: %s', a, k) - - - class MyOtherModel(models.Model): - _name = 'my.other.model' - - def button_do_stuff(self): - self.env['my.model'].with_delay().my_method('a', k=2) - - -In the snippet of code above, when we call ``button_do_stuff``, a job **capturing -the method and arguments** will be postponed. It will be executed as soon as the -Jobrunner has a free bucket, which can be instantaneous if no other job is -running. - - -Features: - -* Views for jobs, jobs are stored in PostgreSQL -* Jobrunner: execute the jobs, highly efficient thanks to PostgreSQL's NOTIFY -* Channels: give a capacity for the root channel and its sub-channels and - segregate jobs in them. Allow for instance to restrict heavy jobs to be - executed one at a time while little ones are executed 4 at a times. -* Retries: Ability to retry jobs by raising a type of exception -* Retry Pattern: the 3 first tries, retry after 10 seconds, the 5 next tries, - retry after 1 minutes, ... -* Job properties: priorities, estimated time of arrival (ETA), custom - description, number of retries -* Related Actions: link an action on the job view, such as open the record - concerned by the job diff --git a/addons/queue_job/readme/HISTORY.rst b/addons/queue_job/readme/HISTORY.rst deleted file mode 100644 index ef19313..0000000 --- a/addons/queue_job/readme/HISTORY.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. [ The change log. The goal of this file is to help readers - understand changes between version. The primary audience is - end users and integrators. Purely technical changes such as - code refactoring must not be mentioned here. - - This file may contain ONE level of section titles, underlined - with the ~ (tilde) character. Other section markers are - forbidden and will likely break the structure of the README.rst - or other documents where this fragment is included. ] - -Next -~~~~ - -* [ADD] Run jobrunner as a worker process instead of a thread in the main - process (when running with --workers > 0) -* [REF] ``@job`` and ``@related_action`` deprecated, any method can be delayed, - and configured using ``queue.job.function`` records -* [MIGRATION] from 13.0 branched at rev. e24ff4b diff --git a/addons/queue_job/readme/INSTALL.rst b/addons/queue_job/readme/INSTALL.rst deleted file mode 100644 index bff2c04..0000000 --- a/addons/queue_job/readme/INSTALL.rst +++ /dev/null @@ -1 +0,0 @@ -Be sure to have the ``requests`` library. diff --git a/addons/queue_job/readme/ROADMAP.rst b/addons/queue_job/readme/ROADMAP.rst deleted file mode 100644 index 34cc20e..0000000 --- a/addons/queue_job/readme/ROADMAP.rst +++ /dev/null @@ -1,18 +0,0 @@ -* After creating a new database or installing ``queue_job`` on an - existing database, Odoo must be restarted for the runner to detect it. - -* When Odoo shuts down normally, it waits for running jobs to finish. - However, when the Odoo server crashes or is otherwise force-stopped, - running jobs are interrupted while the runner has no chance to know - they have been aborted. In such situations, jobs may remain in - ``started`` or ``enqueued`` state after the Odoo server is halted. - Since the runner has no way to know if they are actually running or - not, and does not know for sure if it is safe to restart the jobs, - it does not attempt to restart them automatically. Such stale jobs - therefore fill the running queue and prevent other jobs to start. - You must therefore requeue them manually, either from the Jobs view, - or by running the following SQL statement *before starting Odoo*: - -.. code-block:: sql - - update queue_job set state='pending' where state in ('started', 'enqueued') diff --git a/addons/queue_job/readme/USAGE.rst b/addons/queue_job/readme/USAGE.rst deleted file mode 100644 index b1a0e6a..0000000 --- a/addons/queue_job/readme/USAGE.rst +++ /dev/null @@ -1,455 +0,0 @@ -To use this module, you need to: - -#. Go to ``Job Queue`` menu - -Developers -~~~~~~~~~~ - -Delaying jobs -------------- - -The fast way to enqueue a job for a method is to use ``with_delay()`` on a record -or model: - - -.. code-block:: python - - def button_done(self): - self.with_delay().print_confirmation_document(self.state) - self.write({"state": "done"}) - return True - -Here, the method ``print_confirmation_document()`` will be executed asynchronously -as a job. ``with_delay()`` can take several parameters to define more precisely how -the job is executed (priority, ...). - -All the arguments passed to the method being delayed are stored in the job and -passed to the method when it is executed asynchronously, including ``self``, so -the current record is maintained during the job execution (warning: the context -is not kept). - -Dependencies can be expressed between jobs. To start a graph of jobs, use ``delayable()`` -on a record or model. The following is the equivalent of ``with_delay()`` but using the -long form: - -.. code-block:: python - - def button_done(self): - delayable = self.delayable() - delayable.print_confirmation_document(self.state) - delayable.delay() - self.write({"state": "done"}) - return True - -Methods of Delayable objects return itself, so it can be used as a builder pattern, -which in some cases allow to build the jobs dynamically: - -.. code-block:: python - - def button_generate_simple_with_delayable(self): - self.ensure_one() - # Introduction of a delayable object, using a builder pattern - # allowing to chain jobs or set properties. The delay() method - # on the delayable object actually stores the delayable objects - # in the queue_job table - ( - self.delayable() - .generate_thumbnail((50, 50)) - .set(priority=30) - .set(description=_("generate xxx")) - .delay() - ) - -The simplest way to define a dependency is to use ``.on_done(job)`` on a Delayable: - -.. code-block:: python - - def button_chain_done(self): - self.ensure_one() - job1 = self.browse(1).delayable().generate_thumbnail((50, 50)) - job2 = self.browse(1).delayable().generate_thumbnail((50, 50)) - job3 = self.browse(1).delayable().generate_thumbnail((50, 50)) - # job 3 is executed when job 2 is done which is executed when job 1 is done - job1.on_done(job2.on_done(job3)).delay() - -Delayables can be chained to form more complex graphs using the ``chain()`` and -``group()`` primitives. -A chain represents a sequence of jobs to execute in order, a group represents -jobs which can be executed in parallel. Using ``chain()`` has the same effect as -using several nested ``on_done()`` but is more readable. Both can be combined to -form a graph, for instance we can group [A] of jobs, which blocks another group -[B] of jobs. When and only when all the jobs of the group [A] are executed, the -jobs of the group [B] are executed. The code would look like: - -.. code-block:: python - - from odoo.addons.queue_job.delay import group, chain - - def button_done(self): - group_a = group(self.delayable().method_foo(), self.delayable().method_bar()) - group_b = group(self.delayable().method_baz(1), self.delayable().method_baz(2)) - chain(group_a, group_b).delay() - self.write({"state": "done"}) - return True - -When a failure happens in a graph of jobs, the execution of the jobs that depend on the -failed job stops. They remain in a state ``wait_dependencies`` until their "parent" job is -successful. This can happen in two ways: either the parent job retries and is successful -on a second try, either the parent job is manually "set to done" by a user. In these two -cases, the dependency is resolved and the graph will continue to be processed. Alternatively, -the failed job and all its dependent jobs can be canceled by a user. The other jobs of the -graph that do not depend on the failed job continue their execution in any case. - -Note: ``delay()`` must be called on the delayable, chain, or group which is at the top -of the graph. In the example above, if it was called on ``group_a``, then ``group_b`` -would never be delayed (but a warning would be shown). - -It is also possible to split a job into several jobs, each one processing a part of the -work. This can be useful to avoid very long jobs, parallelize some task and get more specific -errors. Usage is as follows: - -.. code-block:: python - - def button_split_delayable(self): - ( - self # Can be a big recordset, let's say 1000 records - .delayable() - .generate_thumbnail((50, 50)) - .set(priority=30) - .set(description=_("generate xxx")) - .split(50) # Split the job in 20 jobs of 50 records each - .delay() - ) - -The ``split()`` method takes a ``chain`` boolean keyword argument. If set to -True, the jobs will be chained, meaning that the next job will only start when the previous -one is done: - -.. code-block:: python - - def button_increment_var(self): - ( - self - .delayable() - .increment_counter() - .split(1, chain=True) # Will exceute the jobs one after the other - .delay() - ) - - -Enqueing Job Options --------------------- - -* priority: default is 10, the closest it is to 0, the faster it will be - executed -* eta: Estimated Time of Arrival of the job. It will not be executed before this - date/time -* max_retries: default is 5, maximum number of retries before giving up and set - the job state to 'failed'. A value of 0 means infinite retries. -* description: human description of the job. If not set, description is computed - from the function doc or method name -* channel: the complete name of the channel to use to process the function. If - specified it overrides the one defined on the function -* identity_key: key uniquely identifying the job, if specified and a job with - the same key has not yet been run, the new job will not be created - -Configure default options for jobs ----------------------------------- - -In earlier versions, jobs could be configured using the ``@job`` decorator. -This is now obsolete, they can be configured using optional ``queue.job.function`` -and ``queue.job.channel`` XML records. - -Example of channel: - -.. code-block:: XML - - - sale - - - -Example of job function: - -.. code-block:: XML - - - - action_done - - - - - -The general form for the ``name`` is: ``.method``. - -The channel, related action and retry pattern options are optional, they are -documented below. - -When writing modules, if 2+ modules add a job function or channel with the same -name (and parent for channels), they'll be merged in the same record, even if -they have different xmlids. On uninstall, the merged record is deleted when all -the modules using it are uninstalled. - - -**Job function: model** - -If the function is defined in an abstract model, you can not write -```` -but you have to define a function for each model that inherits from the abstract model. - - -**Job function: channel** - -The channel where the job will be delayed. The default channel is ``root``. - -**Job function: related action** - -The *Related Action* appears as a button on the Job's view. -The button will execute the defined action. - -The default one is to open the view of the record related to the job (form view -when there is a single record, list view for several records). -In many cases, the default related action is enough and doesn't need -customization, but it can be customized by providing a dictionary on the job -function: - -.. code-block:: python - - { - "enable": False, - "func_name": "related_action_partner", - "kwargs": {"name": "Partner"}, - } - -* ``enable``: when ``False``, the button has no effect (default: ``True``) -* ``func_name``: name of the method on ``queue.job`` that returns an action -* ``kwargs``: extra arguments to pass to the related action method - -Example of related action code: - -.. code-block:: python - - class QueueJob(models.Model): - _inherit = 'queue.job' - - def related_action_partner(self, name): - self.ensure_one() - model = self.model_name - partner = self.records - action = { - 'name': name, - 'type': 'ir.actions.act_window', - 'res_model': model, - 'view_type': 'form', - 'view_mode': 'form', - 'res_id': partner.id, - } - return action - - -**Job function: retry pattern** - -When a job fails with a retryable error type, it is automatically -retried later. By default, the retry is always 10 minutes later. - -A retry pattern can be configured on the job function. What a pattern represents -is "from X tries, postpone to Y seconds". It is expressed as a dictionary where -keys are tries and values are seconds to postpone as integers: - - -.. code-block:: python - - { - 1: 10, - 5: 20, - 10: 30, - 15: 300, - } - -Based on this configuration, we can tell that: - -* 5 first retries are postponed 10 seconds later -* retries 5 to 10 postponed 20 seconds later -* retries 10 to 15 postponed 30 seconds later -* all subsequent retries postponed 5 minutes later - -**Job Context** - -The context of the recordset of the job, or any recordset passed in arguments of -a job, is transferred to the job according to an allow-list. - -The default allow-list is `("tz", "lang", "allowed_company_ids", "force_company", "active_test")`. It can -be customized in ``Base._job_prepare_context_before_enqueue_keys``. -**Bypass jobs on running Odoo** - -When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately. - -To do so you can set `QUEUE_JOB__NO_DELAY=1` in your environment. - -**Bypass jobs in tests** - -When writing tests on job-related methods is always tricky to deal with -delayed recordsets. To make your testing life easier -you can set `queue_job__no_delay=True` in the context. - -Tip: you can do this at test case level like this - -.. code-block:: python - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.env = cls.env(context=dict( - cls.env.context, - queue_job__no_delay=True, # no jobs thanks - )) - -Then all your tests execute the job methods synchronously -without delaying any jobs. - -Testing -------- - -**Asserting enqueued jobs** - -The recommended way to test jobs, rather than running them directly and synchronously is to -split the tests in two parts: - - * one test where the job is mocked (trap jobs with ``trap_jobs()`` and the test - only verifies that the job has been delayed with the expected arguments - * one test that only calls the method of the job synchronously, to validate the - proper behavior of this method only - -Proceeding this way means that you can prove that jobs will be enqueued properly -at runtime, and it ensures your code does not have a different behavior in tests -and in production (because running your jobs synchronously may have a different -behavior as they are in the same transaction / in the middle of the method). -Additionally, it gives more control on the arguments you want to pass when -calling the job's method (synchronously, this time, in the second type of -tests), and it makes tests smaller. - -The best way to run such assertions on the enqueued jobs is to use -``odoo.addons.queue_job.tests.common.trap_jobs()``. - -Inside this context manager, instead of being added in the database's queue, -jobs are pushed in an in-memory list. The context manager then provides useful -helpers to verify that jobs have been enqueued with the expected arguments. It -even can run the jobs of its list synchronously! Details in -``odoo.addons.queue_job.tests.common.JobsTester``. - -A very small example (more details in ``tests/common.py``): - -.. code-block:: python - - # code - def my_job_method(self, name, count): - self.write({"name": " ".join([name] * count) - - def method_to_test(self): - count = self.env["other.model"].search_count([]) - self.with_delay(priority=15).my_job_method("Hi!", count=count) - return count - - # tests - from odoo.addons.queue_job.tests.common import trap_jobs - - # first test only check the expected behavior of the method and the proper - # enqueuing of jobs - def test_method_to_test(self): - with trap_jobs() as trap: - result = self.env["model"].method_to_test() - expected_count = 12 - - trap.assert_jobs_count(1, only=self.env["model"].my_job_method) - trap.assert_enqueued_job( - self.env["model"].my_job_method, - args=("Hi!",), - kwargs=dict(count=expected_count), - properties=dict(priority=15) - ) - self.assertEqual(result, expected_count) - - - # second test to validate the behavior of the job unitarily - def test_my_job_method(self): - record = self.env["model"].browse(1) - record.my_job_method("Hi!", count=12) - self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!") - -If you prefer, you can still test the whole thing in a single test, by calling -``jobs_tester.perform_enqueued_jobs()`` in your test. - -.. code-block:: python - - def test_method_to_test(self): - with trap_jobs() as trap: - result = self.env["model"].method_to_test() - expected_count = 12 - - trap.assert_jobs_count(1, only=self.env["model"].my_job_method) - trap.assert_enqueued_job( - self.env["model"].my_job_method, - args=("Hi!",), - kwargs=dict(count=expected_count), - properties=dict(priority=15) - ) - self.assertEqual(result, expected_count) - - trap.perform_enqueued_jobs() - - record = self.env["model"].browse(1) - record.my_job_method("Hi!", count=12) - self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!") - -**Execute jobs synchronously when running Odoo** - -When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately. - -To do so you can set ``QUEUE_JOB__NO_DELAY=1`` in your environment. - -.. WARNING:: Do not do this in production - -**Execute jobs synchronously in tests** - -You should use ``trap_jobs``, really, but if for any reason you could not use it, -and still need to have job methods executed synchronously in your tests, you can -do so by setting ``queue_job__no_delay=True`` in the context. - -Tip: you can do this at test case level like this - -.. code-block:: python - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.env = cls.env(context=dict( - cls.env.context, - queue_job__no_delay=True, # no jobs thanks - )) - -Then all your tests execute the job methods synchronously without delaying any -jobs. - -In tests you'll have to mute the logger like: - - @mute_logger('odoo.addons.queue_job.models.base') - -.. NOTE:: in graphs of jobs, the ``queue_job__no_delay`` context key must be in at - least one job's env of the graph for the whole graph to be executed synchronously - - -Tips and tricks ---------------- - -* **Idempotency** (https://www.restapitutorial.com/lessons/idempotency.html): The queue_job should be idempotent so they can be retried several times without impact on the data. -* **The job should test at the very beginning its relevance**: the moment the job will be executed is unknown by design. So the first task of a job should be to check if the related work is still relevant at the moment of the execution. - -Patterns --------- -Through the time, two main patterns emerged: - -1. For data exposed to users, a model should store the data and the model should be the creator of the job. The job is kept hidden from the users -2. For technical data, that are not exposed to the users, it is generally alright to create directly jobs with data passed as arguments to the job, without intermediary models. diff --git a/addons/queue_job/security/ir.model.access.csv b/addons/queue_job/security/ir.model.access.csv deleted file mode 100644 index 4def7dc..0000000 --- a/addons/queue_job/security/ir.model.access.csv +++ /dev/null @@ -1,8 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_queue_job_manager,queue job manager,queue_job.model_queue_job,queue_job.group_queue_job_manager,1,1,1,1 -access_queue_job_lock_manager,queue job lock manager,queue_job.model_queue_job_lock,queue_job.group_queue_job_manager,1,0,0,0 -access_queue_job_function_manager,queue job functions manager,queue_job.model_queue_job_function,queue_job.group_queue_job_manager,1,1,1,1 -access_queue_job_channel_manager,queue job channel manager,queue_job.model_queue_job_channel,queue_job.group_queue_job_manager,1,1,1,1 -access_queue_requeue_job,queue requeue job manager,queue_job.model_queue_requeue_job,queue_job.group_queue_job_manager,1,1,1,1 -access_queue_jobs_to_done,queue jobs to done manager,queue_job.model_queue_jobs_to_done,queue_job.group_queue_job_manager,1,1,1,1 -access_queue_jobs_to_cancelled,queue jobs to cancelled manager,queue_job.model_queue_jobs_to_cancelled,queue_job.group_queue_job_manager,1,1,1,1 diff --git a/addons/queue_job/security/security.xml b/addons/queue_job/security/security.xml deleted file mode 100644 index 947644e..0000000 --- a/addons/queue_job/security/security.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Job Queue - 20 - - - Job Queue Manager - - - - - - - Job Queue multi-company - - - ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] - - - diff --git a/addons/queue_job/static/description/icon.png b/addons/queue_job/static/description/icon.png deleted file mode 100644 index c3bbca6..0000000 Binary files a/addons/queue_job/static/description/icon.png and /dev/null differ diff --git a/addons/queue_job/static/description/icon.svg b/addons/queue_job/static/description/icon.svg deleted file mode 100644 index a0a6afb..0000000 --- a/addons/queue_job/static/description/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/queue_job/static/description/index.html b/addons/queue_job/static/description/index.html deleted file mode 100644 index 82bed11..0000000 --- a/addons/queue_job/static/description/index.html +++ /dev/null @@ -1,998 +0,0 @@ - - - - - -README.rst - - - -
- - - -Odoo Community Association - -
-

Job Queue

- -

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

-

This addon adds an integrated Job Queue to Odoo.

-

It allows to postpone method calls executed asynchronously.

-

Jobs are executed in the background by a Jobrunner, in their own transaction.

-

Example:

-
-from odoo import models, fields, api
-
-class MyModel(models.Model):
-   _name = 'my.model'
-
-   def my_method(self, a, k=None):
-       _logger.info('executed with a: %s and k: %s', a, k)
-
-
-class MyOtherModel(models.Model):
-    _name = 'my.other.model'
-
-    def button_do_stuff(self):
-        self.env['my.model'].with_delay().my_method('a', k=2)
-
-

In the snippet of code above, when we call button_do_stuff, a job capturing -the method and arguments will be postponed. It will be executed as soon as the -Jobrunner has a free bucket, which can be instantaneous if no other job is -running.

-

Features:

-
    -
  • Views for jobs, jobs are stored in PostgreSQL
  • -
  • Jobrunner: execute the jobs, highly efficient thanks to PostgreSQL’s NOTIFY
  • -
  • Channels: give a capacity for the root channel and its sub-channels and -segregate jobs in them. Allow for instance to restrict heavy jobs to be -executed one at a time while little ones are executed 4 at a times.
  • -
  • Retries: Ability to retry jobs by raising a type of exception
  • -
  • Retry Pattern: the 3 first tries, retry after 10 seconds, the 5 next tries, -retry after 1 minutes, …
  • -
  • Job properties: priorities, estimated time of arrival (ETA), custom -description, number of retries
  • -
  • Related Actions: link an action on the job view, such as open the record -concerned by the job
  • -
-

Table of contents

- -
-

Installation

-

Be sure to have the requests library.

-
-
-

Configuration

-
    -
  • Using environment variables and command line:
      -
    • Adjust environment variables (optional):
        -
      • ODOO_QUEUE_JOB_CHANNELS=root:4 or any other channels configuration. -The default is root:1
      • -
      • if xmlrpc_port is not set: ODOO_QUEUE_JOB_PORT=8069
      • -
      -
    • -
    • Start Odoo with --load=web,queue_job -and --workers greater than 1. [1]
    • -
    -
  • -
  • Keep in mind that the number of workers should be greater than the number of -channels. queue_job will reuse normal Odoo workers to process jobs. It -will not spawn its own workers.
  • -
  • Using the Odoo configuration file:
  • -
-
-[options]
-(...)
-workers = 6
-server_wide_modules = web,queue_job
-
-(...)
-[queue_job]
-channels = root:2
-
-
    -
  • Environment variables have priority over the configuration file.
  • -
  • Confirm the runner is starting correctly by checking the odoo log file:
  • -
-
-...INFO...queue_job.jobrunner.runner: starting
-...INFO...queue_job.jobrunner.runner: initializing database connections
-...INFO...queue_job.jobrunner.runner: queue job runner ready for db <dbname>
-...INFO...queue_job.jobrunner.runner: database connections ready
-
-
    -
  • Create jobs (eg using base_import_async) and observe they -start immediately and in parallel.
  • -
  • Tip: to enable debug logging for the queue job, use ---log-handler=odoo.addons.queue_job:DEBUG
  • -
- - - - - -
[1]It works with the threaded Odoo server too, although this way -of running Odoo is obviously not for production purposes.
-
    -
  • Jobs that remain in enqueued or started state (because, for instance, their worker has been killed) will be automatically re-queued.
  • -
-
-
-

Usage

-

To use this module, you need to:

-
    -
  1. Go to Job Queue menu
  2. -
-
-

Developers

-
-

Delaying jobs

-

The fast way to enqueue a job for a method is to use with_delay() on a record -or model:

-
-def button_done(self):
-    self.with_delay().print_confirmation_document(self.state)
-    self.write({"state": "done"})
-    return True
-
-

Here, the method print_confirmation_document() will be executed asynchronously -as a job. with_delay() can take several parameters to define more precisely how -the job is executed (priority, …).

-

All the arguments passed to the method being delayed are stored in the job and -passed to the method when it is executed asynchronously, including self, so -the current record is maintained during the job execution (warning: the context -is not kept).

-

Dependencies can be expressed between jobs. To start a graph of jobs, use delayable() -on a record or model. The following is the equivalent of with_delay() but using the -long form:

-
-def button_done(self):
-    delayable = self.delayable()
-    delayable.print_confirmation_document(self.state)
-    delayable.delay()
-    self.write({"state": "done"})
-    return True
-
-

Methods of Delayable objects return itself, so it can be used as a builder pattern, -which in some cases allow to build the jobs dynamically:

-
-def button_generate_simple_with_delayable(self):
-    self.ensure_one()
-    # Introduction of a delayable object, using a builder pattern
-    # allowing to chain jobs or set properties. The delay() method
-    # on the delayable object actually stores the delayable objects
-    # in the queue_job table
-    (
-        self.delayable()
-        .generate_thumbnail((50, 50))
-        .set(priority=30)
-        .set(description=_("generate xxx"))
-        .delay()
-    )
-
-

The simplest way to define a dependency is to use .on_done(job) on a Delayable:

-
-def button_chain_done(self):
-    self.ensure_one()
-    job1 = self.browse(1).delayable().generate_thumbnail((50, 50))
-    job2 = self.browse(1).delayable().generate_thumbnail((50, 50))
-    job3 = self.browse(1).delayable().generate_thumbnail((50, 50))
-    # job 3 is executed when job 2 is done which is executed when job 1 is done
-    job1.on_done(job2.on_done(job3)).delay()
-
-

Delayables can be chained to form more complex graphs using the chain() and -group() primitives. -A chain represents a sequence of jobs to execute in order, a group represents -jobs which can be executed in parallel. Using chain() has the same effect as -using several nested on_done() but is more readable. Both can be combined to -form a graph, for instance we can group [A] of jobs, which blocks another group -[B] of jobs. When and only when all the jobs of the group [A] are executed, the -jobs of the group [B] are executed. The code would look like:

-
-from odoo.addons.queue_job.delay import group, chain
-
-def button_done(self):
-    group_a = group(self.delayable().method_foo(), self.delayable().method_bar())
-    group_b = group(self.delayable().method_baz(1), self.delayable().method_baz(2))
-    chain(group_a, group_b).delay()
-    self.write({"state": "done"})
-    return True
-
-

When a failure happens in a graph of jobs, the execution of the jobs that depend on the -failed job stops. They remain in a state wait_dependencies until their “parent” job is -successful. This can happen in two ways: either the parent job retries and is successful -on a second try, either the parent job is manually “set to done” by a user. In these two -cases, the dependency is resolved and the graph will continue to be processed. Alternatively, -the failed job and all its dependent jobs can be canceled by a user. The other jobs of the -graph that do not depend on the failed job continue their execution in any case.

-

Note: delay() must be called on the delayable, chain, or group which is at the top -of the graph. In the example above, if it was called on group_a, then group_b -would never be delayed (but a warning would be shown).

-

It is also possible to split a job into several jobs, each one processing a part of the -work. This can be useful to avoid very long jobs, parallelize some task and get more specific -errors. Usage is as follows:

-
-def button_split_delayable(self):
-    (
-        self  # Can be a big recordset, let's say 1000 records
-        .delayable()
-        .generate_thumbnail((50, 50))
-        .set(priority=30)
-        .set(description=_("generate xxx"))
-        .split(50)  # Split the job in 20 jobs of 50 records each
-        .delay()
-    )
-
-

The split() method takes a chain boolean keyword argument. If set to -True, the jobs will be chained, meaning that the next job will only start when the previous -one is done:

-
-def button_increment_var(self):
-    (
-        self
-        .delayable()
-        .increment_counter()
-        .split(1, chain=True) # Will exceute the jobs one after the other
-        .delay()
-    )
-
-
-
-

Enqueing Job Options

-
    -
  • priority: default is 10, the closest it is to 0, the faster it will be -executed
  • -
  • eta: Estimated Time of Arrival of the job. It will not be executed before this -date/time
  • -
  • max_retries: default is 5, maximum number of retries before giving up and set -the job state to ‘failed’. A value of 0 means infinite retries.
  • -
  • description: human description of the job. If not set, description is computed -from the function doc or method name
  • -
  • channel: the complete name of the channel to use to process the function. If -specified it overrides the one defined on the function
  • -
  • identity_key: key uniquely identifying the job, if specified and a job with -the same key has not yet been run, the new job will not be created
  • -
-
-
-

Configure default options for jobs

-

In earlier versions, jobs could be configured using the @job decorator. -This is now obsolete, they can be configured using optional queue.job.function -and queue.job.channel XML records.

-

Example of channel:

-
-<record id="channel_sale" model="queue.job.channel">
-    <field name="name">sale</field>
-    <field name="parent_id" ref="queue_job.channel_root" />
-</record>
-
-

Example of job function:

-
-<record id="job_function_sale_order_action_done" model="queue.job.function">
-    <field name="model_id" ref="sale.model_sale_order" />
-    <field name="method">action_done</field>
-    <field name="channel_id" ref="channel_sale" />
-    <field name="related_action" eval='{"func_name": "custom_related_action"}' />
-    <field name="retry_pattern" eval="{1: 60, 2: 180, 3: 10, 5: 300}" />
-</record>
-
-

The general form for the name is: <model.name>.method.

-

The channel, related action and retry pattern options are optional, they are -documented below.

-

When writing modules, if 2+ modules add a job function or channel with the same -name (and parent for channels), they’ll be merged in the same record, even if -they have different xmlids. On uninstall, the merged record is deleted when all -the modules using it are uninstalled.

-

Job function: model

-

If the function is defined in an abstract model, you can not write -<field name="model_id" ref="xml_id_of_the_abstract_model"</field> -but you have to define a function for each model that inherits from the abstract model.

-

Job function: channel

-

The channel where the job will be delayed. The default channel is root.

-

Job function: related action

-

The Related Action appears as a button on the Job’s view. -The button will execute the defined action.

-

The default one is to open the view of the record related to the job (form view -when there is a single record, list view for several records). -In many cases, the default related action is enough and doesn’t need -customization, but it can be customized by providing a dictionary on the job -function:

-
-{
-    "enable": False,
-    "func_name": "related_action_partner",
-    "kwargs": {"name": "Partner"},
-}
-
-
    -
  • enable: when False, the button has no effect (default: True)
  • -
  • func_name: name of the method on queue.job that returns an action
  • -
  • kwargs: extra arguments to pass to the related action method
  • -
-

Example of related action code:

-
-class QueueJob(models.Model):
-    _inherit = 'queue.job'
-
-    def related_action_partner(self, name):
-        self.ensure_one()
-        model = self.model_name
-        partner = self.records
-        action = {
-            'name': name,
-            'type': 'ir.actions.act_window',
-            'res_model': model,
-            'view_type': 'form',
-            'view_mode': 'form',
-            'res_id': partner.id,
-        }
-        return action
-
-

Job function: retry pattern

-

When a job fails with a retryable error type, it is automatically -retried later. By default, the retry is always 10 minutes later.

-

A retry pattern can be configured on the job function. What a pattern represents -is “from X tries, postpone to Y seconds”. It is expressed as a dictionary where -keys are tries and values are seconds to postpone as integers:

-
-{
-    1: 10,
-    5: 20,
-    10: 30,
-    15: 300,
-}
-
-

Based on this configuration, we can tell that:

-
    -
  • 5 first retries are postponed 10 seconds later
  • -
  • retries 5 to 10 postponed 20 seconds later
  • -
  • retries 10 to 15 postponed 30 seconds later
  • -
  • all subsequent retries postponed 5 minutes later
  • -
-

Job Context

-

The context of the recordset of the job, or any recordset passed in arguments of -a job, is transferred to the job according to an allow-list.

-

The default allow-list is (“tz”, “lang”, “allowed_company_ids”, “force_company”, “active_test”). It can -be customized in Base._job_prepare_context_before_enqueue_keys. -Bypass jobs on running Odoo

-

When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately.

-

To do so you can set QUEUE_JOB__NO_DELAY=1 in your environment.

-

Bypass jobs in tests

-

When writing tests on job-related methods is always tricky to deal with -delayed recordsets. To make your testing life easier -you can set queue_job__no_delay=True in the context.

-

Tip: you can do this at test case level like this

-
-@classmethod
-def setUpClass(cls):
-    super().setUpClass()
-    cls.env = cls.env(context=dict(
-        cls.env.context,
-        queue_job__no_delay=True,  # no jobs thanks
-    ))
-
-

Then all your tests execute the job methods synchronously -without delaying any jobs.

-
-
-

Testing

-

Asserting enqueued jobs

-

The recommended way to test jobs, rather than running them directly and synchronously is to -split the tests in two parts:

-
-
    -
  • one test where the job is mocked (trap jobs with trap_jobs() and the test -only verifies that the job has been delayed with the expected arguments
  • -
  • one test that only calls the method of the job synchronously, to validate the -proper behavior of this method only
  • -
-
-

Proceeding this way means that you can prove that jobs will be enqueued properly -at runtime, and it ensures your code does not have a different behavior in tests -and in production (because running your jobs synchronously may have a different -behavior as they are in the same transaction / in the middle of the method). -Additionally, it gives more control on the arguments you want to pass when -calling the job’s method (synchronously, this time, in the second type of -tests), and it makes tests smaller.

-

The best way to run such assertions on the enqueued jobs is to use -odoo.addons.queue_job.tests.common.trap_jobs().

-

Inside this context manager, instead of being added in the database’s queue, -jobs are pushed in an in-memory list. The context manager then provides useful -helpers to verify that jobs have been enqueued with the expected arguments. It -even can run the jobs of its list synchronously! Details in -odoo.addons.queue_job.tests.common.JobsTester.

-

A very small example (more details in tests/common.py):

-
-# code
-def my_job_method(self, name, count):
-    self.write({"name": " ".join([name] * count)
-
-def method_to_test(self):
-    count = self.env["other.model"].search_count([])
-    self.with_delay(priority=15).my_job_method("Hi!", count=count)
-    return count
-
-# tests
-from odoo.addons.queue_job.tests.common import trap_jobs
-
-# first test only check the expected behavior of the method and the proper
-# enqueuing of jobs
-def test_method_to_test(self):
-    with trap_jobs() as trap:
-        result = self.env["model"].method_to_test()
-        expected_count = 12
-
-        trap.assert_jobs_count(1, only=self.env["model"].my_job_method)
-        trap.assert_enqueued_job(
-            self.env["model"].my_job_method,
-            args=("Hi!",),
-            kwargs=dict(count=expected_count),
-            properties=dict(priority=15)
-        )
-        self.assertEqual(result, expected_count)
-
-
- # second test to validate the behavior of the job unitarily
- def test_my_job_method(self):
-     record = self.env["model"].browse(1)
-     record.my_job_method("Hi!", count=12)
-     self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!")
-
-

If you prefer, you can still test the whole thing in a single test, by calling -jobs_tester.perform_enqueued_jobs() in your test.

-
-def test_method_to_test(self):
-    with trap_jobs() as trap:
-        result = self.env["model"].method_to_test()
-        expected_count = 12
-
-        trap.assert_jobs_count(1, only=self.env["model"].my_job_method)
-        trap.assert_enqueued_job(
-            self.env["model"].my_job_method,
-            args=("Hi!",),
-            kwargs=dict(count=expected_count),
-            properties=dict(priority=15)
-        )
-        self.assertEqual(result, expected_count)
-
-        trap.perform_enqueued_jobs()
-
-        record = self.env["model"].browse(1)
-        record.my_job_method("Hi!", count=12)
-        self.assertEqual(record.name, "Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi! Hi!")
-
-

Execute jobs synchronously when running Odoo

-

When you are developing (ie: connector modules) you might want -to bypass the queue job and run your code immediately.

-

To do so you can set QUEUE_JOB__NO_DELAY=1 in your environment.

-
-

Warning

-

Do not do this in production

-
-

Execute jobs synchronously in tests

-

You should use trap_jobs, really, but if for any reason you could not use it, -and still need to have job methods executed synchronously in your tests, you can -do so by setting queue_job__no_delay=True in the context.

-

Tip: you can do this at test case level like this

-
-@classmethod
-def setUpClass(cls):
-    super().setUpClass()
-    cls.env = cls.env(context=dict(
-        cls.env.context,
-        queue_job__no_delay=True,  # no jobs thanks
-    ))
-
-

Then all your tests execute the job methods synchronously without delaying any -jobs.

-

In tests you’ll have to mute the logger like:

-
-@mute_logger(‘odoo.addons.queue_job.models.base’)
-
-

Note

-

in graphs of jobs, the queue_job__no_delay context key must be in at -least one job’s env of the graph for the whole graph to be executed synchronously

-
-
-
-

Tips and tricks

-
    -
  • Idempotency (https://www.restapitutorial.com/lessons/idempotency.html): The queue_job should be idempotent so they can be retried several times without impact on the data.
  • -
  • The job should test at the very beginning its relevance: the moment the job will be executed is unknown by design. So the first task of a job should be to check if the related work is still relevant at the moment of the execution.
  • -
-
-
-

Patterns

-

Through the time, two main patterns emerged:

-
    -
  1. For data exposed to users, a model should store the data and the model should be the creator of the job. The job is kept hidden from the users
  2. -
  3. For technical data, that are not exposed to the users, it is generally alright to create directly jobs with data passed as arguments to the job, without intermediary models.
  4. -
-
-
-
-
-

Known issues / Roadmap

-
    -
  • After creating a new database or installing queue_job on an -existing database, Odoo must be restarted for the runner to detect it.
  • -
  • When Odoo shuts down normally, it waits for running jobs to finish. -However, when the Odoo server crashes or is otherwise force-stopped, -running jobs are interrupted while the runner has no chance to know -they have been aborted. In such situations, jobs may remain in -started or enqueued state after the Odoo server is halted. -Since the runner has no way to know if they are actually running or -not, and does not know for sure if it is safe to restart the jobs, -it does not attempt to restart them automatically. Such stale jobs -therefore fill the running queue and prevent other jobs to start. -You must therefore requeue them manually, either from the Jobs view, -or by running the following SQL statement before starting Odoo:
  • -
-
-update queue_job set state='pending' where state in ('started', 'enqueued')
-
-
-
-

Changelog

- - -
-
-

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
  • -
  • ACSONE SA/NV
  • -
-
-
-

Contributors

- -
-
-

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:

-

guewen

-

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/addons/queue_job/static/lib/vis/vis-network.min.css b/addons/queue_job/static/lib/vis/vis-network.min.css deleted file mode 100644 index d708f17..0000000 --- a/addons/queue_job/static/lib/vis/vis-network.min.css +++ /dev/null @@ -1 +0,0 @@ -.vis-overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:0 0 10px 0 rgba(0,0,0,.5)}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow:after,div.vis-color-picker div.vis-arrow:before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:hsla(0,0%,100%,0) #fff hsla(0,0%,100%,0) hsla(0,0%,100%,0);border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(180deg,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#4c4c4c",endColorstr="#131313",GradientType=0)}div.vis-color-picker div.vis-new-color{left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{position:absolute;width:140px;height:20px;border:1px solid rgba(0,0,0,.1);border-radius:5px;top:380px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-color-picker div.vis-initial-color{left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper:after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);border:1px solid #999;box-shadow:0 0 3px 0 #aaa;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2,#385380);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(180deg,#3876c2 0,#385380);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#3876c2",endColorstr="#385380",GradientType=0);box-shadow:0 0 1px 0 #111927;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:none}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(180deg,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#9d9d9d",endColorstr="#c8c8c8",GradientType=0)}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);border:1px solid #999;box-shadow:0 0 3px 0 #aaa;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:transparent;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0) rgba(136,183,213,0) rgba(136,183,213,0) rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0) rgba(194,225,245,0) rgba(194,225,245,0) #f2faff;border-width:12px;margin-top:-12px}div.vis-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none;z-index:5}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url("data:image/png;base64,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");bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url("data:image/png;base64,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");bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url("data:image/png;base64,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");bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url("data:image/png;base64,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");bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url("data:image/png;base64,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");bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url("data:image/png;base64,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");bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url("data:image/png;base64,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");bottom:50px;right:15px}div.vis-network div.vis-manipulation{box-sizing:content-box;border:0 solid #d6d9d8;border-bottom:1px;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff,#fcfcfc 48%,#fafafa 50%,#fcfcfc);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(180deg,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ffffff",endColorstr="#fcfcfc",GradientType=0);padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network button.vis-edit-mode,div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network button.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-color:transparent;background-position:20px 3px;background-repeat:no-repeat;background-image:url("data:image/png;base64,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/GMzLdsAADvGaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICAgICAgICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTQtMDItMTRUMTE6NTU6MzUrMDE6MDA8L3htcDpDcmVhdGVEYXRlPgogICAgICAgICA8eG1wOk1ldGFkYXRhRGF0ZT4yMDE0LTAyLTE0VDEyOjA1OjE3KzAxOjAwPC94bXA6TWV0YWRhdGFEYXRlPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNC0wMi0xNFQxMjowNToxNyswMTowMDwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6NjU0YmM5YmQtMWI2Yi1jYjRhLTllOWQtNWY2MzgxNDVjZjk0PC94bXBNTTpJbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjk4MmM2MGIwLWUzZjMtMDk0MC04MjU0LTFiZTliNWE0ZTE4MzwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjk4MmM2MGIwLWUzZjMtMDk0MC04MjU0LTFiZTliNWE0ZTE4MzwveG1wTU06T3JpZ2luYWxEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SGlzdG9yeT4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNyZWF0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo5ODJjNjBiMC1lM2YzLTA5NDAtODI1NC0xYmU5YjVhNGUxODM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMTRUMTE6NTU6MzUrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjIxODYxNmM2LTM1MWMtNDI0OS04YWFkLWJkZDQ2ZTczNWE0NDwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0xNFQxMTo1NTozNSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NjU0YmM5YmQtMWI2Yi1jYjRhLTllOWQtNWY2MzgxNDVjZjk0PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAyLTE0VDEyOjA1OjE3KzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L3htcE1NOkhpc3Rvcnk+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjc8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+NzwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+cZUZMwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA2ElEQVR42gDLADT/AS0tLUQFBQUVFxcXtPHx8fPl5eUNCAgITCkpKesEHx8fGgYGBjH+/v4a+Pj4qgQEBFU6OjodMTExzwQUFBSvEBAQEfX19SD19fVqNDQ0CElJSd/9/f2vAwEBAfrn5+fkBwcHLRYWFgsXFxfz29vbo9LS0uwDDQ0NDfPz81orKysXIyMj+ODg4Avh4eEa/f391gMkJCRYPz8/KUhISOMCAgKh8fHxHRsbGx4UFBQQBDk5OeY7Ozv7CAgItPb29vMEBASaJSUlTQ0NDesDAEwpT0Ko8Ri2AAAAAElFTkSuQmCC");border:none;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network button.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode button.vis-button,div.vis-network div.vis-manipulation button.vis-button{float:left;font-family:verdana;font-size:12px;border:none;box-sizing:content-box;-moz-border-radius:15px;border-radius:15px;background-color:transparent;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;cursor:pointer;padding:0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-manipulation button.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation button.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation button.vis-button.vis-back{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNTowMTowOSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTU6MDE6MDkrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOmI2YjQwMjVkLTAxNjQtMzU0OC1hOTdlLTQ4ZmYxMWM3NTYzMzwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDpmOWQ3OGY4ZC1lNzY0LTc1NDgtODZiNy1iNmQ1OGMzZDg2OTc8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTU6MDE6MDkrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOmI2YjQwMjVkLTAxNjQtMzU0OC1hOTdlLTQ4ZmYxMWM3NTYzMzwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNTowMTowOSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOmY5ZDc4ZjhkLWU3NjQtNzU0OC04NmI3LWI2ZDU4YzNkODY5Nzwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4jq1U/AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAVTSURBVHjanFVfTFNnFP+d77ve8qeVFbBrpcVgRrCRFikFByLxwSAaE32oRCHD6JMxxhhn8G2RxxH3MsOTbyYsmCAxPMmMMYtkIUYmK60OO0qAK23BFlNob0uh3x7WS5jLZPpLbm6+k/P9zrm5v9855PF4UFhYCABgjIExBgAgIqRSqRIi6gDQRkQ1RGTB3wgR0e8AHgH4Sa/XR/EBiAiJRAJ04cIF5Ofng4g2n0gkUkxENwF0c843LzHGQEQQQkCLExEA9ALotVgsUQAQQmgNQhJCbF5kjCEUCl0moj4t5na7fTU1NUpVVVXUYrEkASAcDhe8efOmxOfzWScmJqoBdBNR99LS0hWz2dynNSSEAF28eBGFhYVgjCEcDn9HRD1EhIMHD3o9Hs9kWVlZAh9BKBQqGB4edr58+dKZ+6JbJpOpBwBWV1fB6+rqIMsyIpHIFcZYL2MMra2tY5cuXRrfuXNnBtvAYDBk3G63oqpqZm5uzgrgSDKZjBoMhueZTAbc5XIhFouVEtFTxhiOHTs2dv78eS8+Efv374+oqpqZnZ21cs5PJJPJPlmWkyynnBuMMTQ0NHi7uro+mVyDx+Pxulwu71ZOlkqlSonoJhGhvb39s8k1nDx50ss5hyRJN9PpdKlERB2aWjSVaEilUvzBgwcORVEs5eXloXPnzk1sV8BkMiUdDofP7/dXZ7PZDilnIhw4cGBeS1pbW2P37t1zBwKBikQiUUREWFhYsHHO0d7evm0Ru90+/+rVq2rO+XGJiJxEhMrKyhgAjI6OWoeHh5tWVla+4JzDZrO9bW5unhwcHGzz+/32np4e+xaDbfoHAMxmc6ijo2O0oqIiJkkSNjY2HBIRmRljMJvNyWfPnln7+/tPMMZQXl6+0NbW9qK2tjYcj8floaEhqKpq+HCkbD3PzMwYBgYG0NXV9UuusFna2kEgELAQEQ4dOvSis7PzN41Ar9dnrl27NqCNkv/C3bt3zy4tLVmICJxzEBFJRBQmorLFxcWCqqqq0Pj4eO3Y2JhbUZTdra2tL2pra8OJRGLHnTt3zkqS9K+huHU4EhHMZnMoGo0W5OIh7nK5jjLGKq1W69vDhw8rRqMxMjc3t2t5eXnX5ORklc/nM+fl5SWnpqa+0uv1K/n5+Ws6nW5NluXNd15e3ppOp1uz2WyzZ86cGQ0Gg6ZAIFCZzWZ/lYjokRDiuN/vt7W0tMw3NTUpbrd78P79++5gMFgRiUTKHj58WMYYQ3V19etTp05tq6Lp6Wkb5xxCiEfc7XZPM8a6FxcXTfX19a/1en2Gcy5qamreNjY2/qGq6joRZe12+9Tp06e3JY/FYgWPHz8+mhvr3/CWlpbk+vp6PmOseWVlBS6XS9GSJUkSdrs93NDQ8Oe+ffvC/8fJIyMjddFo9Esi6pVleVjT2m0A8Hq9zqGhIefnjoknT544A4GAM/eDbxMReFNTE0pKSpKqqsaI6Pj8/LxVVdWM3W6PfCr5xMTE1zllXS0uLn6aSqXAGxsbodPpoNfrn6uqCs75EUVRrJFIZMfevXsXdTrdxseIE4mEPDIyUu/3++tynd8yGo29RIR0Og26fv06ioqKwBgD5xzv3r27zBjrIyJIkgSHwzFZWVmp7NmzJ1ZaWpoAgGg0WqgoSvHMzIw1GAw6tvjhitFo7NPW5fv370Hd3d0oKCgA53zTQMvLy+VCiKuSJH0rSdLmztZytIWv5RPRD0T0Y3Fx8dzWfby6ugopHo//w4mcc8iyPMc5v5FOp7/PZrOdQohWInIC2C2EgBBigYi8Qoifs9lsv06nWyIiaFxagXg8jr8GAGxuIe7LBeWhAAAAAElFTkSuQmCC")}div.vis-network div.vis-manipulation div.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-none{padding:0;line-height:23px}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation button.vis-button.vis-add{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDo0MDoyOSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6NDA6MjkrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjVkNWIwNmQwLTVmMjAtOGE0NC1hMzIwLWZmMTEzMzQwNDc0YjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo2OWVmYWE1NS01ZTI5LTIzNGUtYTUzMy0xNDkxYjM1NDNmYmE8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6NDA6MjkrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjVkNWIwNmQwLTVmMjAtOGE0NC1hMzIwLWZmMTEzMzQwNDc0Yjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDo0MDoyOSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjY5ZWZhYTU1LTVlMjktMjM0ZS1hNTMzLTE0OTFiMzU0M2ZiYTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz5WKqp9AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYXSURBVHjafFZtUFTXGX7e9z27sveuMCwYV8ElrA7YSFYHtJUPkaaI0aRqG8wP00zUzljDINNSA/2ROtpO24SxnahlxjYd7SSjmUkymcxYlDhQPzHGisEVp8HwYWCVVVgEsrsuLnL74+5uqTF9Z+7cO/d8PO95zvO851BlZSV0XQcAMDOYGQBARDhX3JRmMDYZwLPMWAzGHACYIgwS46oBNBNwtOL8CwE8EkSEUCgE2rJlC2w2G4go8Zwo/bMDgnoG6gxLfAAAYvPDMCCszKTAMIAGAhrWnf15AAAMwwARIRKJgDZv3gy73Q4iAjPjxIr9VVOMRhbAYKB8zvrO0llrfEsdKwLZek6YAPSFvtSu3GtLawu0ZJ6625SHGBQB1T88t6MxvopgMAjaunUrdF0HM+P4yv27DMYeJmB1RqW3Jnf3tQX2p0L4P9EXuqEd7PmDp+XuMU9sRbvXnnt1TxxACgoKYLVacbzsQDUJGkSATe6qi28uPtzusM6Kxie6NHLGUX3lxVUNX9StPHnn4wy3njuUYcu6n2pNi66avcEXnByP/nv8aiaIyrqz2gO5A9+9FI1GIfn5+WhZdTAdjFMkwMvZOy7uWnTAOz3L4Yk71m3t69fdfTDoUGTBeHTUfiHQ6lo7Z2OXJvpDAChKe+aOCdKRKWxZ2+1qb3yyd3GYmRkQ7GQBVs99wfv6on3eR2k4PdTkDEbH7IuS8/svld/561PJS/pDk1/bzwx94pze7xc5v/H+YPY6r5BAkdrJzODTK46lE6PeYEJt7u+8j+OZwCBiEAgAoNgKJoEQf6PvNvdrXgtZoNhSf7q0KZ3B2AQmVMze0Jmt54S/DcDCVig2NcvEUGxJAE4Pl+YOr0iv6BRSIPAmBeBZAmHlE2sH4p1uhrq1s0MnnEQMBsf8wRASAICQQCCITN1X7/sOuc0kgOVp3/fPs2WHv+coG7gQOJUnLGsUCTxEjPzUohEA+NfIWUdtx0+efzA1kSSkIGyBAQNCKgHAEBAJ3u79U7kiAcWoem/gb5Fd33nrH3kp+SMWtuAB+GllMJxMjCx9QRgA3uiqL5kwHiTlpxb3smlfMDGYGPP1hcMAkJvs8ScpfdJspdj+MK6Pf+5+u29vyb4lR4+BGEziVESAkEpw6Av1OhUpHCz4qOXbzFWz4Ncdj/v/o08Lt92ODDgZDCEFJYoUGH4mzugP92puPTf0pD3H7wvfdFZdqSxnMtWjoGAAmG9fOLxjwesdjT2/XzIQ7ks3sycYMSEwGHNtWf5bkX5NkYCJBxUBXiGV0XHvosOt54Zey33j/K+8P33++vjnbiGJbbLE+J9SANAb6nJ2B79wcUwETAwQQ7fMjPzMvfP8ja87HUIKMOiaAqMZhrGmLdAy78eZrwwsTS0eObTs+IdtgVanxBUExqGbb5VzrIISGIoUXsmqbgEhJldCQWqRf27SvPAn/o8XmgLhZsUkR4ll37mhk3n94Z4OlzY/7NLcYZfm7o1z2zT4vsvUNSXqprBCkmiTFbPX90/fh8GIT2sf+zTPdDMf4dVnNg4z+E0ixsGeBs9jd5ViSgLHjCb/peaR+MD3d4/ZJg2llyuG2Vwy7QWAs8PNnn1f7vkGSGxAzE6mk+kxkx/p/4unffSCR0hAoL1EBCYiPNdWNcwkNQTCR7feWX6g+7f/A7I8rcw/U6UEe0Ndrhc/W7mtL9ztmqlSgstSS/zTJ28dalpOpkRryrwbhwBACgsLMWPGDOT4ll3qyeqAkJTdCF7P/CrUY/GkLL1rE+2hTbSH8+0Lb/WEuhzhyaA905blf9Vd/895WnZwLHrPevir/cvOB1oLYpTtLrm6oYGIMDExAaqtrUVKSgqYGSKCk0WHq5ikkWEWtNL0imv5qUW+RclLRjJsrhBAuH1/QL8R7HR4xy5nescuP23E6hOA6mLv+sb4uTw6Ogqqq6uDpmkQkcStorX4XRcM1FjZ+kvFFjCJKU1WpkNJJUqIMtX1RyLeX3JtQ0JRhmGYZ/L27duRnJycuFGISOJ9pqh5lrB6iYgqGOxRrOaa54DcZmKvkJxk8JHC9rKh+KVhOsD4+Dj+MwADIf8n5m4xGwAAAABJRU5ErkJggg==")}div.vis-network div.vis-edit-mode button.vis-button.vis-edit,div.vis-network div.vis-manipulation button.vis-button.vis-edit{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNVQxNDoxMjoyNSswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDVUMTQ6MTI6MjUrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjY5OTM3ZGZjLTJjNzQtYTU0YS05OTIzLTQyMmZhNDNkMjljNDwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDozOWNhNzE5ZC03YzNlLTUyNGEtYmY1NS03NGVmMmM1MzE0YTc8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDVUMTQ6MTI6MjUrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjY5OTM3ZGZjLTJjNzQtYTU0YS05OTIzLTQyMmZhNDNkMjljNDwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNVQxNDoxMjoyNSswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjM5Y2E3MTlkLTdjM2UtNTI0YS1iZjU1LTc0ZWYyYzUzMTRhNzwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4ykninAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYpSURBVHjafFZtTFvnFX7Oea+NudiY2Hwam4CBlgQwXdKREDKUoYg0jbRJ29RJ2VZ1mjRFUxSpA3VTfkzJfkQbS7spU6rtx5Z2UtppScjaHxvLuiatWi2jLEoMIUDCh23g2gbj7+tPuPvhOurawPl1dc99n+c55z33fV46ceIEZFkGADAziAgAQERoe/9ZK4GPM/AcgbsIXAcABCgMvkfAqAa89eDoJyF8LogIqqqChoaGYDAYHr8kItS8uc8iIH6iAa9IkAo5EAQX8pqmgUVBCBggYFgDhv0/GAsBgKZpICJkMhnQ4OAgZFkGEYGZUXmp+0cS+CKBwWA0DVRPOg5Zl2q6zaHyJlnVAMQXVTkwHrUqH0Xsvn+tdQAAMQDgpPLS2MViFY8rkGUZzIzaS/t/xqCzGggtz9e697zsnKhoLUtim4jOq/LE6x7X0nsh16dEZ5a/O3a2SCAOHjwInU6Hujd6ThJ4mCDQ+b2G232v7v6vwarPbQn8MGlMr+X0kpE3Wr5Zt5hL5HPhqYSdQIfKJ+yhxDPKWC6Xg+jt7UXD5b5KBt1kCHS85Ljd8/On3NupfnhFaZj4rWff1B98B1R/hnUmKd36bdtCNl4g0en4edNE/cXwLq8qMTMIPAQwmo/WuHvObA8+9c58k/dKtD0TyZWXN5YGA7ej7epKxspM//7SoNOdWc/Jyq2wiwhDzPxT8cP0jys3VMM7OmL0/77zn4Ydui3b8uiK0jD7RrA77c9Wd57cefPpF+2T6bWsFPWkaiPTCWvTsZpHFU+XrS+8G3AR08F6X+1FJvBxQQzHQOWk2SmrW4FPX/U2LVwPuDZj+fJKl2khPpeyAqA9rzR/YqwuiWXX8taN/CabGkrVuq9YJlkQQDjOAJ5jAhz9Vt9W4N5/rNp8I+vtMV/aZm4zLnUNNt0urdYnF68HWoJj4Wo1mLGUNRr8LEgDgNqeCh8xQIKOsgC7iAjVe83rT9zQa8uNM28u70kspessu8q8zq/V3NcZpVzb9+0zmVhOvvvrhaMVzrJg0zeq7xMVCCwdpnWSGBqjUyJwLTFgbvxie3w31uoWR1Y74r60rdxZqrR8q85t2W2MGCp12bm/KC3hyaSTiMhxuGrKcahqpbjOaDOoEhOEoFqJQCCJvqA85I6bfTdDjQlf2lbxVNlS6wt19yy7jRHZZlDnrinNj/6sHMhnNw2Ogco7O79e5fm/xQywRBBCEAuwn4gQ96bkYj4Vyuq9N1Z3Bj4Od5bs0MXt/dZZ21ctiqFan174q985P+Lfp+U1g7XDON/1ctP458WlVjLyJhOISZE0wM0S1QfuRC3lTjkJAKKEtNC9eIOhSh9xHLZOJRZTFuXDsEoStLkR/768ummsaJG9Pb9oe+9J+xaeSVokiQDSJphAo5uaBuWjiKP4QTqS1cUWU7ayesN66wu22frD1vmVW6GW6T8u9eVjGyZzs+w78Nqu0a2mbvVu1KEJQAgeZRL0liQYyx+GOmKeQpu0rMYsAJPNEFGD2dLodLIy6c9Ys7G8yeSUl3tf2/X3rcBVJSOv34l3sCBogi7z1LH/rBHjl4IJ93/ncQFAnjeImJD0Z8zuCwu9q3djDXqTlAKID5xv+9t2R8n8VcUFBljQ8Gyfe40BYBM4DwDLt8Kue79ZcFkbzfEdbUbv+oN4c9KTtsfm1MbYQqqh+2zrVZYKs/7Ef+byimt1POYiJhDhPBFBIiIEXhxfs7/dfYoIF+auBfYTE/pebx/V8hqBP2ODvD34yvuh/WCAmU75Bx6sIgaI/v5+6PV6JLqUsYr7dpDAoehs0h73pHTWrvKgThYbRSt9UmSjef3MpaUvBz4O72UmADgTOPJguGiZor+/HyUlJWBmJFz+D8xTtlUiOpbwpmrmrweeSXrT+g11k4SBN3RGKUcAVCVdFhyP1nreDbY//NPyEXUlU/Pp4XYycGT6V0Ux2WwWdO7cOZSWlkII8diX7SPPNgDaKdbxoNAxwATBAEkEEgSWCEQAqPAMwqvMdCEwMO0tVqZpWsGTT58+DaPR+PhGIYQAAAgh0P7B3ioW/B0iGiCGiwXbCuOHFSJys6AbYFye2T+xWhT3WYJEIoH/DQBMw3kes8OJPgAAAABJRU5ErkJggg==")}div.vis-network div.vis-edit-mode button.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation button.vis-button.vis-connect{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDozODo1NyswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6Mzg6NTcrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjlmYjUwMDU0LWE3ODEtMWQ0OC05ZTllLTU2ZWQ5YzhlYjdjNjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo3ZWRhMjI0MC0yYTQxLTNlNDQtYWM2My1iNzNiYTE5OWI3Y2E8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6Mzg6NTcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjlmYjUwMDU0LWE3ODEtMWQ0OC05ZTllLTU2ZWQ5YzhlYjdjNjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDozODo1NyswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjdlZGEyMjQwLTJhNDEtM2U0NC1hYzYzLWI3M2JhMTk5YjdjYTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4ubxs+AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAUtSURBVHjajJZ/bNT1Gcdfz/P53PV6B4W7VltLqdAaplIOiMOoyxxJCSs/Gv/yB4gzJroAosmmDklwkYWR0bQsdmkykoojTpcsWYLxD/lRZdMQkTHRtkLZRqG0tIVe7662vTu43n32x/VKZ/jh89cn38/zvN7P5/l88zwf2blzJz6fDwARQUSm1n8s31CM0/VAnbNmsUPuAsDpgEO+Bg4C7//iyv5hvmMiQiqVQpqamvB6vVNwEeG1JZtCBrYi/MrkAwDNgjhwAlbzICBLA0rDb0+/839C6XQaaWxspLCw8Dp86cbNmqVFJQddE6KzdjZ9D89g+B6fSyCOcyn1nxil+O9xKg5HqWFSHGXLjrP7W/ICqVQK2bNnDz6fDxFh65KNvxbHDhF4rJj2bXPo+IGfcW5h5xL4f99P+FCEMIAob75x9t0dAMlkElNXV4e1lteXbNqiQoMaeOFOjrdU868SD2luYyEP6dUh+sYmSHeOU6GO5Z8VLx5+NNZxIpPJ5AS2L3upROCoCvz8Lo7vnkf77cAHhpiz/zIL9vWz8L8p/NvupmM0Q7pjnAoLqz8tDrc8MnQqYVUVhVdF4LEg7b+rvDn8wDDlH0WoPpukLJImSBaMwjcJqmwWts2jPZLG/8kwYVFeVdXXZcFf4yVDc2cNKfBFmD9X+0ncCP58F48eG+Feo2CAUkvs4dl0V/uJvdXLiiV+ut++n7YLSfxPfMMG54ChzB3WIesVWB2i82bw1AR6fJR7C4VsfYiv6u/k3A9nEgP4zXke8DiYHyAOMK+QxPIgnZ9GqSHr1itQJ8DK2fTerDQ+S/bHRXQJaHSCwNIZ2Xh+7+S3VAmwNMBA/tuPZtErgKquUmdMWIFlRURvdamRNEXGwIWrlP47pTMzLiunxghGMwTLvcTWlHAp77s4QNSrYMQtss6ZMgWqCm5cHoDHO1nbk6K8zEN8+3zatv2Hn1b59EqJZdxmYUERg9P9KwpIiAOTdWUWBXuLzB/vZG3P1Un4PNp2d1MbmyD45TWCxuCsQm0x56bHGHFYEZwxok7toAA9Sfw3hCcoL/NOwi9QO5wmWO1j4JEgZxTkodmcWRGkf3pcX0r8xoAaBixKu4U5/xwndM+0tpAvS6mP+PZK2nb1UBvPEKwKMLDvPj4ESGc55lGy303sdJKQdZB2rkMdctAB/4gzN+/Q2ENNd4LyUi/xN+bTtquX2thk5nk4wI3gAF+OMNcA1nFQDfK+BY5GqbkwWabTY5QZhXWlnNx1ntrY1Rz87fuvw29m/Sn8J+PUGAFj5T19baA1IspuBZp7cx1x4SwG1cEf+lgRSROs8jGwb+Ht4QB/GSSsAhYano39LWIBxNEIbP14hPDuiyS2VtJuHXQlKKvxM/jiXDq/D/xPlwifGMkJZB2NIoKpr69nxeiZxLHicFSFVWfGqBidIP3LSjrWltD94CyufF/4kQgPuVz2Lz93+dDRa9eu5QQ8Hg8/iXee+Dy4CKMs7xqn4nwKz9IirhQqmVuB42m8ey+x7LMoD6iAON782eChhqmRuXfvXgKBAKqKqtI0/8nNKrQI4BVYXkzHgzPpC88gWuHL/caXrhLoGiN0apSKr0ZZRBZM7q2w5ZnLR1oAnHOMjY0hra2tFBQUYIyZmstvVT1Z6eDlAuEVq7merxmwueNPDXy9PvybjKP5mctHLk4/XTKZRJqbm/H7/VNw1VyEMYbW4FN3WNWnnchKoy5sHeVGBRX6VWi3ymFx7r11Ix8MTX/y5C2RSPC/AQB61erowbpqSwAAAABJRU5ErkJggg==")}div.vis-network div.vis-manipulation button.vis-button.vis-delete{background-image:url("data:image/png;base64,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/GMzLdsAAEEOaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAyMSA3OS4xNTQ5MTEsIDIwMTMvMTAvMjktMTE6NDc6MTYgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxNC0wMi0wNFQxNDo0MTowNCswMTowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTQtMDItMDRUMTQ6NDE6MDQrMDE6MDA8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOjc3NDkzYmUxLTEyZGItOTg0NC1iNDYyLTg2NGVmNGIzMzM3MTwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE0LTAxLTIyVDE5OjI0OjUxKzAxOjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6RUE2MEEyNEUxOTg0RTMxMUFEQUZFRkU2RUMzMzNFMDM8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDEtMjNUMTk6MTg6MDcrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDowNmE3NWYwMy04MDdhLWUzNGYtYjk1Zi1jZGU2MjM0Mzg4OGY8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTQtMDItMDRUMTQ6NDE6MDQrMDE6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL3BuZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmRlcml2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+Y29udmVydGVkIGZyb20gYXBwbGljYXRpb24vdm5kLmFkb2JlLnBob3Rvc2hvcCB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOjc3NDkzYmUxLTEyZGItOTg0NC1iNDYyLTg2NGVmNGIzMzM3MTwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxNC0wMi0wNFQxNDo0MTowNCswMTowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c3RSZWY6aW5zdGFuY2VJRD54bXAuaWlkOjA2YTc1ZjAzLTgwN2EtZTM0Zi1iOTVmLWNkZTYyMzQzODg4Zjwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SUQ+eG1wLmRpZDpFQTc2MkY5Njc0ODNFMzExOTQ4QkQxM0UyQkU3OTlBMTwvc3RSZWY6ZG9jdW1lbnRJRD4KICAgICAgICAgICAgPHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD54bXAuZGlkOjczQjYyQUFEOTE4M0UzMTE5NDhCRDEzRTJCRTc5OUExPC9zdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5zUkdCIElFQzYxOTY2LTIuMTwvcGhvdG9zaG9wOklDQ1Byb2ZpbGU+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDA5MC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+NzIwMDkwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI0PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4aYJzYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAYGSURBVHjalJZ7UJTnFcZ/73m/72PdJY1RbhoQp6lkXRAvmIYxdCUadLVOozPNtGObap1JsKipjiShbdoRbeKEiQHpQK3xj0xa03aamTbaTGyAYV1QGeqFi+JyiZFLAlmESBkWRmS3fyzslGkmnZ5/v/M873Oe75zzvqqoqAibzQaAiKCUAkApRdHIK/NFsx2NR91nOSILADDoJyzNaM4xxbtvPHh0iC+JiYkJ1OHDh4mJiUEpFSXPv/ziPC28TIiXDCOSrAClQDSEpsCwJPIhrEBRQpiSytXlQwDhcBilFPfu3UMVFxdjt9ujFTzfcLBADCoEEAFr1ZbrrNjch2vtEImPBgHob7fTcWE+bVXJNJ/NiFQlEGLvieXHKmYqGB8fRx05cgSbzYaIsPvywV8pKFaA7fGtLTzz61YWpo/xVTHQbufsq5lcez9zWuWhk5mvFwMEg0H0+vXrMU2Tn1wp3CtCiQ5DjGd3A/m/v8IDCZP8r4iNmyRrWx/j/5qktykZpXKzAjVDVxPzGqemptDr1q1jX3NRnIJarcDKK2hgR2ULXRfncv7UYv7xpovhnhiW5Mz+kefeSKO6LJ1A1xzEuk/Ojm4mRibpuZaMZW3OCtRUND60NmiICCIUShisx7a2sLMiQn4s77uEQgIabnqdfHIlgT1/qQeg8vs5dHhdCNB1wYn3RIiC995j26stjAbsNH+YiZJCESnS1Y/XxIXu8r4YIPv/VkVs3CTnTy2ms34xro1+sp9po6sxlTu34ultmsPVvy6is86FCHgO+DDs49zpjufBpCG+seYOC9OHaTidieicb9ouVAhKtouAseI710ma7pLuqwmgYfHqAFt+6WdLoQ/LBl11Lm7VudAa8vb72PCin9TlAWIsGGhLACD+kSAZnusYBii1XQAPYWDllt6ov2lrBkDBR2+6Ofuak2//3M+G/T4wAAPW7fPhKfRTVeqk9qQbFKRmDUTxS3N7QYGYmwzCkqklBGlPDEcTNv+sg9tNCbTXuvBWujE0bHrZj9JE1B/wU1Pm5PwJN6YBS9a2kVvQEcWnrh5GTFD3lxkYkqRMgYQlwVldUvDnen73LHTUuqitdKM0eAr9AFQfd1J/yo2aJn+2sn4Wdn5qEFODJskgBIjx5T0uCrQA08pnIjS9PERDjPnfOKXAMEBECUoGEIHBj+2zkt76UQ6dXheGAev3+cg74Kf6uJPqcicbfuond7cPy4SOiy7+tD9nFvZurx00KOk3CNEC+mE+vjSPBc7IWqgqTaPT60IMcO/xsXGa3HfKjRgRdbl7/KDg0jtubje6aHj7c7J3dgLQ2zoPwwQ91SooOQdAW1VKVMHty0kA5Bb48BycJn/LjWFGbLv4thvvb53kFvjJ+XEdWkPfjQVR/CcNKYgGMc8JWt5Fa2j+MIPPuyI2pa4IoHSkt6vLIuRaQ9q32khzt4GCxtNu6k46GeiIR2lIfDQQsafPzq1LGRGL9Gk9d+vrwewvfHPQOoexQVjxdB/auk/zmaUMdsfz6bVUtIalT7bxveP1ZHh6GPDPYeSzeD69kcpIfxymFWLNrka+ljhBTWkWwz2JiJT84YHnz2iPx0P20PkmRF5i6HYiwZFJsn/YzdezbzE3cQibY5xV266z6RfXohakb+xB9CjanCD9qTbW7Grk4WV38VZm0l6dhQiEw9taHSuDqrS0FIfDwXM3X9mHMsvRAk/sauDpQy38P+GtzOTGB9mEpkD0C2dS8n8zOjqK9ng8WJZFU+JTjasGvaCNXPpvJBPoMlm0OoDNMfWVxONfWNSUPUZ7TUQ56tCZlPwSgMnJSVRpaSmxsbFE1raw82ZxAZZRQUiBYUKGp5UlOX2krBzmoUVjiIKhHge9rfPo+Wcy3ZeXIYASgL1/X5RfMXMvj46OosrLy7HZbGitUUohIuzoem0RofALaOsghgWGjky0MiJTL8b0lOvI8hN1DKXKP0jd3TNTWDgcJhgMoo4ePYrD4Yi+KmaeLlprnrtXFo9h/AAlG1AqE8yFmBrC+jO0bgH9EVpO/1F2Dc5g//OAsbEx/j0Af+USsQynL1UAAAAASUVORK5CYII=")}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px} \ No newline at end of file diff --git a/addons/queue_job/static/lib/vis/vis-network.min.js b/addons/queue_job/static/lib/vis/vis-network.min.js deleted file mode 100644 index aa18971..0000000 --- a/addons/queue_job/static/lib/vis/vis-network.min.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * vis-network - * https://visjs.github.io/vis-network/ - * - * A dynamic, browser-based visualization library. - * - * @version 9.0.4 - * @date 2021-03-16T05:44:27.440Z - * - * @copyright (c) 2011-2017 Almende B.V, http://almende.com - * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs - * - * @license - * vis.js is dual licensed under both - * - * 1. The Apache 2.0 License - * http://www.apache.org/licenses/LICENSE-2.0 - * - * and - * - * 2. The MIT License - * http://opensource.org/licenses/MIT - * - * vis.js may be distributed under either license. - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).vis=t.vis||{})}(this,(function(t){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function n(t,e){return t(e={exports:{}},e.exports),e.exports}var o=function(t){return t&&t.Math==Math&&t},r=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof e&&e)||function(){return this}()||Function("return this")(),s=function(t){try{return!!t()}catch(t){return!0}},a=!s((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),h={}.propertyIsEnumerable,l=Object.getOwnPropertyDescriptor,d={f:l&&!h.call({1:2},1)?function(t){var e=l(this,t);return!!e&&e.enumerable}:h},c=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},u={}.toString,f=function(t){return u.call(t).slice(8,-1)},p="".split,v=s((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==f(t)?p.call(t,""):Object(t)}:Object,g=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},y=function(t){return v(g(t))},m=function(t){return"object"==typeof t?null!==t:"function"==typeof t},b=function(t,e){if(!m(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!m(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!m(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!m(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},w={}.hasOwnProperty,k=function(t,e){return w.call(t,e)},_=r.document,x=m(_)&&m(_.createElement),E=function(t){return x?_.createElement(t):{}},O=!a&&!s((function(){return 7!=Object.defineProperty(E("div"),"a",{get:function(){return 7}}).a})),C=Object.getOwnPropertyDescriptor,S={f:a?C:function(t,e){if(t=y(t),e=b(e,!0),O)try{return C(t,e)}catch(t){}if(k(t,e))return c(!d.f.call(t,e),t[e])}},T=/#|\.prototype\./,M=function(t,e){var i=D[P(t)];return i==B||i!=I&&("function"==typeof e?s(e):!!e)},P=M.normalize=function(t){return String(t).replace(T,".").toLowerCase()},D=M.data={},I=M.NATIVE="N",B=M.POLYFILL="P",z=M,N={},A=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t},F=function(t,e,i){if(A(t),void 0===e)return t;switch(i){case 0:return function(){return t.call(e)};case 1:return function(i){return t.call(e,i)};case 2:return function(i,n){return t.call(e,i,n)};case 3:return function(i,n,o){return t.call(e,i,n,o)}}return function(){return t.apply(e,arguments)}},j=function(t){if(!m(t))throw TypeError(String(t)+" is not an object");return t},R=Object.defineProperty,L={f:a?R:function(t,e,i){if(j(t),e=b(e,!0),j(i),O)try{return R(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},H=a?function(t,e,i){return L.f(t,e,c(1,i))}:function(t,e,i){return t[e]=i,t},W=S.f,q=function(t){var e=function(e,i,n){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,i)}return new t(e,i,n)}return t.apply(this,arguments)};return e.prototype=t.prototype,e},V=function(t,e){var i,n,o,s,a,h,l,d,c=t.target,u=t.global,f=t.stat,p=t.proto,v=u?r:f?r[c]:(r[c]||{}).prototype,g=u?N:N[c]||(N[c]={}),y=g.prototype;for(o in e)i=!z(u?o:c+(f?".":"#")+o,t.forced)&&v&&k(v,o),a=g[o],i&&(h=t.noTargetGet?(d=W(v,o))&&d.value:v[o]),s=i&&h?h:e[o],i&&typeof a==typeof s||(l=t.bind&&i?F(s,r):t.wrap&&i?q(s):p&&"function"==typeof s?F(Function.call,s):s,(t.sham||s&&s.sham||a&&a.sham)&&H(l,"sham",!0),g[o]=l,p&&(k(N,n=c+"Prototype")||H(N,n,{}),N[n][o]=s,t.real&&y&&!y[o]&&H(y,o,s)))},U=Math.ceil,Y=Math.floor,X=function(t){return isNaN(t=+t)?0:(t>0?Y:U)(t)},G=Math.min,K=function(t){return t>0?G(X(t),9007199254740991):0},Q=Math.max,$=Math.min,Z=function(t,e){var i=X(t);return i<0?Q(i+e,0):$(i,e)},J=function(t){return function(e,i,n){var o,r=y(e),s=K(r.length),a=Z(n,s);if(t&&i!=i){for(;s>a;)if((o=r[a++])!=o)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},tt={includes:J(!0),indexOf:J(!1)},et={},it=tt.indexOf,nt=function(t,e){var i,n=y(t),o=0,r=[];for(i in n)!k(et,i)&&k(n,i)&&r.push(i);for(;e.length>o;)k(n,i=e[o++])&&(~it(r,i)||r.push(i));return r},ot=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],rt=Object.keys||function(t){return nt(t,ot)},st={f:Object.getOwnPropertySymbols},at=function(t){return Object(g(t))},ht=Object.assign,lt=Object.defineProperty,dt=!ht||s((function(){if(a&&1!==ht({b:1},ht(lt({},"a",{enumerable:!0,get:function(){lt(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},i=Symbol(),n="abcdefghijklmnopqrst";return t[i]=7,n.split("").forEach((function(t){e[t]=t})),7!=ht({},t)[i]||rt(ht({},e)).join("")!=n}))?function(t,e){for(var i=at(t),n=arguments.length,o=1,r=st.f,s=d.f;n>o;)for(var h,l=v(arguments[o++]),c=r?rt(l).concat(r(l)):rt(l),u=c.length,f=0;u>f;)h=c[f++],a&&!s.call(l,h)||(i[h]=l[h]);return i}:ht;V({target:"Object",stat:!0,forced:Object.assign!==dt},{assign:dt});var ct=N.Object.assign,ut=[].slice,ft={},pt=function(t,e,i){if(!(e in ft)){for(var n=[],o=0;o=.1;)(p=+r[c++%s])>d&&(p=d),f=Math.sqrt(p*p/(1+l*l)),e+=f=a<0?-f:f,i+=l*f,!0===u?t.lineTo(e,i):t.moveTo(e,i),d-=p,u=!u}var Ot={circle:wt,dashedLine:Et,database:xt,diamond:function(t,e,i,n){t.beginPath(),t.lineTo(e,i+n),t.lineTo(e+n,i),t.lineTo(e,i-n),t.lineTo(e-n,i),t.closePath()},ellipse:_t,ellipse_vis:_t,hexagon:function(t,e,i,n){t.beginPath();var o=2*Math.PI/6;t.moveTo(e+n,i);for(var r=1;r<6;r++)t.lineTo(e+n*Math.cos(o*r),i+n*Math.sin(o*r));t.closePath()},roundRect:kt,square:function(t,e,i,n){t.beginPath(),t.rect(e-n,i-n,2*n,2*n),t.closePath()},star:function(t,e,i,n){t.beginPath(),i+=.1*(n*=.82);for(var o=0;o<10;o++){var r=o%2==0?1.3*n:.5*n;t.lineTo(e+r*Math.sin(2*o*Math.PI/10),i-r*Math.cos(2*o*Math.PI/10))}t.closePath()},triangle:function(t,e,i,n){t.beginPath(),i+=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i-(a-s)),t.lineTo(e+r,i+s),t.lineTo(e-r,i+s),t.lineTo(e,i-(a-s)),t.closePath()},triangleDown:function(t,e,i,n){t.beginPath(),i-=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i+(a-s)),t.lineTo(e+r,i-s),t.lineTo(e-r,i-s),t.lineTo(e,i+(a-s)),t.closePath()}};var Ct=n((function(t){function e(t){if(t)return function(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}(t)}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i,n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var o=0;o=a?t?"":void 0:(n=r.charCodeAt(s))<55296||n>56319||s+1===a||(o=r.charCodeAt(s+1))<56320||o>57343?t?r.charAt(s):n:t?r.slice(s,s+2):o-56320+(n-55296<<10)+65536}},Tt={codeAt:St(!1),charAt:St(!0)},Mt="__core-js_shared__",Pt=r[Mt]||function(t,e){try{H(r,t,e)}catch(i){r[t]=e}return e}(Mt,{}),Dt=Function.toString;"function"!=typeof Pt.inspectSource&&(Pt.inspectSource=function(t){return Dt.call(t)});var It,Bt,zt,Nt=Pt.inspectSource,At=r.WeakMap,Ft="function"==typeof At&&/native code/.test(Nt(At)),jt=n((function(t){(t.exports=function(t,e){return Pt[t]||(Pt[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.9.1",mode:"pure",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})})),Rt=0,Lt=Math.random(),Ht=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++Rt+Lt).toString(36)},Wt=jt("keys"),qt=function(t){return Wt[t]||(Wt[t]=Ht(t))},Vt=r.WeakMap;if(Ft){var Ut=Pt.state||(Pt.state=new Vt),Yt=Ut.get,Xt=Ut.has,Gt=Ut.set;It=function(t,e){return e.facade=t,Gt.call(Ut,t,e),e},Bt=function(t){return Yt.call(Ut,t)||{}},zt=function(t){return Xt.call(Ut,t)}}else{var Kt=qt("state");et[Kt]=!0,It=function(t,e){return e.facade=t,H(t,Kt,e),e},Bt=function(t){return k(t,Kt)?t[Kt]:{}},zt=function(t){return k(t,Kt)}}var Qt,$t,Zt={set:It,get:Bt,has:zt,enforce:function(t){return zt(t)?Bt(t):It(t,{})},getterFor:function(t){return function(e){var i;if(!m(e)||(i=Bt(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return i}}},Jt=!s((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype})),te=qt("IE_PROTO"),ee=Object.prototype,ie=Jt?Object.getPrototypeOf:function(t){return t=at(t),k(t,te)?t[te]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?ee:null},ne="process"==f(r.process),oe=function(t){return"function"==typeof t?t:void 0},re=function(t,e){return arguments.length<2?oe(N[t])||oe(r[t]):N[t]&&N[t][e]||r[t]&&r[t][e]},se=re("navigator","userAgent")||"",ae=r.process,he=ae&&ae.versions,le=he&&he.v8;le?$t=(Qt=le.split("."))[0]+Qt[1]:se&&(!(Qt=se.match(/Edge\/(\d+)/))||Qt[1]>=74)&&(Qt=se.match(/Chrome\/(\d+)/))&&($t=Qt[1]);var de,ce,ue,fe=$t&&+$t,pe=!!Object.getOwnPropertySymbols&&!s((function(){return!Symbol.sham&&(ne?38===fe:fe>37&&fe<41)})),ve=pe&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,ge=jt("wks"),ye=r.Symbol,me=ve?ye:ye&&ye.withoutSetter||Ht,be=function(t){return k(ge,t)&&(pe||"string"==typeof ge[t])||(pe&&k(ye,t)?ge[t]=ye[t]:ge[t]=me("Symbol."+t)),ge[t]},we=be("iterator"),ke=!1;[].keys&&("next"in(ue=[].keys())?(ce=ie(ie(ue)))!==Object.prototype&&(de=ce):ke=!0);var _e=null==de||s((function(){var t={};return de[we].call(t)!==t}));_e&&(de={}),_e&&!k(de,we)&&H(de,we,(function(){return this}));var xe,Ee={IteratorPrototype:de,BUGGY_SAFARI_ITERATORS:ke},Oe=a?Object.defineProperties:function(t,e){j(t);for(var i,n=rt(e),o=n.length,r=0;o>r;)L.f(t,i=n[r++],e[i]);return t},Ce=re("document","documentElement"),Se=qt("IE_PROTO"),Te=function(){},Me=function(t){return"