mypy: Add annotations to api/integrations/asana/zulip_asana_mirror.
This commit is contained in:
parent
8d603a4489
commit
082fbf631f
|
@ -33,18 +33,20 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import base64
|
import base64
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import List, Dict, Optional, Any, Tuple
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
from six.moves.urllib import request as urllib_request
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import dateutil.tz
|
from dateutil.tz import gettz
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
print(e, file=sys.stderr)
|
print(e, file=sys.stderr)
|
||||||
print("Please install the python-dateutil package.", file=sys.stderr)
|
print("Please install the python-dateutil package.", file=sys.stderr)
|
||||||
|
@ -67,20 +69,22 @@ client = zulip.Client(email=config.ZULIP_USER, api_key=config.ZULIP_API_KEY,
|
||||||
site=config.ZULIP_SITE, client="ZulipAsana/" + VERSION)
|
site=config.ZULIP_SITE, client="ZulipAsana/" + VERSION)
|
||||||
|
|
||||||
def fetch_from_asana(path):
|
def fetch_from_asana(path):
|
||||||
|
# type: (str) -> Optional[Dict[str, Any]]
|
||||||
"""
|
"""
|
||||||
Request a resource through the Asana API, authenticating using
|
Request a resource through the Asana API, authenticating using
|
||||||
HTTP basic auth.
|
HTTP basic auth.
|
||||||
"""
|
"""
|
||||||
auth = base64.encodestring('%s:' % (config.ASANA_API_KEY,))
|
auth = base64.encodestring(b'%s:' % (config.ASANA_API_KEY,))
|
||||||
headers = {"Authorization": "Basic %s" % auth}
|
headers = {"Authorization": "Basic %s" % auth}
|
||||||
|
|
||||||
url = "https://app.asana.com/api/1.0" + path
|
url = "https://app.asana.com/api/1.0" + path
|
||||||
request = urllib.request.Request(url, None, headers)
|
request = urllib_request.Request(url, None, headers) # type: ignore
|
||||||
result = urllib.request.urlopen(request)
|
result = urllib_request.urlopen(request) # type: ignore
|
||||||
|
|
||||||
return json.load(result)
|
return json.load(result)
|
||||||
|
|
||||||
def send_zulip(topic, content):
|
def send_zulip(topic, content):
|
||||||
|
# type: (str, str) -> Dict[str, str]
|
||||||
"""
|
"""
|
||||||
Send a message to Zulip using the configured stream and bot credentials.
|
Send a message to Zulip using the configured stream and bot credentials.
|
||||||
"""
|
"""
|
||||||
|
@ -93,11 +97,12 @@ def send_zulip(topic, content):
|
||||||
return client.send_message(message)
|
return client.send_message(message)
|
||||||
|
|
||||||
def datestring_to_datetime(datestring):
|
def datestring_to_datetime(datestring):
|
||||||
|
# type: (str) -> datetime
|
||||||
"""
|
"""
|
||||||
Given an ISO 8601 datestring, return the corresponding datetime object.
|
Given an ISO 8601 datestring, return the corresponding datetime object.
|
||||||
"""
|
"""
|
||||||
return dateutil.parser.parse(datestring).replace(
|
return dateutil.parser.parse(datestring).replace(
|
||||||
tzinfo=dateutil.tz.gettz('Z'))
|
tzinfo=gettz('Z'))
|
||||||
|
|
||||||
class TaskDict(dict):
|
class TaskDict(dict):
|
||||||
"""
|
"""
|
||||||
|
@ -105,9 +110,11 @@ class TaskDict(dict):
|
||||||
object where each of the keys is an attribute for easy access.
|
object where each of the keys is an attribute for easy access.
|
||||||
"""
|
"""
|
||||||
def __getattr__(self, field):
|
def __getattr__(self, field):
|
||||||
|
# type: (TaskDict, str) -> Any
|
||||||
return self.get(field)
|
return self.get(field)
|
||||||
|
|
||||||
def format_topic(task, projects):
|
def format_topic(task, projects):
|
||||||
|
# type: (TaskDict, Dict[str, str]) -> str
|
||||||
"""
|
"""
|
||||||
Return a string that will be the Zulip message topic for this task.
|
Return a string that will be the Zulip message topic for this task.
|
||||||
"""
|
"""
|
||||||
|
@ -117,6 +124,7 @@ def format_topic(task, projects):
|
||||||
return "%s: %s" % (project_name, task.name)
|
return "%s: %s" % (project_name, task.name)
|
||||||
|
|
||||||
def format_assignee(task, users):
|
def format_assignee(task, users):
|
||||||
|
# type: (TaskDict, Dict[str, str]) -> str
|
||||||
"""
|
"""
|
||||||
Return a string describing the task's assignee.
|
Return a string describing the task's assignee.
|
||||||
"""
|
"""
|
||||||
|
@ -130,6 +138,7 @@ def format_assignee(task, users):
|
||||||
return assignee_info
|
return assignee_info
|
||||||
|
|
||||||
def format_due_date(task):
|
def format_due_date(task):
|
||||||
|
# type: (TaskDict) -> str
|
||||||
"""
|
"""
|
||||||
Return a string describing the task's due date.
|
Return a string describing the task's due date.
|
||||||
"""
|
"""
|
||||||
|
@ -140,6 +149,7 @@ def format_due_date(task):
|
||||||
return due_date_info
|
return due_date_info
|
||||||
|
|
||||||
def format_task_creation_event(task, projects, users):
|
def format_task_creation_event(task, projects, users):
|
||||||
|
# type: (TaskDict, Dict[str, str], Dict[str, str]) -> Tuple[str, str]
|
||||||
"""
|
"""
|
||||||
Format the topic and content for a newly-created task.
|
Format the topic and content for a newly-created task.
|
||||||
"""
|
"""
|
||||||
|
@ -159,6 +169,7 @@ def format_task_creation_event(task, projects, users):
|
||||||
return topic, content
|
return topic, content
|
||||||
|
|
||||||
def format_task_completion_event(task, projects, users):
|
def format_task_completion_event(task, projects, users):
|
||||||
|
# type: (TaskDict, Dict[str, str], Dict[str, str]) -> Tuple[str, str]
|
||||||
"""
|
"""
|
||||||
Format the topic and content for a completed task.
|
Format the topic and content for a completed task.
|
||||||
"""
|
"""
|
||||||
|
@ -174,12 +185,14 @@ def format_task_completion_event(task, projects, users):
|
||||||
return topic, content
|
return topic, content
|
||||||
|
|
||||||
def since():
|
def since():
|
||||||
|
# type: () -> datetime
|
||||||
"""
|
"""
|
||||||
Return a newness threshold for task events to be processed.
|
Return a newness threshold for task events to be processed.
|
||||||
"""
|
"""
|
||||||
# If we have a record of the last event processed and it is recent, use it,
|
# If we have a record of the last event processed and it is recent, use it,
|
||||||
# else process everything from ASANA_INITIAL_HISTORY_HOURS ago.
|
# else process everything from ASANA_INITIAL_HISTORY_HOURS ago.
|
||||||
def default_since():
|
def default_since():
|
||||||
|
# type: () -> datetime
|
||||||
return datetime.utcnow() - timedelta(
|
return datetime.utcnow() - timedelta(
|
||||||
hours=config.ASANA_INITIAL_HISTORY_HOURS)
|
hours=config.ASANA_INITIAL_HISTORY_HOURS)
|
||||||
|
|
||||||
|
@ -191,8 +204,7 @@ def since():
|
||||||
max_timestamp_processed = datetime.fromtimestamp(timestamp)
|
max_timestamp_processed = datetime.fromtimestamp(timestamp)
|
||||||
logging.info("Reading from resume file: " + datestring)
|
logging.info("Reading from resume file: " + datestring)
|
||||||
except (ValueError, IOError) as e:
|
except (ValueError, IOError) as e:
|
||||||
logging.warn("Could not open resume file: %s" % (
|
logging.warn("Could not open resume file: " + str(e))
|
||||||
e.message or e.strerror,))
|
|
||||||
max_timestamp_processed = default_since()
|
max_timestamp_processed = default_since()
|
||||||
else:
|
else:
|
||||||
logging.info("No resume file, processing an initial history.")
|
logging.info("No resume file, processing an initial history.")
|
||||||
|
@ -203,6 +215,7 @@ def since():
|
||||||
return max(max_timestamp_processed, default_since())
|
return max(max_timestamp_processed, default_since())
|
||||||
|
|
||||||
def process_new_events():
|
def process_new_events():
|
||||||
|
# type: () -> None
|
||||||
"""
|
"""
|
||||||
Forward new Asana task events to Zulip.
|
Forward new Asana task events to Zulip.
|
||||||
"""
|
"""
|
||||||
|
@ -268,7 +281,7 @@ def process_new_events():
|
||||||
# resolve.
|
# resolve.
|
||||||
if not result.get("result"):
|
if not result.get("result"):
|
||||||
logging.warn("Malformed result, exiting:")
|
logging.warn("Malformed result, exiting:")
|
||||||
logging.warn(result)
|
logging.warn(str(result))
|
||||||
return
|
return
|
||||||
|
|
||||||
if result["result"] != "success":
|
if result["result"] != "success":
|
||||||
|
|
Loading…
Reference in a new issue