mypy: Add annotations to api/integrations/asana/zulip_asana_mirror.

This commit is contained in:
AZtheAsian 2017-01-04 18:29:50 -07:00 committed by Tim Abbott
parent 8d603a4489
commit 082fbf631f

View file

@ -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":