newsletter script

This commit is contained in:
Fritz Grimpen 2024-12-10 18:45:25 +01:00
parent 3bc150c088
commit 983b970219
2 changed files with 79 additions and 7 deletions

View file

@ -0,0 +1,29 @@
Ultrakurzfassung:
Du zahlst aktuell {{ fee_old|format_currency(currency='EUR') }} Beitrag im Monat beim CCC Bremen. {% if direct_debit -%}
Wenn du nichts machst, erhöht sich dein Beitrag ab dem 01.01.2025 auf {{ 20|format_currency(currency='EUR') }} und wir ziehen das entsprechend ein.
{%- else -%}
Wenn du uns nichts anderes mitteilst, fordern wir ab dem 01.01.2025 einen Beitrag von {{ 20|format_currency(currency='EUR') }} und du müsstest deine Überweisungen / Daueraufträge dementsprechend anpassen.
{%- endif %} Du kannst Einfluss nehmen auf die Höhe deines Beitrags, den Zahlungsturnus sowie -modus: https://wiki.ccchb.de/wiki/Beitrag
Langfassung:
Moin {{ name }},
die Mitgliederversammlung des Chaos Computer Club Bremen e.V. hat am 19.11.2024 eine neue Beitragsordnung beschlossen, die am 01.01.2025 in Kraft treten wird. Im Folgenden erläutern wir kurz, warum das getan wurde und gehen anschließend darauf ein, was das für dich konkret bedeutet und welche Optionen du hast.
Wir haben aktuell durchschnittliche monatliche Einnahmen von circa {{ 770|format_currency(currency='EUR') }}; davon kommen {{ 250|format_currency(currency='EUR') }} vom überregionalen CCC e.V. und sollen für Projekte ausgegeben werden. Dem gegenüber stehen {{ 675|format_currency(currency='EUR') }} durchschnittliche monatliche Ausgaben. Wir geben also aktuell einen Teil der Projektmittel für z.B. Raummiete aus.
Um in Zukunft mindestens {{ 250|format_currency(currency='EUR') }} im Monat für Projekte ausgeben zu können, und auch um die Möglichkeit zu haben, mit Hilfe eines Mietkostenzuschusses des CCC e.V. in neue Räume umziehen zu können, ist die Beitragserhöhung notwendig.
Was bedeutet das für dich?
Du zahlst aktuell {{ fee_old|format_currency(currency='EUR') }} Beitrag im Monat beim CCC Bremen. Falls wir von dir nicht hören, erhöht sich dein Beitrag ab dem 01.01.2025 automatisch auf {{ 20|format_currency(currency='EUR') }} im Monat. {% if direct_debit -%}
Da du uns Lastschrifteinzug erlaubt hast, ziehen wir das automatisch passend ein und du musst nichts machen.
{%- else -%}
Du zahlst aktuell per Überweisung, also müsstest du möglicherweise deinen Dauerauftrag anpassen, oder den neuen Betrag bei deinen Überweisungen berücksichtigen.
{%- endif %}
Falls der Beitrag für dich zu hoch oder zu niedrig ist, oder du Zahlungsturnus und -modus ändern willst, folge bitte den Instruktionen auf https://wiki.ccchb.de/wiki/Beitrag
Viele Grüße,
Fritz Grimpen und Nora Bruns für den Vorstand des CCC Bremen e.V.

View file

@ -144,6 +144,34 @@ def sepa_mandate(env: Env):
mailfile.write(bytes(msg)) mailfile.write(bytes(msg))
csvwriter.writerow(mandate) csvwriter.writerow(mandate)
def newsletter(env: Env):
csvreader = csv.DictReader(env.args.csvfile, fieldnames=env.args.csv_fields)
csvwriter = csv.DictWriter(env.args.output, fieldnames=csvreader.fieldnames + ["message_id"])
csvwriter.writeheader()
template = env.jinja2_env.get_template(env.args.template)
for member in csvreader:
email_address = member.get('email')
if not email_address:
continue
msg = email.message.EmailMessage()
msg["From"] = env.args.email_from
msg["To"] = email_address
msg["Date"] = env.args.email_date or datetime.datetime.now()
msg["Subject"] = env.args.subject
msg["Message-Id"] = member.get("message_id") or email.utils.make_msgid(domain="verein.ccchb.de")
member['message_id'] = msg["Message-Id"]
template_args = {
"name": member.get("name"),
"email": email_address,
"direct_debit": member.get("sepa_mandate"),
"fee_old": decimal.Decimal(member.get("monthly_fee") or '0.0'),
"member": member,
}
msg.set_content(template.render(**template_args))
with (env.args.output_dir / member['message_id']).open("wb") as mailfile:
mailfile.write(bytes(msg))
csvwriter.writerow(member)
def sepa_mandate_build_pdf(env, sepa_mandate): def sepa_mandate_build_pdf(env, sepa_mandate):
with tempfile.TemporaryDirectory() as tempdir: with tempfile.TemporaryDirectory() as tempdir:
tempdir_path = pathlib.Path(tempdir) tempdir_path = pathlib.Path(tempdir)
@ -199,6 +227,7 @@ def main():
fee_invoice_parser.add_argument("-k", "--lookup-key", choices={'number', 'id'}, default='id', help='lookup key for invoice') fee_invoice_parser.add_argument("-k", "--lookup-key", choices={'number', 'id'}, default='id', help='lookup key for invoice')
fee_invoice_parser.add_argument("csvfile", type=argparse.FileType("r"), default="-", nargs="?") fee_invoice_parser.add_argument("csvfile", type=argparse.FileType("r"), default="-", nargs="?")
fee_invoice_parser.set_defaults(func=fee_invoice) fee_invoice_parser.set_defaults(func=fee_invoice)
fee_invoice_parser.set_defaults(requires_tryton=True)
sepa_mandate_parser = subparsers.add_parser('sepa-mandate') sepa_mandate_parser = subparsers.add_parser('sepa-mandate')
sepa_mandate_parser.add_argument('-o', '--output', type=argparse.FileType("w"), default='-') sepa_mandate_parser.add_argument('-o', '--output', type=argparse.FileType("w"), default='-')
@ -208,6 +237,16 @@ def main():
sepa_mandate_parser.add_argument('--latex-template', help='latex template in $TEXINPUTS') sepa_mandate_parser.add_argument('--latex-template', help='latex template in $TEXINPUTS')
sepa_mandate_parser.add_argument('csvfile', type=argparse.FileType('r'), default='-', nargs='?') sepa_mandate_parser.add_argument('csvfile', type=argparse.FileType('r'), default='-', nargs='?')
sepa_mandate_parser.set_defaults(func=sepa_mandate) sepa_mandate_parser.set_defaults(func=sepa_mandate)
sepa_mandate_parser.set_defaults(requires_tryton=True)
newsletter_parser = subparsers.add_parser('newsletter')
newsletter_parser.add_argument('-o', '--output', type=argparse.FileType("w"), default='-')
newsletter_parser.add_argument('-F', '--csv_fields', type=lambda f: f.split(','), help='csv fields')
newsletter_parser.add_argument("-s", "--subject", default="")
newsletter_parser.add_argument('template')
newsletter_parser.add_argument('csvfile', type=argparse.FileType('r'), default='-', nargs='?')
newsletter_parser.set_defaults(func=newsletter)
newsletter_parser.set_defaults(requires_tryton=False)
args = parser.parse_args() args = parser.parse_args()
@ -236,17 +275,21 @@ def main():
if "from" in env.config["email"]: if "from" in env.config["email"]:
env.args.email_from = env.config["email"]["from"] env.args.email_from = env.config["email"]["from"]
if not args.uri: if not args.uri and args.requires_tryton:
args.uri = input("URI for Tryton: ") args.uri = input("URI for Tryton: ")
if not args.username: if not args.username and args.requires_tryton:
args.username = input("Username for Tryton: ") args.username = input("Username for Tryton: ")
if not args.password: if not args.password and args.requires_tryton:
args.password = getpass.getpass(f"Password for Tryton user `{args.username}': ") args.password = getpass.getpass(f"Password for Tryton user `{args.username}': ")
if args.requires_tryton:
with triad.client.Session.start(args.uri, args.username, args.password) as session: with triad.client.Session.start(args.uri, args.username, args.password) as session:
env.session = session env.session = session
args.func(env) args.func(env)
else:
args.func(env)
main() if __name__ == '__main__':
main()