Repository navigation
Releases: Carlstain/django-outreach
Release list
v0.3.0
Added
OUTREACH_SEND_LIMIT_PER_HOURandOUTREACH_SEND_DELAY(bothNoneby default, Celery mode only). Over the limit, deliveries stay pending instead of failing, and the taskoutreach.release_deliveriesreleases them as the hourly window frees up.- The dashboard shows the hourly limit filling, the send preview says how many mails go now and how many wait, and the mailing page has a Waiting for the hourly limit tile.
- Migration
0011addsMailingDelivery.released_at.outreach_resume_deliveriesleaves waiting deliveries to the gate.
v0.2.1
Changed
- The warning logged for a failed delivery now ends with the numeric SMTP reply code (
smtp 554) when the error carries one. The reply text is still never logged or stored.
v0.2.0
Added
- The CSV import preview can add the rows to an audience, an existing one or a new name. Each new, updated or kept row has a checkbox, ticked by default, and a file of existing rows can still be confirmed to fill the audience. It needs
change_prospect.
v0.1.1
Changed
OUTREACH_RESEND_COOLDOWNnow defaults to 10 seconds instead of 24 hours, which still stops a double submit. A host that relied on the 24 hour window must setOUTREACH_RESEND_COOLDOWN = timedelta(hours=24). The help page and the dashboard show the window in seconds, minutes or hours.
Added
- The prospect list shows a Cooldown badge, and the prospect page shows when the prospect can be mailed again, while the resend cooldown applies.
v0.1.0
It includes everything since 0.1.0b6. django-outreach is at version 0.x: use it in production with caution, since a minor version before 1.0 may change the interface.
Added
- Mail language and Spanish and Portuguese: a Language field on the compose page and on templates picks the language of the footer, the unsubscribe page,
{% greeting %},{% today %}and{% member_since %}(staff text is never translated). A mailing keeps its language, the unsubscribe link carries?lang=when it differs from the site, and the new settingOUTREACH_MAIL_LANGUAGE(checkoutreach.E004) sets the compose default. Each staff member can pick the language of the screens in a header menu, stored in theoutreach_languagecookie. New catalogueses,ptandpt_BR(drafts, to be reviewed by a native speaker) next tofr. Migration0010addsMailing.languageandEmailTemplate.language. See the topic Languages. - Celery dispatch:
OUTREACH_DISPATCH = "celery"queues oneoutreach.deliver_mailtask per recipient, with retries on transient SMTP errors, instead of sending inside the admin request. The task uses the Celery app of the host project. New settingsOUTREACH_CELERY_MAX_RECIPIENTS_PER_SEND(default 5000),OUTREACH_CELERY_QUEUEandOUTREACH_DELIVERY_MAX_RETRIES(default 3), theceleryextra and theoutreach_resume_deliveriescommand. - Migration
0003: theMailingDeliverymodel, one row per recipient of a mailing. The Mailing admin shows how many are pending. - System checks
outreach.E002,outreach.E003andoutreach.W001. - Audiences: a named, static list of prospects (
Audience,Prospect.audiences, migration0005). Prospects are added and removed with two actions of the prospect list (the add page can also create the audience from a typed name), the list can be filtered by audience, and the Audiences pages and permissions (view,add,change,delete) are new. Sending to an audience uses the existing filter, Select all and Send a mail. Two event kinds record the changes in the history of a prospect. - Retry failed deliveries: the detail page of a mailing lists every recipient with its status (pending, sent or failed), and a failed one can be sent again with a Retry button or Retry all failed, which needs
outreach.change_prospect.ProspectMailer.retryclaims the prospect again and goes throughdeliver, so an unsubscribed prospect is still skipped, and the resend cooldown is not applied. No schema change. - Signals
prospect_subscribed,prospect_unsubscribed,mail_sentandmail_failed(moduleoutreach.signals), sent after commit and without breaking the sender when a receiver raises.ProspectSubscriptions.subscribecreates a prospect from a host signup form, with a required consent source kept in a new Subscribed event kind. The event kind changes the choices ofProspectEvent.kind(migration). - CSV import duplicate detection: the preview tells a new row, a row whose email exists, a row that repeats an earlier row of the file and a row that will update apart, with a count of each. An option, for staff with
change_prospect, updates existing prospects: a filled cell replaces the stored value, an empty cell or a missing column changes nothing, and the email, status, account link, last mailed date and audiences are never changed. An update adds an Edited event byimport. The existing emails are looked up in chunks instead of one query per row.ProspectCsvImporter.applyreturns anImportResult, and the new row statuses are Will update and Repeated in the file. No schema change. - Host variables: a project registers its own
{{ name | fallback }}variables with@mail_variable_library.register(label=..., example=...)in amail_variables.pymodule of an installed app, found at startup. They are computed only for the messages that use them,Noneor empty takes the fallback, a function that raises fails that recipient, and they appear in the editor menu, the help page and the email template preview. A tag can not take the name of a variable. See the topic Host variables. - Send a test to me on step 2 of a send: one copy of the mail goes to the address of the staff member, rendered for the prospect shown in the preview, with a
[Test]subject, a dead unsubscribe link and noList-Unsubscribeheaders. Nothing is written and no signal is sent.ProspectMailer.send_sample, and an optionalunsubscribe_urlforProspectMailer.render. - Scheduled send, Celery mode only: a Send at field on the compose page stores the mailing with
scheduleddeliveries and claims nobody. The taskoutreach.send_due_mailings(Celery beat) or the commandoutreach_send_due_mailings(cron) starts what is due, re-checks the cooldown and queues the deliveries. A mailing can be cancelled until it starts.MailingScheduleris the service. Migration0007addsMailing.scheduled_forand thescheduleddelivery status. - Answering requests about a person: a prospect page downloads everything stored about them as JSON (
view_prospect), and the action Erase and suppress the selected prospects (delete_prospect, with a confirmation page) deletes prospects with their history, deliveries and audience memberships and keeps only the suppression hash of the address. A newprospect_erasedsignal carries the address so the host can erase its own copies. No schema change.
Changed
-
A Celery retry reports "N queued, 0 skipped." like a send, instead of "N queued.". The delivery task raises the same
ImproperlyConfiguredmessage as the admin whenOUTREACH_BASE_URLis not set.ProspectMailer.from_settings(actor)builds the mailer from the setting, and the send and retry code shares one claim step and one cooldown rule. No other behaviour changes. -
A failed delivery keeps a failure kind (
MailingDelivery.failure_kind: refused, server or content) instead of the error text, which could hold the address of the recipient and the reply of the mail server. The prospect history, the dashboard, the mailing page and the data download show the kind. Theoutreachlogger records the delivery id, the kind and the exception class. The history of older mail failed events is blanked. Migrations0008and0009.TransientDeliveryErrorcarries the kind, andlast_erroris gone. -
The detail page of a mailing no longer shows the mail server's error for a failed recipient, which can contain the address. It shows a short message instead.
-
EmailTemplate.created_byis now an optional foreign key to the host user model (cleared when the account is deleted) instead of a username string, andupdated_byrecords who changed a template last. Migration0004matches the existing usernames to users throughUSERNAME_FIELD: a template whose author no longer matches an account ends up without an author.EmailTemplates.saveandcreatetake the user instead of a username. -
Prospect,MailingDeliveryandEmailTemplateshare the abstract modelsBaseModel(uuid,created_at,updated_at) andAuthoredModel(addscreated_by,updated_by). No schema change except the above. -
Synchronous sending stays the default but is not recommended in production:
outreach.W001reports it incheck --deploy. Its behaviour and theOUTREACH_MAX_RECIPIENTS_PER_SENDlimit (100) are unchanged. -
ProspectMailer.sendgoes through the newqueueanddeliversteps, andMailReporthas aqueuedlist, filled in Celery mode.
Notes
- Upgrading from
0.1.0b6: runmigrate. Migrations0002to0010are new.0004links template authors to users and drops the author of a template with no matching account, and0009blanks the details of old mail failed events. - The
es,ptandpt_BRcatalogues are drafts that no native speaker has reviewed yet. - Scheduled send needs Celery beat or cron to start a mailing, see the Celery topic.
v0.1.0b6
Changed
- The header logo of mails (
OUTREACH_EMAIL_LOGO_IN_HEADER) is centered horizontally. When no header text is set, the logo stands alone and the site name is no longer repeated as text under it (it stays the imagealt). A header text is shown under the logo, centered with it. Without a header logo nothing changes. New template context keyshas_header_logoandheader_logo_alt.
v0.1.0b5
Added
- Notices: a template or a mail not marked as newsletter is a notice to members. It has no unsubscribe footer and no
List-Unsubscribeheaders, goes to members only (new skip reasonnot_member) and still reaches members who unsubscribed from newsletters. A notice text cannot contain{{ unsubscribe_url }}. - Header text on templates and mailings, shown at the top of the mail instead of the site name when set.
OUTREACH_EMAIL_LOGO_IN_HEADERandOUTREACH_EMAIL_LOGO_IN_FOOTERputOUTREACH_LOGO_STATIC_PATHin the header and the footer of mails. The logo should be PNG or JPG: Gmail and Outlook do not show SVG.- Migration
0002:header_textandis_newsletteronEmailTemplateandMailing. Existing rows become newsletters. - Documentation, built with Sphinx and published on Read the Docs: installation, configuration, a user guide for the staff, topics for developers and a reference for the models, services, URLs, permissions and templates.
- Docstrings on the public classes and functions, which feed the reference.
Changed
- The CSV import is built into the app. It keeps the preview and confirm steps, the same columns, aliases, limits and rules (existing emails skipped, one invalid row blocks the file), and reports row errors by line number. A repeated email in one file is imported once.
- The CI runs 8 jobs instead of 25: the oldest and newest Python and Django on SQLite and PostgreSQL, the host variants in one job, and pushes to
mainplus pull requests only.
Removed
- The
django-import-exportandtablibdependencies. Only Django and nh3 remain. Spreadsheet formats (xlsx, json, tsv) are no longer read or written: the import and the export are CSV only. - The
IMPORT_EXPORT_ESCAPE_FORMULAE_ON_EXPORTsetting and the system checkoutreach.W001. The export now always escapes cells that start a spreadsheet formula. - The URL name
outreach_admin:outreach_prospect_export. The export is the admin action Export the selected prospects as CSV. import_exportis no longer needed inINSTALLED_APPS. Leaving it there does no harm.
v0.1.0b4
Added
manage.py outreach_load_userscreates a prospect for each active user account that has none, with--dry-runand--include-inactive. Prospects are created throughProspect.save(), so suppression and linking apply, and anaddedevent bysystemis recorded. Existing prospects are left untouched and the output is counts only. There is no signal: loading is always explicit.OUTREACH_USER_PROSPECT_FIELDSsetting: a dotted path to a callable(user) -> dictthat supplieskind,first_name,last_name,organization,cityorcountryfor the command.
v0.1.0b3
The first beta published on PyPI. The repository is public from this release.
Added
Prospect.avatar_tone, a number from 0 to 5 used for the avatar tint. It is a property, so there is no migration.
Changed
- New default look: a plum accent on warm neutrals, flat cards with a border and no shadow, and smaller corner radii. The default
OUTREACH_THEMEcolours, the stylesheet defaults and the email card change with it. A project that sets everyOUTREACH_THEMEkey is unaffected. - Each prospect avatar takes one of six tints, chosen from its initials. The dashboard tiles show a status dot and the recent activity shows the prospect avatar.
Security
- A
{% button %}placed inside a link or another tag is rejected by the compose form and refused when a mail is rendered. It could previously write an anchor inside an attribute after the final sanitising pass. - A button link may not contain a space, and an
httporhttpslink needs a host. - The suppression list honours
SECRET_KEY_FALLBACKS, like the unsubscribe links. A key rotation no longer lets a removed prospect that is imported again be mailed while the old key is listed. - An import file larger than
OUTREACH_MAX_IMPORT_BYTES(2 MB by default) is rejected before it is parsed.