Skip to content

Commit

Permalink
feat: improve demo startup time
Browse files Browse the repository at this point in the history
Until now demo users were created in the bootstrap.sh script
during the container start when demo mode is activated. This
was really slow and resulted in the demo version being offline
for up to a minute after a restart.

With this update during the container start only the old data
will be cleaned up. Demo users are created on the fly by user
requests.
  • Loading branch information
mrmn2 committed Feb 8, 2025
1 parent 2e1939f commit 64a0aa2
Show file tree
Hide file tree
Showing 21 changed files with 409 additions and 194 deletions.
6 changes: 2 additions & 4 deletions bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ fi
HOST_PORT="${HOST_PORT:-8000}"

python manage.py migrate
if [ "$DEMO_MODE" = "TRUE" ]; then
python manage.py create_demo_data
fi
python manage.py clean_up_shared_pdfs
python manage.py clean_up

python -m gunicorn --bind 0.0.0.0:$HOST_PORT --workers 3 core.wsgi:application
1 change: 1 addition & 0 deletions pdfding/core/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,5 @@

# demo mode
DEMO_MODE = False
DEMO_MAX_USERS = 10
DEMO_MODE_RESTART_INTERVAL = 60
1 change: 1 addition & 0 deletions pdfding/core/settings/prod.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,6 @@
if os.environ.get('DEMO_MODE', 'FALSE') == 'TRUE':
DEMO_MODE = True
DEMO_MODE_RESTART_INTERVAL = os.environ.get('DEMO_MODE_RESTART_INTERVAL', 60) # in minutes
DEMO_MAX_USERS = int(os.environ.get('DEMO_MAX_USERS', 500))
else:
DEMO_MODE = False
17 changes: 16 additions & 1 deletion pdfding/e2e/test_users_e2e.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import Mock, patch

from allauth.socialaccount.models import SocialAccount
from django.test import override_settings
from django.urls import reverse
Expand Down Expand Up @@ -281,8 +283,21 @@ def test_default_theme(self):
expect(self.page.locator('body')).to_have_css('background-color', 'oklch(0.279 0.041 260.031)')
expect(self.page.locator('#logo_div')).to_have_css('background-color', 'rgb(71, 147, 204)')

@patch('users.views.create_demo_user')
@patch('users.views.uuid4', return_value='123456789')
@override_settings(DEMO_MODE=True)
def test_login_demo_mode(self):
def test_login_demo_mode(self, mock_uuid4, mock_create_demo_user):
email = '12345678@pdfding.com'
mock_user = Mock()
mock_user.email = email
mock_create_demo_user.return_value = mock_user

with sync_playwright() as p:
self.open(reverse('home'), p)
expect(self.page.locator("#demo_mode")).to_be_visible()

self.page.get_by_role("button", name="Create User").click()
expect(self.page.locator("#demo_user")).to_contain_text("Demo user successfully created")
expect(self.page.locator("#demo_user")).to_contain_text(
f"You can log into your temporary account with: {email} / demo"
)
Empty file removed pdfding/pdf/management/__init__.py
Empty file.
19 changes: 0 additions & 19 deletions pdfding/pdf/management/commands/clean_up_shared_pdfs.py

This file was deleted.

10 changes: 0 additions & 10 deletions pdfding/pdf/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from urllib.parse import parse_qs, urlparse
from uuid import uuid4

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.files import File
from django.db.models.functions import Lower
Expand Down Expand Up @@ -296,12 +295,3 @@ def get_pdf_info_list(profile: Profile) -> list[tuple]:
pdf_info_list.append((pdf.name, pdf_size))

return pdf_info_list


def get_demo_pdf():
"""Get the pdf file used in the demo mode."""

file_path = settings.BASE_DIR / 'users' / 'management' / 'commands' / 'demo_data' / 'demo.pdf'
demo_pdf = File(file=open(file_path, 'rb'), name=file_path.name)

return demo_pdf
34 changes: 0 additions & 34 deletions pdfding/pdf/tests/test_management.py

This file was deleted.

5 changes: 0 additions & 5 deletions pdfding/pdf/tests/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,3 @@ def test_get_pdf_info_list(self):
expected_info_list = [(f'pdf_{i}', 8885) for i in range(3)]

self.assertEqual(generated_info_list, expected_info_list)

def test_get_demo_pdf(self):
demo_pdf = service.get_demo_pdf()

self.assertEqual(demo_pdf.size, 26140)
8 changes: 4 additions & 4 deletions pdfding/pdf/views/pdf_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pdf import forms, service
from pdf.models import Pdf, Tag
from users.models import Profile
from users.service import convert_hex_to_rgb
from users.service import convert_hex_to_rgb, get_demo_pdf


class BasePdfMixin:
Expand Down Expand Up @@ -43,7 +43,7 @@ def obj_save(form: forms.AddForm | forms.AddFormNoFile, request: HttpRequest, __
pdf = form.save(commit=False)

if settings.DEMO_MODE:
pdf_file = service.get_demo_pdf()
pdf_file = get_demo_pdf()
pdf.file = pdf_file
else:
pdf_file = form.files['file']
Expand Down Expand Up @@ -95,7 +95,7 @@ def obj_save(form: forms.BulkAddForm | forms.BulkAddFormNoFile, request: HttpReq
pdf_info_list = []

if settings.DEMO_MODE:
files = [service.get_demo_pdf()]
files = [get_demo_pdf()]
else:
files = form.files.getlist('file')

Expand Down Expand Up @@ -379,7 +379,7 @@ def post(self, request: HttpRequest):
pdf = self.get_object(request, pdf_id)

if settings.DEMO_MODE:
updated_pdf = service.get_demo_pdf()
updated_pdf = get_demo_pdf()
else:
updated_pdf = request.FILES.get('updated_pdf')

Expand Down
30 changes: 22 additions & 8 deletions pdfding/templates/socialaccount/snippets/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,36 @@

{% if DEMO_MODE %}
{% with '12345'|make_list as int_list %}
<div id="demo_mode" class="pt-12 pb-6">
<div id="demo_mode" class="mt-6 pt-4 pb-6 border-t border-gray-400">
<h2>Demo Mode</h2>
<div>
<span class="text-sm [&>span]:bg-slate-300 dark:[&>span]:bg-slate-500 [&>span]:rounded-sm [&>span]:px-1">
You can log into a shared account with:
<span>user_{{ int_list|random }}@pdfding.com</span> / <span>demo</span>
</span>
</div>
<div class="pt-4">
<span class="text-sm">The demo mode has a couple of <span class="font-bold">restrictions</span>:</span>
</div>
<ul class="list-disc pl-5 pt-1 text-sm">
<li>The demo instance is reset every hour, so any changes you make will be lost. </li>
<li>The demo instance is reset every hour, so any changes you make will be lost.</li>
<li>File uploads are not allowed when adding a new PDF. Instead, a dummy PDF will be used.</li>
<li>PDFs are read-only. So any changes in the viewer's editor won't be saved.</li>
<li>Don't enter private information. Assume all data can be read by other users.</li>
</ul>
</div>
<div id="demo_user" class="pt-2" x-data="{ in_progress: false }">
<button x-show="!in_progress"
@click="in_progress = true"
hx-post="{% url 'create_demo_user' %}"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-target="#demo_user"
hx-swap="innerHTML">
Create User
</button>
<button class="-pt-4" x-show="in_progress" x-cloak type="button" disabled>
<div class="flex justify-center">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>Processing</span>
</div
</button>
</div>
{% endwith %}
{% endif %}
File renamed without changes.
49 changes: 49 additions & 0 deletions pdfding/users/management/commands/clean_up.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import logging
from pathlib import Path
from shutil import copy

from django.conf import settings
from django.core.management.base import BaseCommand
from pdf.models import SharedPdf

logger = logging.getLogger('management')


class Command(BaseCommand):
help = "Clean up data on start up"

def handle(self, *args, **kwargs):
clean_up_deleted_shared_pdfs()

if settings.DEMO_MODE:
clean_demo_db()


def clean_up_deleted_shared_pdfs():
"""Delete shared PDFs with a deletion date in the past."""

logger.info('Cleaning up shared PDFs with a deletion date in the past.')

shared_pdfs = SharedPdf.objects.all()

for shared_pdf in shared_pdfs:
if shared_pdf.deleted:
shared_pdf.delete()


def clean_demo_db(
db_path: Path = settings.BASE_DIR / 'db' / 'db.sqlite3',
after_migration_db_path: Path = settings.BASE_DIR / 'db' / 'migrated.sqlite3',
):
"""Clean the database for the demo mode. In this state the db will have applied migrations but no users or pdfs."""

logger.info('Cleaning up the demo database.')

# If the after_migration_db_path is not present, then the container is started for the first time.
# This means only the migrations were applied until now, so we want preserve this state.
if not after_migration_db_path.exists():
copy(db_path, after_migration_db_path)
# otherwise replace the migrations db.
else:
db_path.unlink()
copy(after_migration_db_path, db_path)
105 changes: 0 additions & 105 deletions pdfding/users/management/commands/create_demo_data.py

This file was deleted.

Loading

0 comments on commit 64a0aa2

Please sign in to comment.