Parcourir la source

Add `import_mailling_list` command to bulk import a mailling list

Jocelyn Delalande il y a 6 ans
Parent
commit
eac24dbb41

+ 17 - 8
doc/admin/maillists.md

@@ -63,14 +63,8 @@ des inscriptions sont faites par d'autres moyens (mail, interface du serveur de
 liste de discussions… etc), ces modifications risquent d'être écrasées par la
 gestion d'abonnements de Coin.
 
-- La commande de synchro est lancée à chaque abonnement/désabonnement. Si vous
-  inscrivez 100 membres d'un coup, ça pourrait être un peu long si ça passe
-  par SSH. Astuce quand vous initialisez vos listes pour la première fois donc :
-
-    1. mettre une commande de synchro bidon,
-    2. faire toutes vos inscriptions
-    3. mettre la vraie commande de synchro
-    4. lancer une synchro manuelle de chaque liste
+- La commande de synchro est lancée à chaque abonnement/désabonnement. Il y a
+  un outil d'import « en masse » : [import_mailling_list](#méthode-b-importer-des-abonnements-en-masse).
 
 Mise en place
 -------------
@@ -118,8 +112,23 @@ envisageables en recourant à un petit script sur mesure.
 
 ### 3. Ajouter des listes
 
+Deux méthodes, selon que vous voulez initialiser la liste avec une vide ou
+pré-remplie avec une liste d'abonnés.
+
+#### Méthode A : créer une liste vide
 
 Se rendre dans l'admin de coin et dans la nouvelle catégorie « Listes mail »,
 renseigner les listes mail que l'on souhaite voir gérées par Coin.
 
+#### Méthode B : importer des abonnements « en masse »
+
+Pour créer une liste et faire un import initial de tou·te·s ses abonné·e·s d'un
+coup, vous pouvez utiliser la commande `./manage.py import_mailling_list` qui
+permet de créer une liste à partir de son adresse, son nom et d'un fichier
+texte contenant les adresses à abonner (qui doivent correspondre à des membres
+renseignés dans coin).
+
+Pour plus d'infos : `./manage.py import_mailling_list --help`
 
+*NB : Il vous faudra ensuite aller renseigner, via l'interface d'admin de coin,
+la description complète de la liste (celle que verront les membres).*

+ 1 - 0
maillists/management/__init__.py

@@ -0,0 +1 @@
+

+ 0 - 0
maillists/management/commands/__init__.py


+ 113 - 0
maillists/management/commands/import_mailling_list.py

@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import sys
+
+from django.core.management.base import BaseCommand, CommandError
+from django.db import transaction
+
+from coin.members.models import Member
+from maillists.models import (
+    MaillingList,
+    MaillingListSubscription,
+    skip_maillist_sync,
+)
+
+"""Import a text file of email addresses into mailling list subscription"
+
+Create a new mailling-list subscribing the provided addresses. The script will
+try to map email addresses to members, and stop if some addresses do not belong
+to any member.
+
+This command takes care to avoid triggering a sync per single subscription.
+"""
+
+
+class Command(BaseCommand):
+    help = __doc__
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'subscribers_file',
+            help="The text file with the subscribed email addresses, one per line",
+        )
+        parser.add_argument(
+            '--email',
+            help='Mail address of the list',
+            required=True,
+        )
+        parser.add_argument(
+            '--verbose-name',
+            help='The full human-targeted name of the list',
+            required=True,
+        )
+
+        parser.add_argument(
+            '--force',
+            help='Import email adresses skipping those who do not belong to any member',
+            action='store_true',
+            default=False
+        )
+
+        parser.add_argument(
+            '--dry-run',
+            help='Do not write anything to database, just parse the file and show unknown addresses',
+            action='store_true',
+            default=False
+        )
+
+    @staticmethod
+    def _iter_emails(filename):
+        with open(filename) as f:
+            for l in f.readlines():
+                yield l.strip()
+
+    @staticmethod
+    def _get_unknown_email(emails):
+        for email in emails:
+            try:
+                Member.objects.get(email=email)
+            except Member.DoesNotExist:
+                yield email
+
+    @transaction.atomic
+    def handle(self, subscribers_file, email, verbose_name, force, dry_run, *args, **kwargs):
+        ml = MaillingList.objects.create(
+            short_name=email.split('@')[0],
+            email=email,
+            description='À RENSEIGNER',
+            verbose_name=verbose_name,
+        )
+        unknown_emails = []
+        with skip_maillist_sync():
+            for email in self._iter_emails(subscribers_file):
+                try:
+                    member = Member.objects.get(email=email)
+                except Member.DoesNotExist:
+                    unknown_emails.append(email)
+                else:
+                    mls = MaillingListSubscription(
+                        member=member,
+                        maillinglist=ml,
+                    )
+                    mls.skip_sync = True
+                    mls.save()
+
+        # Do it once… (db will be rollback if it fails)
+        sys.stdout.write('Pousse la liste sur le serveur… ',)
+        ml.sync_to_list_server()
+        print('OK')
+
+        if (len(unknown_emails) > 0) and not force:
+            print('ERREUR : Ces adresses ne correspondent à aucun membre')
+            for email in unknown_emails:
+                print(email)
+
+            raise CommandError(
+                "Rien n'a été créé en base, utiliser --force au besoin.")
+
+        elif force or len(unknown_emails) == 0:
+            if dry_run:
+                # exception triggers rollback
+                raise CommandError(
+                    "--dry-run est utilisée, rien n'a été écrit en base")