text: # -*- coding: utf-8 -*-
  
  """This module provides access to the filter REST api of Camunda."""
  
  from __future__ import annotations
  import datetime as dt
  import dataclasses
  import typing
  
  import pycamunda
  import pycamunda.task
  import pycamunda.variable
  import pycamunda.base
  import pycamunda.resource
  from pycamunda.request import QueryParameter, PathParameter, BodyParameter, BodyParameterContainer
  
  URL_SUFFIX = '/filter'
  
  
  __all__ = ['GetList', 'Count', 'Get', 'Create', 'Update', 'Delete', 'Execute', 'ExecuteCount']
  
  
  @dataclasses.dataclass
  class Query:
      """Data class of query."""
  
      @classmethod
      def load(cls, **kwargs) -> Query:
          query = cls()
          for key, value in kwargs.items():
              setattr(query, key, value)
          return query
  
  
  @dataclasses.dataclass
  class Properties:
      """Data class of properties."""
  
      @classmethod
      def load(cls, **kwargs) -> Properties:
          properties = cls()
          for key, value in kwargs.items():
              setattr(properties, key, value)
          return properties
  
  
  @dataclasses.dataclass
  class Filter:
      """Data class of filter as returned by the REST api of Camunda."""
      id_: str
      resource_type: str
      name: str
      owner: str
      query: Query
      properties: Properties
      item_count: int = None
  
      @classmethod
      def load(cls, data: typing.Mapping[str, typing.Any]) -> Filter:
          return cls(
              id_=data['id'],
              resource_type=data['resourceType'],
              name=data['name'],
              owner=data['owner'],
              query=Query.load(**data['query']),
              properties=Properties.load(**data['properties']),
              item_count=data.get('itemCount', None)
          )
  
  
  class GetList(pycamunda.base.CamundaRequest):
  
      id_ = QueryParameter('filterId')
      resource_type = QueryParameter('resourceType')
      name = QueryParameter('name')
      name_like = QueryParameter('nameLike')
      owner = QueryParameter('owner')
      item_count = QueryParameter('itemCount')
      sort_by = QueryParameter(
          'sortBy',
          mapping={
              'id_': 'filterId',
              'first_name': 'firstName',
              'last_name': 'lastName',
              'email': 'email'
          }
      )
      ascending = QueryParameter(
          'sortOrder',
          mapping={True: 'asc', False: 'desc'},
          provide=lambda self, obj, obj_type: vars(obj).get('sort_by', None) is not None
      )
      first_result = QueryParameter('firstResult')
      max_results = QueryParameter('maxResults')
  
      def __init__(
          self,
          url: str,
          id_: str = None,
          name: str = None,
          name_like: str = None,
          owner: str = None,
          item_count: bool = False,
          sort_by: str = None,
          ascending: bool = True,
          first_result: int = None,
          max_results: int = None
      ):
          """Query for a list of filters using a list of parameters. The size of the result set can be
          retrieved by using the Get Count request.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          :param name: Filter by the name of the filter.
          :param name_like: Filter by a substring of the name of the filter.
          :param owner: Filter by the user id of the owner of the filter.
          :param item_count: Return the number of items matched by the respective filters.
          :param sort_by: Sort the results by `id_`, `first_name`, `last_name` or `email` of the user.
          :param ascending: Sort order.
          :param first_result: Pagination of results. Index of the first result to return.
          :param max_results: Pagination of results. Maximum number of results to return.
          """
          super().__init__(url=url + URL_SUFFIX)
          self.id_ = id_
          self.resource_type = 'Task'
          self.name = name
          self.name_like = name_like
          self.owner = owner
          self.item_count = item_count
          self.sort_by = sort_by
          self.ascending = ascending
          self.first_result = first_result
          self.max_results = max_results
  
      def __call__(self, *args, **kwargs) -> typing.Tuple[Filter]:
          """Send the request"""
          response = super().__call__(pycamunda.base.RequestMethod.GET, *args, **kwargs)
  
          return tuple(Filter.load(filter_json) for filter_json in response.json())
  
  
  class Count(pycamunda.base.CamundaRequest):
  
      id_ = QueryParameter('filterId')
      resource_type = QueryParameter('resourceType')
      name = QueryParameter('name')
      name_like = QueryParameter('nameLike')
      owner = QueryParameter('owner')
  
      def __init__(
          self,
          url: str,
          id_:str = None,
          name: str = None,
          name_like: str = None,
          owner: str = None
      ):
          """Count filters.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          :param name: Filter by the name of the filter.
          :param name_like: Filter by a substring of the name of the filter.
          :param owner: Filter by the user id of the owner of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/count')
          self.id_ = id_
          self.resource_type = 'Task'
          self.name = name
          self.name_like = name_like
          self.owner = owner
  
      def __call__(self, *args, **kwargs) -> int:
          """Send the request"""
          response = super().__call__(pycamunda.base.RequestMethod.GET, *args, **kwargs)
  
          return response.json()['count']
  
  
  class Get(pycamunda.base.CamundaRequest):
  
      id_ = PathParameter('filterId')
      item_count = QueryParameter('itemCount')
  
      def __init__(self, url: str, id_: str, item_count: bool = False):
          """Query for a filter.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          :param item_count: Return the number of items matched by the respective filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/{filterId}')
          self.id_ = id_
          self.item_count = item_count
  
      def __call__(self, *args, **kwargs) -> Filter:
          """Send the request"""
          response = super().__call__(pycamunda.base.RequestMethod.GET, *args, **kwargs)
  
          return Filter.load(response.json())
  
  
  class _Criteria(pycamunda.base.CamundaRequest):
  
      process_instance_id = BodyParameter('processInstanceId')
      process_instance_business_key = BodyParameter('processInstanceBusinessKey')
      process_instance_business_key_like = BodyParameter('processInstanceBusinessKeyLike')
      process_definition_id = BodyParameter('processDefinitionId')
      process_definition_key = BodyParameter('processDefinitionKey')
      process_definition_key_in = BodyParameter('processDefinitionKeyIn')
      process_definition_name = BodyParameter('processDefinitionName')
      process_definition_name_like = BodyParameter('processDefinitionNameLike')
      case_instance_id = BodyParameter('caseInstanceId')
      case_instance_business_key = BodyParameter('caseInstanceBusinessKey')
      case_instance_business_key_like = BodyParameter('caseInstanceBusinessKeyLike')
      case_definition_id = BodyParameter('caseDefinitionId')
      case_definition_key = BodyParameter('caseDefinitionKey')
      case_definition_name = BodyParameter('caseDefinitionName')
      case_definition_name_like = BodyParameter('caseDefinitionNameLike')
      active = BodyParameter('active')
      activity_instance_id_in = BodyParameter('activityInstanceIdIn')
      execution_id = BodyParameter('executionId')
      assignee = BodyParameter('assignee')
      assignee_in = BodyParameter('assigneeIn')
      assignee_like = BodyParameter('assigneeLike')
      task_owner = BodyParameter('owner')
      candidate_group = BodyParameter('candidateGroup')
      candidate_groups = BodyParameter('candidateGroups')
      candidate_user = BodyParameter('candidateUser')
      involved_user = BodyParameter('involvedUser')
      unassigned = BodyParameter('unassigned')
      delegation_state = BodyParameter('delegationState')
      task_definition_key = BodyParameter('taskDefinitionKey')
      task_definition_key_in = BodyParameter('taskDefinitionKeyIn')
      task_definition_key_like = BodyParameter('taskDefinitionKeyLike')
      task_name = BodyParameter('name')
      task_name_like = BodyParameter('nameLike')
      description = BodyParameter('description')
      description_like = BodyParameter('descriptionLike')
      priority = BodyParameter('priority')
      max_priority = BodyParameter('maxPriority')
      min_priority = BodyParameter('minPriority')
      tenant_id_in = BodyParameter('tenantIdIn')
      without_tenant_id = BodyParameter('withoutTenantId')
      created_before = BodyParameter('createdBefore')
      created_after = BodyParameter('createdAfter')
      due_before = BodyParameter('dueBefore')
      due_after = BodyParameter('dueAfter')
      follow_up_after = BodyParameter('followUpAfter')
      follow_up_before = BodyParameter('followUpBefore')
      follow_up_before_or_not_existent = BodyParameter('followUpBeforeOrNotExistent')
      query = BodyParameterContainer(
          'query',
          process_instance_id,
          process_instance_business_key,
          process_instance_business_key_like,
          process_definition_id,
          process_definition_key,
          process_definition_key_in,
          process_definition_name,
          process_definition_name_like,
          case_instance_id,
          case_instance_business_key,
          case_instance_business_key_like,
          case_definition_id,
          case_definition_key,
          case_definition_name,
          case_definition_name_like,
          active,
          activity_instance_id_in,
          execution_id,
          assignee,
          assignee_in,
          assignee_like,
          task_owner,
          candidate_group,
          candidate_groups,
          candidate_user,
          involved_user,
          unassigned,
          delegation_state,
          task_definition_key,
          task_definition_key_in,
          task_definition_key_like,
          task_name,
          task_name_like,
          description,
          description_like,
          priority,
          max_priority,
          min_priority,
          tenant_id_in,
          without_tenant_id,
          created_before,
          created_after,
          due_before,
          due_after,
          follow_up_after,
          follow_up_before,
          follow_up_before_or_not_existent
      )
  
      def add_process_instance_criteria(
          self,
          id_: str = ...,
          business_key: str = ...,
          business_key_like: str = ...
      ) -> None:
          """Add criteria that filter by process instance.
  
          :param id_: Filter by the id of the process instance.
          :param business_key: Filter by the business key of the process instance.
          :param business_key_like: Filter by a substring of the business key of the filter.
          """
          if id_ is not Ellipsis:
              self.process_instance_id = id_
          if business_key is not Ellipsis:
              self.process_instance_business_key = business_key
          if business_key_like is not Ellipsis:
              self.process_instance_business_key_like = business_key_like
  
      def add_process_definition_criteria(
          self,
          id_: str = ...,
          key: str = ...,
          key_in: typing.Iterable[str] = ...,
          name: str = ...,
          name_like: str = ...
      ) -> None:
          """Add criteria that filter by the process definition.
  
          :param id_: Filter by the id of the process definition.
          :param key: Filter by the key of the process definition.
          :param key_in: Filter by a substring of the key of the process definition.
          :param name: Filter by the name of the process definition.
          :param name_like: Filter by a substring of the name of the process definition.
          """
          if id_ is not Ellipsis:
              self.process_definition_id = id_
          if key is not Ellipsis:
              self.process_definition_key = key
          if key_in is not Ellipsis:
              self.process_definition_key_in = key_in
          if name is not Ellipsis:
              self.process_definition_name = name
          if name_like is not Ellipsis:
              self.process_definition_name_like = name_like
  
      def add_case_instance_criteria(
          self,
          id_: str = ...,
          business_key: str = ...,
          business_key_like: str = ...
      ):
          """Add criteria that filter by the case instance.
  
          :param id_: Filter by the id of the case instance.
          :param business_key: Filter by the business key of the case instance.
          :param business_key_like: Filter by a substring of the business key of the case instance.
          """
          if id_ is not Ellipsis:
              self.case_instance_id = id_
          if business_key is not Ellipsis:
              self.case_instance_business_key = business_key
          if business_key_like is not Ellipsis:
              self.case_instance_business_key_like = business_key_like
  
          return self
  
      def add_case_definition_criteria(
          self,
          id_: str = ...,
          key: str = ...,
          name: str = ...,
          name_like: str = ...
      ) -> None:
          """Add criteria that filter by the case definition.
  
          :param id_: Filter by the id of the case definition.
          :param key: Filter by the key of the case definition.
          :param name: Filter by the name of the case definition.
          :param name_like: Filter by a substring of the name of the case definition.
          """
          if id_ is not Ellipsis:
              self.case_definition_id = id_
          if key is not Ellipsis:
              self.case_definition_key = key
          if name is not Ellipsis:
              self.case_definition_name = name
          if name_like is not Ellipsis:
              self.case_definition_name_like = name_like
  
      def add_other_criteria(
          self,
          active: bool = ...,
          activity_instance_id_in: typing.Iterable[str] = ...,
          execution_id: str = ...
      ) -> None:
          """Add criteria that filter by active status, activity instance or execution id.
  
          :param active: Filter only active tasks.
          :param activity_instance_id_in: Filter by activity instance ids.
          :param execution_id: Filter by the execution id.
          """
          if active is not Ellipsis:
              self.active = active or None
          if activity_instance_id_in is not Ellipsis:
              self.activity_instance_id_in = activity_instance_id_in
          if execution_id is not Ellipsis:
              self.execution_id = execution_id
  
      def add_user_criteria(
          self,
          assignee: str = ...,
          assignee_in: typing.Iterable[str] = ...,
          assignee_like: str = ...,
          task_owner: str = ...,
          candidate_group: str = ...,
          candidate_groups: typing.Iterable[str] = ...,
          candidate_user: str = ...,
          involved_user: str = ...,
          unassigned: bool = ...,
          delegation_state: typing.Union[str, pycamunda.task.DelegationState] = ...
      ) -> None:
          """Add criteria that filter by user.
  
          :param assignee: Filter by the assignee of the task.
          :param assignee_in: Filter whether assignee of the task is one of multiple ones.
          :param assignee_like: Filter by a substring of the assignee of the task.
          :param task_owner: Filter by the owner of the task.
          :param candidate_group: Filter by the candidate group of the task.
          :param candidate_groups: Filter whether the candidate group of the task is one of multiple
                                   ones.
          :param candidate_user: Filter by the candidate user of the task.
          :param involved_user: TODO
          :param unassigned: Filter only unassigned tasks.
          :param delegation_state: Filter by delegation state.
          """
          if candidate_user is not Ellipsis and (
                  candidate_group is not Ellipsis or candidate_groups is not Ellipsis):
              raise pycamunda.PyCamundaException(
                  'candidate user and candidate groups must not be both provided.'
              )
  
          if assignee is not Ellipsis:
              self.assignee = assignee
          if assignee_in is not Ellipsis:
              self.assignee_in = assignee_in
          if assignee_like is not Ellipsis:
              self.assignee_like = assignee_like
          if task_owner is not Ellipsis:
              self.task_owner = task_owner
          if candidate_group is not Ellipsis:
              self.candidate_group = candidate_group
          if candidate_groups is not Ellipsis:
              self.candidate_groups = candidate_groups
          if candidate_user is not Ellipsis:
              self.candidate_user = candidate_user
          if involved_user is not Ellipsis:
              self.involved_user = involved_user
          if unassigned is not Ellipsis:
              self.unassigned = unassigned
          if delegation_state is not Ellipsis:
              self.delegation_state = None
              if delegation_state is not None:
                  self.delegation_state = pycamunda.task.DelegationState(delegation_state)
  
      def add_task_criteria(
          self,
          definition_key: str = ...,
          definition_key_in: typing.Iterable[str] = ...,
          definition_key_like: str = ...,
          task_name: str = ...,
          task_name_like: str = ...,
          description: str = ...,
          description_like: str = ...,
          priority: int = ...,
          max_priority: int = ...,
          min_priority: int = ...,
          tenant_id_in: typing.Iterable[str] = ...,
          without_tenant_id: bool = False
      ) -> None:
          """Add criteria that filter by task.
  
          :param definition_key: Filter by the definition key of the task.
          :param definition_key_in: Filter whether definition key of the task is one of multiple ones.
          :param definition_key_like: Filter by a substring of the definition key of the task.
          :param task_name: Filter by the name of the task.
          :param task_name_like:  Filter by a substring of the name of the task.
          :param description: Filter by the description of the task.
          :param description_like: Filter by a substring of the description of the task.
          :param priority: Filter by the priority of the task.
          :param max_priority: Filter by a maximum priority of the task.
          :param min_priority: Filter by a minimum priority of the task.
          :param tenant_id_in: Filter whether the tenant id is one of multiple ones.
          :param without_tenant_id: Filter only tasks without tenant id.
          """
          if definition_key is not Ellipsis:
              self.task_definition_key = definition_key
          if definition_key_in is not Ellipsis:
              self.task_definition_key_in = definition_key_in
          if definition_key_like is not Ellipsis:
              self.task_definition_key_like = definition_key_like
          if task_name is not Ellipsis:
              self.task_name = task_name
          if task_name_like is not Ellipsis:
              self.task_name_like = task_name_like
          if description is not Ellipsis:
              self.description = description
          if description_like is not Ellipsis:
              self.description_like = description_like
          if priority is not Ellipsis:
              self.priority = priority
          if max_priority is not Ellipsis:
              self.max_priority = max_priority
          if min_priority is not Ellipsis:
              self.min_priority = min_priority
          if tenant_id_in is not Ellipsis:
              self.tenant_id_in = tenant_id_in
          self.without_tenant_id = without_tenant_id
  
      def add_datetime_criteria(
          self,
          created_before: dt.datetime = ...,
          created_after: dt.datetime = ...,
          due_before: dt.datetime = ...,
          due_after: dt.datetime = ...,
          follow_up_after: dt.datetime = ...,
          follow_up_before: dt.datetime = ...,
          follow_up_before_or_not_existent: dt.datetime = ...
      ) -> None:
          """Add criteria that filter by datetime. Datetime objects are expected to contain timezone
          information.
  
          :param created_before: Filter by tasks that were created before the given date.
          :param created_after: Filter by tasks that were created after the given date.
          :param due_before: Filter by tasks where due date has already passed at given date.
          :param due_after: Filter by tasks where due date has not passed at given date.
          :param follow_up_after: Filter by tasks that have a follow up date that has not passed yet
                                  at given date.
          :param follow_up_before: Filter by tasks that have a follow up date that has already passed
                                   at given date.
          :param follow_up_before_or_not_existent: Filter by tasks that do not have a follow up date
                                                   or one that has already passed.
          """
          if created_before is not Ellipsis:
              self.created_before = created_before
          if created_after is not Ellipsis:
              self.created_after = created_after
          if due_before is not Ellipsis:
              self.due_before = due_before
          if due_after is not Ellipsis:
              self.due_after = due_after
          if follow_up_after is not Ellipsis:
              self.follow_up_after = follow_up_after
          if follow_up_before is not Ellipsis:
              self.follow_up_before = follow_up_before
          if follow_up_before_or_not_existent is not Ellipsis:
              self.follow_up_before_or_not_existent = follow_up_before_or_not_existent
  
      def __call__(self, *args, **kwargs):
          return super().__call__(*args, **kwargs)
  
  
  class Create(_Criteria):
  
      resource_type = BodyParameter('resourceType')
      name = BodyParameter('name')
      owner = BodyParameter('owner')
  
      def __init__(self, url: str, name: str, owner: str = None):
          """Create a new filter.
  
          :param url: Camunda Rest engine URL.
          :param name: Name of the filter.
          :param owner: User id of the owner of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/create')
          self.resource_type = 'Task'
          self.name = name
          self.owner = owner
  
      def __call__(self, *args, **kwargs) -> Filter:
          """Send the request"""
          response = super().__call__(pycamunda.base.RequestMethod.POST, *args, **kwargs)
  
          return Filter.load(response.json())
  
  
  class Update(_Criteria):
  
      id_ = PathParameter('id')
      resource_type = BodyParameter('resourceType')
      name = BodyParameter('name')
      owner = BodyParameter('owner')
  
      def __init__(self, url: str, id_: str, name: str = None, owner: str = None):
          """Update a filter.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          :param name: Name of the filter.
          :param owner: User id of the owner of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/{id}')
          self.id_ = id_
          self.resource_type = 'Task'
          self.name = name
          self.owner = owner
  
      def __call__(self, *args, **kwargs) -> None:
          """Send the request"""
          super().__call__(pycamunda.base.RequestMethod.PUT, *args, **kwargs)
  
  
  class Delete(pycamunda.base.CamundaRequest):
  
      id_ = PathParameter('id')
  
      def __init__(self, url: str, id_: str):
          """Delete a filter.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/{id}')
          self.id_ = id_
  
      def __call__(self, *args, **kwargs) -> None:
          """Send the request."""
          super().__call__(pycamunda.base.RequestMethod.DELETE, *args, **kwargs)
  
  
  class Execute(_Criteria):
  
      id_ = PathParameter('id')
  
      def __init__(self, url: str, id_: str, single_result: bool = False):
          """Execute a filter.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/{id}')
          self.id_ = id_
          self.single_result = single_result
  
      @property
      def url(self) -> str:
          return super().url + ('/singleResult' if self.single_result else '/list')
  
      def body_parameters(self, apply: typing.Callable = ...) -> typing.Dict[str, typing.Any]:
          return super().body_parameters(apply=apply)['query']
  
      def __call__(
          self, *args, **kwargs
      ) -> typing.Union[pycamunda.task.Task, typing.Tuple[pycamunda.task.Task]]:
          """Send the request."""
          if self.body_parameters():
              response = super().__call__(pycamunda.base.RequestMethod.POST, *args, **kwargs)
          else:
              response = super().__call__(pycamunda.base.RequestMethod.GET, *args, **kwargs)
  
          if self.single_result:
              return pycamunda.task.Task.load(response.json())
          return tuple(pycamunda.task.Task.load(task_json) for task_json in response.json())
  
  
  class ExecuteCount(pycamunda.base.CamundaRequest):
  
      id_ = PathParameter('id')
  
      def __init__(self, url: str, id_: str):
          """Get the number of results returned by executing a filter.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the filter.
          """
          super().__init__(url=url + URL_SUFFIX + '/{id}/count')
          self.id_ = id_
  
      def __call__(self, *args, **kwargs) -> int:
          """Send the request."""
          response = super().__call__(pycamunda.base.RequestMethod.GET, *args, **kwargs)
  
          return response.json()['count']
  
  
  class Options(pycamunda.base.CamundaRequest):
  
      id_ = PathParameter('id')
  
      def __init__(self, url: str, id_=None):
          """Get a list of options the currently authenticated user can perform on the filter
          resource.
  
          :param url: Camunda Rest engine URL.
          :param id_: Id of the user.
          """
          super().__init__(url=url + URL_SUFFIX)
          self.id_ = id_
  
      @property
      def url(self):
          return super().url + (f'/{self.id_}' if self.id_ is not None else '')
  
      def __call__(self, *args, **kwargs):
          """Send the request"""
          response = super().__call__(pycamunda.base.RequestMethod.OPTIONS, *args, **kwargs)
  
          return pycamunda.resource.ResourceOptions.load(response.json())
  