zulip/zerver/webhooks/trello/view/card_actions.py

256 lines
9.9 KiB
Python
Raw Normal View History

from typing import Dict, Tuple, Any, Optional, MutableMapping, Mapping, Text
from datetime import datetime
from .exceptions import UnknownUpdateCardAction
from .templates import TRELLO_SUBJECT_TEMPLATE, TRELLO_MESSAGE_TEMPLATE
SUPPORTED_CARD_ACTIONS = [
u'updateCard',
u'createCard',
u'addLabelToCard',
u'removeLabelFromCard',
u'addMemberToCard',
u'removeMemberFromCard',
u'addAttachmentToCard',
u'addChecklistToCard',
u'commentCard'
]
CREATE = u'createCard'
CHANGE_LIST = u'changeList'
CHANGE_NAME = u'changeName'
SET_DESC = u'setDesc'
CHANGE_DESC = u'changeDesc'
REMOVE_DESC = u'removeDesc'
ARCHIVE = u'archiveCard'
REOPEN = u'reopenCard'
SET_DUE_DATE = u'setDueDate'
CHANGE_DUE_DATE = u'changeDueDate'
REMOVE_DUE_DATE = u'removeDueDate'
ADD_LABEL = u'addLabelToCard'
REMOVE_LABEL = u'removeLabelFromCard'
ADD_MEMBER = u'addMemberToCard'
REMOVE_MEMBER = u'removeMemberFromCard'
ADD_ATTACHMENT = u'addAttachmentToCard'
ADD_CHECKLIST = u'addChecklistToCard'
COMMENT = u'commentCard'
TRELLO_CARD_URL_TEMPLATE = u'[{card_name}]({card_url})'
ACTIONS_TO_MESSAGE_MAPPER = {
CREATE: u'created {card_url_template}',
CHANGE_LIST: u'moved {card_url_template} from {old_list} to {new_list}',
CHANGE_NAME: u'renamed the card from "{old_name}" to {card_url_template}',
SET_DESC: u'set description for {card_url_template} to\n~~~ quote\n{desc}\n~~~\n',
CHANGE_DESC: u'changed description for {card_url_template} from\n~~~ quote\n{old_desc}\n~~~\nto\n~~~ quote\n{desc}\n~~~\n',
REMOVE_DESC: u'removed description from {card_url_template}',
ARCHIVE: u'archived {card_url_template}',
REOPEN: u'reopened {card_url_template}',
SET_DUE_DATE: u'set due date for {card_url_template} to {due_date}',
CHANGE_DUE_DATE: u'changed due date for {card_url_template} from {old_due_date} to {due_date}',
REMOVE_DUE_DATE: u'removed the due date from {card_url_template}',
ADD_LABEL: u'added a {color} label with \"{text}\" to {card_url_template}',
REMOVE_LABEL: u'removed a {color} label with \"{text}\" from {card_url_template}',
ADD_MEMBER: u'added {member_name} to {card_url_template}',
REMOVE_MEMBER: u'removed {member_name} from {card_url_template}',
ADD_ATTACHMENT: u'added [{attachment_name}]({attachment_url}) to {card_url_template}',
ADD_CHECKLIST: u'added the {checklist_name} checklist to {card_url_template}',
COMMENT: u'commented on {card_url_template}\n~~~ quote\n{text}\n~~~\n'
}
def prettify_date(date_string):
# type: (str) -> str
return date_string.replace('T', ' ').replace('.000', '').replace('Z', ' UTC')
def process_card_action(payload, action_type):
# type: (Mapping[str, Any], Text) -> Optional[Tuple[Text, Text]]
proper_action = get_proper_action(payload, action_type)
if proper_action is not None:
return get_subject(payload), get_body(payload, proper_action)
return None
def get_proper_action(payload, action_type):
# type: (Mapping[str, Any], Text) -> Optional[Text]
if action_type == 'updateCard':
data = get_action_data(payload)
old_data = data['old']
card_data = data['card']
if data.get('listBefore'):
return CHANGE_LIST
if old_data.get('name'):
return CHANGE_NAME
if old_data.get('desc') == "":
return SET_DESC
if old_data.get('desc'):
if card_data.get('desc') == "":
return REMOVE_DESC
else:
return CHANGE_DESC
if old_data.get('due', False) is None:
return SET_DUE_DATE
if old_data.get('due'):
if card_data.get('due', False) is None:
return REMOVE_DUE_DATE
else:
return CHANGE_DUE_DATE
if old_data.get('closed') is False and card_data.get('closed'):
return ARCHIVE
if old_data.get('closed') and card_data.get('closed') is False:
return REOPEN
# we don't support events for when a card is moved up or down
# within a single list
if old_data.get('pos'):
return None
raise UnknownUpdateCardAction()
return action_type
def get_subject(payload):
# type: (Mapping[str, Any]) -> Text
data = {
'board_name': get_action_data(payload)['board'].get('name')
}
return TRELLO_SUBJECT_TEMPLATE.format(**data)
def get_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
message_body = ACTIONS_TO_FILL_BODY_MAPPER[action_type](payload, action_type)
creator = payload['action']['memberCreator'].get('fullName')
return TRELLO_MESSAGE_TEMPLATE.format(full_name=creator, rest=message_body)
def get_added_checklist_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'checklist_name': get_action_data(payload)['checklist'].get('name'),
}
return fill_appropriate_message_content(payload, action_type, data)
def get_added_attachment_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'attachment_url': get_action_data(payload)['attachment'].get('url'),
'attachment_name': get_action_data(payload)['attachment'].get('name'),
}
return fill_appropriate_message_content(payload, action_type, data)
def get_updated_card_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'card_name': get_card_name(payload),
'old_list': get_action_data(payload)['listBefore'].get('name'),
'new_list': get_action_data(payload)['listAfter'].get('name'),
}
return fill_appropriate_message_content(payload, action_type, data)
def get_renamed_card_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'old_name': get_action_data(payload)['old'].get('name'),
'new_name': get_action_data(payload)['old'].get('name'),
}
return fill_appropriate_message_content(payload, action_type, data)
def get_added_label_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'color': get_action_data(payload).get('value'),
'text': get_action_data(payload).get('text'),
}
return fill_appropriate_message_content(payload, action_type, data)
def get_managed_member_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'member_name': payload['action']['member'].get('fullName')
}
return fill_appropriate_message_content(payload, action_type, data)
def get_comment_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'text': get_action_data(payload)['text'],
}
return fill_appropriate_message_content(payload, action_type, data)
def get_managed_due_date_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'due_date': prettify_date(get_action_data(payload)['card'].get('due'))
}
return fill_appropriate_message_content(payload, action_type, data)
def get_changed_due_date_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'due_date': prettify_date(get_action_data(payload)['card'].get('due')),
'old_due_date': prettify_date(get_action_data(payload)['old'].get('due'))
}
return fill_appropriate_message_content(payload, action_type, data)
def get_managed_desc_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'desc': prettify_date(get_action_data(payload)['card']['desc'])
}
return fill_appropriate_message_content(payload, action_type, data)
def get_changed_desc_body(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
data = {
'desc': prettify_date(get_action_data(payload)['card']['desc']),
'old_desc': prettify_date(get_action_data(payload)['old']['desc'])
}
return fill_appropriate_message_content(payload, action_type, data)
def get_body_by_action_type_without_data(payload, action_type):
# type: (Mapping[str, Any], Text) -> Text
return fill_appropriate_message_content(payload, action_type)
def fill_appropriate_message_content(payload, action_type, data=None):
# type: (Mapping[str, Any], Text, Optional[Dict[str, Any]]) -> Text
data = {} if data is None else data
data['card_url_template'] = data.get('card_url_template', get_filled_card_url_template(payload))
message_body = get_message_body(action_type)
return message_body.format(**data)
def get_filled_card_url_template(payload):
# type: (Mapping[str, Any]) -> Text
return TRELLO_CARD_URL_TEMPLATE.format(card_name=get_card_name(payload), card_url=get_card_url(payload))
def get_card_url(payload):
# type: (Mapping[str, Any]) -> Text
return u'https://trello.com/c/{}'.format(get_action_data(payload)['card'].get('shortLink'))
def get_message_body(action_type):
# type: (Text) -> Text
return ACTIONS_TO_MESSAGE_MAPPER[action_type]
def get_card_name(payload):
# type: (Mapping[str, Any]) -> Text
return get_action_data(payload)['card'].get('name')
def get_action_data(payload):
# type: (Mapping[str, Any]) -> Mapping[str, Any]
return payload['action'].get('data')
ACTIONS_TO_FILL_BODY_MAPPER = {
CREATE: get_body_by_action_type_without_data,
CHANGE_LIST: get_updated_card_body,
CHANGE_NAME: get_renamed_card_body,
SET_DESC: get_managed_desc_body,
CHANGE_DESC: get_changed_desc_body,
REMOVE_DESC: get_body_by_action_type_without_data,
ARCHIVE: get_body_by_action_type_without_data,
REOPEN: get_body_by_action_type_without_data,
SET_DUE_DATE: get_managed_due_date_body,
CHANGE_DUE_DATE: get_changed_due_date_body,
REMOVE_DUE_DATE: get_body_by_action_type_without_data,
ADD_LABEL: get_added_label_body,
REMOVE_LABEL: get_added_label_body,
ADD_MEMBER: get_managed_member_body,
REMOVE_MEMBER: get_managed_member_body,
ADD_ATTACHMENT: get_added_attachment_body,
ADD_CHECKLIST: get_added_checklist_body,
COMMENT: get_comment_body,
}