mirror of
https://github.com/suitenumerique/drive.git
synced 2026-08-17 20:15:40 +02:00
✨(backend) add file type fixtures to create_demo
The --file-types flag creates one fixture per category (docx, xlsx, pptx, pdf, image, video, zip, mp3, binary) so developers can populate a local demo database covering all filter-relevant types, without having to upload real files manually.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
# ruff: noqa: S106
|
||||
"""create_demo management command"""
|
||||
|
||||
import logging
|
||||
@@ -19,6 +18,72 @@ from demo import defaults
|
||||
fake = Faker()
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
DEFAULT_PARENT = object()
|
||||
|
||||
|
||||
FILE_TYPE_ITEMS = (
|
||||
{
|
||||
"title": "Demo text document",
|
||||
"filename": "demo-text-document.docx",
|
||||
"mimetype": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
},
|
||||
{
|
||||
"title": "Demo spreadsheet",
|
||||
"filename": "demo-spreadsheet.xlsx",
|
||||
"mimetype": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
},
|
||||
{
|
||||
"title": "Demo presentation",
|
||||
"filename": "demo-presentation.pptx",
|
||||
"mimetype": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
},
|
||||
{
|
||||
"title": "Demo PDF",
|
||||
"filename": "demo-pdf.pdf",
|
||||
"mimetype": "application/pdf",
|
||||
},
|
||||
{
|
||||
"title": "Demo image",
|
||||
"filename": "demo-image.png",
|
||||
"mimetype": "image/png",
|
||||
},
|
||||
{
|
||||
"title": "Demo video",
|
||||
"filename": "demo-video.mp4",
|
||||
"mimetype": "video/mp4",
|
||||
},
|
||||
{
|
||||
"title": "Demo archive",
|
||||
"filename": "demo-archive.zip",
|
||||
"mimetype": "application/zip",
|
||||
},
|
||||
{
|
||||
"title": "Demo audio",
|
||||
"filename": "demo-audio.mp3",
|
||||
"mimetype": "audio/mpeg",
|
||||
},
|
||||
{
|
||||
"title": "Demo other file",
|
||||
"filename": "demo-other.bin",
|
||||
"mimetype": "application/octet-stream",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_or_create_demo_user(email):
|
||||
"""Get an existing demo user or create it when absent."""
|
||||
user, _created = models.User.objects.get_or_create(
|
||||
sub=email,
|
||||
defaults={
|
||||
"admin_email": email,
|
||||
"email": email,
|
||||
"password": "!",
|
||||
"is_superuser": False,
|
||||
"is_active": True,
|
||||
"is_staff": False,
|
||||
},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
class Timeit:
|
||||
@@ -67,56 +132,51 @@ def create_users():
|
||||
"""Create random users"""
|
||||
for user_id in range(defaults.NB_OBJECTS["users"]):
|
||||
email = f"user.test{user_id:d}@example.com"
|
||||
yield factories.UserFactory(
|
||||
admin_email=email,
|
||||
email=email,
|
||||
sub=email,
|
||||
password="!",
|
||||
is_superuser=False,
|
||||
is_active=True,
|
||||
is_staff=False,
|
||||
)
|
||||
yield get_or_create_demo_user(email)
|
||||
|
||||
|
||||
def create_dev_users():
|
||||
"""Create development users"""
|
||||
for dev_user in defaults.DEV_USERS:
|
||||
email = dev_user["email"]
|
||||
user = factories.UserFactory(
|
||||
admin_email=email,
|
||||
email=email,
|
||||
sub=email,
|
||||
password="!",
|
||||
is_superuser=False,
|
||||
is_active=True,
|
||||
is_staff=False,
|
||||
)
|
||||
user = get_or_create_demo_user(email)
|
||||
|
||||
create_item(user)
|
||||
yield user
|
||||
|
||||
|
||||
def create_item(user):
|
||||
def create_item(
|
||||
user,
|
||||
title=None,
|
||||
file_data=None,
|
||||
parent=DEFAULT_PARENT,
|
||||
):
|
||||
"""Create file item with the given user as creator"""
|
||||
file_data = file_data or {}
|
||||
content = file_data.get("content") or fake.sentence(nb_words=50).encode()
|
||||
if parent is DEFAULT_PARENT:
|
||||
parent = factories.ItemFactory(
|
||||
creator=user,
|
||||
users=[(user, models.RoleChoices.OWNER)],
|
||||
type=models.ItemTypeChoices.FOLDER,
|
||||
)
|
||||
|
||||
item = factories.ItemFactory(
|
||||
type=models.ItemTypeChoices.FILE,
|
||||
update_upload_state=models.ItemUploadStateChoices.READY,
|
||||
link_reach=models.LinkReachChoices.AUTHENTICATED,
|
||||
link_role=models.LinkRoleChoices.READER,
|
||||
creator=user,
|
||||
users=[(user, models.RoleChoices.OWNER)] if parent is None else None,
|
||||
parent=parent,
|
||||
title=fake.sentence(nb_words=4),
|
||||
filename="content.txt",
|
||||
title=title or fake.sentence(nb_words=4),
|
||||
filename=file_data.get("filename", "content.txt"),
|
||||
description=fake.sentence(nb_words=10),
|
||||
mimetype="text/plain",
|
||||
mimetype=file_data.get("mimetype", "text/plain"),
|
||||
size=len(content),
|
||||
)
|
||||
|
||||
default_storage.save(item.file_key, BytesIO(fake.sentence(nb_words=50).encode()))
|
||||
default_storage.save(item.file_key, BytesIO(content))
|
||||
|
||||
return item
|
||||
|
||||
@@ -128,7 +188,22 @@ def create_items(users):
|
||||
yield create_item(user)
|
||||
|
||||
|
||||
def create_demo(stdout):
|
||||
def create_file_type_items(user):
|
||||
"""Create one ready file for each file type category described in issue #597."""
|
||||
for file_type_item in FILE_TYPE_ITEMS:
|
||||
yield create_item(
|
||||
user,
|
||||
title=file_type_item["title"],
|
||||
file_data={
|
||||
"filename": file_type_item["filename"],
|
||||
"mimetype": file_type_item["mimetype"],
|
||||
"content": f"{file_type_item['title']} fixture".encode(),
|
||||
},
|
||||
parent=None,
|
||||
)
|
||||
|
||||
|
||||
def create_demo(stdout, *, file_types=False):
|
||||
"""
|
||||
Create a database with demo data for developers to work in a realistic environment.
|
||||
"""
|
||||
@@ -152,6 +227,28 @@ def create_demo(stdout):
|
||||
role=models.RoleChoices.READER,
|
||||
)
|
||||
|
||||
if file_types:
|
||||
with Timeit(stdout, "Creating file type items"):
|
||||
list(create_file_type_items(dev_users[0]))
|
||||
|
||||
with Timeit(stdout, "Sharing development users items with demo users"):
|
||||
dev_items = models.Item.objects.filter(
|
||||
creator__in=dev_users, type=models.ItemTypeChoices.FILE
|
||||
).exclude(accesses__user__in=users)
|
||||
for item in dev_items:
|
||||
nb_users = secrets.randbelow(len(users)) + 1
|
||||
shared_users = fake.random_sample(elements=users, length=nb_users)
|
||||
for user in shared_users:
|
||||
factories.UserItemAccessFactory(
|
||||
item=item,
|
||||
user=user,
|
||||
role=models.RoleChoices.READER,
|
||||
)
|
||||
|
||||
names = ", ".join(user.short_name for user in shared_users)
|
||||
item.title = f"{item.title} (shared with {names})"
|
||||
item.save(update_fields=["title"])
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""A management command to create a demo database."""
|
||||
@@ -167,6 +264,13 @@ class Command(BaseCommand):
|
||||
default=False,
|
||||
help="Force command execution despite DEBUG is set to False",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--file_types",
|
||||
"--file-types",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Create items for several file types",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Handling of the management command."""
|
||||
@@ -178,4 +282,7 @@ class Command(BaseCommand):
|
||||
)
|
||||
)
|
||||
|
||||
create_demo(self.stdout)
|
||||
create_demo(
|
||||
self.stdout,
|
||||
file_types=options["file_types"],
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from django.test import override_settings
|
||||
import pytest
|
||||
|
||||
from core import models
|
||||
from demo.management.commands.create_demo import FILE_TYPE_ITEMS
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
@@ -32,3 +33,50 @@ def test_commands_create_demo():
|
||||
# assert dev users have doc accesses
|
||||
user = models.User.objects.get(email="drive@drive.world")
|
||||
assert models.ItemAccess.objects.filter(user=user).exists()
|
||||
|
||||
|
||||
@override_settings(DEBUG=True)
|
||||
def test_commands_create_demo_shares_dev_users_items_with_demo_users():
|
||||
"""The create_demo command should share existing dev users items with demo users."""
|
||||
call_command("create_demo")
|
||||
|
||||
dev_user = models.User.objects.get(email="drive@drive.world")
|
||||
dev_items = models.Item.objects.filter(creator=dev_user, type=models.ItemTypeChoices.FILE)
|
||||
assert dev_items.count() == 2
|
||||
for item in dev_items:
|
||||
assert models.ItemAccess.objects.filter(item=item).exclude(user=dev_user).exists()
|
||||
|
||||
|
||||
@override_settings(DEBUG=True)
|
||||
def test_commands_create_demo_with_file_types():
|
||||
"""The create_demo command should optionally add files for filter development."""
|
||||
call_command("create_demo", "--file_types")
|
||||
|
||||
expected_filenames = {item["filename"] for item in FILE_TYPE_ITEMS}
|
||||
expected_mimetypes = {item["mimetype"] for item in FILE_TYPE_ITEMS}
|
||||
file_type_items = models.Item.objects.filter(filename__in=expected_filenames)
|
||||
|
||||
assert file_type_items.count() == len(FILE_TYPE_ITEMS)
|
||||
assert set(file_type_items.values_list("mimetype", flat=True)) == expected_mimetypes
|
||||
assert all(item.is_root for item in file_type_items)
|
||||
|
||||
dev_user = models.User.objects.get(email="drive@drive.world")
|
||||
for item in file_type_items:
|
||||
assert models.ItemAccess.objects.filter(item=item).exclude(user=dev_user).exists()
|
||||
|
||||
|
||||
@mock.patch(
|
||||
"demo.defaults.NB_OBJECTS",
|
||||
{
|
||||
"users": 10,
|
||||
"files": 10,
|
||||
"max_users_per_document": 5,
|
||||
},
|
||||
)
|
||||
@override_settings(DEBUG=True)
|
||||
def test_commands_create_demo_can_be_run_twice_without_resetting_database():
|
||||
"""The create_demo command should reuse deterministic demo users."""
|
||||
call_command("create_demo", "--file_types")
|
||||
call_command("create_demo", "--file_types")
|
||||
|
||||
assert models.User.objects.filter(email="drive@drive.world").count() == 1
|
||||
|
||||
Reference in New Issue
Block a user