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"""
|
"""create_demo management command"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@@ -19,6 +18,72 @@ from demo import defaults
|
|||||||
fake = Faker()
|
fake = Faker()
|
||||||
|
|
||||||
logger = logging.getLogger(__file__)
|
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:
|
class Timeit:
|
||||||
@@ -67,56 +132,51 @@ def create_users():
|
|||||||
"""Create random users"""
|
"""Create random users"""
|
||||||
for user_id in range(defaults.NB_OBJECTS["users"]):
|
for user_id in range(defaults.NB_OBJECTS["users"]):
|
||||||
email = f"user.test{user_id:d}@example.com"
|
email = f"user.test{user_id:d}@example.com"
|
||||||
yield factories.UserFactory(
|
yield get_or_create_demo_user(email)
|
||||||
admin_email=email,
|
|
||||||
email=email,
|
|
||||||
sub=email,
|
|
||||||
password="!",
|
|
||||||
is_superuser=False,
|
|
||||||
is_active=True,
|
|
||||||
is_staff=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_dev_users():
|
def create_dev_users():
|
||||||
"""Create development users"""
|
"""Create development users"""
|
||||||
for dev_user in defaults.DEV_USERS:
|
for dev_user in defaults.DEV_USERS:
|
||||||
email = dev_user["email"]
|
email = dev_user["email"]
|
||||||
user = factories.UserFactory(
|
user = get_or_create_demo_user(email)
|
||||||
admin_email=email,
|
|
||||||
email=email,
|
|
||||||
sub=email,
|
|
||||||
password="!",
|
|
||||||
is_superuser=False,
|
|
||||||
is_active=True,
|
|
||||||
is_staff=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
create_item(user)
|
create_item(user)
|
||||||
yield 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"""
|
"""Create file item with the given user as creator"""
|
||||||
parent = factories.ItemFactory(
|
file_data = file_data or {}
|
||||||
creator=user,
|
content = file_data.get("content") or fake.sentence(nb_words=50).encode()
|
||||||
users=[(user, models.RoleChoices.OWNER)],
|
if parent is DEFAULT_PARENT:
|
||||||
type=models.ItemTypeChoices.FOLDER,
|
parent = factories.ItemFactory(
|
||||||
)
|
creator=user,
|
||||||
|
users=[(user, models.RoleChoices.OWNER)],
|
||||||
|
type=models.ItemTypeChoices.FOLDER,
|
||||||
|
)
|
||||||
|
|
||||||
item = factories.ItemFactory(
|
item = factories.ItemFactory(
|
||||||
type=models.ItemTypeChoices.FILE,
|
type=models.ItemTypeChoices.FILE,
|
||||||
update_upload_state=models.ItemUploadStateChoices.READY,
|
update_upload_state=models.ItemUploadStateChoices.READY,
|
||||||
link_reach=models.LinkReachChoices.AUTHENTICATED,
|
link_reach=models.LinkReachChoices.AUTHENTICATED,
|
||||||
link_role=models.LinkRoleChoices.READER,
|
link_role=models.LinkRoleChoices.READER,
|
||||||
creator=user,
|
creator=user,
|
||||||
|
users=[(user, models.RoleChoices.OWNER)] if parent is None else None,
|
||||||
parent=parent,
|
parent=parent,
|
||||||
title=fake.sentence(nb_words=4),
|
title=title or fake.sentence(nb_words=4),
|
||||||
filename="content.txt",
|
filename=file_data.get("filename", "content.txt"),
|
||||||
description=fake.sentence(nb_words=10),
|
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
|
return item
|
||||||
|
|
||||||
@@ -128,7 +188,22 @@ def create_items(users):
|
|||||||
yield create_item(user)
|
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.
|
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,
|
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):
|
class Command(BaseCommand):
|
||||||
"""A management command to create a demo database."""
|
"""A management command to create a demo database."""
|
||||||
@@ -167,6 +264,13 @@ class Command(BaseCommand):
|
|||||||
default=False,
|
default=False,
|
||||||
help="Force command execution despite DEBUG is set to 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):
|
def handle(self, *args, **options):
|
||||||
"""Handling of the management command."""
|
"""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
|
import pytest
|
||||||
|
|
||||||
from core import models
|
from core import models
|
||||||
|
from demo.management.commands.create_demo import FILE_TYPE_ITEMS
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
@@ -32,3 +33,50 @@ def test_commands_create_demo():
|
|||||||
# assert dev users have doc accesses
|
# assert dev users have doc accesses
|
||||||
user = models.User.objects.get(email="drive@drive.world")
|
user = models.User.objects.get(email="drive@drive.world")
|
||||||
assert models.ItemAccess.objects.filter(user=user).exists()
|
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