Skip to content

Commit

Permalink
Merge pull request #26 from communitiesuk/FS-1510-email-application-c…
Browse files Browse the repository at this point in the history
…onfirmation-to-notification-bug-fix

validation update for key "answers":
  • Loading branch information
ramsharma-prog authored Sep 28, 2022
2 parents 2d51bbb + 16f8583 commit 6b82bf9
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 114 deletions.
112 changes: 75 additions & 37 deletions app/notification/application/map_contents.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
from __future__ import annotations

import collections
from dataclasses import dataclass
from datetime import datetime
from io import BytesIO
from io import StringIO
from typing import TYPE_CHECKING

from config import Config
from flask import current_app
from fsd_utils.config.notify_constants import NotifyConstants

if TYPE_CHECKING:
from app.notification.model.notification import Notification


@dataclass
class Application:
contact_info: str
questions: dict
questions: bytes
submission_date: str
fund_name: dict
fund_round: dict
application_id: dict
fund_name: str
round_name: str
reference: str

@property
def format_submission_date(self):
Expand All @@ -25,80 +32,111 @@ def format_submission_date(self):
).strftime("%Y-%m-%d")

@staticmethod
def from_json(data):
"""Function calls ApplicationData class to map
the application contents.
def from_notification(notification: Notification):
"""Function calls Application class to map
application contents.
Args:
data: Takes incoming json_data & converts into class object
from Notification.from_json
data: Takes an instance of Notification class.
Returns:
ApplicationData object with application contents.
Application object containing application contents.
"""
current_app.logger.info(f"Mapping contents for {data.template_type}")
application = data.content[NotifyConstants.APPLICATION_FIELD]
current_app.logger.info(
f"Mapping contents for {notification.template_type}"
)
application_data = notification.content[
NotifyConstants.APPLICATION_FIELD
]
return Application(
contact_info=data.contact_info,
questions=Application.bytes_object_for_questions_answers(data),
submission_date=application.get("date_submitted"),
fund_name=application.get("project_name"),
fund_round=application.get("round_id"),
application_id=application.get("id"),
contact_info=notification.contact_info,
questions=Application.bytes_object_for_questions_answers(
notification
),
submission_date=application_data.get("date_submitted"),
fund_name=Config.FUND_NAME,
round_name=application_data.get("round_name"),
reference=application_data.get("reference"),
)

@staticmethod
def get_forms(data) -> list:
forms = data.content[NotifyConstants.APPLICATION_FIELD][
def get_forms(notification: Notification) -> list:
forms = notification.content[NotifyConstants.APPLICATION_FIELD][
NotifyConstants.APPLICATION_FORMS_FIELD
]
return forms

@staticmethod
def get_form_names(data) -> list:
def get_form_names(notification: Notification) -> list:
form_names = []
forms = Application.get_forms(data)
forms = Application.get_forms(notification)
for form in forms:
form_names.append(form.get(NotifyConstants.APPLICATION_NAME_FIELD))
return list(dict.fromkeys(form_names))

@staticmethod
def get_questions_and_answers(data) -> dict:
question_answers = collections.defaultdict(dict)
forms = Application.get_forms(data)
form_names = Application.get_form_names(data)
def get_questions_and_answers(notification: Notification) -> dict:
"""function takes the form data and returns
dict of questions & answers.
"""
questions_answers = collections.defaultdict(dict)
forms = Application.get_forms(notification)
form_names = Application.get_form_names(notification)

for form_name in form_names:
for form in forms:
if form_name in form[NotifyConstants.APPLICATION_NAME_FIELD]:
for question in form[
NotifyConstants.APPLICATION_QUESTIONS_FIELD
]:
for fields in question["fields"]:
question_answers[form_name][
fields["title"]
] = fields.get("answer")
return question_answers
for field in question["fields"]:
answer = field.get("answer")
if field["type"] == "file":
# we check if the question type is "file"
# then we remove the aws
# key attached to the answer

if isinstance(answer, str):
questions_answers[form_name][
field["title"]
] = answer.split("/")[-1]
else:
questions_answers[form_name][
field["title"]
] = answer

else:
questions_answers[form_name][
field["title"]
] = answer
return questions_answers

@staticmethod
def format_questions_answers_with_stringIO(data) -> str:
"""Function formats the data for readability with StringIO."""
json_file = Application.get_questions_and_answers(data)
def format_questions_answers_with_stringIO(
notification: Notification,
) -> str:
"""Function formats dict of questions/answers
for readability with StringIO."""
json_file = Application.get_questions_and_answers(notification)
output = StringIO()

output.write(f"{Config.FUND_NAME}\n")
for form_name, values in json_file.items():
output.write(f"- {form_name}\n")
output.write(f"\n- {form_name}\n")
for questions, answers in values.items():
output.write(f" . {questions}: ")
output.write(f"{answers}\n")
return output.getvalue()

@staticmethod
def bytes_object_for_questions_answers(data) -> BytesIO:
def bytes_object_for_questions_answers(
notification: Notification,
) -> BytesIO:
"""Function creates a memory object for question & answers
with ByteIO from StringIO.
"""
stringIO_data = Application.format_questions_answers_with_stringIO(
data
notification
)
convert_to_bytes = bytes(stringIO_data, "utf-8")
bytes_object = BytesIO(convert_to_bytes)
Expand Down
53 changes: 29 additions & 24 deletions app/notification/magic_link/map_contents.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from flask import current_app
from fsd_utils.config.notify_constants import NotifyConstants

if TYPE_CHECKING:
from app.notification.model.notification import Notification


@dataclass
class MagicLink:
Expand All @@ -13,50 +19,49 @@ class MagicLink:
contact_help_email: str

@staticmethod
def from_json(data):
def from_notification(notification: Notification):
"""Function calls MagicLink class to map
the application contents.
application contents.
Args:
data: Takes incoming json_data & converts into class object
from Notification.from_json
data: Takes an instance of Notification class.
Returns:
MagicLink object with magic link contents.
MagicLink class object containing magic link contents.
"""
current_app.logger.info(f"Mapping contents for {data.template_type}")
current_app.logger.info(
f"Mapping contents for {notification.template_type}"
)

return MagicLink(
contact_info=data.contact_info,
fund_name=data.content.get(
contact_info=notification.contact_info,
fund_name=notification.content.get(
NotifyConstants.MAGIC_LINK_FUND_NAME_FIELD
),
magic_link=data.content.get(NotifyConstants.MAGIC_LINK_URL_FIELD),
request_new_link_url=data.content.get(
magic_link=notification.content.get(
NotifyConstants.MAGIC_LINK_URL_FIELD
),
request_new_link_url=notification.content.get(
NotifyConstants.MAGIC_LINK_REQUEST_NEW_LINK_URL_FIELD
),
contact_help_email=data.content.get(
contact_help_email=notification.content.get(
NotifyConstants.MAGIC_LINK_CONTACT_HELP_EMAIL_FIELD
),
)

@staticmethod
def process_data(data: dict) -> dict:
"""Function process the incoming json for magic link
such as if the fund name is not provided by the user then
by default function adds the fund name as "FUNDS"
def process_data(notification: Notification) -> dict:
"""Function checks if the fund_name exists in the notification
class object. If no, then adds the fund_name as "FUNDS"
Args:
data (dict): takes the json
Returns:
data(dict): returns processed json
data (dict): takes instance of Notification class
"""
data.content.update(
notification.content.update(
{
NotifyConstants.MAGIC_LINK_FUND_NAME_FIELD: data[
"content"
].get(NotifyConstants.MAGIC_LINK_FUND_NAME_FIELD, "Funds")
NotifyConstants.FIELD_FUND_NAME: notification.content.get(
NotifyConstants.FIELD_FUND_NAME, "Funds"
)
}
)
return data
return notification
18 changes: 9 additions & 9 deletions app/notification/model/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Notification:
def from_json(data: dict):
"""
Function will be called in relevant services to map
the json contents
json contents
"""
notification_data = Notification(
template_type=data.get("type"),
Expand All @@ -29,27 +29,27 @@ def from_json(data: dict):
def email_recipient(json_data: dict):
"""
Function matches with the correct template type &
calls the relevant function from process_data.py.
calls the relevant function.
"""
data = Notification.from_json(json_data)
notification = Notification.from_json(json_data)
match json_data.get("type"):
case NotifyConstants.TEMPLATE_TYPE_MAGIC_LINK:
current_app.logger.info(
f"Validating template type: {data.template_type}"
f"Validating template type: {notification.template_type}"
)
return Notifier.send_magic_link(data)
return Notifier.send_magic_link(notification)

case "APPLICATION_RECORD_OF_SUBMISSION":
current_app.logger.info(
f"Validating template type: {data.template_type})"
f"Validating template type: {notification.template_type})"
)
return Notifier.send_application(data)
return Notifier.send_application(notification)

case "NOTIFICATION" | "REMINDER" | "AWARD":
return f"Currently {data.template_type} service is not available." # noqa
return f"Currently {notification.template_type} service is not available." # noqa

case _:
current_app.logger.exception(
f"Incorrect template type {data.template_type}"
f"Incorrect template type {notification.template_type}"
)
return template_type_error(json_data)
Loading

0 comments on commit 6b82bf9

Please sign in to comment.