diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dd8446f..23b4540 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,21 +13,25 @@ default_language_version: repos: - repo: https://github.com/adamchainz/django-upgrade - rev: 1.25.0 + rev: 1.29.1 hooks: - id: django-upgrade # uv hooks for dependency management - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.7.12 + rev: 0.9.7 hooks: + # Update the uv lockfile + - id: uv-lock + # Update the requirements.txt - id: uv-export # Standard pre-commit hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: check-yaml # - id: check-json @@ -50,12 +54,12 @@ repos: - prettier - prettier-plugin-jinja-template types_or: [html, jinja] - entry: npx prettier --plugin=prettier-plugin-jinja-template --parser=jinja-template --write + entry: bunx prettier --plugin=prettier-plugin-jinja-template --parser=jinja-template --write - id: prettier-all name: Prettier All language: node types_or: [javascript, jsx, ts, tsx, css, scss, json, yaml, markdown] - entry: npx prettier --write + entry: bunx prettier --write - repo: https://github.com/DavidAnson/markdownlint-cli2 rev: v0.18.1 @@ -65,40 +69,49 @@ repos: # Ruff for linting and formatting - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.13 + rev: v0.14.3 hooks: - - id: ruff + # Run the linter. + - id: ruff-check args: [--fix] + # Run the formatter. - id: ruff-format - # Django-specific hooks - - repo: local - hooks: - - id: django-check - name: Django Check - entry: uv run python dashboard_project/manage.py check - language: python - pass_filenames: false - types: [python] - always_run: true - additional_dependencies: [uv] + # # Django-specific hooks + # - repo: local + # hooks: + # - id: django-check + # name: Django Check + # entry: uv run python dashboard_project/manage.py check + # language: python + # pass_filenames: false + # types: [python] + # always_run: true + # additional_dependencies: [uv] - - id: django-check-migrations - name: Django Check Migrations - entry: uv run python dashboard_project/manage.py makemigrations --check --dry-run - language: python - pass_filenames: false - types: [python] - additional_dependencies: [uv] + # - id: django-check-migrations + # name: Django Check Migrations + # entry: uv run python dashboard_project/manage.py makemigrations --check --dry-run + # language: python + # pass_filenames: false + # types: [python] + # additional_dependencies: [uv] # Security checks - repo: https://github.com/pycqa/bandit - rev: 1.8.3 + rev: 1.8.6 hooks: - id: bandit args: [-c, pyproject.toml, -r, dashboard_project] # additional_dependencies: ["bandit[toml]"] + # TOML formatting and linting + - repo: https://github.com/tombi-toml/tombi-pre-commit + rev: v0.6.40 + hooks: + - id: tombi-format + - id: tombi-lint + # # Type checking # - repo: https://github.com/pre-commit/mirrors-mypy # rev: v1.15.0 diff --git a/.prettierrc b/.prettierrc index 98b51e3..f7a1bd8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/prettierrc.json", "arrowParens": "always", "bracketSpacing": true, "embeddedLanguageFormatting": "auto", @@ -29,5 +30,5 @@ } } ], - "plugins": ["prettier-plugin-jinja-template"] + "plugins": ["prettier-plugin-jinja-template", "prettier-plugin-packagejson"] } diff --git a/Dockerfile b/Dockerfile index 262ad61..bce041a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,32 +3,41 @@ FROM python:3.13-slim # Set environment variables + ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 ENV DJANGO_SETTINGS_MODULE=dashboard_project.settings # Set work directory + WORKDIR /app # Install UV for Python package management + RUN pip install uv # Copy project files + COPY pyproject.toml . COPY uv.lock . COPY . . # Install dependencies + RUN uv pip install -e . # Change to the Django project directory + WORKDIR /app/dashboard_project # Collect static files + RUN python manage.py collectstatic --noinput # Change back to the app directory + WORKDIR /app # Run gunicorn + CMD ["gunicorn", "dashboard_project.wsgi:application", "--bind", "0.0.0.0:8000"] diff --git a/Makefile b/Makefile index d5e4256..cfb6067 100644 --- a/Makefile +++ b/Makefile @@ -1,124 +1,148 @@ .PHONY: venv install install-dev lint test format clean run migrate makemigrations superuser setup-node celery celery-beat docker-build docker-up docker-down reset-db setup-dev procfile # Create a virtual environment + venv: - uv venv -p 3.13 + uv venv -p 3.13 # Install production dependencies + install: - uv pip install -e . + uv pip install -e . # Install development dependencies + install-dev: - uv pip install -e ".[dev]" + uv pip install -e ".[dev]" # Run linting + lint: - uv run -m ruff check dashboard_project + uv run -m ruff check dashboard_project # Run tests + test: - uv run -m pytest + uv run -m pytest # Format Python code + format: - uv run -m ruff format dashboard_project - uv run -m black dashboard_project + uv run -m ruff format dashboard_project + uv run -m black dashboard_project # Setup Node.js dependencies + setup-node: - npm install --include=dev + npm install --include=dev # Clean Python cache files + clean: - find . -type d -name "__pycache__" -exec rm -rf {} + - find . -type f -name "*.pyc" -delete - find . -type f -name "*.pyo" -delete - find . -type f -name "*.pyd" -delete - find . -type d -name "*.egg-info" -exec rm -rf {} + - find . -type d -name "*.egg" -exec rm -rf {} + - find . -type d -name ".pytest_cache" -exec rm -rf {} + - find . -type d -name ".coverage" -exec rm -rf {} + - find . -type d -name "htmlcov" -exec rm -rf {} + - find . -type d -name ".ruff_cache" -exec rm -rf {} + - find . -type d -name ".mypy_cache" -exec rm -rf {} + - find . -type d -name ".tox" -exec rm -rf {} + - find . -type d -name "node_modules" -exec rm -rf {} + - rm -rf build/ - rm -rf dist/ + find . -type d -name "__pycache__" -exec rm -rf {} + + find . -type f -name "*.pyc" -delete + find . -type f -name "*.pyo" -delete + find . -type f -name "*.pyd" -delete + find . -type d -name "*.egg-info" -exec rm -rf {} + + find . -type d -name "*.egg" -exec rm -rf {} + + find . -type d -name ".pytest_cache" -exec rm -rf {} + + find . -type d -name ".coverage" -exec rm -rf {} + + find . -type d -name "htmlcov" -exec rm -rf {} + + find . -type d -name ".ruff_cache" -exec rm -rf {} + + find . -type d -name ".mypy_cache" -exec rm -rf {} + + find . -type d -name ".tox" -exec rm -rf {} + + find . -type d -name "node_modules" -exec rm -rf {} + + rm -rf build/ + rm -rf dist/ # Run the development server + run: - cd dashboard_project && uv run python manage.py runserver 8001 + cd dashboard_project && uv run python manage.py runserver 8001 # Run Celery worker for background tasks + celery: - cd dashboard_project && uv run celery -A dashboard_project worker --loglevel=info + cd dashboard_project && uv run celery -A dashboard_project worker --loglevel=info # Run Celery Beat for scheduled tasks + celery-beat: - cd dashboard_project && uv run celery -A dashboard_project beat --scheduler django_celery_beat.schedulers:DatabaseScheduler + cd dashboard_project && uv run celery -A dashboard_project beat --scheduler django_celery_beat.schedulers:DatabaseScheduler # Apply migrations + migrate: - cd dashboard_project && uv run python manage.py migrate + cd dashboard_project && uv run python manage.py migrate # Create migrations + makemigrations: - cd dashboard_project && uv run python manage.py makemigrations + cd dashboard_project && uv run python manage.py makemigrations # Create a superuser + superuser: - cd dashboard_project && uv run python manage.py createsuperuser + cd dashboard_project && uv run python manage.py createsuperuser # Update uv lock file + lock: - uv pip freeze > requirements.lock + uv pip freeze > requirements.lock # Setup pre-commit hooks + setup-pre-commit: - pre-commit install + pre-commit install # Run pre-commit on all files + lint-all: - pre-commit run --all-files + pre-commit run --all-files # Docker commands + docker-build: - docker-compose build + docker-compose build docker-up: - docker-compose up -d + docker-compose up -d docker-down: - docker-compose down + docker-compose down # Initialize or reset the database in development + reset-db: - cd dashboard_project && uv run python manage.py flush --no-input - cd dashboard_project && uv run python manage.py migrate + cd dashboard_project && uv run python manage.py flush --no-input + cd dashboard_project && uv run python manage.py migrate # Start a Redis server in development (if not installed, fallback to SQLite) + run-redis: - redis-server || echo "Redis not installed, using SQLite fallback" + redis-server || echo "Redis not installed, using SQLite fallback" # Start all development services (web, redis, celery, celery-beat) + run-all: - foreman start + foreman start procfile: - foreman start + foreman start # Test Celery task + test-celery: - cd dashboard_project && uv run python manage.py test_celery + cd dashboard_project && uv run python manage.py test_celery # Initialize data integration + init-data-integration: - cd dashboard_project && uv run python manage.py create_default_datasource - cd dashboard_project && uv run python manage.py create_default_datasource - cd dashboard_project && uv run python manage.py test_celery + cd dashboard_project && uv run python manage.py create_default_datasource + cd dashboard_project && uv run python manage.py create_default_datasource + cd dashboard_project && uv run python manage.py test_celery # Setup development environment + setup-dev: venv install-dev migrate create_default_datasource - @echo "Development environment setup complete" + @echo "Development environment setup complete" diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..bb52be8 --- /dev/null +++ b/bun.lock @@ -0,0 +1,204 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "devDependencies": { + "markdownlint-cli2": "^0.18.1", + "prettier": "^3.6.2", + "prettier-plugin-jinja-template": "^2.1.0", + "prettier-plugin-packagejson": "^2.5.19", + }, + }, + }, + "packages": { + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], + + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/katex": ["@types/katex@0.16.7", "", {}, "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-indent": ["detect-indent@7.0.2", "", {}, "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A=="], + + "detect-newline": ["detect-newline@4.0.1", "", {}, "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "git-hooks-list": ["git-hooks-list@4.1.1", "", {}, "sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "globby": ["globby@14.1.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.3", "ignore": "^7.0.3", "path-type": "^6.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.3.0" } }, "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA=="], + + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "katex": ["katex@0.16.25", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q=="], + + "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], + + "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="], + + "markdownlint": ["markdownlint@0.38.0", "", { "dependencies": { "micromark": "4.0.2", "micromark-core-commonmark": "2.0.3", "micromark-extension-directive": "4.0.0", "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", "micromark-util-types": "2.0.2" } }, "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ=="], + + "markdownlint-cli2": ["markdownlint-cli2@0.18.1", "", { "dependencies": { "globby": "14.1.0", "js-yaml": "4.1.0", "jsonc-parser": "3.3.1", "markdown-it": "14.1.0", "markdownlint": "0.38.0", "markdownlint-cli2-formatter-default": "0.0.5", "micromatch": "4.0.8" }, "bin": { "markdownlint-cli2": "markdownlint-cli2-bin.mjs" } }, "sha512-/4Osri9QFGCZOCTkfA8qJF+XGjKYERSHkXzxSyS1hd3ZERJGjvsUao2h4wdnvpHp6Tu2Jh/bPHM0FE9JJza6ng=="], + + "markdownlint-cli2-formatter-default": ["markdownlint-cli2-formatter-default@0.0.5", "", { "peerDependencies": { "markdownlint-cli2": ">=0.0.4" } }, "sha512-4XKTwQ5m1+Txo2kuQ3Jgpo/KmnG+X90dWt4acufg6HVGadTUG5hzHF/wssp9b5MBYOMCnZ9RMPaU//uHsszF8Q=="], + + "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-directive": ["micromark-extension-directive@4.0.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-math": ["micromark-extension-math@3.1.0", "", { "dependencies": { "@types/katex": "^0.16.0", "devlop": "^1.0.0", "katex": "^0.16.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "path-type": ["path-type@6.0.0", "", {}, "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + + "prettier-plugin-jinja-template": ["prettier-plugin-jinja-template@2.1.0", "", { "peerDependencies": { "prettier": "^3.0.0" } }, "sha512-mzoCp2Oy9BDSug80fw3B3J4n4KQj1hRvoQOL1akqcDKBb5nvYxrik9zUEDs4AEJ6nK7QDTGoH0y9rx7AlnQ78Q=="], + + "prettier-plugin-packagejson": ["prettier-plugin-packagejson@2.5.19", "", { "dependencies": { "sort-package-json": "3.4.0", "synckit": "0.11.11" }, "peerDependencies": { "prettier": ">= 1.16.0" }, "optionalPeers": ["prettier"] }, "sha512-Qsqp4+jsZbKMpEGZB1UP1pxeAT8sCzne2IwnKkr+QhUe665EXUo3BAvTf1kAPCqyMv9kg3ZmO0+7eOni/C6Uag=="], + + "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + + "sort-object-keys": ["sort-object-keys@1.1.3", "", {}, "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg=="], + + "sort-package-json": ["sort-package-json@3.4.0", "", { "dependencies": { "detect-indent": "^7.0.1", "detect-newline": "^4.0.1", "git-hooks-list": "^4.0.0", "is-plain-obj": "^4.1.0", "semver": "^7.7.1", "sort-object-keys": "^1.1.3", "tinyglobby": "^0.2.12" }, "bin": { "sort-package-json": "cli.js" } }, "sha512-97oFRRMM2/Js4oEA9LJhjyMlde+2ewpZQf53pgue27UkbEXfHJnDzHlUxQ/DWUkzqmp7DFwJp8D+wi/TYeQhpA=="], + + "synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], + + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + } +} diff --git a/dashboard_project/__main__.py b/dashboard_project/__main__.py index 1327369..926d054 100644 --- a/dashboard_project/__main__.py +++ b/dashboard_project/__main__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# !/usr/bin/env python """ Entry point for Django commands executed as Python modules. This enables commands like `python -m runserver`. diff --git a/dashboard_project/asgi.py b/dashboard_project/asgi.py index 78c9588..b1497b1 100644 --- a/dashboard_project/asgi.py +++ b/dashboard_project/asgi.py @@ -4,7 +4,7 @@ ASGI config for dashboard_project 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/4.0/howto/deployment/asgi/ + """ import os diff --git a/dashboard_project/dashboard/management/__init__.py b/dashboard_project/dashboard/management/__init__.py index 5a95e76..319959a 100644 --- a/dashboard_project/dashboard/management/__init__.py +++ b/dashboard_project/dashboard/management/__init__.py @@ -1,2 +1,3 @@ # dashboard/management/__init__.py + # This file is intentionally left empty to mark the directory as a Python package diff --git a/dashboard_project/dashboard/management/commands/__init__.py b/dashboard_project/dashboard/management/commands/__init__.py index c095440..cec16b7 100644 --- a/dashboard_project/dashboard/management/commands/__init__.py +++ b/dashboard_project/dashboard/management/commands/__init__.py @@ -1,2 +1,3 @@ # dashboard/management/commands/__init__.py + # This file is intentionally left empty to mark the directory as a Python package diff --git a/dashboard_project/dashboard/templatetags/__init__.py b/dashboard_project/dashboard/templatetags/__init__.py index db9731b..d797df1 100644 --- a/dashboard_project/dashboard/templatetags/__init__.py +++ b/dashboard_project/dashboard/templatetags/__init__.py @@ -1,2 +1,3 @@ # dashboard/templatetags/__init__.py + # This file is intentionally left empty to mark the directory as a Python package diff --git a/dashboard_project/dashboard/views.py b/dashboard_project/dashboard/views.py index 401811b..5497be7 100644 --- a/dashboard_project/dashboard/views.py +++ b/dashboard_project/dashboard/views.py @@ -200,12 +200,10 @@ def chat_session_detail_view(request, session_id): # Check if this is an AJAX navigation request if is_ajax_navigation(request): html_content = render_to_string("dashboard/chat_session_detail.html", context, request=request) - return JsonResponse( - { - "html": html_content, - "title": f"Chat Session {session_id} | Chat Analytics", - } - ) + return JsonResponse({ + "html": html_content, + "title": f"Chat Session {session_id} | Chat Analytics", + }) return render(request, "dashboard/chat_session_detail.html", context) @@ -282,12 +280,10 @@ def edit_dashboard_view(request, dashboard_id): # Check if this is an AJAX navigation request if is_ajax_navigation(request): html_content = render_to_string("dashboard/dashboard_form.html", context, request=request) - return JsonResponse( - { - "html": html_content, - "title": f"Edit Dashboard: {dashboard.name} | Chat Analytics", - } - ) + return JsonResponse({ + "html": html_content, + "title": f"Edit Dashboard: {dashboard.name} | Chat Analytics", + }) return render(request, "dashboard/dashboard_form.html", context) @@ -349,6 +345,8 @@ def delete_data_source_view(request, data_source_id): # API views for dashboard data + + @login_required def dashboard_data_api(request, dashboard_id): """API endpoint for dashboard data""" @@ -450,26 +448,24 @@ def search_chat_sessions(request): # Check if this is an AJAX pagination request if request.headers.get("X-Requested-With") == "XMLHttpRequest": - return JsonResponse( - { - "status": "success", - "html_data": render(request, "dashboard/partials/search_results_table.html", context).content.decode( - "utf-8" - ), - "page_obj": { - "number": page_obj.number, - "has_previous": page_obj.has_previous(), - "has_next": page_obj.has_next(), - "previous_page_number": page_obj.previous_page_number() if page_obj.has_previous() else None, - "next_page_number": page_obj.next_page_number() if page_obj.has_next() else None, - "paginator": { - "num_pages": page_obj.paginator.num_pages, - "count": page_obj.paginator.count, - }, + return JsonResponse({ + "status": "success", + "html_data": render(request, "dashboard/partials/search_results_table.html", context).content.decode( + "utf-8" + ), + "page_obj": { + "number": page_obj.number, + "has_previous": page_obj.has_previous(), + "has_next": page_obj.has_next(), + "previous_page_number": page_obj.previous_page_number() if page_obj.has_previous() else None, + "next_page_number": page_obj.next_page_number() if page_obj.has_next() else None, + "paginator": { + "num_pages": page_obj.paginator.num_pages, + "count": page_obj.paginator.count, }, - "query": query, - } - ) + }, + "query": query, + }) return render(request, "dashboard/search_results.html", context) @@ -554,26 +550,24 @@ def data_view(request): # Check if this is an AJAX pagination request if request.headers.get("X-Requested-With") == "XMLHttpRequest": - return JsonResponse( - { - "status": "success", - "html_data": render(request, "dashboard/partials/data_table.html", context).content.decode("utf-8"), - "page_obj": { - "number": page_obj.number, - "has_previous": page_obj.has_previous(), - "has_next": page_obj.has_next(), - "previous_page_number": page_obj.previous_page_number() if page_obj.has_previous() else None, - "next_page_number": page_obj.next_page_number() if page_obj.has_next() else None, - "paginator": { - "num_pages": page_obj.paginator.num_pages, - "count": page_obj.paginator.count, - }, + return JsonResponse({ + "status": "success", + "html_data": render(request, "dashboard/partials/data_table.html", context).content.decode("utf-8"), + "page_obj": { + "number": page_obj.number, + "has_previous": page_obj.has_previous(), + "has_next": page_obj.has_next(), + "previous_page_number": page_obj.previous_page_number() if page_obj.has_previous() else None, + "next_page_number": page_obj.next_page_number() if page_obj.has_next() else None, + "paginator": { + "num_pages": page_obj.paginator.num_pages, + "count": page_obj.paginator.count, }, - "view": view, - "avg_response_time": avg_response_time, - "avg_messages": avg_messages, - "escalation_rate": escalation_rate, - } - ) + }, + "view": view, + "avg_response_time": avg_response_time, + "avg_messages": avg_messages, + "escalation_rate": escalation_rate, + }) return render(request, "dashboard/data_view.html", context) diff --git a/dashboard_project/dashboard/views_export.py b/dashboard_project/dashboard/views_export.py index f43510c..8259cb5 100644 --- a/dashboard_project/dashboard/views_export.py +++ b/dashboard_project/dashboard/views_export.py @@ -91,51 +91,47 @@ def export_chats_csv(request): writer = csv.writer(response) # Write CSV header - writer.writerow( - [ - "Session ID", - "Start Time", - "End Time", - "IP Address", - "Country", - "Language", - "Messages Sent", - "Sentiment", - "Escalated", - "Forwarded HR", - "Full Transcript", - "Avg Response Time (s)", - "Tokens", - "Tokens EUR", - "Category", - "Initial Message", - "User Rating", - ] - ) + writer.writerow([ + "Session ID", + "Start Time", + "End Time", + "IP Address", + "Country", + "Language", + "Messages Sent", + "Sentiment", + "Escalated", + "Forwarded HR", + "Full Transcript", + "Avg Response Time (s)", + "Tokens", + "Tokens EUR", + "Category", + "Initial Message", + "User Rating", + ]) # Write data rows for session in sessions: - writer.writerow( - [ - session.session_id, - session.start_time, - session.end_time, - session.ip_address, - session.country, - session.language, - session.messages_sent, - session.sentiment, - "Yes" if session.escalated else "No", - "Yes" if session.forwarded_hr else "No", - session.full_transcript, - session.avg_response_time, - session.tokens, - session.tokens_eur, - session.category, - session.initial_msg, - session.user_rating, - ] - ) + writer.writerow([ + session.session_id, + session.start_time, + session.end_time, + session.ip_address, + session.country, + session.language, + session.messages_sent, + session.sentiment, + "Yes" if session.escalated else "No", + "Yes" if session.forwarded_hr else "No", + session.full_transcript, + session.avg_response_time, + session.tokens, + session.tokens_eur, + session.category, + session.initial_msg, + session.user_rating, + ]) return response diff --git a/dashboard_project/dashboard_project/asgi.py b/dashboard_project/dashboard_project/asgi.py index 78c9588..b1497b1 100644 --- a/dashboard_project/dashboard_project/asgi.py +++ b/dashboard_project/dashboard_project/asgi.py @@ -4,7 +4,7 @@ ASGI config for dashboard_project 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/4.0/howto/deployment/asgi/ + """ import os diff --git a/dashboard_project/dashboard_project/celery.py b/dashboard_project/dashboard_project/celery.py index 360787d..b7bbee6 100644 --- a/dashboard_project/dashboard_project/celery.py +++ b/dashboard_project/dashboard_project/celery.py @@ -2,18 +2,24 @@ import os from celery import Celery -# Set the default Django settings module for the 'celery' program. +# Set the default Django settings module for the 'celery' program + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings") app = Celery("dashboard_project") # Using a string here means the worker doesn't have to serialize -# the configuration object to child processes. + +# the configuration object to child processes + # - namespace='CELERY' means all celery-related configuration keys -# should have a `CELERY_` prefix. + +# should have a `CELERY_` prefix + app.config_from_object("django.conf:settings", namespace="CELERY") -# Load task modules from all registered Django app configs. +# Load task modules from all registered Django app configs + app.autodiscover_tasks() diff --git a/dashboard_project/dashboard_project/settings.py b/dashboard_project/dashboard_project/settings.py index 4ab2620..f589345 100644 --- a/dashboard_project/dashboard_project/settings.py +++ b/dashboard_project/dashboard_project/settings.py @@ -7,6 +7,7 @@ from pathlib import Path from django.core.management.utils import get_random_secret_key # Load environment variables from .env file if present + try: from dotenv import load_dotenv @@ -14,18 +15,22 @@ try: except ImportError: pass -# Build paths inside the project like this: BASE_DIR / 'subdir'. +# Build paths inside the project like this: BASE_DIR / 'subdir' + BASE_DIR = Path(__file__).resolve().parent.parent -# SECURITY WARNING: keep the secret key used in production secret! +# SECURITY WARNING: keep the secret key used in production secret + SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", get_random_secret_key()) -# SECURITY WARNING: don't run with debug turned on in production! +# SECURITY WARNING: don't run with debug turned on in production + DEBUG = os.environ.get("DJANGO_DEBUG", "True") == "True" ALLOWED_HOSTS = [] # Application definition + INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", @@ -80,6 +85,7 @@ TEMPLATES = [ WSGI_APPLICATION = "dashboard_project.wsgi.application" # Database + DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", @@ -88,6 +94,7 @@ DATABASES = { } # Password validation + AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", @@ -104,12 +111,14 @@ AUTH_PASSWORD_VALIDATORS = [ ] # Internationalization + LANGUAGE_CODE = "en-US" TIME_ZONE = "Europe/Amsterdam" USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) + STATIC_URL = "static/" STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), @@ -125,23 +134,28 @@ STORAGES = { } # Media files + MEDIA_URL = "/media/" MEDIA_ROOT = os.path.join(BASE_DIR, "media") # Default primary key field type + DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Crispy Forms + CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" # Authentication + AUTH_USER_MODEL = "accounts.CustomUser" LOGIN_REDIRECT_URL = "dashboard" LOGOUT_REDIRECT_URL = "login" ACCOUNT_LOGOUT_ON_GET = True # django-allauth + AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", @@ -150,7 +164,9 @@ SITE_ID = 1 ACCOUNT_EMAIL_VERIFICATION = "none" # Celery Configuration + # Check if Redis is available + try: import redis @@ -184,6 +200,7 @@ CELERY_TIMEZONE = TIME_ZONE CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" # Get schedule from environment variables or use defaults + CHAT_DATA_FETCH_INTERVAL = int(os.environ.get("CHAT_DATA_FETCH_INTERVAL", 3600)) # Default: 1 hour CELERY_BEAT_SCHEDULE = { diff --git a/dashboard_project/dashboard_project/wsgi.py b/dashboard_project/dashboard_project/wsgi.py index ec69107..3136932 100644 --- a/dashboard_project/dashboard_project/wsgi.py +++ b/dashboard_project/dashboard_project/wsgi.py @@ -4,7 +4,7 @@ WSGI config for dashboard_project 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/4.0/howto/deployment/wsgi/ + """ import os diff --git a/dashboard_project/data_integration/management/commands/fix_datasource_schema.py b/dashboard_project/data_integration/management/commands/fix_datasource_schema.py index 04c7cb3..d288433 100644 --- a/dashboard_project/data_integration/management/commands/fix_datasource_schema.py +++ b/dashboard_project/data_integration/management/commands/fix_datasource_schema.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# !/usr/bin/env python """ Migration Fix Script for ExternalDataSource diff --git a/dashboard_project/data_integration/management/commands/test_datasource_schema.py b/dashboard_project/data_integration/management/commands/test_datasource_schema.py index 1118287..9b75603 100644 --- a/dashboard_project/data_integration/management/commands/test_datasource_schema.py +++ b/dashboard_project/data_integration/management/commands/test_datasource_schema.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# !/usr/bin/env python """ Test the ExternalDataSource Model Schema diff --git a/dashboard_project/data_integration/models.py b/dashboard_project/data_integration/models.py index b5175c6..06ca964 100644 --- a/dashboard_project/data_integration/models.py +++ b/dashboard_project/data_integration/models.py @@ -39,7 +39,7 @@ class ChatMessage(models.Model): class ExternalDataSource(models.Model): name = models.CharField(max_length=255, default="External API") - api_url = models.URLField(default="https://proto.notso.ai/jumbo/chats") + api_url = models.URLField(default="") auth_username = models.CharField(max_length=255, blank=True, null=True) auth_password = models.CharField( max_length=255, blank=True, null=True diff --git a/dashboard_project/data_integration/tests.py b/dashboard_project/data_integration/tests.py index a39b155..d85108a 100644 --- a/dashboard_project/data_integration/tests.py +++ b/dashboard_project/data_integration/tests.py @@ -1 +1 @@ -# Create your tests here. +# Create your tests here diff --git a/dashboard_project/data_integration/views.py b/dashboard_project/data_integration/views.py index d159334..f669421 100644 --- a/dashboard_project/data_integration/views.py +++ b/dashboard_project/data_integration/views.py @@ -7,7 +7,7 @@ from .models import ExternalDataSource from .tasks import periodic_fetch_chat_data, refresh_specific_source from .utils import fetch_and_store_chat_data -# Create your views here. +# Create your views here def is_superuser(user): diff --git a/dashboard_project/manage.py b/dashboard_project/manage.py index a2baa97..fc183f7 100644 --- a/dashboard_project/manage.py +++ b/dashboard_project/manage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# !/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os diff --git a/dashboard_project/scripts/cleanup_duplicates.py b/dashboard_project/scripts/cleanup_duplicates.py index 5abaa0f..570c70b 100644 --- a/dashboard_project/scripts/cleanup_duplicates.py +++ b/dashboard_project/scripts/cleanup_duplicates.py @@ -4,6 +4,7 @@ import os import sys # Add the project root to sys.path + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings") diff --git a/dashboard_project/scripts/fix_dashboard_data.py b/dashboard_project/scripts/fix_dashboard_data.py index 9f7f236..cc73479 100755 --- a/dashboard_project/scripts/fix_dashboard_data.py +++ b/dashboard_project/scripts/fix_dashboard_data.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +# !/usr/bin/env python + # scripts/fix_dashboard_data.py import os @@ -15,11 +16,13 @@ from django.db import transaction from django.utils.timezone import make_aware # Set up Django environment + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings") django.setup() # SCRIPT CONFIG + CREATE_TEST_DATA = False # Set to True to create sample data if none exists COMPANY_NAME = "Notso AI" # The company name to use diff --git a/dashboard_project/static/css/dashboard.css b/dashboard_project/static/css/dashboard.css index abcca83..fdb6570 100644 --- a/dashboard_project/static/css/dashboard.css +++ b/dashboard_project/static/css/dashboard.css @@ -1,10 +1,11 @@ /** - * dashboard.css - Styles specific to dashboard functionality + +* dashboard.css - Styles specific to dashboard functionality */ -/* Theme variables */ +/*Theme variables */ :root { - /* Light theme (default) */ + /* Light theme (default)*/ --bg-color: #f8f9fa; --text-color: #212529; --card-bg: #ffffff; @@ -26,7 +27,7 @@ color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; } -/* Dark theme */ +/*Dark theme*/ [data-bs-theme="dark"] { --bg-color: #212529; --text-color: #f8f9fa; @@ -47,7 +48,7 @@ --icon-color: #6ea8fe; } -/* Apply theme variables */ +/*Apply theme variables*/ body { background-color: var(--bg-color); color: var(--text-color); @@ -91,7 +92,7 @@ body { background-color: var(--sidebar-bg) !important; } -/* Sidebar navigation styling with dark mode support */ +/*Sidebar navigation styling with dark mode support*/ .sidebar .nav-link { color: var(--text-color); transition: all 0.2s ease; @@ -168,7 +169,7 @@ body { color: var(--text-color); } -/* Footer */ +/*Footer*/ footer { background-color: var(--card-bg); border-top: 1px solid var(--border-color); @@ -182,7 +183,7 @@ footer { background-color: var(--navbar-bg); } -/* Dashboard grid layout */ +/*Dashboard grid layout*/ .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); @@ -193,7 +194,7 @@ footer { /* Increased gap */ } -/* Dashboard widget cards */ +/*Dashboard widget cards*/ .dashboard-widget { display: flex; @@ -254,7 +255,7 @@ footer { /* Consistent padding */ } -/* Chart widgets */ +/*Chart widgets*/ .chart-widget .card-body { display: flex; flex-direction: column; @@ -270,7 +271,7 @@ footer { /* Ensure it takes full width of card body */ } -/* Stat widgets / Stat Cards */ +/*Stat widgets / Stat Cards*/ .stat-card { text-align: center; padding: 1.5rem; @@ -319,7 +320,7 @@ footer { margin-bottom: 0; } -/* Dashboard theme variations */ +/*Dashboard theme variations*/ .dashboard-theme-light .card { background-color: #fff; } @@ -344,7 +345,7 @@ footer { color: #adb5bd; } -/* Time period selector */ +/*Time period selector*/ .time-period-selector { display: flex; align-items: center; @@ -367,7 +368,7 @@ footer { font-size: 0.875rem; } -/* Custom metric selector */ +/*Custom metric selector*/ .metric-selector { max-width: 100%; overflow-x: auto; @@ -388,7 +389,7 @@ footer { border-radius: 0.25rem; } -/* Dashboard loading states */ +/*Dashboard loading states*/ .widget-placeholder { min-height: 300px; background: linear-gradient(90deg, #e9ecef 25%, #f8f9fa 50%, #e9ecef 75%); @@ -413,7 +414,7 @@ footer { } } -/* Dashboard empty states */ +/*Dashboard empty states*/ .empty-state { padding: 2.5rem; @@ -449,7 +450,7 @@ footer { margin-top: 1rem; } -/* Responsive adjustments */ +/*Responsive adjustments*/ @media (width <=767.98px) { .dashboard-grid { grid-template-columns: 1fr; @@ -471,7 +472,7 @@ footer { } } -/* Preserve colored background for stat cards in both themes */ +/*Preserve colored background for stat cards in both themes*/ .col-md-3 .card.stats-card.bg-primary { background-color: var(--bs-primary) !important; color: white !important; @@ -507,7 +508,7 @@ footer { color: var(--bs-dark) !important; } -/* Stats Cards Alignment Fix (Bottom Align, No Overlap) */ +/*Stats Cards Alignment Fix (Bottom Align, No Overlap)*/ .stats-row { display: flex; flex-wrap: wrap; diff --git a/dashboard_project/static/css/style.css b/dashboard_project/static/css/style.css index 6b0763a..20c49b1 100644 --- a/dashboard_project/static/css/style.css +++ b/dashboard_project/static/css/style.css @@ -1,8 +1,9 @@ /** - * style.css - Global styles for the application + +* style.css - Global styles for the application */ -/* General Styles */ +/*General Styles*/ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, @@ -25,14 +26,14 @@ body { /* Ensures body takes at least full viewport height */ } -/* Navbar adjustments (if needed, Bootstrap usually handles this well) */ +/*Navbar adjustments (if needed, Bootstrap usually handles this well)*/ .navbar { box-shadow: 0 2px 4px rgb(0 0 0 / 5%); /* Subtle shadow for depth */ } -/* Helper Classes */ +/*Helper Classes*/ .text-truncate-2 { display: -webkit-box; -webkit-line-clamp: 2; @@ -49,7 +50,7 @@ body { min-width: 150px; } -/* Card styles */ +/*Card styles*/ .card { border: 1px solid #e0e5e9; @@ -91,7 +92,7 @@ body { font-weight: 600; } -/* Sidebar enhancements */ +/*Sidebar enhancements*/ .sidebar { background-color: #fff; @@ -159,7 +160,7 @@ body { margin-top: 1rem; } -/* Dashboard stats cards */ +/*Dashboard stats cards*/ .stats-card { border-radius: 0.5rem; overflow: hidden; @@ -176,14 +177,14 @@ body { opacity: 0.8; } -/* Chart containers */ +/*Chart containers*/ .chart-container { width: 100%; height: 300px; position: relative; } -/* Loading overlay */ +/*Loading overlay*/ .loading-overlay { position: fixed; top: 0; @@ -197,7 +198,7 @@ body { z-index: 9999; } -/* Table enhancements */ +/*Table enhancements*/ .table { border-color: #e0e5e9; } @@ -224,7 +225,7 @@ body { /* Consistent hover with sidebar */ } -/* Form improvements */ +/*Form improvements*/ .form-control, .form-select { border-color: #ced4da; @@ -246,7 +247,7 @@ body { /* Bootstrap focus shadow */ } -/* Button styling */ +/*Button styling*/ .btn { border-radius: 0.375rem; @@ -281,13 +282,13 @@ body { border-color: #545b62; } -/* Alert styling */ +/*Alert styling*/ .alert { border-radius: 0.375rem; padding: 0.9rem 1.25rem; } -/* Chat transcript styling */ +/*Chat transcript styling*/ .chat-transcript { background-color: #f8f9fa; border: 1px solid #e9ecef; @@ -304,7 +305,7 @@ body { margin-bottom: 0; } -/* Footer styling */ +/*Footer styling*/ footer { background-color: #fff; @@ -318,7 +319,7 @@ footer { /* Added for sticky footer */ } -/* Responsive adjustments */ +/*Responsive adjustments*/ @media (width <=767.98px) { .main-content { margin-left: 0; @@ -337,7 +338,7 @@ footer { } } -/* Print styles */ +/*Print styles*/ @media print { .sidebar, .navbar, diff --git a/dashboard_project/static/js/ajax-navigation.js b/dashboard_project/static/js/ajax-navigation.js index 7e069bc..f167429 100644 --- a/dashboard_project/static/js/ajax-navigation.js +++ b/dashboard_project/static/js/ajax-navigation.js @@ -1,8 +1,9 @@ /** - * ajax-navigation.js - JavaScript for AJAX-based navigation across the entire application - * - * This script handles AJAX navigation between pages in the Chat Analytics Dashboard. - * It intercepts link clicks, loads content via AJAX, and updates the browser history. + +* ajax-navigation.js - JavaScript for AJAX-based navigation across the entire application +* +* This script handles AJAX navigation between pages in the Chat Analytics Dashboard. +* It intercepts link clicks, loads content via AJAX, and updates the browser history. */ document.addEventListener("DOMContentLoaded", function () { diff --git a/dashboard_project/static/js/ajax-pagination.js b/dashboard_project/static/js/ajax-pagination.js index 3dcff3a..13176a4 100644 --- a/dashboard_project/static/js/ajax-pagination.js +++ b/dashboard_project/static/js/ajax-pagination.js @@ -1,8 +1,9 @@ /** - * ajax-pagination.js - Common JavaScript for AJAX pagination across the application - * - * This script handles AJAX-based pagination for all pages in the Chat Analytics Dashboard. - * It intercepts pagination link clicks, loads content via AJAX, and updates the browser history. + +* ajax-pagination.js - Common JavaScript for AJAX pagination across the application +* +* This script handles AJAX-based pagination for all pages in the Chat Analytics Dashboard. +* It intercepts pagination link clicks, loads content via AJAX, and updates the browser history. */ document.addEventListener("DOMContentLoaded", function () { diff --git a/dashboard_project/static/js/dashboard.js b/dashboard_project/static/js/dashboard.js index e634d06..8ba13fa 100644 --- a/dashboard_project/static/js/dashboard.js +++ b/dashboard_project/static/js/dashboard.js @@ -1,9 +1,10 @@ /** - * dashboard.js - JavaScript for the dashboard functionality - * - * This file handles the interactive features of the dashboard, - * including chart refreshing, dashboard filtering, and dashboard - * customization. + +* dashboard.js - JavaScript for the dashboard functionality +* +* This file handles the interactive features of the dashboard, +* including chart refreshing, dashboard filtering, and dashboard +* customization. */ document.addEventListener("DOMContentLoaded", function () { diff --git a/dashboard_project/static/js/main.js b/dashboard_project/static/js/main.js index 475bed0..78625fe 100644 --- a/dashboard_project/static/js/main.js +++ b/dashboard_project/static/js/main.js @@ -1,8 +1,9 @@ /** - * main.js - Global JavaScript functionality - * - * This file contains general JavaScript functionality used across - * the entire application, including navigation, forms, and UI interactions. + +* main.js - Global JavaScript functionality +* +* This file contains general JavaScript functionality used across +* the entire application, including navigation, forms, and UI interactions. */ document.addEventListener("DOMContentLoaded", function () { diff --git a/dashboard_project/wsgi.py b/dashboard_project/wsgi.py index ec69107..3136932 100644 --- a/dashboard_project/wsgi.py +++ b/dashboard_project/wsgi.py @@ -4,7 +4,7 @@ WSGI config for dashboard_project 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/4.0/howto/deployment/wsgi/ + """ import os diff --git a/dev.sh b/dev.sh index 7bafe9a..3c48b7b 100755 --- a/dev.sh +++ b/dev.sh @@ -1,10 +1,13 @@ #!/bin/bash + # LiveGraphsDjango Development Helper Script # Set UV_LINK_MODE to copy to avoid hardlink warnings + export UV_LINK_MODE=copy # Function to print section header + print_header() { echo "======================================" echo "🚀 $1" @@ -12,6 +15,7 @@ print_header() { } # Display help menu + if [[ $1 == "help" ]] || [[ $1 == "-h" ]] || [[ $1 == "--help" ]] || [[ -z $1 ]]; then print_header "LiveGraphsDjango Development Commands" echo "Usage: ./dev.sh COMMAND" @@ -33,6 +37,7 @@ if [[ $1 == "help" ]] || [[ $1 == "-h" ]] || [[ $1 == "--help" ]] || [[ -z $1 ]] fi # Start Redis server + if [[ $1 == "redis-start" ]]; then print_header "Starting Redis Server" redis-server --daemonize yes @@ -46,6 +51,7 @@ if [[ $1 == "redis-start" ]]; then fi # Test Redis connection + if [[ $1 == "redis-test" ]]; then print_header "Testing Redis Connection" cd dashboard_project && python manage.py test_redis @@ -53,6 +59,7 @@ if [[ $1 == "redis-test" ]]; then fi # Stop Redis server + if [[ $1 == "redis-stop" ]]; then print_header "Stopping Redis Server" redis-cli shutdown @@ -61,6 +68,7 @@ if [[ $1 == "redis-stop" ]]; then fi # Run migrations + if [[ $1 == "migrate" ]]; then print_header "Running Migrations" cd dashboard_project && UV_LINK_MODE=copy uv run python manage.py migrate @@ -68,6 +76,7 @@ if [[ $1 == "migrate" ]]; then fi # Make migrations + if [[ $1 == "makemigrations" ]]; then print_header "Creating Migrations" cd dashboard_project && UV_LINK_MODE=copy uv run python manage.py makemigrations @@ -75,6 +84,7 @@ if [[ $1 == "makemigrations" ]]; then fi # Create superuser + if [[ $1 == "superuser" ]]; then print_header "Creating Superuser" cd dashboard_project && UV_LINK_MODE=copy uv run python manage.py createsuperuser @@ -82,6 +92,7 @@ if [[ $1 == "superuser" ]]; then fi # Test Celery + if [[ $1 == "test-celery" ]]; then print_header "Testing Celery" cd dashboard_project && UV_LINK_MODE=copy uv run python manage.py test_celery @@ -89,6 +100,7 @@ if [[ $1 == "test-celery" ]]; then fi # View Celery logs + if [[ $1 == "logs-celery" ]]; then print_header "Celery Worker Logs" echo "Press Ctrl+C to exit" @@ -97,6 +109,7 @@ if [[ $1 == "logs-celery" ]]; then fi # View Celery Beat logs + if [[ $1 == "logs-beat" ]]; then print_header "Celery Beat Logs" echo "Press Ctrl+C to exit" @@ -105,6 +118,7 @@ if [[ $1 == "logs-beat" ]]; then fi # Django shell + if [[ $1 == "shell" ]]; then print_header "Django Shell" cd dashboard_project && UV_LINK_MODE=copy uv run python manage.py shell @@ -112,6 +126,7 @@ if [[ $1 == "shell" ]]; then fi # Start the application + if [[ $1 == "start" ]]; then print_header "Starting LiveGraphsDjango Application" ./start.sh @@ -119,6 +134,7 @@ if [[ $1 == "start" ]]; then fi # Invalid command + echo "❌ Unknown command: $1" echo "Run './dev.sh help' to see available commands" exit 1 diff --git a/package.json b/package.json index 48d481e..a0a1fc4 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,26 @@ { - "devDependencies": { - "markdownlint-cli2": "^0.18.1", - "prettier": "^3.5.3", - "prettier-plugin-jinja-template": "^2.1.0" - }, "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", - "lint:md": "markdownlint-cli2 \"**/*.md\" \"!.trunk/**\" \"!.venv/**\" \"!node_modules/**\"", - "lint:md:fix": "markdownlint-cli2 --fix \"**/*.md\" \"!.trunk/**\" \"!.venv/**\" \"!node_modules/**\"" + "lint:md": "markdownlint-cli2 \"**/*.md\"", + "lint:md:fix": "bun lint:md -- --fix" + }, + "devDependencies": { + "markdownlint-cli2": "^0.18.1", + "prettier": "^3.6.2", + "prettier-plugin-jinja-template": "^2.1.0", + "prettier-plugin-packagejson": "^2.5.19" }, "markdownlint-cli2": { "config": { - "MD007": { - "indent": 4, - "start_indented": false, - "start_indent": 4 - }, "MD013": false, - "MD030": { - "ul_single": 3, - "ol_single": 2, - "ul_multi": 3, - "ol_multi": 2 - }, "MD033": false }, "ignores": [ - "node_modules", ".git", - "*.json" + ".trunk", + ".venv", + "node_modules" ] } } diff --git a/pyproject.toml b/pyproject.toml index 383a16a..7680220 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,18 +4,16 @@ version = "0.1.0" description = "Live Graphs Django Dashboard" readme = "README.md" requires-python = ">=3.13" -authors = [{ name = "LiveGraphs Team" }] license = { text = "MIT" } - +authors = [{ name = "LiveGraphs Team" }] classifiers = [ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.13", "Framework :: Django", "Framework :: Django :: 5.2", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.13", ] - dependencies = [ "bleach[css]>=6.2.0", "celery[sqlalchemy]>=5.5.2", @@ -37,6 +35,11 @@ dependencies = [ "xlsxwriter>=3.2.3", ] +[project.urls] +"Bug Tracker" = "https://github.com/kjanat/livegraphsdjango/issues" +"Documentation" = "https://github.com/kjanat/livegraphsdjango#readme" +"Source" = "https://github.com/kjanat/livegraphsdjango" + [dependency-groups] dev = [ "bandit>=1.8.3", @@ -55,14 +58,64 @@ dev = [ requires = ["setuptools>=69.0.0", "wheel>=0.42.0"] build-backend = "setuptools.build_meta" -[tool.setuptools] -packages = ["dashboard_project"] +[tool.bandit] +exclude_dirs = [ + "tests", + "venv", + ".venv", + ".git", + "__pycache__", + "migrations", + "**/create_sample_data.py", +] +skips = ["B101"] +targets = ["dashboard_project"] -[tool.setuptools.package-data] -"dashboard_project" = ["static/**/*", "templates/**/*", "media/**/*"] +[tool.coverage.run] +source = ["dashboard_project"] +omit = [ + "dashboard_project/manage.py", + "dashboard_project/*/migrations/*", + "dashboard_project/*/tests/*", +] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "raise NotImplementedError", + "if __name__ == .__main__.:", + "pass", + "raise ImportError", +] + +[tool.django-stubs] +django_settings_module = "dashboard_project.settings" + +[tool.mypy] +python_version = "3.13" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = false +disallow_incomplete_defs = false +plugins = ["mypy_django_plugin.main"] + +[[tool.mypy.overrides]] +module = ["django.*", "rest_framework.*"] +ignore_missing_imports = true + +[tool.pytest.ini_options] +filterwarnings = [ + "ignore::DeprecationWarning", + "ignore::PendingDeprecationWarning", +] +python_files = "test_*.py" +testpaths = ["dashboard_project"] +DJANGO_SETTINGS_MODULE = "dashboard_project.settings" [tool.ruff] -# Exclude a variety of commonly ignored directories. +preview = true +# Exclude a variety of commonly ignored directories exclude = [ ".bzr", ".direnv", @@ -91,11 +144,9 @@ exclude = [ "site-packages", "venv", ] - -# Same as Black. +# Same as Black line-length = 120 indent-width = 4 - # Assume Python 3.13 target-version = "py313" @@ -110,62 +161,8 @@ quote-style = "double" indent-style = "space" line-ending = "lf" -[tool.bandit] -exclude_dirs = [ - "tests", - "venv", - ".venv", - ".git", - "__pycache__", - "migrations", - "**/create_sample_data.py", -] -skips = ["B101"] -targets = ["dashboard_project"] +[tool.setuptools] +packages = ["dashboard_project"] -[tool.mypy] -python_version = "3.13" -warn_return_any = true -warn_unused_configs = true -disallow_untyped_defs = false -disallow_incomplete_defs = false -plugins = ["mypy_django_plugin.main"] - -[[tool.mypy.overrides]] -module = ["django.*", "rest_framework.*"] -ignore_missing_imports = true - -[tool.django-stubs] -django_settings_module = "dashboard_project.settings" - -[tool.pytest.ini_options] -DJANGO_SETTINGS_MODULE = "dashboard_project.settings" -python_files = "test_*.py" -testpaths = ["dashboard_project"] -filterwarnings = [ - "ignore::DeprecationWarning", - "ignore::PendingDeprecationWarning", -] - -[tool.coverage.run] -source = ["dashboard_project"] -omit = [ - "dashboard_project/manage.py", - "dashboard_project/*/migrations/*", - "dashboard_project/*/tests/*", -] - -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "raise NotImplementedError", - "if __name__ == .__main__.:", - "pass", - "raise ImportError", -] - -[project.urls] -"Documentation" = "https://github.com/kjanat/livegraphsdjango#readme" -"Source" = "https://github.com/kjanat/livegraphsdjango" -"Bug Tracker" = "https://github.com/kjanat/livegraphsdjango/issues" +[tool.setuptools.package-data] +"dashboard_project" = ["static/__/*", "templates/__/*", "media/**/*"] diff --git a/start.sh b/start.sh index 04547ce..73a490a 100755 --- a/start.sh +++ b/start.sh @@ -1,14 +1,18 @@ #!/bin/bash + # Set UV_LINK_MODE to copy to avoid hardlink warnings + export UV_LINK_MODE=copy # Check if Redis is running + if ! redis-cli ping >/dev/null 2>&1; then echo "Starting Redis server..." redis-server --daemonize yes sleep 1 # Verify Redis is now running + if redis-cli ping >/dev/null 2>&1; then echo "✅ Redis server is now running" else @@ -22,6 +26,7 @@ else fi # Set environment variables for Redis if it's running + if redis-cli ping >/dev/null 2>&1; then export CELERY_BROKER_URL=redis://localhost:6379/0 export CELERY_RESULT_BACKEND=redis://localhost:6379/0 @@ -33,4 +38,5 @@ else fi # Start the application using foreman + foreman start