-
Notifications
You must be signed in to change notification settings - Fork 21
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
Set shiftremindermail to optional #559
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few comments.
Assuming that newly create TapirUsers get the optional-but-on-by-default mails correctly set (maybe write a test for that?), we could deploy and right after deploying open a shell on the server to enable the mail for all users manually. That would be easier than a data migration I think.
I haven't done data migration yet so if you feel like it, you could make one, then we'd have an example.
There is another problem: if we add a new mail later, that is optional and enabled by default, it wont be added to the field in TapirUser. We could manually add it each time, but it looks likely that we would forget.
What if, instead of saving which mails are enabled in an ArrayField, we create a separate model with user, mail_id and enabled columns. Then, in user_wants_to_or_has_to_receive_mail
, we first look if the corresponding user/mail pair exists and if it does, use that value. If it doesn't, use the default value from the mail class.
That would also eliminate the need for data migrations or manual work.
tapir/core/tapir_email_base.py
Outdated
@@ -17,9 +17,17 @@ | |||
|
|||
all_emails: Dict[str, Type[TapirEmailBase]] = {} | |||
|
|||
enabled_by_default_options = Literal[True, False, "both"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the problem with using None instead of "both"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think readability. For example, if I want both default and non-default values, None
would read like I don't get None of it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even as an optional parameter? It's quite standard at least in Python.
It would look like get_mails()
for all mails, get_mails(mandatory=True)
with just one parameter, get_mails(mandatory=False, enabled_by_default=True)
with both, isn't that fine?
Co-authored-by: Théophile MADET <[email protected]>
I agree with your idea of having a new model |
Moved the slack discussion here:
Any idea how to create the Form to ask with CheckboxSelectMultiple which mail people want to receive? My attempt so far would be
|
I think in the Future we need also the possibility for staff to decide which mails are optional and enabled-by-default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic looks good 👍
I think the style and naming can be improved in a few places, also there's a few tests we can add. I left comments.
mandatory_mails = forms.MultipleChoiceField( | ||
required=False, | ||
choices=get_mail_types(enabled_by_default="both", optional=False), | ||
label=_("Important Mails"), | ||
widget=CheckboxSelectMultiple(), | ||
initial=[ | ||
m[0] for m in get_mail_types(enabled_by_default="both", optional=False) | ||
], | ||
) | ||
|
||
def __init__(self, *args, **kwargs): | ||
tapir_user: TapirUser = kwargs.pop("tapir_user") | ||
super().__init__(*args, **kwargs) | ||
self.fields["mandatory_mails"].disabled = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the disabled=True
can be put directly in the field definition
mandatory_mails = forms.MultipleChoiceField( | |
required=False, | |
choices=get_mail_types(enabled_by_default="both", optional=False), | |
label=_("Important Mails"), | |
widget=CheckboxSelectMultiple(), | |
initial=[ | |
m[0] for m in get_mail_types(enabled_by_default="both", optional=False) | |
], | |
) | |
def __init__(self, *args, **kwargs): | |
tapir_user: TapirUser = kwargs.pop("tapir_user") | |
super().__init__(*args, **kwargs) | |
self.fields["mandatory_mails"].disabled = True | |
mandatory_mails = forms.MultipleChoiceField( | |
required=False, | |
choices=get_mail_types(enabled_by_default="both", optional=False), | |
label=_("Important Mails"), | |
widget=CheckboxSelectMultiple(), | |
initial=[ | |
m[0] for m in get_mail_types(enabled_by_default="both", optional=False) | |
], | |
disabled=True, | |
) | |
def __init__(self, *args, **kwargs): | |
tapir_user: TapirUser = kwargs.pop("tapir_user") | |
super().__init__(*args, **kwargs) |
def get_optional_mails_enabledbydefault(): | ||
return [m[0] for m in get_mail_types(optional=True, enabled_by_default=True)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unused
def get_optional_mails_enabledbydefault(): | |
return [m[0] for m in get_mail_types(optional=True, enabled_by_default=True)] |
@@ -239,6 +234,31 @@ def check_password(self, raw_password): | |||
return False | |||
return True | |||
|
|||
def optional_mails_by_user(self) -> List[str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd name this something like get_enabled_and_optional_mail_ids()
:
- It's not returning the mail class but the mail ID
- It doesn't return all optional mails but just the enabled ones
by_user
is not necessary since it's a method of user
class MailChoice(models.Model): | ||
name = models.CharField(max_length=256, blank=False, choices=get_optional_mails) | ||
choice = models.BooleanField() | ||
|
||
class Meta: | ||
constraints = [ | ||
models.UniqueConstraint( | ||
fields=["name", "choice"], name="mail-name-chosen-constraint" | ||
) | ||
] | ||
|
||
def __str__(self): | ||
return self.name | ||
|
||
|
||
class OptionalMails(models.Model): | ||
user = models.ForeignKey( | ||
"accounts.TapirUser", | ||
null=False, | ||
related_name="mail_setting", | ||
on_delete=models.CASCADE, | ||
) | ||
mail = models.ForeignKey( | ||
MailChoice, | ||
on_delete=models.CASCADE, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this not be a single model with mail_id
, user
, enabled
as field? I think that would be clearer.
Also, with a single model you could have mail_id, user
as unique contraint pair.
In the current solution, you could have a user linked to two MailChoice
that have opposite values for the same mail id.
|
||
def mails_not_mandatory(default: bool | None = True) -> List[Tuple[str, str]]: | ||
return [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an advantage to returning the id, name pair here? We could just return the list of classes directly and let the callers of get_mail_type
then decide what they what to do with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought tuples were necessary for choices, but have to check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a few more tests:
- A normal member can't edit the mail preferences of another member
- Member office can edit the mail preferences of another member
- Normal members can edit their own preferences
- Sending post data to the preference view with disabled mandatory mails doesn't actually disable the mails (if someone edited the HTML to enable the fields for example)
61f0682
to
868d8ef
Compare
This PR changes ShiftReminderMail to be optional and so resolves #302
The
MultipleChoiceField
keeps tuples, e.g.but the
ArrayField
only stores the keys, e.g.["1", "2"]
.default
atget_mails_mandatory
to a literal bećause I foundNone
not really helpful. Nowboth
represents all types of mails, default and non-default ones.Open: