diff --git a/packages/syft/src/syft/service/notification/email_templates.py b/packages/syft/src/syft/service/notification/email_templates.py index 5710991fdcc..1a2471833b0 100644 --- a/packages/syft/src/syft/service/notification/email_templates.py +++ b/packages/syft/src/syft/service/notification/email_templates.py @@ -10,6 +10,7 @@ if TYPE_CHECKING: # relative + from ..request.request import Request from .notifications import Notification @@ -662,8 +663,11 @@ def email_title(notification: "Notification", context: AuthedServiceContext) -> @staticmethod def email_body(notification: "Notification", context: AuthedServiceContext) -> str: + # relative + from ..request.request import RequestStatus + notification.linked_obj = cast(LinkedObject, notification.linked_obj) - request_obj = notification.linked_obj.resolve_with_context( + request_obj: Request = notification.linked_obj.resolve_with_context( context=context ).unwrap() badge_color = "red" if request_obj.status.name == "REJECTED" else "green" @@ -672,11 +676,26 @@ def email_body(notification: "Notification", context: AuthedServiceContext) -> s request_name = request_obj.requesting_user_name request_email = request_obj.requesting_user_email request_time = request_obj.request_time - request_status = request_obj.status.name # fails in l0 check right now + request_status = request_obj.status + request_status_name = request_status.name # fails in l0 check right now request_changes = ",".join( [change.__class__.__name__ for change in request_obj.changes] ) + deny_reason_html = "" + if request_status == RequestStatus.REJECTED: + deny_reason_or_err = request_obj.get_deny_reason(context=context) + if deny_reason_or_err.is_err(): + deny_reason = None + deny_reason = deny_reason_or_err.unwrap() + + if not isinstance(deny_reason, str) or not len(deny_reason): + deny_reason = ( + "No reason provided, please contact the admin for more information." + ) + + deny_reason_html = f"
Deny Reason: {deny_reason}
" + head = """Date: {request_time}
Status:
Changes:
{request_changes}
diff --git a/packages/syft/src/syft/service/request/request.py b/packages/syft/src/syft/service/request/request.py
index c898c73fde0..7c5495a756e 100644
--- a/packages/syft/src/syft/service/request/request.py
+++ b/packages/syft/src/syft/service/request/request.py
@@ -455,7 +455,28 @@ def html_description(self) -> str:
return desc
+ @property
+ def deny_reason(self) -> str | SyftError:
+ code = self.code
+ if isinstance(code, SyftError):
+ return code
+
+ code_status: UserCodeStatusCollection = code.status_link.resolve
+ return code_status.first_denial_reason
+
+ @as_result(SyftException)
+ def get_deny_reason(self, context: AuthedServiceContext) -> str | None:
+ code = self.get_user_code(context).unwrap()
+ if code is None:
+ return None
+
+ code_status = code.get_status(context).unwrap()
+ return code_status.first_denial_reason
+
def _coll_repr_(self) -> dict[str, str | dict[str, str]]:
+ # relative
+ from ...util.notebook_ui.components.sync import Badge
+
if self.status == RequestStatus.APPROVED:
badge_color = "badge-green"
elif self.status == RequestStatus.PENDING:
@@ -463,7 +484,18 @@ def _coll_repr_(self) -> dict[str, str | dict[str, str]]:
else:
badge_color = "badge-red"
- status_badge = {"value": self.status.name.capitalize(), "type": badge_color}
+ status_badge = Badge(
+ value=self.status.name.capitalize(),
+ badge_class=badge_color,
+ ).to_html()
+
+ if self.status == RequestStatus.REJECTED:
+ deny_reason = self.deny_reason
+ if isinstance(deny_reason, str) and len(deny_reason) > 0:
+ status_badge += (
+ "
"
+ f"Deny Reason: {deny_reason}"
+ )
user_data = [
self.requesting_user_name,
@@ -505,10 +537,11 @@ def codes(self) -> Any:
message="This type of request does not have code associated with it."
)
+ @as_result(SyftException)
def get_user_code(self, context: AuthedServiceContext) -> UserCode | None:
for change in self.changes:
if isinstance(change, UserCodeStatusChange):
- return change.get_user_code(context)
+ return change.get_user_code(context).unwrap()
return None
@property
@@ -641,7 +674,7 @@ def is_l0_deployment(self) -> bool:
return bool(self.code) and self.code.is_l0_deployment
def get_is_l0_deployment(self, context: AuthedServiceContext) -> bool:
- code = self.get_user_code(context)
+ code = self.get_user_code(context).unwrap()
if code:
return code.is_l0_deployment
else:
@@ -1355,6 +1388,7 @@ def code(self) -> UserCode:
return self.linked_user_code._resolve_cache
return self.linked_user_code.resolve
+ @as_result(SyftException)
def get_user_code(self, context: AuthedServiceContext) -> UserCode:
return self.linked_user_code.resolve_with_context(context).unwrap()