How-to: Add a Job (Task) Type ============================= In order to add a new :term:`Job Type`, the following steps shall be used: 1. Each job is uniquely identified by two keys: platform and type name. Platform is a value from the :py:class:`ppc_robot_lib.platform.PlatformType` enum. Type name is a short identifier which should describe the job type and should be consistent with other platforms, e.g. ``custom_report``, ``url_check_report``, ``ad_multivariate_test_report`` and so on. 2. Create a new subpackage in ``ppc_robot_task_types//`` (e.g. ``ppc_robot_task_types/adwords/custom_report``). The ``__init__.py`` file can be blank. 3. Create your first version of the report: create a module named ``_v1.py``. The file should contain a class named ``TypeNameV1`` that implements the :py:class:`ppc_robot_lib.tasks.abstract_task_type.AbstractReportType`:: from typing import Any, Dict from ppc_robot_lib.platform import PlatformType from ppc_robot_lib.tasks.abstract_task_type import AbstractReportType class TypeNameV1(AbstractReportType): @classmethod def get_name(cls) -> str: return 'type_name' # Short type name. @classmethod def get_platform(cls) -> PlatformType: return PlatformType.ADWORDS # Return the correct platform constant. @classmethod def get_version(cls): return 1 # Return the version @classmethod def get_level(cls) -> TaskLevel: return TaskLevel.CLIENT_ACCOUNT @classmethod def get_description(cls) -> TaskDescription: # Gets the task description displayed in the interface. See the TaskDescription object for available fields. return TaskDescription( title='My First Task Type', short_description='Short task type description', description='

Longer description, HTML can be used.

', ) @classmethod def upgrade_from_previous(cls, parameters: Dict[str, Any]) -> Dict[str, Any]: # Should upgrade parameters if they changed in the new version. # Since this is the first version, we can safely return the input parameters. return parameters @classmethod def get_default_parameters(cls) -> Dict[str, Any]: return {} # Return default parameters.ยจ def execute(self, parameters: Dict[str, Any]): pass .. note:: Pay extra attention to the ``get_level()`` method. This method determines level of objects on which the task operates. By returning ``TaskLevel.SERVICE_ACCOUNT``, you will instruct PPC Robot that the report is not created for individual Client Account, but for a whole Service Account. This means that the user will not be able to select individual account in the last step of record creation and Service Account Selection will have be done in the second step. This is used for Managed Accounts (MCC) overview reports, so make sure you check this method when copying code from other reports! 4. Implement the ``execute()`` method. This method is called when the report is run. You can use any of the :doc:`../reporting/index`. Example:: from ppc_robot_lib.adwords import Query from ppc_robot_lib.output.types import Table from ppc_robot_lib.reporting.input import download_adwords_report from ppc_robot_lib.reporting.output import write_tables from ppc_robot_lib.utils.types import Column class TypeNameV1(AbstractReportType): # ... other methods ... def execute(self, parameters: Dict[str, Any]): # Download a report. query = Query( select=['AccountDescriptiveName', 'Clicks'], from_report='ACCOUNT_PERFORMANCE_REPORT' ) report = download_adwords_report(query) # Output the table. write_tables([ Table( report, sheet_name='Report', header=[ Column('Account Name', 'AccountDescriptiveName'), Column('Total Clicks', 'Clicks'), ] ) ]) 5. Testing the job type: you can test the job type without the user interface or any additional dependencies (however, it is recommended to start the rate limiter before you start testing your job). 1. In the ``dev_scripts/reports`` folder, create a new JSON file, for example ``task_type.json`` (the name is not significant). The contents of the file should look like this:: { "platform": "adwords", "taskType": "type_name", "taskTypeVersion": 1, "parameters": { } } The parameters dictionary will be passed directly to your report type, so fill-in the values accordingly to your task type. 2. Run the following script (you can also create a new PyCharm configuration):: $ python dev_scripts/test_report.py -c ID dev_scripts/reports/task_type.json Where ID is an ID of ``ClientAccount`` in the database that sould be used for the test. Look for rows in the ``robot_client_accounts`` table. 6. **You should have a working job type by now.** The type will already be available in the interface for selection, but there is no interface for entering parameters for this type. You have to create a so-called parameter editor, which is a Vue.js component used to manage the parameters. Create a new file in ``ppc_robot_web/assets/js/TaskTypes//`` named ``.vue`` (please note the CamelCase naming convention used in JavaScript): .. code-block:: html The component will receive 4 parameters: * ``parameters``: parameters object, this is the dictionary that is passed to `create_steps`. * ``store``: instance of ``ReportEditorStore`` which contains the parameters. If you set its ``allowContinue`` property to ``false``, user will not be able to save the parameters. * ``defaultParameters``: default parameters for the given report type (as returned by the ``get_default_parameters()`` method). Useful if your report consists of multiple tables that can be configured by user -- you can simply create a new table by coping the default one. * ``validation``: validation result. See `documentation for the Vuelidate library`_. Additionally, the editor has one method: ``getValidations()``. This method should return the validation schema that is used to validate the parameters -- see `documentation for the Vuelidate library`_ for more details. Additionally, ``AdWords/AdExtensionReport.vue`` is a nice example of validation in action. 7. One last change to make! In ``ppc_robot_web/assets/js/TaskTypes/editors.js``, add your component to the ``EDITOR_MAP`` (it is a nested object, so keep the structure):: export const EDITOR_MAP = { // ... 'adwords': { 'type_name': d(require('./AdWords/TypeName.vue')), } }; .. _documentation for the Vuelidate library: https://monterail.github.io/vuelidate/