[project]
name = "bpp_iplweb"
version = "202605.1372"
description = ""
authors = [
    { name = "Michał Pasternak", email = "michal.dtz@gmail.com" }
]
requires-python = ">=3.11,<3.15"
dependencies = [
    "Django>=5.2.14,<5.3",
    "arrow>=1.3,<2",
    "numpy>=2.4.4",
    "pygad>=3.6.0",
    "django-denorm-iplweb>=1.10.2",
    "django-tabular-permissions==2.9.3",
    "simplejson>=4.1.1,<5",
    "django-reversion>=6,<7",
    "django-reversion-compare>=0.19.2",
    "channels[daphne]>=4.3.2,<5",
    "uvicorn[standard]>=0.46.0",
    "channels-redis>=4,<5",
    "django-flexible-reports>=0.2.12",
    "django-group-by==0.3.1",
    "django-columns==0.1.0",
    "django-dynamic-admin-columns>=0.4.4",
    "tablib==3.9.0",
    "openpyxl>=3.1.5",
    "django-dirtyfields==1.9.9",
    "django-messages-extends==0.6.3",
    "django-autoslug==1.9.9",
    "django-autocomplete-light==4.0.0",
    "django-admin-tools==0.9.3",
    "xlrd==2.0.2",
    "celery>=5.6.3",
    "django-celery-results==2.6.0",
    "django-tables2>=3.0.0,<3.1",
    "django-sendfile2>=0.7.2",
    "Unidecode>=1.3,<2",
    "django-cookie-law==2.2.0",
    "django-classy-tags==4.1.0",
    "pyoai==2.5.0",
    "django-crispy-forms>=2.6,<3",
    "crispy-forms-foundation>=1.1.0,<2",
    "django-compressor==4.6.0",
    "nh3>=0.3.5",
    "django-session-security>=2.6.8",
    "django-password-policies-iplweb==0.9.0",
    "dj-pagination==2.5.0",
    "requests>=2.34.0",
    "django-extensions>=3.2,<5",
    "django-minify-html>=1.14,<2",
    "Pillow>=12.2,<14",
    "django-grappelli>=5.0.0,<6",
    "django-multiseek==0.9.49",
    "django-braces>=1.15.0",
    "django-loginas==0.3.14",
    "django-webmaster-verification==0.4.3",
    "django-favicon-plus-reloaded>=1.2,<2",
    "django-querysetsequence==0.18",
    "pypandoc>=1.13,<2",
    "django-static-sitemaps>=5.0.0",
    "django-model-utils==5.0.0",
    "django-fsm-2>=4,<5",
    "django-mptt>=0.16,<1",
    "wosclient==0.1.5",
    "MOAI-iplweb>=2.0.2",
    "django_redis_iplweb>=6.0.1,<6.1",
    "django-filter>=25.2,<25.3",
    "dbfread>=2.0.7",
    "djangorestframework==3.17.1",
    "Markdown>=3.7,<4",
    "django-admin-sortable2>=2.3.1,<3",
    "cssmin==0.2.0",
    "rjsmin>=1.2.1",
    "django-import-export>=4.4.1,<5",
    "pymed-iplweb==0.8.11",
    "django-taggit>=5,<7",
    "weasyprint>=66.0",
    "requests-oauthlib==2.0.0",
    "django-cacheops>=7.2,<8",
    "django-constance>=4.3.5",
    "djangoql==0.19.1",
    "django-weasyprint>=2.5.0",
    "django-templated-email>=3.0.0",
    "django-formtools>=2.5.1,<3",
    "pathspec>=1.1.1",
    "django-environ>=0.13.0",
    "django-celery-email>=3.0.0",
    "django-easy-audit>=1.3.9a2,<2",
    "crossrefapi>=1.7.0",
    "urllib3>=2.7.0,<3.0.0",
    "pandas>=3.0.3",
    "django-tinymce>=5.0.0",
    "crispy-bootstrap5>=2026.3",
    "crispy-bootstrap3>=2022.1",
    "twisted[http2,tls]>=25.5.0",
    "django-dbtemplates-iplweb>=4.3.2",
    "django-soft-delete>=1.0.23",
    "tqdm>=4.67.3",
    "xhtml2pdf>=0.2.16",
    "isbnlib>=3.10.14",
    "chardet>=7.4.3",
    "python-dateutil>=2.9.0.post0",
    "thefuzz>=0.22.1",
    "celery-singleton>=0.3.1",
    "rollbar>=1.3.0",
    "django-prometheus>=2.4.1",
    "ortools>=9.15.6755",
    "psycopg2-binary>=2.9.12", # ZOSTAW psycopg2 dla django-denorm
    "python-docx>=1.1.0", # Post-processing DOCX dla page breaks
    "questionary>=2.0.0", # Interactive CLI menus
    "bibtexparser>=2.0.0b7", # BibTeX import
    "langdetect>=1.0.9", # Language detection for imports
    "beautifulsoup4>=4.14.3", # importer_publikacji.providers.www imports at app-ready time
    "gunicorn>=26.0.0", # authserver WSGI runner (moved from docker/authserver build-time uv pip install)
    "watchdog>=5.0.3", # --reload mode dla uvicorn/celery w dev compose (moved from entrypoint `uv pip install`)
    "sqlparse>=0.5.4", # Dependabot: transitive via Django, bumped to fix DoS in format_list_of_tuples
    "django-countdown",
    "django-formdefaults>=0.6.2",
    # django-pg-baseline: zewnetrzny pakiet zarzadzajacy baseline.sql + monkey-patch
    # tworzenia testowej bazy. Dawniej wewnetrzny `src/django_pg_baseline/`.
    # Dependency runtime, bo INSTALLED_APPS odwoluje sie do niego z kazdego srodowiska
    # (testy/dev/baseline_rebuild). Sam runtime nic nie kosztuje gdy PG_BASELINE
    # niezdefiniowane (no-op AppConfig.ready).
    "django-pg-baseline>=0.3.0",
]

[project.optional-dependencies]
ldap = ["django-auth-ldap>=5.3.0"]
office365 = ["django-microsoft-auth>=3.0.1"]
baseline-rebuild = ["testcontainers[postgres]>=4.14.2"]
dev = [
    "bumpver>=2023.1124",
    "pytest-recording>=0.12.1",
    "vcrpy>=8.1.1,<9",
    "towncrier>=22.8.0",
    "pytest-sugar>=0.9.7",
    "pytest-rerunfailures>=14.0",
    "django-debug-toolbar>=6.3.0",
    "model-bakery>=1.23.4",
    "django-dynamic-fixture>=1.8.0",
    "django-webtest==1.9.14",
    "WebTest==3.0.0",
    "Sphinx>=7,<9",
    "coveralls>=4.1.0,<5",
    "psutil>=7.2.2",
    # pytest 8.x: pytest-testcontainers-django (0.2.0) deklaruje pytest<9.
    # Wewnetrznie korzysta tylko z stabilnego API hookow (pytest_load_initial_conftests
    # etc.) — wiec dziala z pytest 9, ale upstream constraint wymusza <9 w
    # resolverze. Po bumpie pytest-testcontainers-django na pytest>=8,<10
    # mozna podniesc te wersje. Patrz pytest-env constraint nizej.
    "pytest>=8,<9",
    "pytest-django>=4.12.0",
    "pytest-httpserver>=1.1.5",
    "werkzeug>=3.1.6", # Dependabot: transitive via pytest-httpserver, bump for safe_join() fixes
    "pytest-cov>=7.1.0",
    "pytest-mock>=3.7.0",
    "pytest-xdist>=2.3.0",
    "pytest-shard>=0.1.2",
    "pytest-timeout>=2.1.0",
    "pyparsing>=3.3.2",
    "ruff>=0.15.12",
    "djlint>=1.36,<2",
    "pre-commit>=4.6.0",
    "twine>=5.1.0",
    "ipdb>=0.13.9",
    "ipython>=8.2.0",
    "playwright>=1.59.0,<2",
    "pytest-playwright>=0.4.0",
    # pytest-env 1.3.0+ wymaga pytest>=9.0.2 (sprawdz `pip show pytest-env`)
    # — zostaje przy 1.2.0, kompatybilnym z pytest 8.x. Zachowuje sie tak
    # samo dla naszego usage (env=DJANGO_ALLOW_ASYNC_UNSAFE=1 etc.). Po
    # bumpie pytest do 9.x, podnies tez te wersje.
    "pytest-env>=1.1.5,<1.3",
    "nest-asyncio>=1.6.0",
    "testcontainers[postgres]>=4.14.2",
    # pytest-testcontainers + bridge: dawniej wewnetrzny `src/testcontainers_bpp/`.
    # Plugin auto-loaduje sie przez entry-point `pytest11` — `-p ...` w pytest.ini
    # przestaje byc potrzebne. Konfiguracja w [tool.pytest-testcontainers-django].
    "pytest-testcontainers>=0.1.0",
    "pytest-testcontainers-django>=0.2.0",
    # django-run-site: CLI orchestrator dev stack-u. Dawniej `manage.py run_site`
    # + helpery w `_run_site_helpers/`. Instalowany w dev-deps zeby `uv run run-site`
    # mialo dostep do CLI bez globalnego `uv tool install`. Konfiguracja: runsite.toml.
    "run-site>=0.4.0",
    # django-dev-helpers: autologin endpoint + dotfiles dla agentow LLM. Dawniej
    # `src/django_bpp/views_run_site_autologin.py`. Aktywuje sie tylko gdy run-site
    # ustawi DJANGO_DEV_HELPERS_ENABLED=1 — w produkcji no-op.
    "django-dev-helpers>=0.1.9",
]

# [project.scripts] i [project.entry-points] usuniete - BPP nie jest
# instalowany jako package (uv sync --no-install-project wszedzie).
# Pluginy pytest (pytest-testcontainers, pytest-testcontainers-django,
# django-pg-baseline) auto-loaduja sie przez wlasne entry-pointy `pytest11`.
# Skrypt `bpp-manage.py` byl dead code (Makefile/Dockerfile uzywaja
# `python src/manage.py` bezposrednio).

[tool.setuptools.packages.find]
where = ["src"]
include = [
    "api_v1",
    "bpp",
    "crossref_bpp",
    "django_bpp",
    "ewaluacja2021",
    "fixtures",
    "import_common",
    "import_dyscyplin",
    "import_list_if",
    "import_pracownikow",
    "integrator2",
    "komparator_pbn",
    "zglos_publikacje",
    "long_running",
    "miniblog",
    "notifications",
    "nowe_raporty",
    "oswiadczenia",
    "pbn_api",
    "pbn_export_queue",
    "raport_slotow",
    "ranking_autorow",
    "rozbieznosci_dyscyplin",
    "rozbieznosci_if",
    "test_bpp"
]

[tool.setuptools.package-data]
"*" = [
    "static/**/*",
    "locale/**/*.mo",
    "locale/**/*.po",
    "migrations/*.sql",
    "migrations/*.txt",
    "fixtures/*",
    "fixtures/*.json"
]

[tool.bumpver]
current_version = "v202605.1372"
version_pattern = "vYYYY0M.BUILD[-TAGNUM]"
commit_message = "bump version {old_version} -> {new_version}"
commit = true
tag = false
push = false

[tool.uv]
environments = ["python_version >= '3.10' and python_version < '3.15' and platform_python_implementation != 'PyPy'"]

# Polityka: KAZDA NOWA zewnetrzna zaleznosc powinna miec prebuilt wheel dla
# naszej macierzy (Linux x86_64 + macOS arm64, Python 3.10-3.14). Sdist
# wykonuje `setup.py` podczas instalacji - klasyczny wektor supply-chain
# (zlosliwy kod uruchomiony przez `uv sync`). Praktyka #1 z lirantal/
# pypi-security-best-practices.
#
# Pre-existing exceptions (sdist-only, accepted risk - audit kwiecien 2026):
# crispy-forms-foundation, cssmin, django-autocomplete-light, django-columns,
# django-static-sitemaps, django-tabular-permissions, langdetect, pylatexenc,
# pyoai, python-ldap, wsgiutils. Wymiana na wheel-publishing alternatywy
# tracked w follow-up. Patrz docs/SECURITY_PRACTICES.md.
#
# Egzekwowanie: brak hard-gate (`uv` nie ma czystego "wheel-only with
# exceptions" trybu - albo no-build globalnie, albo nic). Polegamy na:
# 1. PR review (PULL_REQUEST_TEMPLATE.md ma checkbox dla nowych dep)
# 2. Trivy CVE scan w docker-bake build (Faza 2 build-docker-images.yml)
# 3. uv-secure CVE scan w dependency-audit.yml workflow
#
# Workspace package (bpp-iplweb) NIE jest instalowany - `uv sync` wszedzie
# uzywa --no-install-project. Pluginy pytest (pytest-testcontainers,
# pytest-testcontainers-django, django-pg-baseline) auto-loaduja sie przez
# wlasne entry-pointy `pytest11`.
#
# Gdy nowa dep nie ma wheel: znajdz alternatywe LUB zglos wheel u
# maintainera. NIE dodawaj do listy wyjatkow bez review.

# Jawne zadeklarowanie indeksu PyPI jako jedynego, domyslnego zrodla
# pakietow. Chroni przed dependency confusion: gdyby kiedys pojawil sie
# prywatny indeks, ten wpis musi pozostac jawnie default = true a nowy
# indeks dodawany jako pierwszy z `explicit = true` aby tylko wybrane
# pakiety byly z niego pobierane. Patrz docs/SECURITY_PRACTICES.md.
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
default = true

[tool.uv.sources]
# django-countdown jeszcze nie ma na PyPI (PyPI nie wpuscil pakietu pod ta nazwa).
# Pobieramy z taga GitHubowego — `uv` cachuje sklon pod `~/.cache/uv/git-v0/`,
# kolejne `uv sync` nie ciagna ponownie. Po publikacji na PyPI: wymien wpis na
# wersjowany constraint w [project] dependencies i usun ta linie.
django-countdown = { git = "https://github.com/iplweb/django-countdown.git", tag = "v0.2.0" }

# pytest-testcontainers-django jeszcze nie ma na PyPI (PyPI rate-limit po
# stronie maintainera). Pobieramy z taga GitHubowego — `uv` cachuje sklon
# pod `~/.cache/uv/git-v0/`, kolejne `uv sync` nie ciagna ponownie.
# Po publikacji na PyPI: usun ten blok w calosci, `uv sync` przejdzie
# na `>=0.2.0` z [project.optional-dependencies].dev.
#
# CI/Docker: builderskie obrazy nie instaluja [dev], wiec brak tego pakietu
# na PyPI nie blokuje produkcyjnego buildu. Production runtime nie potrzebuje
# pytest-testcontainers* ani run-site/django-dev-helpers.
pytest-testcontainers-django = { git = "https://github.com/iplweb/pytest-testcontainers-django", tag = "v0.2.0" }

# Konfiguracja `pytest-testcontainers-django` — pluginu pytest, ktory
# startuje kontenery PG/Redis przed importem Django settings i wstrzykuje
# host:port do os.environ. Zastepuje wewnetrzny `src/testcontainers_bpp/`.
#
# - `postgres_image`: zachowujemy wlasny obraz BPP (ma plpython3u i locale
#   pl_PL.UTF-8) zamiast vanilla `postgres:16` — schemat i dane zaleza
#   od tego.
# - `postgres_init_scripts`: baseline.sql jest mountowany do
#   /docker-entrypoint-initdb.d/, dzieki czemu jest replayowany przez
#   entrypoint Postgresa zanim TCP zacznie akceptowac polaczenia. Razem
#   z `db_test_template_env` daje "instant" tworzenie test-DB przez
#   CREATE DATABASE WITH TEMPLATE.
# - `db_*_env`: nazwy zmiennych srodowiskowych ktore plugin pisze do
#   os.environ. Pasuja do tych, ktore Django settings (`base.py`) czyta
#   przez `env(...)`.
# - `redis_enabled`: BPP wymaga Redisa do testow (channels, cacheops).
[tool.pytest-testcontainers-django]
postgres_image = "iplweb/bpp_dbserver:psql-16.13"
postgres_user = "bpp"
postgres_password = "password"
postgres_database = "bpp"
postgres_init_scripts = ["baseline-sql/baseline.sql"]
postgres_template = "bpp"
postgres_env = { POSTGRESQL_UNSAFE_BUT_FAST = "1", POSTGRESQL_MAX_LOCKS_PER_TRANSACTION = "512" }
db_host_env = "DJANGO_BPP_DB_HOST"
db_port_env = "DJANGO_BPP_DB_PORT"
db_name_env = "DJANGO_BPP_DB_NAME"
db_user_env = "DJANGO_BPP_DB_USER"
db_password_env = "DJANGO_BPP_DB_PASSWORD"
db_test_template_env = "DJANGO_BPP_TEST_TEMPLATE"
skip_dotenv_env = "DJANGO_BPP_SKIP_DOTENV"
# Migracja env-var semantyki: poprzednio `BPP_USE_TESTCONTAINERS=0`/=`1`
# wlaczalo/wylaczalo plugin (domyslnie ON). Nowy plugin uzywa
# `PYTEST_TESTCONTAINERS_DISABLE=1` jako "wylacz" (domyslnie OFF =
# wlaczone). Workflow:
#   - lokalnie: nic nie ustawiamy → kontenery startuja same.
#   - CI z docker-compose: `PYTEST_TESTCONTAINERS_DISABLE=1` (patrz
#     docker-compose.test.yml) zeby plugin nie startowal docker-in-docker.
#   - reuse miedzy runs: `PYTEST_TESTCONTAINERS_REUSE=1`.
disable_env = "PYTEST_TESTCONTAINERS_DISABLE"
reuse_env = "PYTEST_TESTCONTAINERS_REUSE"
redis_enabled = true
redis_image = "redis:7-alpine"
redis_host_env = "DJANGO_BPP_REDIS_HOST"
redis_port_env = "DJANGO_BPP_REDIS_PORT"

[tool.bumpver.file_patterns]
"pyproject.toml" = ['current_version = "{version}"',
    'version = "{pep440_version}"']
"src/django_bpp/version.py" = ['VERSION = "{pep440_version}"']
"package.json" = ['"version": "{version}",']
"Makefile" = [ 'DOCKER_VERSION={pep440_version}' ]




[tool.ruff]
line-length = 88
target-version = "py310"
extend-exclude = [
    "src/*/migrations/*",
    "src/nowe_raporty/tests",
    "src/raport_slotow/tests",
    "src/pbn_api/tests",
    "src/pbn_export_queue/tests",
    "src/bpp/tests",
    "src/zglos_publikacje/tests",
    "src/import_common/tests",
    "src/django_bpp/staticroot/notifications/js/tests",
    "src/notifications/static/notifications/js/tests",
    "src/fixtures"
]

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "W", "C90", "DJ"]
ignore = ["E501", "B008", "DJ008"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"

[tool.ruff.lint.isort]
known-first-party = ["bpp", "django_bpp"]
split-on-trailing-comma = true

[tool.djlint]
# Walidacja templatów Django pod kątem strukturalnych pułapek minifikatora
# HTML (django-minify-html). Zakres celowo wąski — egzekwujemy reguły
# które bezpośrednio łapią bugi rozjeżdżające layout (puste/niezamknięte
# tagi), ignorujemy kosmetykę (formatowanie, indentacja, alt-text)
# żeby nie blokować pre-commit-u na istniejącym codebase.
profile = "django"
extension = "html"
# Reguły aktywne (przykłady):
#   H020 - empty tag pair (puste <li></li>, <span></span> itd.)
#   H025 - orphan/unmatched tag
#   T001 - variables not wrapped in spacing
# Reguły wyciszone (głośne na obecnym codebase, niezwiązane z minify bugiem):
#   H006 - img alt attribute (accessibility, do osobnego sprintu)
#   H013 - img alt attribute (alias H006)
#   H019 - http -> https w atrybutach
#   H021 - inline styles (mamy historyczne)
#   H023 - entity references (czyste &amp;)
#   H030/H031 - meta description / og tags
#   T002/T003 - quote style w {% %} tagach
#   D018/J018 - django/jinja link patterns
ignore = "H006,H008,H013,H014,H019,H021,H023,H030,H031,T001,T002,T003,D018,J018"

[tool.coverage.run]
# Support for parallel test runners
parallel = true
concurrency = ["multiprocessing"]
# Measure branch coverage
branch = true
# Omit test files and migrations from coverage
omit = [
    "*/migrations/*",
    "*/tests/*",
    "*/test_*.py",
    "*/__pycache__/*",
    "*/site-packages/*"
]

[tool.coverage.report]
# Exclude test code and migrations from report
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
    "if typing.TYPE_CHECKING:",
]
precision = 2

[tool.coverage.html]
directory = "cov_html"

[tool.coverage.xml]
output = "cov.xml"

[build-system]
requires = ["setuptools>=82.0.1", "wheel"]
build-backend = "setuptools.build_meta"

[dependency-groups]
dev = [
    "pytest-repeat>=0.9.4",
    "vulture>=2.16",
]

[tool.towncrier]
package = "bpp"
version = "202605.1372"
package_dir = "src"
filename = "HISTORY.rst"

[[tool.towncrier.type]]
directory = "bugfix"
showcontent = true
name = "Naprawione"

[[tool.towncrier.type]]
directory = "doc"
showcontent = true
name = "Dokumentacja"


[[tool.towncrier.type]]
directory = "feature"
showcontent = true
name = "Usprawnienie"


[[tool.towncrier.type]]
directory = "removal"
showcontent = true
name = "Usunięto"
