Support to export blocks/mutes as CSV files (#626)
This commit is contained in:
parent
4a9109271d
commit
67d755e6d3
|
@ -65,6 +65,16 @@ urlpatterns = [
|
||||||
settings.CsvFollowers.as_view(),
|
settings.CsvFollowers.as_view(),
|
||||||
name="settings_export_followers_csv",
|
name="settings_export_followers_csv",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"@<handle>/settings/import_export/blocks.csv",
|
||||||
|
settings.CsvBlocks.as_view(),
|
||||||
|
name="settings_export_blocks_csv",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"@<handle>/settings/import_export/mutes.csv",
|
||||||
|
settings.CsvMutes.as_view(),
|
||||||
|
name="settings_export_mutes_csv",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"@<handle>/settings/migrate_in/",
|
"@<handle>/settings/migrate_in/",
|
||||||
settings.MigrateInPage.as_view(),
|
settings.MigrateInPage.as_view(),
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<small>{{ numbers.blocks }} {{ numbers.blocks|pluralize:"people,people" }}</small>
|
<small>{{ numbers.blocks }} {{ numbers.blocks|pluralize:"people,people" }}</small>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<a href="{% url "settings_export_blocks_csv" handle=identity.handle %}">Download CSV</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<small>{{ numbers.mutes }} {{ numbers.mutes|pluralize:"people,people" }}</small>
|
<small>{{ numbers.mutes }} {{ numbers.mutes|pluralize:"people,people" }}</small>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<a href="{% url "settings_export_mutes_csv" handle=identity.handle %}">Download CSV</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -88,3 +88,92 @@ def test_export_followers(
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.content.strip() == b"Account address\r\ntest@example2.com"
|
assert response.content.strip() == b"Account address\r\ntest@example2.com"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_export_blocks(
|
||||||
|
client_with_user: Client,
|
||||||
|
identity: Identity,
|
||||||
|
identity2: Identity,
|
||||||
|
stator: StatorRunner,
|
||||||
|
httpx_mock: HTTPXMock,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Validates the "export a CSV of blocked users" functionality works
|
||||||
|
"""
|
||||||
|
# Block remote_identity
|
||||||
|
IdentityService(identity).block(identity2)
|
||||||
|
|
||||||
|
# Run stator to process it
|
||||||
|
stator.run_single_cycle()
|
||||||
|
|
||||||
|
# Download the CSV
|
||||||
|
response = client_with_user.get(
|
||||||
|
f"/@{identity.handle}/settings/import_export/blocks.csv"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.content.strip() == b"Account address\r\ntest@example2.com"
|
||||||
|
|
||||||
|
# Unblock should clear the CSV content
|
||||||
|
IdentityService(identity).unblock(identity2)
|
||||||
|
|
||||||
|
# Run stator to process it
|
||||||
|
stator.run_single_cycle()
|
||||||
|
|
||||||
|
response = client_with_user.get(
|
||||||
|
f"/@{identity.handle}/settings/import_export/blocks.csv"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.content.strip() == b"Account address"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_export_mutes(
|
||||||
|
client_with_user: Client,
|
||||||
|
identity: Identity,
|
||||||
|
identity2: Identity,
|
||||||
|
stator: StatorRunner,
|
||||||
|
httpx_mock: HTTPXMock,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Validates the "export a CSV of muted users" functionality works
|
||||||
|
"""
|
||||||
|
# Mute remote_identity
|
||||||
|
IdentityService(identity).mute(identity2)
|
||||||
|
|
||||||
|
# Run stator to process it
|
||||||
|
stator.run_single_cycle()
|
||||||
|
|
||||||
|
# Download the CSV
|
||||||
|
response = client_with_user.get(
|
||||||
|
f"/@{identity.handle}/settings/import_export/mutes.csv"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert (
|
||||||
|
response.content.strip()
|
||||||
|
== b"Account address,Hide notifications\r\ntest@example2.com,false"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mute remote_identity
|
||||||
|
IdentityService(identity).mute(identity2, include_notifications=True)
|
||||||
|
|
||||||
|
# Run stator to process it
|
||||||
|
stator.run_single_cycle()
|
||||||
|
|
||||||
|
# Download the CSV
|
||||||
|
response = client_with_user.get(
|
||||||
|
f"/@{identity.handle}/settings/import_export/mutes.csv"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert (
|
||||||
|
response.content.strip()
|
||||||
|
== b"Account address,Hide notifications\r\ntest@example2.com,true"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unmute should clear the CSV content
|
||||||
|
IdentityService(identity).unmute(identity2)
|
||||||
|
response = client_with_user.get(
|
||||||
|
f"/@{identity.handle}/settings/import_export/mutes.csv"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.content.strip() == b"Account address,Hide notifications"
|
||||||
|
|
|
@ -6,8 +6,10 @@ from django.views.generic import View
|
||||||
from users.views.settings.delete import DeleteIdentity # noqa
|
from users.views.settings.delete import DeleteIdentity # noqa
|
||||||
from users.views.settings.follows import FollowsPage # noqa
|
from users.views.settings.follows import FollowsPage # noqa
|
||||||
from users.views.settings.import_export import ( # noqa
|
from users.views.settings.import_export import ( # noqa
|
||||||
|
CsvBlocks,
|
||||||
CsvFollowers,
|
CsvFollowers,
|
||||||
CsvFollowing,
|
CsvFollowing,
|
||||||
|
CsvMutes,
|
||||||
ImportExportPage,
|
ImportExportPage,
|
||||||
)
|
)
|
||||||
from users.views.settings.interface import InterfacePage # noqa
|
from users.views.settings.interface import InterfacePage # noqa
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.shortcuts import redirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import FormView, View
|
from django.views.generic import FormView, View
|
||||||
|
|
||||||
from users.models import Follow, InboxMessage
|
from users.models import Block, Follow, InboxMessage
|
||||||
from users.views.base import IdentityViewMixin
|
from users.views.base import IdentityViewMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,3 +147,35 @@ class CsvFollowers(CsvView):
|
||||||
|
|
||||||
def get_handle(self, follow: Follow):
|
def get_handle(self, follow: Follow):
|
||||||
return follow.source.handle
|
return follow.source.handle
|
||||||
|
|
||||||
|
|
||||||
|
class CsvBlocks(CsvView):
|
||||||
|
columns = {
|
||||||
|
"Account address": "get_handle",
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = "blocked_accounts.csv"
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return self.identity.outbound_blocks.active().filter(mute=False)
|
||||||
|
|
||||||
|
def get_handle(self, block: Block):
|
||||||
|
return block.target.handle
|
||||||
|
|
||||||
|
|
||||||
|
class CsvMutes(CsvView):
|
||||||
|
columns = {
|
||||||
|
"Account address": "get_handle",
|
||||||
|
"Hide notifications": "get_notification",
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = "muted_accounts.csv"
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return self.identity.outbound_blocks.active().filter(mute=True)
|
||||||
|
|
||||||
|
def get_handle(self, mute: Block):
|
||||||
|
return mute.target.handle
|
||||||
|
|
||||||
|
def get_notification(self, mute: Block):
|
||||||
|
return mute.include_notifications
|
||||||
|
|
Loading…
Reference in New Issue