Skip to content

Commit

Permalink
Merge pull request #296 from ropable/master
Browse files Browse the repository at this point in the history
Additional exception handling for email, TracPlus, DFES harvests
  • Loading branch information
ropable authored Sep 25, 2024
2 parents a3d73dc + 93039ea commit 0d29aaf
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 34 deletions.
2 changes: 1 addition & 1 deletion kustomize/overlays/prod/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ patches:
- path: service_patch.yaml
images:
- name: ghcr.io/dbca-wa/resource_tracking
newTag: 1.4.17
newTag: 1.4.18
18 changes: 9 additions & 9 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "resource_tracking"
version = "1.4.17"
version = "1.4.18"
description = "DBCA internal corporate application to download and serve data from remote tracking devices."
authors = ["DBCA OIM <[email protected]>"]
license = "Apache-2.0"
Expand All @@ -19,7 +19,7 @@ django-tastypie = "0.14.7"
django-geojson = "4.1.0"
unicodecsv = "0.14.1"
whitenoise = { version = "6.7.0", extras = ["brotli"] }
azure-storage-blob = "12.22.0"
azure-storage-blob = "12.23.0"
sentry-sdk = { version = "2.14.0", extras = ["django"] }

[tool.poetry.group.dev.dependencies]
Expand Down
49 changes: 37 additions & 12 deletions tracking/email_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ def get_imap(mailbox="INBOX"):
imap.login(settings.EMAIL_USER, settings.EMAIL_PASSWORD)
imap.select(mailbox)
return imap
except IMAP4.error:
LOGGER.warning("Unable to log into mailbox")
except IMAP4.error as err:
LOGGER.warning(f"Unable to log into mailbox: {err}")
return None


def email_get_unread(imap, from_email_address):
"""Returns (status, list of UIDs) of unread emails from a sending email address."""
search = '(UNSEEN UNFLAGGED FROM "{}")'.format(from_email_address)
status, response = imap.search(None, search)
try:
status, response = imap.search(None, search)
except IMAP4.abort as err:
LOGGER.warning(f"Unable to search unread emails: {err}")
return None

if status != "OK":
return status, response
# Return status and list of unread email UIDs.
Expand All @@ -34,7 +39,11 @@ def email_fetch(imap, uid):
Email is returned as an email.Message class object.
"""
message = None
status, response = imap.fetch(str(uid), "(BODY.PEEK[])")
try:
status, response = imap.fetch(str(uid), "(BODY.PEEK[])")
except IMAP4.abort as err:
LOGGER.warning(f"Unable to fetch email: {err}")
return None

if status != "OK":
return status, response
Expand All @@ -51,23 +60,39 @@ def email_fetch(imap, uid):

def email_mark_read(imap, uid):
"""Flag an email as 'Seen' based on passed-in UID."""
status, response = imap.store(str(uid), "+FLAGS", r"\Seen")
return status, response
try:
status, response = imap.store(str(uid), "+FLAGS", r"\Seen")
return status, response
except IMAP4.error as err:
LOGGER.warning(f"Unable to mark email read: {err}")
return None


def email_mark_unread(imap, uid):
"""Remove the 'Seen' flag from an email based on passed-in UID."""
status, response = imap.store(str(uid), "-FLAGS", r"\Seen")
return status, response
try:
status, response = imap.store(str(uid), "-FLAGS", r"\Seen")
return status, response
except IMAP4.error as err:
LOGGER.warning(f"Unable to mark email unread: {err}")
return None


def email_delete(imap, uid):
"""Flag an email for deletion."""
status, response = imap.store(str(uid), "+FLAGS", r"\Deleted")
return status, response
try:
status, response = imap.store(str(uid), "+FLAGS", r"\Deleted")
return status, response
except IMAP4.error as err:
LOGGER.warning(f"Unable to delete email: {err}")
return None


def email_flag(imap, uid):
"""Flag an email as unprocessable."""
status, response = imap.store(str(uid), "+FLAGS", r"\Flagged")
return status, response
try:
status, response = imap.store(str(uid), "+FLAGS", r"\Flagged")
return status, response
except IMAP4.error as err:
LOGGER.warning(f"Unable to flag email: {err}")
return None
36 changes: 26 additions & 10 deletions tracking/harvest.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import csv
from django.conf import settings
from django.utils import timezone
from imaplib import IMAP4
import logging
from imaplib import IMAP4

import requests
from django.conf import settings
from django.utils import timezone

from tracking import email_utils
from tracking.models import Device, LoggedPoint
from tracking.utils import (
validate_latitude_longitude,
parse_dfes_feature,
parse_dplus_payload,
parse_iriditrak_message,
parse_mp70_payload,
parse_spot_message,
parse_iriditrak_message,
parse_dplus_payload,
parse_tracplus_row,
parse_dfes_feature,
validate_latitude_longitude,
)

LOGGER = logging.getLogger("tracking")
Expand Down Expand Up @@ -334,8 +335,19 @@ def save_dfes_feed():
"""Download and process the DFES API endpoint (returns GeoJSON), create new devices, update existing."""
LOGGER.info("Querying DFES API")
resp = requests.get(url=settings.DFES_URL, auth=(settings.DFES_USER, settings.DFES_PASS))
resp.raise_for_status()
features = resp.json()["features"]

# Don't raise an exception on non-200 response.
if not resp.status_code == 200:
LOGGER.warning("DFES API response returned non-200 status")
return

# Parse the API response.
try:
features = resp.json()["features"]
except requests.models.JSONDecodeError:
LOGGER.warning("Error parsing DFES API response")
return

LOGGER.info(f"DFES API returned {len(features)} features, processing")

updated_device = 0
Expand Down Expand Up @@ -410,7 +422,11 @@ def save_dfes_feed():
def save_tracplus_feed():
"""Query the TracPlus API, create logged points per device, update existing devices."""
LOGGER.info("Harvesting TracPlus feed")
response = requests.get(settings.TRACPLUS_URL)
try:
response = requests.get(settings.TRACPLUS_URL)
except requests.ConnectTimeout:
LOGGER.warning("TracPlus API request timed out")
return

# The TracPlus API frequently throttles requests.
if response.status_code == 429:
Expand Down

0 comments on commit 0d29aaf

Please sign in to comment.