Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

22368 Emailer - Add letter attachment to stage 1 AR email #2865

Merged
merged 3 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions queue_services/entity-emailer/q_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@


affiliation_type: Final = 'bc.registry.affiliation'
dissolution_type: Final = 'bc.registry.dissolution'


async def run(loop, email_info): # pylint: disable=too-many-locals
Expand Down Expand Up @@ -88,7 +89,7 @@ def subscription_options():
functools.partial(signal_handler, sig_loop=loop, sig_nc=nc, task=close)
)

if email_info['type'] == affiliation_type:
if email_info['type'] in [affiliation_type, dissolution_type]:
payload = email_info
else:
payload = {'email': email_info}
Expand All @@ -104,11 +105,11 @@ def subscription_options():

if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], 'f:t:o:i:', ['fid=', 'etype=', 'option=', 'identifier='])
opts, args = getopt.getopt(sys.argv[1:], 'f:t:o:i:n:', ['fid=', 'etype=', 'option=', 'identifier=', 'name='])
except getopt.GetoptError:
print('q_cli.py -f <filing_id> -t <email_type> -o <option> -i <identifier>')
print('q_cli.py -f <filing_id> -t <email_type> -o <option> -i <identifier> -n <name>')
sys.exit(2)
fid, etype, option, identifier = None, None, None, None
fid, etype, option, identifier, name = None, None, None, None, None
for opt, arg in opts:
if opt in ('-f', '--fid'):
fid = arg
Expand All @@ -118,12 +119,16 @@ def subscription_options():
option = arg
elif opt in ('-i', '--identifier'):
identifier = arg
if not etype or (etype not in [affiliation_type] and not all([fid, etype, option])):
elif opt in ('-n', '--name'):
name = arg
if not etype or (etype not in [affiliation_type, dissolution_type] and not all([fid, etype, option])):
print('q_cli.py -f <filing_id> -t <email_type> -o <option> -i <identifier>')
sys.exit()
elif etype and etype in [affiliation_type] and not all([fid, etype]):
print('q_cli.py -f <filing_id> -t <email_type>')
sys.exit()
elif etype and etype in [dissolution_type] and not all([fid, etype, identifier, name]):
print('q_cli.py -f <furnishing_id> -t <email_type> -i <identifier> -n <furnishing_name>')

if etype in [affiliation_type]:
msg_id = str(uuid.uuid4())
Expand All @@ -139,6 +144,19 @@ def subscription_options():
'identifier': identifier,
'data': {'filing': {'header': {'filingId': fid}}},
}
elif etype in [dissolution_type]:
msg_id = str(uuid.uuid4())
time = datetime.utcfromtimestamp(time.time()).replace(tzinfo=timezone.utc).isoformat()
email_info = {
'specversion': '1.x-wip',
'type': etype,
'source': 'furnishingJob',
'id': msg_id,
'time': time,
'datacontenttype': 'application/json',
'identifier': identifier,
'data': {'furnishing': {'type':'INVOLUNTARY_DISSOLUTION', 'furnishingId': fid, 'furnishingName': name}},
}
else:
email_info = {'filingId': fid, 'type': etype, 'option': option, 'identifier': identifier}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from __future__ import annotations

from datetime import datetime
from http import HTTPStatus
from pathlib import Path
from typing import Tuple

Expand Down Expand Up @@ -196,7 +197,7 @@ def substitute_template_parts(template_code: str) -> str:
return template_code


def get_jurisdictions(identifier: str, token: str) -> str:
def get_jurisdictions(identifier: str, token: str) -> dict:
"""Get jurisdictions call."""
headers = {
'Accept': 'application/json',
Expand All @@ -206,5 +207,10 @@ def get_jurisdictions(identifier: str, token: str) -> str:
response = requests.get(
f'{current_app.config.get("LEGAL_API_URL")}/mras/{identifier}', headers=headers
)

return response
if response.status_code != HTTPStatus.OK:
return None
try:
return response.json()
except Exception: # noqa B902; pylint: disable=W0703;
current_app.logger.error('Failed to get MRAS response')
return None
argush3 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
"""Email processing rules and actions for involuntary_dissolution stage 1 overdue ARs notifications."""
from __future__ import annotations

import base64
from datetime import datetime
from http import HTTPStatus
from pathlib import Path

import requests
from entity_queue_common.service_utils import logger
from flask import current_app
from jinja2 import Template
from legal_api.models import Furnishing
from legal_api.models import Business, Furnishing

from entity_emailer.email_processors import get_entity_dashboard_url, get_jurisdictions, substitute_template_parts

Expand Down Expand Up @@ -63,6 +66,9 @@ def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-l
recipients = list(set(recipients))
recipients = ', '.join(filter(None, recipients)).strip()

# get attachments
pdfs = _get_pdfs(token, business, furnishing)

legal_name = business.legal_name
subject = f'Attention {business_identifier} - {legal_name}'

Expand All @@ -71,7 +77,8 @@ def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-l
'requestBy': '[email protected]',
'content': {
'subject': subject,
'body': f'{html_out}'
'body': f'{html_out}',
'attachments': pdfs
}
}

Expand All @@ -85,7 +92,7 @@ def get_extra_provincials(response: dict):
name = jurisdiction.get('name')
if name:
extra_provincials.append(name)

extra_provincials.sort()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add sorting so that the order presented in email will match the design.

return extra_provincials


Expand All @@ -99,3 +106,38 @@ def post_process(email_msg: dict, status: str):
if status == Furnishing.FurnishingStatus.FAILED:
furnishing.notes = 'Failure to send email'
furnishing.save()


def _get_pdfs(
token: str,
business: Business,
furnishing: Furnishing
) -> list:
"""Get the pdf for the involuntary dissolution stage 1."""
# get pdf for overdue ARs
if furnishing.furnishing_name not in \
[Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR,
Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR_XPRO]:
return []
headers = {
'Accept': 'application/pdf',
'Authorization': f'Bearer {token}'
}

furnishing_pdf = requests.get(
f'{current_app.config.get("LEGAL_API_URL")}/businesses/'
f'{business.identifier}/furnishings/{furnishing.id}/document',
headers=headers
)

if furnishing_pdf.status_code != HTTPStatus.OK:
logger.error('Failed to get pdf for furnishing: %s', furnishing.id)
return []

furnishing_pdf_encoded = base64.b64encode(furnishing_pdf.content)
return [{
'fileName': 'Notice of Commencement of Dissolution.pdf',
'fileBytes': furnishing_pdf_encoded.decode('utf-8'),
'fileUrl': '',
'attachOrder': '1'
}]
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Unit Tests for the involuntary_dissolution_stage_1_notification processor."""
from http import HTTPStatus
from unittest.mock import MagicMock, patch

import requests

from entity_emailer.email_processors import involuntary_dissolution_stage_1_notification
from tests.unit import create_business, create_furnishing # noqa: I003


def test_involuntary_dissolution_stage_1_notification(app, session, mocker):
def test_involuntary_dissolution_stage_1_notification(app, session):
"""Assert that the test_involuntary_dissolution_stage_1_notification can be processed."""
token = 'token'
message_id = '16fd2111-8baf-433b-82eb-8c7fada84ccc'
Expand All @@ -40,12 +45,19 @@ def test_involuntary_dissolution_stage_1_notification(app, session, mocker):
}
}

# test processor
mocker.patch(
'entity_emailer.email_processors.involuntary_dissolution_stage_1_notification.get_jurisdictions',
return_value=[])
email = involuntary_dissolution_stage_1_notification.process(message_payload, token)
mock_response = MagicMock()
mock_response.status_code = HTTPStatus.OK
mock_response.json.return_value = {}
with patch.object(
involuntary_dissolution_stage_1_notification, '_get_pdfs', return_value=[{'TEST': 'TEST'}]
) as mock_get_pdfs:
with patch.object(requests, 'get', return_value=mock_response):
email = involuntary_dissolution_stage_1_notification.process(message_payload, token)

assert email['content']['subject'] == f'Attention {business_identifier} - Test Business'
assert email['recipients'] == '[email protected]'
assert email['content']['body']
assert email['content']['subject'] == f'Attention {business_identifier} - Test Business'
assert email['recipients'] == '[email protected]'
assert email['content']['body']
assert email['content']['attachments']
assert mock_get_pdfs.call_args[0][0] == token
assert mock_get_pdfs.call_args[0][1] == business
assert mock_get_pdfs.call_args[0][2] == furnishing
4 changes: 4 additions & 0 deletions queue_services/entity-emailer/tests/unit/test_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ def test_involuntary_dissolution_stage_1_notification(app, db, session, mocker,
mocker.patch(
'entity_emailer.email_processors.involuntary_dissolution_stage_1_notification.get_jurisdictions',
return_value=[])
mocker.patch(
'entity_emailer.email_processors.involuntary_dissolution_stage_1_notification._get_pdfs',
return_value=[]
)

message_payload = {
'specversion': '1.x-wip',
Expand Down
Loading