handlers.quality_check_handler

class QualityCheckHandler(handlers.eventhandler.EventHandler):

Whenever somebody updates an Application, we run a quality check and create a to-do if there are quality gaps.
Default rules: Description needs to be filled, Lifecycle must be defined for current day.

Configuration:
todo_roles: List of roles that will receive the to-do. Can be any of the following:

  • __FIRST_MATCHING__: Assign the to-do only to the first subscription that matches the roles below.
  • <name of a specific subscription role>: matches any subscription with that role, e.g. "Application Owner".
  • <subscription type>:<name of a specific subscription role>: matches any subscription with that role and type, e.g. "RESPONSIBLE:Application Owner".
  • __CREATOR__: the creator of the fact sheet.
  • __EDITOR__: the person that just modified the fact sheet.
  • __ANY_RESPONSIBLE__: any subscription with type "RESPONSIBLE"
  • __ANY_ACCOUNTABLE__: any subscription with type "ACCOUNTABLE"
  • __ANY_RESPONSIBLE_OR_ACCOUNTABLE__: any subscription with type either "RESPONSIBLE" or "ACCOUNTABLE"
  • __ANY__: any subscription.

todo_skip_invalid_entra_users: Optional: Set to true to skip to-do recipients that are not enabled users in Entra ID (e.g. a deactivated Application Owner). With __FIRST_MATCHING__, the next role is tried instead. todo_fallback_fs_subscription: Optional: Used if todo_roles yields no recipient. Dict with id (fact sheet id), subscription_type (e.g. "RESPONSIBLE") and subscription_role (e.g. "Data Quality Manager"); the to-do is assigned to all subscribers of that fact sheet with that type and role. Omit type or role to match any. todo_fallback_emails: Optional: List of mail addresses, used if neither todo_roles nor todo_fallback_fs_subscription yield a recipient.

todo_title: Title of the to-do. If this automation is configured multiple times, make sure to use unique titles, otherwise the automatic deletion of todos might overlap.
todo_category: Category of the to-do, should be ACTION_ITEM.
todo_due_in_days: Due date of the to-do items.
delete_outdated_todos: Set to true if outdated to-dos (for fs with non-matching conditions or with no quality errors) shall be deleted automatically. recreate_todo: Set to true if all to-dos should be regenerated, e.g. by a scheduled cron run, so that the to-do- notifications will be sent again. Should only be used in scheduled automations, e.g. in scheduled initial sync, not in live automation.

rules: The default configuration contains an example. Keys are fact sheet types (optionally <type>:<category1>,<category2>), * for rules that apply to all fact sheet types, or relation names.

Optional configuration for editorial management:
editorial_status_field: Optional: Field name where the editorial status (e.g. "Draft") is stored.
editorial_status_initial: Optional: Initial value for the editorial status field, e.g. "Draft"
editorial_status_prefix_field: Optional: Field name where the prefix for the editorial status (e.g. [DRAFT Expires 2025-01-31]) is stored.
editorial_details: Optional: Field where the editorial details are stored, e.g. the instructions for the end user.
deletion_days_field: Optional: Field that stores the expiration date of a draft fact sheet.

name = 'Quality Check'
description = 'Whenever somebody updates an Application, we run a quality check and create a to-do (if failed) or set the status to published.'
conditions = {'event_types': ['FactSheetUpdatedEvent', 'RelationCreatedEvent', 'RelationUpdatedEvent', 'RelationSwitchEvent', 'RelationArchivedEvent', 'RelationDeletedEvent'], 'fs_types': ['Application']}
supports_initial_sync = True
config_template = {'user_ids': [], 'todo_roles': ['__CREATOR__', 'Steward'], 'todo_skip_invalid_entra_users': False, 'todo_fallback_fs_subscription': {}, 'todo_fallback_emails': [], 'todo_title': 'Please fix quality gaps', 'todo_fs_type': 'Application', 'todo_description': '', 'todo_category': 'ACTION_ITEM', 'todo_due_in_days': 30, 'delete_outdated_todos': True, 'recreate_todo': False, 'condition': ["return fs['lifecycle_status'] != 'endOfLife'"], 'rules': {'Application': [{'name': 'Description', 'rule': "len(fs.get('description', '')) > 10", 'error': 'Description must be longer than 10 characters.'}, {'name': 'Lifecycle', 'rule': "len(fs.get('lifecycle_status', '')) > 1", 'error': 'Lifecycle for today must be defined: At least one date must be in the past.'}], 'relApplicationToUserGroup': [{'name': 'Description of relation', 'rule': "rel['description']", 'error': 'Description on User Group relation must be set.'}]}, 'editorial_status_field': 'editorialStatus', 'editorial_status_initial': 'Draft', 'editorial_status_prefix_field': 'editorialStatusPostfix', 'editorial_details': 'editorialDetails', 'deletion_days_field': 'editorialDraftExpiryDate'}
def data_requirements(self):

Declare the exact fact-sheet data this handler needs, so the webhook event loop (process_event in workspace/views_module.py) can load a single lean FastFactSheet per event instead of the full fact sheet.

Return a dict: { "fields": [], "relations": [], "subscriptions": bool, # True if handle() reads fs["subscriptions"] "tags": bool, # True if handle() reads tags / has_tag() "documents": bool, "acls": bool, "completion": bool, # True if handle() reads fs["completion"] "base_fields": [], # optional; omit/None = full # default base field set. A list restricts the # base scalars to just those (id and type are # always loaded). Advanced opt-in for max speed. } Fields referenced by this handler's trigger are folded in automatically by the event loop (into "fields"), so only declare what handle() itself needs on top of them.

Return None (the default) to signal "requirements cannot be declared statically" - e.g. handlers that exec/eval config-supplied code. The event loop then loads the full fact sheet for that event, so correctness is always preserved. This is a standalone hook (deliberately not tied to the initial_sync_* hooks, which are slated to migrate onto it later).

def handle(self, event, fs, **kwargs):

Called automatically whenever a FactSheetUpdated event comes in via webhook.

def quality_check(self, event, fs, rules, rel=None, **kwargs):

Called automatically whenever a FactSheetUpdated event comes in via webhook.

def get_todo_user_filter(self):

Returns a filter for to-do recipients (see get_notification_users), or None if all users are accepted.

def get_fallback_notification_users(self, user_filter=None):

Recipients if todo_roles yields nobody: todo_fallback_fs_subscription, then todo_fallback_emails.

def delete_outdated_todos(self, fs, title):
def initial_sync_event(self, fs):