diff --git a/modoboa/admin/management/commands/_import.py b/modoboa/admin/management/commands/_import.py deleted file mode 100644 index 514bceb5d..000000000 --- a/modoboa/admin/management/commands/_import.py +++ /dev/null @@ -1,53 +0,0 @@ -import os.path -import sys -import csv -from progressbar import ProgressBar, Percentage, Bar, ETA - -from modoboa.core.models import User -from modoboa.lib import events -from modoboa.lib.exceptions import Conflict - -from ... import lib - - -def import_csv(filename, options): - """Import objects from a CSV file.""" - superadmin = User.objects.filter(is_superuser=True).first() - if not os.path.exists(filename): - print('File not found') - sys.exit(1) - - num_lines = sum(1 for line in open(filename)) - pbar = ProgressBar( - widgets=[Percentage(), Bar(), ETA()], maxval=num_lines - ).start() - with open(filename, 'r') as f: - reader = csv.reader(f, delimiter=';') - i = 0 - for row in reader: - i += 1 - pbar.update(i) - if not row: - continue - - try: - fct = getattr(lib, "import_%s" % row[0].strip()) - except AttributeError: - fct = events.raiseQueryEvent( - 'ImportObject', row[0].strip()) - if not fct: - continue - fct = fct[0] - - try: - fct(superadmin, row, options) - except Conflict: - if options['continue_if_exists']: - continue - - raise Conflict( - "Object already exists: %s" - % options['sepchar'].join(row[:2]) - ) - - pbar.finish() diff --git a/modoboa/admin/management/commands/export_domains.py b/modoboa/admin/management/commands/export_domains.py deleted file mode 100644 index 6af70c586..000000000 --- a/modoboa/admin/management/commands/export_domains.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys -import csv -from optparse import make_option - -from django.core.management.base import BaseCommand - -from modoboa.core.extensions import exts_pool -from ...models import Domain - - -class Command(BaseCommand): - help = 'Export domains and domain aliases to a csv' - - option_list = BaseCommand.option_list + ( - make_option( - '--sepchar', action='store_true', dest='sepchar', default=';' - ), - ) - - def handle(self, *args, **kwargs): - exts_pool.load_all() - csvwriter = csv.writer(sys.stdout, delimiter=kwargs['sepchar']) - for dom in Domain.objects.all(): - dom.to_csv(csvwriter) diff --git a/modoboa/admin/management/commands/export_identities.py b/modoboa/admin/management/commands/export_identities.py deleted file mode 100644 index 020e948c6..000000000 --- a/modoboa/admin/management/commands/export_identities.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -import csv -from optparse import make_option - -from django.core.management.base import BaseCommand - -from modoboa.core.models import User -from modoboa.core.extensions import exts_pool -from ...models import Alias - - -class Command(BaseCommand): - help = 'Export identities (mailbox and aliases) to a csv' - - option_list = BaseCommand.option_list + ( - make_option( - '--sepchar', action='store_true', dest='sepchar', default=';' - ), - ) - - def handle(self, *args, **kwargs): - exts_pool.load_all() - - csvwriter = csv.writer(sys.stdout, delimiter=kwargs['sepchar']) - for u in User.objects.all(): - u.to_csv(csvwriter) - - for a in Alias.objects.prefetch_related('aliasrecipient_set'): - a.to_csv(csvwriter) diff --git a/modoboa/admin/management/commands/import_domains.py b/modoboa/admin/management/commands/import_domains.py deleted file mode 100644 index 562a9b6f2..000000000 --- a/modoboa/admin/management/commands/import_domains.py +++ /dev/null @@ -1,27 +0,0 @@ -from optparse import make_option - -from django.core.management.base import BaseCommand - -from modoboa.core.extensions import exts_pool - -from ._import import import_csv - - -class Command(BaseCommand): - args = 'csvfile' - help = 'Import domains and domain aliases from a csv file' - - option_list = BaseCommand.option_list + ( - make_option( - '--sepchar', action='store_true', dest='sepchar', default=';' - ), - make_option( - '--continue-if-exists', action='store_true', - dest='continue_if_exists', default=True - ) - ) - - def handle(self, *args, **kwargs): - exts_pool.load_all() - for filename in args: - import_csv(filename, kwargs) diff --git a/modoboa/admin/management/commands/import_identities.py b/modoboa/admin/management/commands/import_identities.py deleted file mode 100644 index 4cce81e83..000000000 --- a/modoboa/admin/management/commands/import_identities.py +++ /dev/null @@ -1,30 +0,0 @@ -from optparse import make_option - -from django.core.management.base import BaseCommand - -from modoboa.core.extensions import exts_pool - -from ._import import import_csv - - -class Command(BaseCommand): - args = 'csvfile' - help = 'Import identities from a csv file' - - def add_arguments(self, parser): - """Add extra arguments to command.""" - parser.add_argument( - "--sepchar", type=str, default=";", - help="Separator used in file.") - parser.add_argument( - "--continue-if-exists", action="store_true", - dest='continue_if_exists', default=True, - help="Continue even if an entry already exists.") - parser.add_argument( - "--crypt-password", action="store_true", dest="crypt_password", - default=False, help="Encrypt provided passwords.") - - def handle(self, *args, **options): - exts_pool.load_all() - for filename in args: - import_csv(filename, options) diff --git a/modoboa/admin/management/commands/modo.py b/modoboa/admin/management/commands/modo.py new file mode 100644 index 000000000..36f4469ac --- /dev/null +++ b/modoboa/admin/management/commands/modo.py @@ -0,0 +1,20 @@ +"""Modoboa main management command.""" + +from subcommand.base import SubcommandCommand + +from .subcommands._export import ExportCommand +from .subcommands._import import ImportCommand + + +class Command(SubcommandCommand): + """Top management command for modoboa. + + $ python manage.py modo + """ + + help = "Modoboa top management command." + + subcommands = { + "export": ExportCommand, + "import": ImportCommand, + } diff --git a/modoboa/admin/management/commands/subcommands/__init__.py b/modoboa/admin/management/commands/subcommands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/admin/management/commands/subcommands/_export.py b/modoboa/admin/management/commands/subcommands/_export.py new file mode 100644 index 000000000..6cf4c6e91 --- /dev/null +++ b/modoboa/admin/management/commands/subcommands/_export.py @@ -0,0 +1,57 @@ +"""Django management command to export admin objects.""" + +import csv +import sys + +from django.core.management.base import BaseCommand + +from modoboa.core.models import User +from modoboa.core.extensions import exts_pool +from .... import models + + +class ExportCommand(BaseCommand): + """Command class.""" + + help = "Export domains or identities using CSV format" + + def add_arguments(self, parser): + """Add arguments to command.""" + parser.add_argument( + "--sepchar", type=str, dest="sepchar", default=";", + help="Separator used in generated file." + ) + parser.add_argument( + "objtype", type=str, choices=["domains", "identities"], + help="The type of object to export (domains or identities)") + + def export_domains(self): + """Export all domains.""" + for dom in models.Domain.objects.all(): + dom.to_csv(self.csvwriter) + + def export_identities(self): + """Export all identities.""" + for u in User.objects.all(): + u.to_csv(self.csvwriter) + dumped_aliases = [] + qset = ( + models.AliasRecipient.objects.filter(r_alias__isnull=False) + .distinct("r_alias") + .select_related("r_alias") + .prefetch_related("r_alias__aliasrecipient_set") + ) + for alr in qset: + alr.r_alias.to_csv(self.csvwriter) + dumped_aliases += [alr.r_alias.pk] + qset = ( + models.Alias.objects.exclude(pk__in=dumped_aliases) + .prefetch_related('aliasrecipient_set') + ) + for alias in qset: + alias.to_csv(self.csvwriter) + + def handle(self, *args, **options): + exts_pool.load_all() + self.csvwriter = csv.writer(sys.stdout, delimiter=options['sepchar']) + getattr(self, "export_{}".format(options["objtype"]))() diff --git a/modoboa/admin/management/commands/subcommands/_import.py b/modoboa/admin/management/commands/subcommands/_import.py new file mode 100644 index 000000000..83eecd522 --- /dev/null +++ b/modoboa/admin/management/commands/subcommands/_import.py @@ -0,0 +1,84 @@ +"""Django management command to import admin objects.""" + +import csv +import os + +import progressbar + +from django.core.management.base import BaseCommand, CommandError + +from modoboa.core import models as core_models +from modoboa.core.extensions import exts_pool +from modoboa.lib import events +from modoboa.lib.exceptions import Conflict + +from .... import lib + + +class ImportCommand(BaseCommand): + """Command class.""" + + args = "csvfile" + help = "Import identities from a csv file" + + def add_arguments(self, parser): + """Add extra arguments to command.""" + parser.add_argument( + "--sepchar", type=str, default=";", + help="Separator used in file.") + parser.add_argument( + "--continue-if-exists", action="store_true", + dest='continue_if_exists', default=False, + help="Continue even if an entry already exists.") + parser.add_argument( + "--crypt-password", action="store_true", + default=False, help="Encrypt provided passwords.") + parser.add_argument( + "files", type=str, nargs="+", help="CSV files to import.") + + def _import(self, filename, options): + """Import domains or identities.""" + superadmin = ( + core_models.User.objects.filter(is_superuser=True).first() + ) + if not os.path.exists(filename): + raise CommandError("File not found") + + num_lines = sum(1 for line in open(filename) if line) + pbar = progressbar.ProgressBar( + widgets=[ + progressbar.Percentage(), progressbar.Bar(), progressbar.ETA() + ], maxval=num_lines + ).start() + with open(filename) as f: + reader = csv.reader(f, delimiter=options["sepchar"]) + i = 0 + for row in reader: + if not row: + continue + try: + fct = getattr(lib, "import_%s" % row[0].strip()) + except AttributeError: + fct = events.raiseQueryEvent( + "ImportObject", row[0].strip()) + if not fct: + continue + fct = fct[0] + try: + fct(superadmin, row, options) + except Conflict: + if options["continue_if_exists"]: + continue + raise CommandError( + "Object already exists: {}".format( + options["sepchar"].join(row[:2]))) + i += 1 + pbar.update(i) + + pbar.finish() + + def handle(self, *args, **options): + """Command entry point.""" + exts_pool.load_all() + for filename in options["files"]: + self._import(filename, options) diff --git a/requirements.txt b/requirements.txt index cab74df2b..941583e64 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,5 @@ django-passwords==0.3.4 rfc6266 progressbar==2.3 djangorestframework==3.3.1 +django-subcommand2==0.1.1 +