def send_expiration_sms_reminder(sp_subscription_pk): try: sp_subscription = SystemPaySubscription.objects.select_related( "subscription__person", "alias" ).get(pk=sp_subscription_pk) except SystemPaySubscription.DoesNotExist: return recipient = sp_subscription.subscription.person if ( not recipient.contact_phone or not is_french_number(recipient.contact_phone) or not is_mobile_number(recipient.contact_phone) ): return connection_params = generate_token_params(recipient) url = shorten_url( add_params_to_urls(front_url("view_payments"), connection_params), secret=True ) send_sms( f"Votre carte bleue arrive à expiration. Pour continuer votre don régulier à la France insoumise, " f"mettez là à jour : {url}\n" f"Merci encore de votre soutien !", recipient.contact_phone, )
def get_subscriber_data(self): data = super().get_subscriber_data() return { **data, "login_query": mark_safe(urlencode(generate_token_params(self))), "greeting": self.formule_adresse, "full_name": self.get_full_name(), "short_name": self.get_short_name(), "ancienne_region": self.ancienne_region, "region": self.region, "departement": self.departement, "city": self.location_city, "short_address": self.short_address, "short_location": self.short_location(), "full_address": self.html_full_address(), }
def send_emails(self, config, status): sending = status._active & ~status._email_envoye if self.verbosity >= 1 and sending.sum(): self.stdout.write(f"{sending.sum()} emails à envoyer.\n") persons = { str(p.id): p for p in Person.objects.filter(id__in=status.loc[sending, "id"]) } with open(config["email_html_file"]) as f: html_template = Template(f.read()) with open(config["email_text_file"]) as f: text_template = Template(f.read()) locale.setlocale(locale.LC_TIME, "fr_FR.UTF-8") with open(config["email_sent_file"], mode="a") as f: for g in grouper( tqdm(status.loc[sending].itertuples(), total=sending.sum(), disable=None), EMAILS_BY_CONNECTION, ): connection = get_connection() with connection: for i, row in enumerate(g): person = persons[row.id] context = Context({ "email": person.email, "login_query": urlencode(generate_token_params(person)), "limit_time": row.subscribe_limit.strftime("%A %d %B avant %Hh"), }) html_message = html_template.render(context) text_message = text_template.render(context) msg = EmailMultiAlternatives( subject=config["email_subject"], body=text_message, from_email=config.get( "email_from", "La France insoumise <*****@*****.**>", ), to=[person.email], connection=connection, ) msg.attach_alternative(html_message, "text/html") msg.send(fail_silently=False) f.write(f"{row.id}\n") f.flush()
def connection_params(self, obj): if obj.pk: return format_html( '{params} <a class="button" href="{invalidate_link}">Invalider les liens</a>', params=urlencode(generate_token_params(obj)), invalidate_link=reverse("admin:people_person_invalidate_link", args=(obj.pk, )), ) else: return "-"
def test_can_retrieve_person_information_with_login_link(self): self.client.logout() params = generate_token_params(self.person) url = add_query_params_to_url( reverse("api_people_retrieve"), { "id": str(self.person.id), "no_session": "o", **params }, ) res = self.client.get(url) self.assertEqual(res.status_code, status.HTTP_200_OK)
def data_from_person(person, tmp_tags=None): data = {} is_animateur = Q(is_referent=True) | Q(is_manager=True) inscriptions = [ ("evenements_yes" if person.events.upcoming().count() > 0 else "evenements_no"), ("groupe_yes" if person.supportgroups.active().count() > 0 else "groupe_no"), ("groupe_certifié_yes" if person.supportgroups.active().certified().count() > 0 else "groupe_certifié_no"), ("groupe_anim_yes" if person.memberships.active().filter(is_animateur).count() > 0 else "groupe_anim_no"), ("groupe_certifié_anim_yes" if person.memberships.active().filter( is_animateur & Q(supportgroup__subtypes__label__in=settings. CERTIFIED_GROUP_SUBTYPES)).count() > 0 else "groupe_certifié_anim_no"), ("country-{}".format( person.location_country if person.location_country else "FR")), ] data["FIRST_NAME"] = person.first_name data["LAST_NAME"] = person.last_name data["MERGE_GENDER"] = person.gender data["MERGE_ZIPCODE"] = (person.location_zip if (person.location_country == "FR" or not person.location_country) else str( person.location_country)) data["MERGE_INSCRIPTIONS"] = ",".join(inscriptions) data["MERGE_LOGIN_QUERY"] = urlencode(generate_token_params(person)) data["MERGE_TAGS"] = ( "," + ",".join(t.label for t in person.tags.filter(exported=True)) + ",") data["MERGE_REGION"] = " ".join([person.region, person.ancienne_region]) data["MERGE_ANNEE_DE_NAISSANCE"] = (person.date_of_birth.year if person.date_of_birth else None) if tmp_tags: data["MERGE_TAGS"] = "," + ",".join(tmp_tags) + data["MERGE_TAGS"] if len(data["MERGE_TAGS"]) > 255: warnings.warn("Tag string is longer than 255 characters for " + person.email) return data
def update_and_draw(self, config, do_it=False, **options): status = get_current_status(config) stats = get_stats(status, config) new_draws = status.id.isin([ id for name, g in status[status._available].groupby(["college"]) for id in g["id"].iloc[:stats.loc[name, "to_draw"]] ]) if new_draws.sum() == 0: return # obligé de passer par UTC sinon Pandas fait chier :( limit = (pd.Timestamp(timezone.now() + timezone.timedelta( hours=config["subscribe_period"])).astimezone( status.subscribe_limit.dt.tz).replace(minute=0, second=0, microsecond=0)) status.loc[new_draws, "subscribe_limit"] = limit if self.verbosity >= 1: drawn_counts = Counter(status.loc[new_draws, "college"]) print("Tirage :") for g, c in drawn_counts.items(): print(f"{g}: {c} personnes") if do_it: status[[c for c in status.columns if not c.startswith("_")]].to_csv(config["status_file"]) tag_current = PersonTag.objects.get( label=f"{config['tag_prefix']} - ouvert") active_persons = { str(p.id): p for p in Person.objects.filter(id__in=status.loc[status._active | new_draws, "id"]) } if do_it: with transaction.atomic(): # on retire la possibilité de s'inscrire aux précédents tag_current.people.remove(*tag_current.people.all()) # on ajoute les nouveaux aux deux tags tag_current.people.add(*status.loc[status._active | new_draws, "id"].map(active_persons)) with open(config["email_html_file"]) as f: html_template = Template(f.read()) with open(config["email_text_file"]) as f: text_template = Template(f.read()) connection = get_connection() locale.setlocale(locale.LC_TIME, "fr_FR.UTF-8") with connection: for i, row in enumerate(status.loc[new_draws].itertuples()): person = active_persons[row.id] context = Context({ "email": person.email, "login_query": urlencode(generate_token_params(person)), "limit_time": row.subscribe_limit.strftime("%A %d %B avant %Hh"), }) html_message = html_template.render(context) text_message = text_template.render(context) msg = EmailMultiAlternatives( subject=config["email_subject"], body=text_message, from_email=config.get( "email_from", "La France insoumise <*****@*****.**>", ), to=[person.email], connection=connection, ) msg.attach_alternative(html_message, "text/html") print(row.college, end="\n" if i % 80 == 79 else "", flush=True) if do_it: msg.send(fail_silently=False) print() # newline
def send_mosaico_email( code, subject, from_email, recipients, recipient_type="to", bindings=None, connection=None, backend=None, fail_silently=False, preferences_link=True, reply_to=None, attachments=None, ): """Send an email from a Mosaico template :param code: the code identifying the Mosaico template :param subject: the subject line of the email :param from_email: the address from which the email is to be sent :param recipients: a list of recipients to which the email will be send; alternatively, a single address :param bindings: a dictionary of replacements variables and their target values in the Mosaico template :param connection: an optional email server connection to use to send the emails :param backend: if no connection is given, an optional mail backend to use to send the emails :param fail_silently: whether any error should be raised, or just be ignored; by default it will raise :param gen_connection_params_function: a function that takes a recipient and generates connection params """ try: iter(recipients) except TypeError: recipients = [recipients] if recipient_type not in ["to", "cc", "bcc"]: raise ValueError("`recipient_type` must be to, cc or bcc") if bindings is None: bindings = {} if connection is None: connection = get_connection(backend, fail_silently) if preferences_link: bindings["preferences_link"] = bindings["PREFERENCES_LINK"] = front_url( "contact" ) bindings["unsubscribe_link"] = bindings["UNSUBSCRIBE_LINK"] = front_url( "unsubscribe" ) link_bindings = { key: value for key, value in bindings.items() if is_front_url(value) } html_template = loader.get_template(f"mail_templates/{code}.html") try: text_template = loader.get_template(f"mail_templates/{code}.txt") except TemplateDoesNotExist: text_template = None with connection: for recipient in recipients: # recipient can be either a Person or an email address if isinstance(recipient, Person): if recipient.role is not None and not recipient.role.is_active: continue connection_params = generate_token_params(recipient) for key, value in link_bindings.items(): if isinstance(value, AutoLoginUrl): bindings[key] = add_params_to_urls(value, connection_params) bindings["MERGE_LOGIN"] = urlencode(connection_params) context = get_context_from_bindings(code, recipient, bindings) html_message = html_template.render(context=context) text_message = ( text_template.render( context={k: conditional_html_to_text(v) for k, v in context.items()} ) if text_template else generate_plain_text(html_message) ) email = EmailMultiAlternatives( subject=subject, body=text_message, from_email=from_email, reply_to=reply_to, connection=connection, **{ recipient_type: [ recipient.email if isinstance(recipient, Person) else recipient ] }, ) email.attach_alternative(html_message, "text/html") if attachments is not None: for attachment in attachments: if isinstance(attachment, MIMEBase): email.attach(attachment) elif isinstance(attachment, dict): email.attach(**attachment) else: email.attach(*attachment) email.send(fail_silently=fail_silently)
def get_subscriber_data(self): data = super().get_subscriber_data() return {**data, "login_query": urlencode(generate_token_params(self))}