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

Refactor portal serializer + Add email logic for updates on Content #291

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

vcai122
Copy link
Contributor

@vcai122 vcai122 commented Apr 25, 2024

  • refactor portal serializer to use abstract class + deal with special case for form data

  • set up email configs + email utils (wrapper around django email)

  • set up email notifs on content save/update

Copy link
Member

@judtinzhang judtinzhang left a comment

Choose a reason for hiding this comment

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

Looks good! Some comments and questions

EMAIL_PORT = os.environ.get("SMTP_PORT", 587)
EMAIL_HOST_USER = os.environ.get("SMTP_USERNAME", "")
EMAIL_HOST_PASSWORD = os.environ.get("SMTP_PASSWORD", "")
DEFAULT_FROM_EMAIL = os.environ.get("SMTP_FROM_EMAIL", EMAIL_HOST_USER)
Copy link
Member

Choose a reason for hiding this comment

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

When would DEFAULT_FROM_EMAIL not be EMAIL_HOST_USER?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

host user might be [email protected] and default from might be Penn Mobile <[email protected]>



def get_backend_manager_emails():
if group := Group.objects.filter(name="backend_managers").first():
Copy link
Member

Choose a reason for hiding this comment

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

nit: prefer moving backend_managers into a variable so it can be re-used for other purposes in the future

prev = self.__class__.objects.filter(id=self.id).first()
super().save(*args, **kwargs)
if prev is None:
return self._on_create()
Copy link
Member

Choose a reason for hiding this comment

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

nit: no need for return here since

Copy link
Contributor Author

Choose a reason for hiding this comment

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

wait yes need for return here because i want need to hard stop since prev is None.
it was originally
self._on_create() return

but i changed it to be fancy

Copy link
Member

Choose a reason for hiding this comment

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

Wait then why don't you just have an elif for the latter clause?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually yea i could. I did return because i thought i would have more conditions to send an email

Copy link
Member

Choose a reason for hiding this comment

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

yep up to you

validated_data["status"] = Content.STATUS_DRAFT

# auto add all target populations of a kind if not specified
auto_add_kind = [
Copy link
Member

Choose a reason for hiding this comment

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

Nice

return super().update(instance, validated_data)


class PollSerializer(ContentSerializer):
Copy link
Member

Choose a reason for hiding this comment

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

nit: Do you think its worth to override the PollSerializer to auto set multiselect=False? It is defaulted to False but just to be safe I'm not sure

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think its fine, because also this means we don't have to change when we add multiselect. And even if a malicious actor sent us multiselect=True, we need to first approve it and even if we approve it won't do anything cuz the frontend code doesn't use it right now

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good


return super().create(validated_data)

def update(self, instance, validated_data):
# if Poll is updated, then approve should be false
# if Content is updated, then approve should be false

Copy link
Member

Choose a reason for hiding this comment

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

For update, I think we also need the TargetPopulations logic you have above (ex. If user changes the target populations to empty then we need to add all of them)


def get_backend_manager_emails():
if group := Group.objects.filter(name="backend_managers").first():
return group.user_set.values_list("email", flat=True)
Copy link
Member

Choose a reason for hiding this comment

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

If a person doesn't have an email, then the return value with have a None in the list, which I'd imagine isn't ideal? Probably need to filter out the None's

subject=subject,
message=message,
from_email=None,
recipient_list=recipient_list if isinstance(recipient_list, list) else [recipient_list],
Copy link
Member

Choose a reason for hiding this comment

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

Not a huge fan of this line, especially since the name of the variable has "list" in it. Can we just enforce that recipient_list is always of type list?

success = django_send_mail(
subject=subject,
message=message,
from_email=None,
Copy link
Member

Choose a reason for hiding this comment

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

Any reason this is None?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if we set to None, it uses the default one specified in settings (base.py)


@shared_task(name="utils.send_mail")
def send_mail(subject, recipient_list, message=None, html_message=None):
if recipient_list is None:
Copy link
Member

Choose a reason for hiding this comment

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

When will this be the case? I feel like if this is ever None we shouldn't fail silently like this (the return value of this function is never checked)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did this because I thought this could just be "intended bevahior" for this function for sake of ease lol.

A case where we need this right now is if a post without a user attached onto it (so old posts) are updated, the recipient list would be None.

I guess the real question is whether this should be the intended behavior of this function. I have no preference. We can throw an error instead and do the None checking on the side of the caller of this function instead

Copy link
Member

Choose a reason for hiding this comment

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

I think I would prefer erroring here. Definitely will save a few minutes if someone needs to debug this code later on

Copy link

codecov bot commented May 7, 2024

Codecov Report

Attention: Patch coverage is 96.92308% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 89.65%. Comparing base (371c8e5) to head (7dfc292).
Report is 8 commits behind head on master.

Files Patch % Lines
backend/portal/models.py 89.47% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #291      +/-   ##
==========================================
+ Coverage   89.23%   89.65%   +0.42%     
==========================================
  Files          60       61       +1     
  Lines        2601     2581      -20     
==========================================
- Hits         2321     2314       -7     
+ Misses        280      267      -13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants