diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml deleted file mode 100644 index 0c4a281..0000000 --- a/.github/workflows/arch.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: pytest-arch -run-name: Run pytest on Arch Linux -on: - push: - branches: 'main' - pull_request: -jobs: - pytest: - runs-on: archlinux:latest - strategy: - matrix: - python-version: ["3.9", "3.13"] - name: pytest-arch-python-${{ matrix.python-version }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip3 install .[tests] - - name: Run pytest - env: - PYTEST_ADDOPTS: "--color=yes" - run: | - python3 -m pytest -v -n auto diff --git a/.github/workflows/check_migrations.yml b/.github/workflows/check_migrations.yml new file mode 100644 index 0000000..86e549e --- /dev/null +++ b/.github/workflows/check_migrations.yml @@ -0,0 +1,23 @@ +name: migrations +run-name: Check if all migrations are created +on: + push: + branches: 'main' + pull_request: +jobs: + pytest: + runs-on: ubuntu-latest + name: migrations-ubuntu + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + - name: Install dependencies + run: | + pip3 install .[django] + - name: Check migrations + run: | + python3 tests/test_django/manage.py makemigrations --dry-run --check diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 6b37756..e930f84 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -1,9 +1,9 @@ name: Python Formatter (isort & black) on: + push: + branches: 'main' pull_request: - branches: [main] - jobs: formatting: runs-on: ubuntu-latest @@ -12,7 +12,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Install isort and black run: | python -m pip install --upgrade pip diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml index 0d379c1..11a3188 100644 --- a/.github/workflows/macOS.yml +++ b/.github/workflows/macOS.yml @@ -10,7 +10,7 @@ jobs: runs-on: macos-latest strategy: matrix: - python-version: ["3.9", "3.13"] + python-version: ["3.10", "3.13"] name: pytest-macos-python-${{ matrix.python-version }} steps: - name: Checkout @@ -30,10 +30,18 @@ jobs: run: | python3 -m venv .venv source .venv/bin/activate - pip install .[tests] - - name: Run pytest + pip install -e .[tests] + - name: Run pytest without Django env: PYTEST_ADDOPTS: "--color=yes" run: | source .venv/bin/activate pytest -v -n auto + - name: Run pytest with Django + env: + PYTEST_ADDOPTS: "--color=yes" + run: | + source .venv/bin/activate + pip install -e .[tests,django_tests,django] + ./tests/test_django/manage.py migrate + pytest -v -n auto diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9256238..af94afd 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -6,10 +6,10 @@ on: pull_request: jobs: pytest: - runs-on: ubuntu:latest + runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.13"] + python-version: ["3.10", "3.13"] name: pytest-ubuntu-python-${{ matrix.python-version }} steps: - name: Checkout @@ -20,9 +20,16 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - pip3 install .[tests] - - name: Run pytest + pip3 install -e .[tests] + - name: Run pytest without Django env: PYTEST_ADDOPTS: "--color=yes" run: | python3 -m pytest -v -n auto + - name: Run pytest with Django + env: + PYTEST_ADDOPTS: "--color=yes" + run: | + pip3 install -e .[tests,django_tests,django] + ./tests/test_django/manage.py migrate + python3 -m pytest -v -n auto diff --git a/.gitignore b/.gitignore index 76369bd..471c6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build .vscode .idea __pycache__ +tests/test_django/db.sqlite3 # pytest-cov .coverage* diff --git a/pyproject.toml b/pyproject.toml index d78f6bd..8dcf968 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,4 @@ include_trailing_comma = true [tool.black] line_length = 120 +exclude = "migrations/" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..ddf7218 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +DJANGO_SETTINGS_MODULE = test_django.settings +pythonpath = ./tests/test_django +markers = + no_django: marks tests that should be run without Django diff --git a/setup.cfg b/setup.cfg index da4d6d0..53698c9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,10 @@ tests = pytest pytest-cov pytest-xdist +django_tests = + pytest-django +django = + django [tool:pytest] testpaths = diff --git a/src/sio3pack/__init__.py b/src/sio3pack/__init__.py index 9761228..8220ad7 100644 --- a/src/sio3pack/__init__.py +++ b/src/sio3pack/__init__.py @@ -1,6 +1,7 @@ __version__ = "0.0.1" from sio3pack.files import LocalFile +from sio3pack.packages.exceptions import ImproperlyConfigured, PackageAlreadyExists from sio3pack.packages.package import Package @@ -14,3 +15,19 @@ def from_file(file: str | LocalFile, django_settings=None) -> Package: if isinstance(file, str): file = LocalFile(file) return Package.from_file(file, django_settings=django_settings) + + +def from_db(problem_id: int) -> Package: + """ + Initialize a package object from the database. + If sio3pack isn't installed with Django support, it should raise an ImproperlyConfigured exception. + If there is no package with the given problem_id, it should raise an UnknownPackageType exception. + :param problem_id: The problem id. + :return: The package object. + """ + try: + import django + + return Package.from_db(problem_id) + except ImportError: + raise ImproperlyConfigured("sio3pack is not installed with Django support.") diff --git a/src/sio3pack/django/__init__.py b/src/sio3pack/django/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/sio3pack/django/sinolpack/__init__.py b/src/sio3pack/django/sinolpack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/sio3pack/django/sinolpack/handler.py b/src/sio3pack/django/sinolpack/handler.py new file mode 100644 index 0000000..0ae67e7 --- /dev/null +++ b/src/sio3pack/django/sinolpack/handler.py @@ -0,0 +1,17 @@ +from sio3pack.django.sinolpack.models import SinolpackPackage +from sio3pack.packages.exceptions import PackageAlreadyExists +from sio3pack.packages.package.django.handler import DjangoHandler + + +class SinolpackDjangoHandler(DjangoHandler): + def save_to_db(self): + """ + Save the package to the database. + """ + if SinolpackPackage.objects.filter(problem_id=self.problem_id).exists(): + raise PackageAlreadyExists(self.problem_id) + + SinolpackPackage.objects.create( + problem_id=self.problem_id, + short_name=self.package.short_name, + ) diff --git a/src/sio3pack/django/sinolpack/migrations/0001_initial.py b/src/sio3pack/django/sinolpack/migrations/0001_initial.py new file mode 100644 index 0000000..c342290 --- /dev/null +++ b/src/sio3pack/django/sinolpack/migrations/0001_initial.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.3 on 2024-12-01 17:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="SinolpackPackage", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("problem_id", models.IntegerField()), + ("short_name", models.CharField(max_length=100)), + ], + ), + ] diff --git a/src/sio3pack/django/sinolpack/migrations/__init__.py b/src/sio3pack/django/sinolpack/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/sio3pack/django/sinolpack/models.py b/src/sio3pack/django/sinolpack/models.py new file mode 100644 index 0000000..961494c --- /dev/null +++ b/src/sio3pack/django/sinolpack/models.py @@ -0,0 +1,10 @@ +from django.db import models + + +class SinolpackPackage(models.Model): + """ + A package for the sinolpack package type. + """ + + problem_id = models.IntegerField() + short_name = models.CharField(max_length=100) diff --git a/src/sio3pack/packages/exceptions.py b/src/sio3pack/packages/exceptions.py index 307b736..1f9e0e4 100644 --- a/src/sio3pack/packages/exceptions.py +++ b/src/sio3pack/packages/exceptions.py @@ -1,4 +1,19 @@ class UnknownPackageType(Exception): - def __init__(self, path: str) -> None: - self.path = path - super().__init__(f"Unknown package type for file {path}.") + def __init__(self, arg: str | int) -> None: + if isinstance(arg, str): + self.path = arg + super().__init__(f"Unknown package type for file {arg}.") + else: + self.problem_id = arg + super().__init__(f"Unknown package type for problem with id={arg}.") + + +class ImproperlyConfigured(Exception): + def __init__(self, message: str) -> None: + super().__init__(message) + + +class PackageAlreadyExists(Exception): + def __init__(self, problem_id: int) -> None: + self.problem_id = problem_id + super().__init__(f"A package already exists for problem with id={problem_id}.") diff --git a/src/sio3pack/packages/package/django/__init__.py b/src/sio3pack/packages/package/django/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/sio3pack/packages/package/django/handler.py b/src/sio3pack/packages/package/django/handler.py new file mode 100644 index 0000000..e838fca --- /dev/null +++ b/src/sio3pack/packages/package/django/handler.py @@ -0,0 +1,14 @@ +from typing import Type + +from sio3pack.packages.exceptions import ImproperlyConfigured + + +class DjangoHandler: + def __init__(self, package: Type["Package"], problem_id: int): + self.package = package + self.problem_id = problem_id + + +class NoDjangoHandler: + def __call__(self, *args, **kwargs): + raise ImproperlyConfigured("sio3pack is not installed with Django support.") diff --git a/src/sio3pack/packages/package/model.py b/src/sio3pack/packages/package/model.py index ba5e45b..9438957 100644 --- a/src/sio3pack/packages/package/model.py +++ b/src/sio3pack/packages/package/model.py @@ -1,9 +1,11 @@ +import importlib from typing import Any from sio3pack import LocalFile from sio3pack.files import File from sio3pack.graph import Graph, GraphOperation from sio3pack.packages.exceptions import UnknownPackageType +from sio3pack.packages.package.django.handler import NoDjangoHandler from sio3pack.test import Test from sio3pack.utils.archive import Archive from sio3pack.utils.classinit import RegisteredSubclassesBase @@ -19,11 +21,23 @@ class Package(RegisteredSubclassesBase): def __init__(self): super().__init__() + @classmethod + def identify(cls, file: LocalFile): + """ + Identify if the package is of this type. + """ + raise NotImplementedError() + @classmethod def from_file(cls, file: LocalFile, django_settings=None): + """ + Create a package from a file. + """ for subclass in cls.subclasses: if subclass.identify(file): - return subclass(file, django_settings) + package = subclass() + package._from_file(file, django_settings) + return package raise UnknownPackageType(file.path) def _from_file(self, file: LocalFile): @@ -34,6 +48,50 @@ def _from_file(self, file: LocalFile): else: self.is_archive = False + @classmethod + def identify_db(cls, problem_id: int): + """ + Identify if the package is of this type. Should check if there + is a package of this type in the database with the given problem_id. + """ + raise NotImplementedError() + + @classmethod + def from_db(cls, problem_id: int): + """ + Create a package from the database. If sio3pack isn't installed with Django + support, it should raise an ImproperlyConfigured exception. If there is no + package with the given problem_id, it should raise an UnknownPackageType + exception. + """ + for subclass in cls.subclasses: + if subclass.identify_db(problem_id): + package = subclass() + package._from_db(problem_id) + return package + raise UnknownPackageType(problem_id) + + def _from_db(self, problem_id: int): + """ + Internal method to setup the package from the database. If sio3pack + isn't installed with Django support, it should raise an ImproperlyConfigured + exception. + """ + self.problem_id = problem_id + + def _setup_django_handler(self, problem_id: int): + try: + import django + + self.django_enabled = True + module_path, class_name = self.django_handler.rsplit(".", 1) + module = importlib.import_module(module_path) + handler = getattr(module, class_name) + self.django = handler(package=self, problem_id=problem_id) + except ImportError: + self.django_enabled = False + self.django = NoDjangoHandler() + def get_task_id(self) -> str: pass @@ -61,8 +119,15 @@ def get_test(self, test_id: str) -> Test: def get_unpack_graph(self) -> GraphOperation | None: pass - def get_run_graph(self, file: File, tests: list[Test] | None = None) -> Graph: + def get_run_graph(self, file: File, tests: list[Test] | None = None) -> GraphOperation | None: + pass + + def get_save_outs_graph(self, tests: list[Test] | None = None) -> GraphOperation | None: pass - def get_save_outs_graph(self, tests: list[Test] | None = None) -> Graph: + def save_to_db(self, problem_id: int): + """ + Save the package to the database. If sio3pack isn't installed with Django + support, it should raise an ImproperlyConfigured exception. + """ pass diff --git a/src/sio3pack/packages/sinolpack/model.py b/src/sio3pack/packages/sinolpack/model.py index 419248f..9d31864 100644 --- a/src/sio3pack/packages/sinolpack/model.py +++ b/src/sio3pack/packages/sinolpack/model.py @@ -5,8 +5,8 @@ import yaml from sio3pack import LocalFile -from sio3pack.files import File from sio3pack.graph import Graph, GraphManager, GraphOperation +from sio3pack.packages.exceptions import ImproperlyConfigured from sio3pack.packages.package import Package from sio3pack.packages.sinolpack.enums import ModelSolutionKind from sio3pack.util import naturalsort_key @@ -18,6 +18,8 @@ class Sinolpack(Package): Represents a OIOIOI's standard problem package. """ + django_handler = "sio3pack.django.sinolpack.handler.SinolpackDjangoHandler" + @classmethod def _find_main_dir(cls, archive: Archive) -> str | None: dirs = list(map(os.path.normcase, archive.dirnames())) @@ -50,6 +52,18 @@ def identify(cls, file: LocalFile) -> bool: except UnrecognizedArchiveFormat: return os.path.exists(os.path.join(path, "in")) and os.path.exists(os.path.join(path, "out")) + @classmethod + def identify_from_db(cls, problem_id: int) -> bool: + """ + Identifies whether problem is a Sinolpack. + + :param problem_id: ID of the problem. + :return: True when problem is a Sinolpack, otherwise False. + """ + from sio3pack.django.sinolpack.models import SinolpackPackage + + return SinolpackPackage.objects.filter(problem_id=problem_id).exists() + def __del__(self): if hasattr(self, "tmpdir"): self.tmpdir.cleanup() @@ -66,6 +80,7 @@ def _from_file(self, file: LocalFile, django_settings=None): archive.extract(to_path=self.tmpdir.name) self.rootdir = os.path.join(self.tmpdir.name, self.short_name) else: + # FIXME: Won't work in sinol-make. self.short_name = os.path.basename(file.path) self.rootdir = file.path @@ -77,6 +92,14 @@ def _from_file(self, file: LocalFile, django_settings=None): self.django_settings = django_settings + self._process_package() + + def _from_db(self, problem_id: int): + super()._from_db(problem_id) + super()._setup_django_handler(problem_id) + if not self.django_enabled: + raise ImproperlyConfigured("sio3pack is not installed with Django support.") + def _default_graph_manager(self) -> GraphManager: return GraphManager( { @@ -140,7 +163,9 @@ def _process_package(self): if not self.has_custom_graph: # Create the graph with processed files. - self.graph_manager = self._default_graph_manager() + # TODO: Uncomment this line when Graph will work. + # self.graph_manager = self._default_graph_manager() + pass def _process_config_yml(self): """ @@ -180,8 +205,8 @@ def _detect_full_name_translations(self): two-letter language code defined in ``settings.py``), if any such key is given. """ self.lang_titles = {} - for lang_code, lang in self._get_from_django_settings("LANGUAGES", [("en", "English")]): - key = "title_%s" % lang_code + for lang_code, _ in self._get_from_django_settings("LANGUAGES", [("en", "English")]): + key = f"title_{lang_code}" if key in self.config: self.lang_titles[lang_code] = self.config[key] @@ -190,7 +215,8 @@ def get_submittable_extensions(self): Returns a list of extensions that are submittable. """ return self.config.get( - "submittable_langs", self._get_from_django_settings("SUBMITTABLE_LANGUAGES", ["c", "cpp", "cxx", "py"]) + "submittable_langs", + self._get_from_django_settings("SUBMITTABLE_LANGUAGES", ["c", "cpp", "cc", "cxx", "py"]), ) def get_model_solution_regex(self): @@ -204,11 +230,14 @@ def _get_model_solutions(self) -> list[tuple[ModelSolutionKind, str]]: """ Returns a list of model solutions, where each element is a tuple of model solution kind and filename. """ + if not os.path.exists(self.get_prog_dir()): + return [] + regex = self.get_model_solution_regex() model_solutions = [] for file in os.listdir(self.get_prog_dir()): match = re.match(regex, file) - if re.match(regex, file) and os.path.isfile(os.path.join(self.get_prog_dir(), file)): + if match and os.path.isfile(os.path.join(self.get_prog_dir(), file)): model_solutions.append((ModelSolutionKind.from_regex(match.group(1)), file)) return model_solutions @@ -270,13 +299,13 @@ def _process_statements(self): return lang_prefs = [""] + [ - "-" + l[0] for l in self._get_from_django_settings("LANGUAGES", [("en", "English"), ("pl", "Polish")]) + f"-{lang}" for lang, _ in self._get_from_django_settings("LANGUAGES", [("en", "English"), ("pl", "Polish")]) ] self.lang_statements = {} for lang in lang_prefs: try: - htmlzipfile = self.get_in_doc_dir(self.short_name + "zad" + lang + ".html.zip") + htmlzipfile = self.get_in_doc_dir(f"{self.short_name}zad{lang}.html.zip") # TODO: what to do with html? # if self._html_disallowed(): # raise ProblemPackageError( @@ -296,12 +325,12 @@ def _process_statements(self): pass try: - pdffile = self.get_in_doc_dir(self.short_name + "zad" + lang + ".pdf") + pdffile = self.get_in_doc_dir(f"{self.short_name}zad{lang}.pdf") if lang == "": self.statement = pdffile else: self.lang_statements[lang[1:]] = pdffile - except: + except FileNotFoundError: pass def _process_attachments(self): @@ -331,3 +360,13 @@ def _unpack_return_data(self, data: dict): """ # TODO: implement. The unpack will probably return tests, so we need to process them. pass + + def save_to_db(self, problem_id: int): + """ + Save the package to the database. If sio3pack isn't installed with Django + support, it should raise an ImproperlyConfigured exception. + """ + self._setup_django_handler(problem_id) + if not self.django_enabled: + raise ImproperlyConfigured("sio3pack is not installed with Django support.") + self.django.save_to_db() diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..29564c3 --- /dev/null +++ b/test.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +cd "`dirname $0`" + +cd tests/test_django +./manage.py makemigrations +./manage.py migrate +cd ../.. + +if [ -z "$1" ]; then + pytest -v tests/ +else + pytest -v tests/ $1 +fi diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..890beac --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,20 @@ +import pytest + +try: + import django + + __django_installed = True +except ImportError: + __django_installed = False + + +def pytest_collection_modifyitems(config, items): + for item in items: + if "no_django" in item.keywords: + if __django_installed: + item.add_marker(pytest.mark.skip(reason="Django is installed, skipping no_django tests.")) + + +def pytest_ignore_collect(collection_path, config): + if not __django_installed: + return "test_django" in str(collection_path) diff --git a/tests/packages/sinolpack/test_sinolpack.py b/tests/packages/sinolpack/test_sinolpack.py index b8860ae..d798c4a 100644 --- a/tests/packages/sinolpack/test_sinolpack.py +++ b/tests/packages/sinolpack/test_sinolpack.py @@ -18,3 +18,15 @@ def test_from_file(get_archived_package): assert package.is_archive else: assert package.rootdir == package_info.path + + +@pytest.mark.no_django +@pytest.mark.parametrize("get_package", ["simple"], indirect=True) +def test_no_django(get_package): + package_info: PackageInfo = get_package() + with pytest.raises(sio3pack.ImproperlyConfigured): + sio3pack.from_db(1) + + package = sio3pack.from_file(package_info.path) + with pytest.raises(sio3pack.ImproperlyConfigured): + package.save_to_db(1) diff --git a/tests/test_django/__init__.py b/tests/test_django/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_django/manage.py b/tests/test_django/manage.py new file mode 100755 index 0000000..8b47138 --- /dev/null +++ b/tests/test_django/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_django.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/tests/test_django/test_django/__init__.py b/tests/test_django/test_django/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_django/test_django/asgi.py b/tests/test_django/test_django/asgi.py new file mode 100644 index 0000000..b83d195 --- /dev/null +++ b/tests/test_django/test_django/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for test_django project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_django.settings") + +application = get_asgi_application() diff --git a/tests/test_django/test_django/settings.py b/tests/test_django/test_django/settings.py new file mode 100644 index 0000000..68bec3e --- /dev/null +++ b/tests/test_django/test_django/settings.py @@ -0,0 +1,125 @@ +""" +Django settings for test_django project. + +Generated by 'django-admin startproject' using Django 5.1.3. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-f$oojxszb)@)-w0&-=4+24&oa$brdv6ltj34n@25=rq1n-kq6&" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + # Here add all sio3pack apps + "sio3pack.django.sinolpack", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "test_django.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "test_django.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/tests/test_django/test_django/urls.py b/tests/test_django/test_django/urls.py new file mode 100644 index 0000000..7a1b55e --- /dev/null +++ b/tests/test_django/test_django/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for test_django project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" + +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path("admin/", admin.site.urls), +] diff --git a/tests/test_django/test_django/wsgi.py b/tests/test_django/test_django/wsgi.py new file mode 100644 index 0000000..a3bfd6b --- /dev/null +++ b/tests/test_django/test_django/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for test_django project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_django.settings") + +application = get_wsgi_application() diff --git a/tests/test_django/test_sio3pack/__init__.py b/tests/test_django/test_sio3pack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_django/test_sio3pack/test_sinolpack.py b/tests/test_django/test_sio3pack/test_sinolpack.py new file mode 100644 index 0000000..6516081 --- /dev/null +++ b/tests/test_django/test_sio3pack/test_sinolpack.py @@ -0,0 +1,22 @@ +import pytest + +import sio3pack +from sio3pack.django.sinolpack.models import SinolpackPackage +from sio3pack.packages import Sinolpack +from tests.fixtures import Compression, PackageInfo, get_archived_package + + +@pytest.mark.django_db +@pytest.mark.parametrize("get_archived_package", [("simple", c) for c in Compression], indirect=True) +def test_simple(get_archived_package): + package_info: PackageInfo = get_archived_package() + assert package_info.type == "sinolpack" + package = sio3pack.from_file(package_info.path) + assert isinstance(package, Sinolpack) + package.save_to_db(1) + assert SinolpackPackage.objects.filter(problem_id=1).exists() + db_package = SinolpackPackage.objects.get(problem_id=1) + assert db_package.short_name == package.short_name + + with pytest.raises(sio3pack.PackageAlreadyExists): + package.save_to_db(1)