# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/classes/50_DomoApplication_Job.ipynb.

# %% auto 0
__all__ = ['DomoTrigger_Schedule', 'DomoTrigger', 'DomoJob', 'RemoteDomoStats_Config_Policy', 'RemoteDomoStats_Config',
           'DomoJob_RemoteDomoStats', 'Watchdog_Config', 'Watchdog_Config_MaxIndexingTime', 'Watchdog_Config__Variance',
           'Watchdog_Config_RowCountVariance', 'Watchdog_Config_ExecutionVariance', 'Watchdog_Config_ErrorDetection',
           'Watchdog_Config_LastDataUpdated', 'Watchdog_Config_CustomQuery', 'Watchdog_ConfigFactory',
           'DomoJob_Watchdog']

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 3
from dataclasses import dataclass, field
from abc import ABC, abstractmethod

from typing import List, Optional
from enum import Enum

import httpx

from nbdev.showdoc import patch_to

import domolibrary.client.DomoAuth as dmda
import datetime as dt

from pprint import pprint

import domolibrary.routes.application as application_routes

import domolibrary.utils.convert as cc

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 5
@dataclass
class DomoTrigger_Schedule:
    schedule_text: str = None
    schedule_type: str = "scheduleTriggered"

    minute: int = None
    hour: int = None
    minute_str: str = None
    hour_str: str = None

    @classmethod
    def _from_str(cls, s_text, s_type):
        sched = cls(schedule_type=s_type, schedule_text=s_text)

        try:
            parsed_hour = s_text.split(" ")[2]
            parsed_minute = s_text.split(" ")[1]

            if "*" in parsed_hour or "/" in parsed_hour:
                sched.hour_str = parsed_hour
            else:
                sched.hour = int(float(parsed_hour))
            if "*" in parsed_minute:
                sched.minute_str = parsed_minute
            else:
                sched.minute = int(float(parsed_minute))

            return sched

        except Exception as e:
            print(f"unable to parse schedule {s_text}")
            print(e)

    def to_obj(self):
        return {"hour": int(self.hour), "minute": int(self.minute)}

    def to_json(self):
        minute = self.minute_str if self.minute_str is not None else str(self.minute)
        hour = self.hour_str if self.hour_str is not None else str(self.hour)
        return {
            "eventEntity": f"0 {minute} {hour} ? * *",
            # old value on Jan 13
            # "eventEntity": f'0 {minute} {hour} 1/1 * ? *',
            "eventType": self.schedule_type,
        }


@dataclass
class DomoTrigger:
    id: str
    job_id: str
    schedule: List[DomoTrigger_Schedule] = None

    @classmethod
    def _from_json(cls, obj):
        return cls(
            id=obj["triggerId"],
            job_id=obj["jobId"],
            schedule=DomoTrigger_Schedule._from_str(
                s_text=obj.get("eventEntity"), s_type=obj.get("eventType")
            ),
        )

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 7
@dataclass
class DomoJob:
    """
    the base class only captures attributes applicable to all jobs (i.e. does not destructure execution_payload onto the class)
    build Application / Job extensions by creating children of the DomoJob class
    """

    auth: dmda.DomoAuth = field(repr=False)

    name: str
    application_id: str

    logs_dataset_id: str = None
    id: str = None
    user_id: str = None
    execution_timeout: int = 1440

    is_enabled: bool = False  # based on triggers
    customer_id: str = None
    created_dt: dt.datetime = None
    updated_dt: dt.datetime = None

    description: str = None

    execution_payload: dict = field(default_factory=lambda: {})
    share_state: dict = field(default_factory=lambda: {})
    accounts: List[str] = field(default_factory=[])
    triggers: List[DomoTrigger] = field(default_factory=[])

    @staticmethod
    def _format_remote_instance(remote_instance):
        if not remote_instance:
            return remote_instance

        return remote_instance.replace(".domo.com", "")

    @staticmethod
    def _convert_API_res_to_DomoJob_base_obj(obj) -> dict:
        """base class for converting an API response into a dictionary with parameters that DomoJob expects"""

        triggers_ls = obj.get("triggers")
        domo_triggers = (
            [DomoTrigger._from_json(tg) for tg in triggers_ls] if triggers_ls else []
        )

        return {
            "id": obj["jobId"],
            "name": obj["jobName"],
            "user_id": obj["userId"],
            "application_id": obj["applicationId"],
            "customer_id": obj["customerId"],
            "execution_timeout": obj["executionTimeout"],
            "execution_payload": obj["executionPayload"],
            "logs_dataset_id": obj["executionPayload"]["metricsDatasetId"],
            "share_state": obj.get("shareState", {}),
            "created_dt": cc.convert_epoch_millisecond_to_datetime(obj["created"]),
            "updated_dt": cc.convert_epoch_millisecond_to_datetime(obj["updated"]),
            "is_enabled": True if triggers_ls else False,
            "description": obj.get("jobDescription"),
            "accounts": obj.get("accounts"),
            "triggers": domo_triggers,
        }

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 8
@patch_to(DomoJob, cls_method=True)
def _from_json(
    cls,
    obj,
    auth,
):
    """_from_json is a required abstract method.  Each DomoJob implementation must have an instance of _from_json"""

    base_obj = cls._convert_API_res_to_DomoJob_base_obj(obj=obj)

    return cls(
        auth=auth,
        **base_obj,
    )


@patch_to(DomoJob, cls_method=True)
async def _get_by_id(
    cls,
    application_id,
    job_id,
    auth: dmda.DomoAuth,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    debug_num_stacks_to_drop=2,
    new_cls: DomoJob = None,  # pass in a child class which has the mandatory "from_json" function
    return_raw: bool = False,
    parent_class=None,
):
    """
    this function will receive the parent_class as an input_parameter (instead of relying on the actual class DomoJob)
    to call the `new_class._from_json()`

    this process will handle converting the JSON obj into 'the correct' class
    """

    res = await application_routes.get_application_job_by_id(
        auth=auth,
        application_id=application_id,
        job_id=job_id,
        session=session,
        debug_api=debug_api,
        parent_class=parent_class,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    if return_raw:
        return res

    cls = new_cls or cls

    return cls._from_json(
        obj=res.response,
        auth=auth,
    )


@patch_to(DomoJob, cls_method=True)
async def get_by_id(
    cls,
    application_id,
    job_id,
    auth: dmda.DomoAuth,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    debug_num_stacks_to_drop=2,
    return_raw: bool = False,
):
    """
    stub abstract function that each `DomoJob` will have.
    note we pass the calling functions class into classmethod _get_by_id()
    so that we can call cls._from_json() during code execution
    """

    return await cls._get_by_id(
        application_id=application_id,
        job_id=job_id,
        auth=auth,
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        return_raw=return_raw,
        new_cls=cls,
    )

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 9
@patch_to(DomoJob, cls_method=True)
async def get_by_id(
    cls,
    application_id,
    job_id,
    auth: dmda.DomoAuth,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    debug_num_stacks_to_drop=2,
    return_raw: bool = False,
):
    """
    stub abstract function that each `DomoJob` will have.
    note we pass the calling functions class into classmethod _get_by_id()
    so that we can call cls._from_json() during code execution
    """

    return await cls._get_by_id(
        application_id=application_id,
        job_id=job_id,
        auth=auth,
        debug_api=debug_api,
        session=session,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        return_raw=return_raw,
        new_cls=cls,
    )

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 10
@patch_to(DomoJob)
def _generate_to_json(self) -> dict:
    """returns a base dictionary representation of the DomoJob class"""

    trigger_ls = [self.triggers[0].schedule.to_json()] if len(self.triggers) > 0 else []

    execution_payload = self.execution_payload or {}
    execution_payload.update({"metricsDatasetId": self.logs_dataset_id})

    return {
        "jobId": self.id,
        "jobName": self.name,
        "userId": self.user_id,
        "applicationId": self.application_id,
        "customerId": self.customer_id,
        "executionTimeout": self.execution_timeout,
        "executionPayload": execution_payload,
        "shareState": self.share_state,
        # created / excluded because generated metadata
        # updated / excluded because generated metadata
        "triggers": trigger_ls,
        "jobDescription": self.description,
        "executionTimeout": self.execution_timeout,
        "accounts": self.accounts,
    }


@patch_to(DomoJob)
def to_json(self):
    """this is an abstract method, each DomoJob implementation must define a to_json() function"""
    return self._generate_to_json()

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 13
@dataclass
class RemoteDomoStats_Config_Policy:
    type: str
    dataset_id: str

    def to_json(self):
        return {self.type: self.dataset_id}

    def __eq__(self, other) -> bool:
        if not isinstance(other, RemoteDomoStats_Config_Policy):
            return False

        return self.type == other.type


@dataclass
class RemoteDomoStats_Config:
    policies: List[RemoteDomoStats_Config_Policy] = field(default_factory=lambda: [])

    def _add_policy(self, report_type, dataset_id):
        new_policy = RemoteDomoStats_Config_Policy(
            type=report_type, dataset_id=dataset_id
        )

        if new_policy not in self.policies:
            self.policies.append(new_policy)

        else:
            policy_index = self.policies.index(new_policy)
            self.policies[policy_index] = new_policy

        return self.policies

    @classmethod
    def _from_json(cls, obj):
        domo_policies = cls()
        [
            domo_policies._add_policy(report_type, dataset_id)
            for report_type, dataset_id in obj.items()
        ]
        return domo_policies

    def to_json(self):
        return {
            report_type: dataset_id
            for policy in self.policies
            for report_type, dataset_id in policy.to_json().items()
        }

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 14
@dataclass
class DomoJob_RemoteDomoStats(DomoJob):
    remote_instance: str = None
    subscriber_job_id: str = None

    config: RemoteDomoStats_Config = None

    @classmethod
    def _from_json(cls, obj, auth):

        return cls(
            **cls._convert_API_res_to_DomoJob_base_obj(obj),
            remote_instance=cls._format_remote_instance(
                obj["executionPayload"]["remoteInstance"]
            ),
            subscriber_job_id=obj["executionPayload"]["subscriberJobId"],
            auth=auth,
            config=RemoteDomoStats_Config._from_json(
                obj["executionPayload"]["policies"]
            )
        )

    @classmethod
    async def get_by_id(
        cls,
        application_id,
        job_id,
        auth: dmda.DomoAuth,
        debug_api: bool = False,
        session: Optional[httpx.AsyncClient] = None,
        debug_num_stacks_to_drop=2,
        return_raw: bool = False,
    ):

        return await cls._get_by_id(
            application_id=application_id,
            job_id=job_id,
            auth=auth,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            return_raw=return_raw,
            new_cls=cls,
            parent_class=cls.__name__,
        )

    def to_json(self):

        s = self._generate_to_json()

        s["executionPayload"].update(
            {
                "policies": self.config.to_json(),
                "remoteInstance": self.remote_instance,
                "subscriberJobId": self.subscriber_job_id,
            }
        )

        return s

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 18
@patch_to(DomoJob_RemoteDomoStats, cls_method=True)
async def create(
    cls,
    auth: dmda.DomoAuth,
    name: str,
    config: RemoteDomoStats_Config,
    application_id: str,
    logs_dataset_id: str,
    description: str = f"created via domolibrary f{dt.date.today()}",
    remote_instance: str = None,
    accounts: List[int] = None,
    triggers: List[DomoTrigger_Schedule] = None,
    execution_timeout: int = 1440,
    return_raw: bool = False,
    debug_api: bool = False,
    debug_num_stacks_to_drop=2,
    session: Optional[httpx.AsyncClient] = None,
):

    triggers_ls = []
    if triggers is not None and len(triggers) > 0:
        triggers_ls = [
            DomoTrigger(id=None, job_id=None, schedule=schedule)
            for schedule in triggers
        ]

    domo_job = cls(
        application_id=application_id,
        auth=auth,
        name=name,
        logs_dataset_id=logs_dataset_id,
        accounts=accounts,
        description=description,
        remote_instance=remote_instance,
        config=config,
        triggers=triggers_ls,
        execution_timeout=execution_timeout,
    )

    body = domo_job.to_json()

    res = await application_routes.create_application_job(
        auth=auth,
        application_id=application_id,
        body=body,
        parent_class=cls.__name__,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    if return_raw:
        return res

    return cls._from_json(res.response, auth=auth)

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 21
@dataclass
class Watchdog_Config(ABC):
    entity_ids: List[str]
    entity_type: str  # dataflow or dataset
    watcher_parameters: dict
    report_type: str

    """base class for the different watchdog report types"""

    @classmethod
    def _from_json(cls, watcher_parameters_obj):  # executionPayload

        return cls(
            entity_ids=watcher_parameters_obj["entityIds"],
            entity_type=watcher_parameters_obj["entityType"],
            report_type=watcher_parameters_obj["type"],
            watcher_parameters=watcher_parameters_obj,
        )

    def _to_json(self):
        return {
            "entityIds": self.entity_ids,
            "entityType": self.entity_type,
            "type": self.report_type,
        }

    @abstractmethod
    def to_json(self):
        return self._to_json()

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 22
@dataclass
class Watchdog_Config_MaxIndexingTime(Watchdog_Config):
    report_type = "max_indexing_time"
    max_indexing_time_mins = 30

    def __post_init__(self):
        self.max_indexing_time_mins = self.watcher_parameters[
            "maxIndexingTimeInMinutes"
        ]

    def to_json(self, **kwargs):
        if kwargs.get("max_indexing_time_mins"):
            self.max_indexing_time_mins = kwargs["max_indexing_time_mins"]

        return {
            **self._to_json(),
            "maxIndexingTimeInMinutes": self.max_indexing_time_mins,
        }


@dataclass
class Watchdog_Config__Variance(Watchdog_Config):
    report_type: str
    variance_percent: int = 10

    def __post_init__(self):
        self.variance_percent = self.watcher_parameters["variancePercent"]

    def to_json(self, **kwargs):
        if kwargs.get("variance_percent"):
            self.variance_percent = kwargs["variance_percent"]

        return {
            **self._to_json(),
            "variancePercent": self.variance_percent,
        }


@dataclass
class Watchdog_Config_RowCountVariance(Watchdog_Config__Variance):
    report_type = "row_count_variance"


@dataclass
class Watchdog_Config_ExecutionVariance(Watchdog_Config__Variance):
    report_type = "execution_variance"


@dataclass
class Watchdog_Config_ErrorDetection(Watchdog_Config):
    report_type: str = "error_detection"

    def to_json(self, **kwargs):
        return {
            **self._to_json(),
        }


@dataclass
class Watchdog_Config_LastDataUpdated(Watchdog_Config):
    report_type: str = "last_data_updated"
    min_data_update_frequency_in_mins: int = 10

    def __post_init__(self):
        self.min_data_update_frequency_in_mins = self.watcher_parameters[
            "minDataUpdateFrequencyInMinutes"
        ]

    def to_json(self, **kwargs):
        if kwargs.get("min_data_update_frequency_in_mins"):
            self.min_data_update_frequency_in_mins = kwargs[
                "min_data_update_frequency_in_mins"
            ]

        return {
            **self._to_json(),
            "minDataUpdateFrequencyInMinutes": self.min_data_update_frequency_in_mins,
        }


@dataclass
class Watchdog_Config_CustomQuery(Watchdog_Config):
    report_type: str = "custom_query"
    sql_query: str = ""

    def __post_init__(self):
        self.sql_query = self.watcher_parameters["sqlQuery"]

    def to_json(self, **kwargs):
        if kwargs.get("sql_query"):
            self.sql_query = kwargs["sql_query"]

        return {
            **self._to_json(),
            "sqlQuery": self.sql_query,
        }

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 23
class Watchdog_ConfigFactory(Enum):
    MAX_INDEXING_TIME = Watchdog_Config_MaxIndexingTime
    ROW_COUNT_VARIANCE = Watchdog_Config_RowCountVariance
    EXECUTION_VARIANCE = Watchdog_Config_ExecutionVariance
    ERROR_DETECTION = Watchdog_Config_ErrorDetection
    LAST_DATA_UPDATED = Watchdog_Config_LastDataUpdated
    CUSTOM_QUERY = Watchdog_Config_CustomQuery

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 25
@dataclass
class DomoJob_Watchdog(DomoJob):
    custom_message: str = None
    remote_instance: str = None

    notify_emails: List[str] = field(default_factory=lambda: [])
    notify_group_ids: List[str] = field(default_factory=lambda: [])
    notify_user_ids: List[str] = field(default_factory=lambda: [])

    config: Watchdog_Config = None
    webhooks: List[str] = None

    @classmethod
    def _from_json(cls, obj, auth):

        remote_instance = cls._format_remote_instance(
            obj["executionPayload"].get("domain")
        )

        watchdog_parameters_obj = obj["executionPayload"]["watcherParameters"]

        config = Watchdog_ConfigFactory[
            watchdog_parameters_obj["type"].upper()
        ].value._from_json(watchdog_parameters_obj)

        return cls(
            **cls._convert_API_res_to_DomoJob_base_obj(obj),
            remote_instance=remote_instance,
            custom_message=obj["executionPayload"]["customMessage"],
            config=config,
            notify_emails=obj["executionPayload"]["notifyEmailAddresses"],
            notify_group_ids=obj["executionPayload"]["notifyGroupIds"],
            notify_user_ids=obj["executionPayload"]["notifyUserIds"],
            webhooks=obj["executionPayload"]["webhooks"],
            auth=auth
        )

    @classmethod
    async def get_by_id(
        cls,
        application_id,
        job_id,
        auth: dmda.DomoAuth,
        debug_api: bool = False,
        session: Optional[httpx.AsyncClient] = None,
        debug_num_stacks_to_drop=2,
        return_raw: bool = False,
    ):

        return await cls._get_by_id(
            application_id=application_id,
            job_id=job_id,
            auth=auth,
            debug_api=debug_api,
            session=session,
            debug_num_stacks_to_drop=debug_num_stacks_to_drop,
            return_raw=return_raw,
            new_cls=cls,
            parent_class=cls.__name__,
        )

    def to_json(self):
        s = self._generate_to_json()

        s["executionPayload"].update(
            {
                "customMessage": self.custom_message,
                "domain": self.remote_instance,
                "notifyEmailAddresses": self.notify_emails,
                "notifyGroupIds": self.notify_group_ids,
                "notifyUserIds": self.notify_user_ids,
                "watcherParameters": self.config.to_json(),
                "webhooks": self.webhooks,
            }
        )
        return s

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 28
@patch_to(DomoJob)
async def update(
    self: DomoJob,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    debug_num_stacks_to_drop=2,
):

    res = await application_routes.update_application_job(
        auth=self.auth,
        body=self.to_json(),
        application_id=self.application_id,
        job_id=self.id,
        debug_api=debug_api,
        session=session,
        parent_class=self.__class__.__name__,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    return res

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 31
@patch_to(DomoJob)
async def execute(
    self,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    debug_num_stacks_to_drop=2,
):

    res = await application_routes.execute_application_job(
        auth=self.auth,
        application_id=self.application_id,
        job_id=self.id,
        debug_api=debug_api,
        session=session,
        parent_class=self.__class__.__name__,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
    )

    return res

# %% ../../nbs/classes/50_DomoApplication_Job.ipynb 34
@patch_to(DomoJob_Watchdog, cls_method=True)
async def create(
    cls,
    auth: dmda.DomoAuth,
    name: str,
    application_id: str,
    config: Watchdog_Config,
    logs_dataset_id: str,
    notify_user_ids: list = None,
    notify_group_ids: list = None,
    notify_emails: list = None,
    triggers: List[DomoTrigger_Schedule] = None,
    description: str = f"created via domolibrary - {dt.date.today()}",
    execution_timeout=1440,
    accounts: list[int] = None,
    remote_instance=None,
    custom_message: str = None,
    webhooks: List[str] = None,
    debug_api: bool = False,
    session: Optional[httpx.AsyncClient] = None,
    return_raw: bool = False,
):

    domo_job = cls(
        auth=auth,
        name=name,
        description=description,
        application_id=application_id,
        execution_timeout=execution_timeout,
        notify_user_ids=notify_user_ids or [],
        notify_group_ids=notify_group_ids or [],
        notify_emails=notify_emails or [],
        triggers=triggers or [],
        accounts=accounts or [],
        logs_dataset_id=logs_dataset_id,
        remote_instance=remote_instance,
        config=config,
        custom_message=custom_message,
        webhooks=webhooks,
    )

    body = domo_job.to_json()

    res = await application_routes.create_application_job(
        auth=auth,
        application_id=application_id,
        body=body,
        debug_api=debug_api,
        session=session,
    )

    if return_raw:
        return res

    return cls._from_json(res.response, auth=auth)
