✨(project) Django boilerplate
This commit introduces a boilerplate inspired by https://github.com/numerique-gouv/impress. The code has been cleaned to remove unnecessary Impress logic and dependencies. Changes made: - Removed Minio, WebRTC, and create bucket from the stack. - Removed the Next.js frontend (it will be replaced by Vite). - Cleaned up impress-specific backend logics. The whole stack remains functional: - All tests pass. - Linter checks pass. - Agent Connexion sources are already set-up. Why clear out the code? To adhere to the KISS principle, we aim to maintain a minimalist codebase. Cloning Impress allowed us to quickly inherit its code quality tools and deployment configurations for staging, pre-production, and production environments. What’s broken? - The tsclient is not functional anymore. - Some make commands need to be fixed. - Helm sources are outdated. - Naming across the project sources are inconsistent (impress, visio, etc.) - CI is not configured properly. This list might be incomplete. Let's grind it.
This commit is contained in:
committed by
lebaudantoine
parent
2d81979b0a
commit
5b1a2b20de
0
src/backend/demo/management/__init__.py
Normal file
0
src/backend/demo/management/__init__.py
Normal file
0
src/backend/demo/management/commands/__init__.py
Normal file
0
src/backend/demo/management/commands/__init__.py
Normal file
154
src/backend/demo/management/commands/create_demo.py
Normal file
154
src/backend/demo/management/commands/create_demo.py
Normal file
@@ -0,0 +1,154 @@
|
||||
# ruff: noqa: S311, S106
|
||||
"""create_demo management command"""
|
||||
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from django import db
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from faker import Faker
|
||||
|
||||
from core import models
|
||||
|
||||
from demo import defaults
|
||||
|
||||
fake = Faker()
|
||||
|
||||
logger = logging.getLogger("impress.commands.demo.create_demo")
|
||||
|
||||
|
||||
def random_true_with_probability(probability):
|
||||
"""return True with the requested probability, False otherwise."""
|
||||
return random.random() < probability
|
||||
|
||||
|
||||
class BulkQueue:
|
||||
"""A utility class to create Django model instances in bulk by just pushing to a queue."""
|
||||
|
||||
BATCH_SIZE = 20000
|
||||
|
||||
def __init__(self, stdout, *args, **kwargs):
|
||||
"""Define the queue as a dict of lists."""
|
||||
self.queue = defaultdict(list)
|
||||
self.stdout = stdout
|
||||
|
||||
def _bulk_create(self, objects):
|
||||
"""Actually create instances in bulk in the database."""
|
||||
if not objects:
|
||||
return
|
||||
|
||||
objects[0]._meta.model.objects.bulk_create(objects, ignore_conflicts=False) # noqa: SLF001
|
||||
# In debug mode, Django keeps query cache which creates a memory leak in this case
|
||||
db.reset_queries()
|
||||
self.queue[objects[0]._meta.model.__name__] = [] # noqa: SLF001
|
||||
|
||||
def push(self, obj):
|
||||
"""Add a model instance to queue to that it gets created in bulk."""
|
||||
objects = self.queue[obj._meta.model.__name__] # noqa: SLF001
|
||||
objects.append(obj)
|
||||
if len(objects) > self.BATCH_SIZE:
|
||||
self._bulk_create(objects)
|
||||
self.stdout.write(".", ending="")
|
||||
|
||||
def flush(self):
|
||||
"""Flush the queue after creating the remaining model instances."""
|
||||
for objects in self.queue.values():
|
||||
self._bulk_create(objects)
|
||||
|
||||
|
||||
class Timeit:
|
||||
"""A utility context manager/method decorator to time execution."""
|
||||
|
||||
total_time = 0
|
||||
|
||||
def __init__(self, stdout, sentence=None):
|
||||
"""Set the sentence to be displayed for timing information."""
|
||||
self.sentence = sentence
|
||||
self.start = None
|
||||
self.stdout = stdout
|
||||
|
||||
def __call__(self, func):
|
||||
"""Behavior on call for use as a method decorator."""
|
||||
|
||||
def timeit_wrapper(*args, **kwargs):
|
||||
"""wrapper to trigger/stop the timer before/after function call."""
|
||||
self.__enter__()
|
||||
result = func(*args, **kwargs)
|
||||
self.__exit__(None, None, None)
|
||||
return result
|
||||
|
||||
return timeit_wrapper
|
||||
|
||||
def __enter__(self):
|
||||
"""Start timer upon entering context manager."""
|
||||
self.start = time.perf_counter()
|
||||
if self.sentence:
|
||||
self.stdout.write(self.sentence, ending=".")
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
"""Stop timer and display result upon leaving context manager."""
|
||||
if exc_type is not None:
|
||||
raise exc_type(exc_value)
|
||||
end = time.perf_counter()
|
||||
elapsed_time = end - self.start
|
||||
if self.sentence:
|
||||
self.stdout.write(f" Took {elapsed_time:g} seconds")
|
||||
|
||||
self.__class__.total_time += elapsed_time
|
||||
return elapsed_time
|
||||
|
||||
|
||||
def create_demo(stdout):
|
||||
"""
|
||||
Create a database with demo data for developers to work in a realistic environment.
|
||||
The code is engineered to create a huge number of objects fast.
|
||||
"""
|
||||
|
||||
queue = BulkQueue(stdout)
|
||||
|
||||
with Timeit(stdout, "Creating users"):
|
||||
for i in range(defaults.NB_OBJECTS["users"]):
|
||||
queue.push(
|
||||
models.User(
|
||||
admin_email=f"user{i:d}@example.com",
|
||||
email=f"user{i:d}@example.com",
|
||||
password="!",
|
||||
is_superuser=False,
|
||||
is_active=True,
|
||||
is_staff=False,
|
||||
language=random.choice(settings.LANGUAGES)[0],
|
||||
)
|
||||
)
|
||||
queue.flush()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""A management command to create a demo database."""
|
||||
|
||||
help = __doc__
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add argument to require forcing execution when not in debug mode."""
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--force",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Force command execution despite DEBUG is set to False",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Handling of the management command."""
|
||||
if not settings.DEBUG and not options["force"]:
|
||||
raise CommandError(
|
||||
(
|
||||
"This command is not meant to be used in production environment "
|
||||
"except you know what you are doing, if so use --force parameter"
|
||||
)
|
||||
)
|
||||
|
||||
create_demo(self.stdout)
|
||||
46
src/backend/demo/management/commands/createsuperuser.py
Normal file
46
src/backend/demo/management/commands/createsuperuser.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""Management user to create a superuser."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
UserModel = get_user_model()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Management command to create a superuser from and email and password."""
|
||||
|
||||
help = "Create a superuser with an email and a password"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Define required arguments "email" and "password"."""
|
||||
parser.add_argument(
|
||||
"--email",
|
||||
help=("Email for the user."),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password",
|
||||
help="Password for the user.",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""
|
||||
Given an email and a password, create a superuser or upgrade the existing
|
||||
user to superuser status.
|
||||
"""
|
||||
email = options.get("email")
|
||||
try:
|
||||
user = UserModel.objects.get(admin_email=email)
|
||||
except UserModel.DoesNotExist:
|
||||
user = UserModel(admin_email=email)
|
||||
message = "Superuser created successfully."
|
||||
else:
|
||||
if user.is_superuser and user.is_staff:
|
||||
message = "Superuser already exists."
|
||||
else:
|
||||
message = "User already existed and was upgraded to superuser."
|
||||
|
||||
user.is_superuser = True
|
||||
user.is_staff = True
|
||||
user.set_password(options["password"])
|
||||
user.save()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(message))
|
||||
Reference in New Issue
Block a user