fixup! 🧑💻(admin) export domain contact info
This commit is contained in:
@@ -9,6 +9,7 @@ and this project adheres to
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- 🧑💻(admin) export domain contact info #1061
|
||||
|
||||
## [1.24.0] - 2026-03-24
|
||||
|
||||
@@ -14,6 +14,34 @@ from requests import exceptions
|
||||
from mailbox_manager import enums, models
|
||||
from mailbox_manager.utils.dimail import DimailAPIClient
|
||||
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
from import_export import resources
|
||||
from import_export.fields import Field
|
||||
from import_export.admin import ExportActionMixin
|
||||
|
||||
|
||||
class DomainResource(resources.ModelResource):
|
||||
""""""
|
||||
owners = Field()
|
||||
admins = Field()
|
||||
|
||||
class Meta:
|
||||
model = models.MailDomain
|
||||
fields = ["name", "slug", "status", "support_email", "owners", "admins"]
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().prefetch_related("accesses__user")
|
||||
|
||||
# def filter_export(self, queryset, **kwargs):
|
||||
# return self.get_queryset()
|
||||
|
||||
def dehydrate_owners(self, domain):
|
||||
return [access.user.email for access in domain.accesses.all() if access.role=="owner"]
|
||||
|
||||
def dehydrate_admins(self, domain):
|
||||
return [access.user.email for access in domain.accesses.all() if access.role=="administrator"]
|
||||
|
||||
|
||||
|
||||
class UserMailDomainAccessInline(admin.TabularInline):
|
||||
"""Inline admin class for mail domain accesses."""
|
||||
@@ -24,9 +52,9 @@ class UserMailDomainAccessInline(admin.TabularInline):
|
||||
|
||||
|
||||
@admin.register(models.MailDomain)
|
||||
class MailDomainAdmin(admin.ModelAdmin):
|
||||
class MailDomainAdmin(ExportActionMixin, admin.ModelAdmin):
|
||||
"""Mail domain admin interface declaration."""
|
||||
|
||||
resource_class = DomainResource
|
||||
list_display = (
|
||||
"name",
|
||||
"organization",
|
||||
@@ -49,9 +77,9 @@ class MailDomainAdmin(admin.ModelAdmin):
|
||||
)
|
||||
autocomplete_fields = ["organization"]
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""Restrict results to the current user's domain and prefetch accesses."""
|
||||
return super().get_queryset(request).prefetch_related("accesses__user")
|
||||
# def get_queryset(self, request):
|
||||
# """Restrict results to the current user's domain and prefetch accesses."""
|
||||
# return super().get_queryset(request).prefetch_related("accesses__user")
|
||||
|
||||
@admin.action(description=_("Import emails from dimail"))
|
||||
def sync_mailboxes_from_dimail(self, request, queryset): # pylint: disable=unused-argument
|
||||
@@ -255,28 +283,6 @@ class MailDomainAdmin(admin.ModelAdmin):
|
||||
% {"domains": ", ".join(excluded_domains)},
|
||||
)
|
||||
|
||||
@admin.action(description=_("Export a list of select domains support email"))
|
||||
def export_domains_contact_list(self, request, queryset): # pylint: disable=unused-argument
|
||||
"""Admin action to export."""
|
||||
response = HttpResponse(
|
||||
content_type="text/csv",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="people_domains_infos-{date.today()}.csv"'
|
||||
},
|
||||
)
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(["name", "status", "support_email", "admins_emails"])
|
||||
for domain in queryset:
|
||||
import pdb; pdb.set_trace()
|
||||
','.join([access.user.email if access.role != "viewer" else None for access in domain.accesses.all()])
|
||||
writer.writerow(
|
||||
[domain.name,
|
||||
domain.status,
|
||||
domain.support_email]
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@admin.register(models.Mailbox)
|
||||
class MailboxAdmin(UserAdmin):
|
||||
"""Admin for mailbox model."""
|
||||
|
||||
@@ -13,6 +13,7 @@ from freezegun import freeze_time
|
||||
from rest_framework import status
|
||||
|
||||
from core import factories as core_factories
|
||||
from mailbox_manager.admin import DomainResource
|
||||
|
||||
from mailbox_manager import enums, factories, models
|
||||
|
||||
@@ -277,32 +278,38 @@ def test_export_domains_infos(client, django_assert_num_queries):
|
||||
admin = core_factories.UserFactory(is_staff=True, is_superuser=True)
|
||||
client.force_login(admin)
|
||||
|
||||
domains = factories.MailDomainFactory.create_batch(
|
||||
10, status=enums.MailDomainStatusChoices.ENABLED
|
||||
)
|
||||
factories.MailDomainAccessFactory(domain=domains[0], role="owner")
|
||||
factories.MailDomainAccessFactory(domain=domains[0], role="administrator")
|
||||
factories.MailDomainAccessFactory(domain=domains[1], role="owner")
|
||||
factories.MailDomainAccessFactory(domain=domains[3], role="owner")
|
||||
factories.MailDomainAccessFactory.create_batch(3, domain=domains[6], role="owner")
|
||||
|
||||
data = {
|
||||
"action": "export_domains_contact_list",
|
||||
"_selected_action": [domain.id for domain in domains],
|
||||
}
|
||||
url = reverse("admin:mailbox_manager_maildomain_changelist")
|
||||
|
||||
domains = [
|
||||
factories.MailDomainEnabledFactory(
|
||||
users=[(core_factories.UserFactory(), "owner")]
|
||||
), factories.MailDomainEnabledFactory(
|
||||
users=[(core_factories.UserFactory(), "owner"),
|
||||
(core_factories.UserFactory(), "administrator")]
|
||||
),factories.MailDomainEnabledFactory(
|
||||
users=[(core_factories.UserFactory(), "owner"),
|
||||
(core_factories.UserFactory(), "viewer")]
|
||||
)
|
||||
]
|
||||
|
||||
resource = DomainResource()
|
||||
|
||||
with freeze_time("2025-12-16"):
|
||||
with django_assert_num_queries(6):
|
||||
response = client.post(url, data, follow=True)
|
||||
with django_assert_num_queries(4):
|
||||
export = resource.export()
|
||||
|
||||
domains_lines = response.content.decode("utf-8").split("\r\n")
|
||||
assert (
|
||||
response.headers["Content-Disposition"]
|
||||
== f'attachment; filename="people_domains_infos-2025-12-16.csv"'
|
||||
)
|
||||
# domains.reverse()
|
||||
import pdb; pdb.set_trace()
|
||||
domains.reverse()
|
||||
assert domains_lines[1:] == [
|
||||
f'{domain.name},{domain.status},{domain.support_email}' + ','.join([access.user.email for access in domain.accesses.all()])
|
||||
assert json.loads(export.json) == [{
|
||||
'name': str(domain.name),
|
||||
'slug': str(domain.slug),
|
||||
'status': str(domain.status),
|
||||
'support_email': domain.support_email,
|
||||
'owners': [access.user.email for access in domain.accesses.filter(role='owner')],
|
||||
'admins': [access.user.email for access in domain.accesses.filter(role='administrator')],
|
||||
}
|
||||
for domain in domains
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ class Base(Configuration):
|
||||
"rest_framework",
|
||||
"treebeard",
|
||||
"lasuite.admin", # must be before django.contrib.admin
|
||||
"import_export",
|
||||
# Django
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
@@ -317,8 +318,9 @@ class Base(Configuration):
|
||||
}
|
||||
|
||||
# Django Admin
|
||||
ADMIN_HEADER_BACKGROUND = values.Value(None)
|
||||
ADMIN_HEADER_COLOR = values.Value(None)
|
||||
ADMIN_HEADER_BACKGROUND = "#32a852"
|
||||
ADMIN_HEADER_COLOR = "#ffffff"
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
|
||||
# Mail
|
||||
EMAIL_BACKEND = values.Value("django.core.mail.backends.smtp.EmailBackend")
|
||||
|
||||
@@ -35,6 +35,7 @@ dependencies = [
|
||||
"django-cors-headers==4.9.0",
|
||||
"django-countries==8.2.0",
|
||||
"django-extensions==4.1",
|
||||
"django-import-export>=4.4.0",
|
||||
"django-lasuite==0.0.25",
|
||||
"django-oauth-toolkit==3.2.0",
|
||||
"django-parler==2.3",
|
||||
|
||||
Generated
+34
@@ -361,6 +361,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff-match-patch"
|
||||
version = "20241021"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0e/ad/32e1777dd57d8e85fa31e3a243af66c538245b8d64b7265bec9a61f2ca33/diff_match_patch-20241021.tar.gz", hash = "sha256:beae57a99fa48084532935ee2968b8661db861862ec82c6f21f4acdd6d835073", size = 39962, upload-time = "2024-10-21T19:41:21.094Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/bb/2aa9b46a01197398b901e458974c20ed107935c26e44e37ad5b0e5511e44/diff_match_patch-20241021-py3-none-any.whl", hash = "sha256:93cea333fb8b2bc0d181b0de5e16df50dd344ce64828226bda07728818936782", size = 43252, upload-time = "2024-10-21T19:41:19.914Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dill"
|
||||
version = "0.4.1"
|
||||
@@ -464,6 +473,20 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-import-export"
|
||||
version = "4.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "diff-match-patch" },
|
||||
{ name = "django" },
|
||||
{ name = "tablib" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/26/279bc8e6cb2c83d1b5dcdca07e932207c3352af11c6d305d6964a2d03ccc/django_import_export-4.4.0.tar.gz", hash = "sha256:9900e99c89027594941074fb4cd63a5f2964975e239021765c0f066003fcd412", size = 2237714, upload-time = "2026-01-10T20:57:35.128Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/e0/f4aa6d2374cc6b53b23f36bd0d5814e1db2769b25931b9908723fa295bb0/django_import_export-4.4.0-py3-none-any.whl", hash = "sha256:2d9b234c0f024d3377167f4d9c5a506e095c5bad98e06d30700e1d0752829e3d", size = 157449, upload-time = "2026-01-10T20:57:33.141Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-lasuite"
|
||||
version = "0.0.25"
|
||||
@@ -1029,6 +1052,7 @@ dependencies = [
|
||||
{ name = "django-cors-headers" },
|
||||
{ name = "django-countries" },
|
||||
{ name = "django-extensions" },
|
||||
{ name = "django-import-export" },
|
||||
{ name = "django-lasuite" },
|
||||
{ name = "django-oauth-toolkit" },
|
||||
{ name = "django-parler" },
|
||||
@@ -1089,6 +1113,7 @@ requires-dist = [
|
||||
{ name = "django-cors-headers", specifier = "==4.9.0" },
|
||||
{ name = "django-countries", specifier = "==8.2.0" },
|
||||
{ name = "django-extensions", specifier = "==4.1" },
|
||||
{ name = "django-import-export", specifier = ">=4.4.0" },
|
||||
{ name = "django-lasuite", specifier = "==0.0.25" },
|
||||
{ name = "django-oauth-toolkit", specifier = "==3.2.0" },
|
||||
{ name = "django-parler", specifier = "==2.3" },
|
||||
@@ -1671,6 +1696,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tablib"
|
||||
version = "3.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/11/00/416d2ba54d7d58a7f7c61bf62dfeb48fd553cf49614daf83312f2d2c156e/tablib-3.9.0.tar.gz", hash = "sha256:1b6abd8edb0f35601e04c6161d79660fdcde4abb4a54f66cc9f9054bd55d5fe2", size = 125565, upload-time = "2025-10-15T18:21:56.263Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/66/6b/32e51d847148b299088fc42d3d896845fd09c5247190133ea69dbe71ba51/tablib-3.9.0-py3-none-any.whl", hash = "sha256:eda17cd0d4dda614efc0e710227654c60ddbeb1ca92cdcfc5c3bd1fc5f5a6e4a", size = 49580, upload-time = "2025-10-15T18:21:44.185Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.14.0"
|
||||
|
||||
Reference in New Issue
Block a user