From 14465fe28609d3d932ee643e0441f6e72d4293b3 Mon Sep 17 00:00:00 2001 From: Samuel Therrien Date: Fri, 22 Mar 2024 14:33:57 -0400 Subject: [PATCH 1/6] Add CI, autofixes, and checks configurations --- .editorconfig | 16 +++ .github/workflows/canopeum_backend.yml | 44 ++++++ .markdownlint.json | 5 + .pre-commit-config.yaml | 28 ++++ .vscode/extensions.json | 47 +++++++ .vscode/settings.json | 114 +++++++++++++++ README.md | 52 +++++-- canopeum_backend/canopeum_backend/asgi.py | 2 +- canopeum_backend/canopeum_backend/models.py | 65 ++++++--- .../canopeum_backend/serializers.py | 29 ++-- canopeum_backend/canopeum_backend/settings.py | 131 +++++++++--------- canopeum_backend/canopeum_backend/urls.py | 54 +++----- canopeum_backend/canopeum_backend/views.py | 86 ++++++++---- canopeum_backend/canopeum_backend/wsgi.py | 2 +- canopeum_backend/manage.py | 12 +- canopeum_backend/pyproject.toml | 131 ++++++++++++++++++ canopeum_backend/requirements-dev.txt | 18 +++ docs/VSCode_select_venv.png | Bin 0 -> 24294 bytes 18 files changed, 660 insertions(+), 176 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/canopeum_backend.yml create mode 100644 .markdownlint.json create mode 100644 .pre-commit-config.yaml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 canopeum_backend/pyproject.toml create mode 100644 canopeum_backend/requirements-dev.txt create mode 100644 docs/VSCode_select_venv.png diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..6c0337a8f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{py,pyi}] +indent_size = 4 diff --git a/.github/workflows/canopeum_backend.yml b/.github/workflows/canopeum_backend.yml new file mode 100644 index 000000000..4c49f37f2 --- /dev/null +++ b/.github/workflows/canopeum_backend.yml @@ -0,0 +1,44 @@ +on: + push: + pull_request: + branches: + - main + +jobs: + mypy: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "windows-latest"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: "**/requirements*.txt" + - run: pip install -r requirements-dev.txt + working-directory: canopeum_backend + - run: mypy . --python-version=3.12 + working-directory: canopeum_backend + + pyright: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "windows-latest"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: "**/requirements*.txt" + - run: pip install -r requirements-dev.txt + working-directory: canopeum_backend + - uses: jakebailey/pyright-action@v2 + with: + python-version: "3.12" + working-directory: canopeum_backend diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..f00360d49 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "default": true, + "MD013": false, // Use line-wrap for Markdown + "MD029": false // Allow explicit ordered list number for lists split by block codes +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..b8cdc39e8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# You can run this locally with `pre-commit run [--all]` +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: pretty-format-json + exclude: ".vscode/.*" # Exclude jsonc + args: [--autofix, --no-sort-keys] + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=lf] + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: check-case-conflict + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 # must match canopeum_backend/requirements-dev.txt + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format + +ci: + autoupdate_schedule: quarterly diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..e726d9428 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,47 @@ +// Keep in alphabetical order +{ + "recommendations": [ + "bierner.github-markdown-preview", + "charliermarsh.ruff", + "davidanson.vscode-markdownlint", + "eamodio.gitlens", + "github.vscode-github-actions", + "ms-python.mypy-type-checker", + "ms-python.python", + "ms-python.vscode-pylance", + // "ms-vscode.powershell", + "pkief.material-icon-theme", + // "redhat.vscode-xml", + "redhat.vscode-yaml", + "tamasfe.even-better-toml", + ], + "unwantedRecommendations": [ + // Must disable in this workspace // + // https://github.com/microsoft/vscode/issues/40239 // + // + // even-better-toml has format on save + "bungcip.better-toml", + // VSCode has implemented an optimized version + "coenraads.bracket-pair-colorizer", + "coenraads.bracket-pair-colorizer-2", + "shardulm94.trailing-spaces", + // Don't use two mypy extensions simultaneously + "matangover.mypy", + // Obsoleted by Pylance + "ms-pyright.pyright", + // + // Don't recommend to autoinstall // + // + // Use Ruff instead + "ms-python.black-formatter", + "ms-python.flake8", + "ms-python.isort", + "ms-python.pylint", + // We use autopep8 + "ms-python.black-formatter", + // Not configurable per workspace, tends to conflict with other linters + "sonarsource.sonarlint-vscode", + // Prefer using VSCode itself as a text editor + "vscodevim.vim", + ], +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0967a03eb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,114 @@ +{ + "editor.rulers": [ + 80, + 120, + ], + "[git-commit]": { + "editor.rulers": [ + 72, + ], + }, + "[markdown]": { + "files.trimTrailingWhitespace": false, + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.eol": "\n", + "editor.comments.insertSpace": true, + "editor.insertSpaces": true, + "editor.detectIndentation": false, + "editor.tabSize": 2, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + // Let dedicated linter (Ruff) organize imports + "source.organizeImports": "never", + }, + "files.associations": { + ".flake8": "properties", + "*.qrc": "xml", + "*.ui": "xml", + ".markdownlint.json": "jsonc", + }, + "search.exclude": { + "**/*.code-search": true, + "*.lock": true, + }, + // Set the default formatter to help avoid Prettier + "[json][jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features", + }, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.tabSize": 4, + "editor.rulers": [ + 72, // PEP8-17 docstrings + // 79, // PEP8-17 default max + // 88, // Black/Ruff default + // 99, // PEP8-17 acceptable max + 120, // Our hard rule + ], + }, + "mypy-type-checker.importStrategy": "fromEnvironment", + "mypy-type-checker.args": [ + // https://github.com/microsoft/vscode-mypy/issues/37#issuecomment-1602702174 + "--config-file=${workspaceFolder}/canopeum_backend/pyproject.toml", + ], + "python.terminal.activateEnvironment": true, + // python.analysis is Pylance (pyright) configurations + "python.analysis.fixAll": [ + "source.convertImportFormat" + // Explicitly omiting "source.unusedImports", can be annoying when commenting code for debugging + ], + "python.analysis.diagnosticMode": "workspace", + "ruff.importStrategy": "fromEnvironment", + // Use the Ruff extension instead + "isort.check": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.codeFormatting.autoCorrectAliases": true, + "powershell.codeFormatting.trimWhitespaceAroundPipe": true, + "powershell.codeFormatting.useConstantStrings": true, + "powershell.codeFormatting.useCorrectCasing": true, + "powershell.codeFormatting.whitespaceBetweenParameters": true, + "powershell.integratedConsole.showOnStartup": false, + "terminal.integrated.defaultProfile.windows": "PowerShell", + "terminal.integrated.defaultProfile.linux": "pwsh", + "terminal.integrated.defaultProfile.osx": "pwsh", + "xml.codeLens.enabled": true, + "xml.format.spaceBeforeEmptyCloseTag": false, + "xml.format.preserveSpace": [ + // Default + "xsl:text", + "xsl:comment", + "xsl:processing-instruction", + "literallayout", + "programlisting", + "screen", + "synopsis", + "pre", + "xd:pre", + // Custom + "string" + ], + "[toml]": { + "editor.defaultFormatter": "tamasfe.even-better-toml" + }, + "evenBetterToml.formatter.alignComments": false, + "evenBetterToml.formatter.alignEntries": false, + "evenBetterToml.formatter.allowedBlankLines": 1, + "evenBetterToml.formatter.arrayAutoCollapse": true, + "evenBetterToml.formatter.arrayAutoExpand": true, + "evenBetterToml.formatter.arrayTrailingComma": true, + "evenBetterToml.formatter.columnWidth": 80, + "evenBetterToml.formatter.compactArrays": true, + "evenBetterToml.formatter.compactEntries": false, + "evenBetterToml.formatter.compactInlineTables": false, + "evenBetterToml.formatter.indentEntries": false, + "evenBetterToml.formatter.indentTables": false, + "evenBetterToml.formatter.inlineTableExpand": false, + "evenBetterToml.formatter.reorderArrays": true, + "evenBetterToml.formatter.trailingNewline": true, + // We like keeping TOML keys in a certain non-alphabetical order that feels more natural + "evenBetterToml.formatter.reorderKeys": false +} diff --git a/README.md b/README.md index 8d8c7caa1..82e8ba190 100644 --- a/README.md +++ b/README.md @@ -11,38 +11,66 @@ Follow these instructions to get the project up and running on your local machin ### Prerequisites For frontend + - Node.js - npm (Node Package Manager) - Mockoon For backend -- Python (3.x recommended) +- Python 3.12 - Docker - ### Installation 1. Clone the repository: -```bash +```shell git clone git@github.com:BesLogic/releaf-canopeum.git cd releaf-canopeum ``` -2. Set up Django backend and Database: (Skip this section for Frontend only) +2. Set up a Python 3.12 virtual environment -```bash -docker compose up +```shell +cd canopeum_backend +python3.12 -m venv .venv +``` + +or on Windows if "python3.12" is not a recognized command: + +```powershell cd canopeum_backend -python -m venv .venv +py -3.12 -m venv .venv +``` + +Then activate the environemnt (you need to do this everytime if your editor isn't configured to do so): + +```shell source .venv/scripts/activate -python -m pip install -r requirements.txt +``` + +and on Windows: + +```powershell +.venv/scripts/activate +``` + +In VSCode (Windows): +`CTRL+Shift+P` (Open Command Palette) > `Python: Select Interpreter` +![VSCode_select_venv](/docs/VSCode_select_venv.png) + +3. Set up Django backend and Database: (Skip this section for Frontend only) + +```shell +docker compose up +cd canopeum_backend +python -m pip install -r requirements-dev.txt python manage.py migrate python manage.py runserver ``` -3. Set up React frontend: +4. Set up React frontend: -```bash +```shell cd canopeum_frontend npm install npm run dev @@ -50,7 +78,7 @@ npm run dev Run mock data (For Frontend only) -```bash +```shell # In another CLI npm install -g @mockoon/cli cd releaf-canopeum/canopeum_frontend @@ -59,7 +87,7 @@ mockoon-cli start --data canopeum-mockoon.json ### Folder Architecture -```bash +```ini project_name/ │ ├── backend/ # Django backend diff --git a/canopeum_backend/canopeum_backend/asgi.py b/canopeum_backend/canopeum_backend/asgi.py index 7a0439bb2..5c2c7e69a 100644 --- a/canopeum_backend/canopeum_backend/asgi.py +++ b/canopeum_backend/canopeum_backend/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'canopeum_backend.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "canopeum_backend.settings") application = get_asgi_application() diff --git a/canopeum_backend/canopeum_backend/models.py b/canopeum_backend/canopeum_backend/models.py index bdeb917c4..458bd85d7 100644 --- a/canopeum_backend/canopeum_backend/models.py +++ b/canopeum_backend/canopeum_backend/models.py @@ -1,12 +1,14 @@ -from django.db import models from django.conf import settings +from django.db import models + class Announcement(models.Model): body = models.TextField(blank=True, null=True) link = models.TextField(blank=True, null=True) + class Batch(models.Model): - site = models.ForeignKey('Site', models.DO_NOTHING, blank=True, null=True) + site = models.ForeignKey("Site", models.DO_NOTHING, blank=True, null=True) created_at = models.DateTimeField(blank=True, null=True) name = models.TextField(blank=True, null=True) sponsor = models.TextField(blank=True, null=True) @@ -15,24 +17,29 @@ class Batch(models.Model): total_number_seed = models.IntegerField(blank=True, null=True) total_propagation = models.IntegerField(blank=True, null=True) + class Batchfertilizer(models.Model): batch = models.ForeignKey(Batch, models.DO_NOTHING, blank=True, null=True) - fertilizer_type = models.ForeignKey('Fertilizertype', models.DO_NOTHING, blank=True, null=True) + fertilizer_type = models.ForeignKey("Fertilizertype", models.DO_NOTHING, blank=True, null=True) + class Batchmulchlayer(models.Model): batch = models.ForeignKey(Batch, models.DO_NOTHING, blank=True, null=True) - mulch_layer_type = models.ForeignKey('Mulchlayertype', models.DO_NOTHING, blank=True, null=True) + mulch_layer_type = models.ForeignKey("Mulchlayertype", models.DO_NOTHING, blank=True, null=True) + class Batchtreetype(models.Model): batch = models.ForeignKey(Batch, models.DO_NOTHING, blank=True, null=True) - tree_type = models.ForeignKey('Treetype', models.DO_NOTHING, blank=True, null=True) + tree_type = models.ForeignKey("Treetype", models.DO_NOTHING, blank=True, null=True) quantity = models.IntegerField(blank=True, null=True) + class Comment(models.Model): body = models.TextField(blank=True, null=True) created_at = models.DateTimeField(blank=True, null=True) auth_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.DO_NOTHING, blank=True, null=True) - post = models.ForeignKey('Post', models.DO_NOTHING, blank=True, null=True) + post = models.ForeignKey("Post", models.DO_NOTHING, blank=True, null=True) + class Contact(models.Model): address = models.TextField(blank=True, null=True) @@ -43,6 +50,7 @@ class Contact(models.Model): instagram_link = models.URLField(blank=True, null=True) linkedin_link = models.URLField(blank=True, null=True) + class Coordinate(models.Model): dms_latitude = models.TextField(blank=True, null=True) dms_longitude = models.TextField(blank=True, null=True) @@ -50,37 +58,45 @@ class Coordinate(models.Model): dd_longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True) address = models.TextField(blank=True, null=True) + class Fertilizertype(models.Model): - name = models.ForeignKey('FertilizertypeInternationalization', models.DO_NOTHING, blank=True, null=True) + name = models.ForeignKey("FertilizertypeInternationalization", models.DO_NOTHING, blank=True, null=True) + class FertilizertypeInternationalization(models.Model): - en = models.TextField(db_column='EN', blank=True, null=True) - fr = models.TextField(db_column='FR', blank=True, null=True) + en = models.TextField(db_column="EN", blank=True, null=True) + fr = models.TextField(db_column="FR", blank=True, null=True) + class Image(models.Model): path = models.TextField(blank=True, null=True) + class Mulchlayertype(models.Model): - name = models.ForeignKey('MulchlayertypeInternationalization', models.DO_NOTHING, blank=True, null=True) + name = models.ForeignKey("MulchlayertypeInternationalization", models.DO_NOTHING, blank=True, null=True) + class MulchlayertypeInternationalization(models.Model): - en = models.TextField(db_column='EN', blank=True, null=True) - fr = models.TextField(db_column='FR', blank=True, null=True) + en = models.TextField(db_column="EN", blank=True, null=True) + fr = models.TextField(db_column="FR", blank=True, null=True) + class Post(models.Model): - site = models.ForeignKey('Site', models.DO_NOTHING, blank=True, null=True) + site = models.ForeignKey("Site", models.DO_NOTHING, blank=True, null=True) body = models.TextField(blank=True, null=True) like_count = models.IntegerField(blank=True, null=True) share_count = models.IntegerField(blank=True, null=True) created_at = models.DateTimeField(blank=True, null=True) + class Postimage(models.Model): image = models.ForeignKey(Image, models.DO_NOTHING, blank=True, null=True) post = models.ForeignKey(Post, models.DO_NOTHING, blank=True, null=True) + class Site(models.Model): name = models.TextField(blank=True, null=True) - site_type = models.ForeignKey('Sitetype', models.DO_NOTHING, blank=True, null=True) + site_type = models.ForeignKey("Sitetype", models.DO_NOTHING, blank=True, null=True) image = models.ForeignKey(Image, models.DO_NOTHING, blank=True, null=True) coordinate = models.ForeignKey(Coordinate, models.DO_NOTHING, blank=True, null=True) description = models.TextField(blank=True, null=True) @@ -91,38 +107,47 @@ class Site(models.Model): contact = models.ForeignKey(Contact, models.DO_NOTHING, blank=True, null=True) announcement = models.ForeignKey(Announcement, models.DO_NOTHING, blank=True, null=True) + class Siteadmin(models.Model): auth_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.DO_NOTHING, blank=True, null=True) site = models.ForeignKey(Site, models.DO_NOTHING, blank=True, null=True) + class Siteimage(models.Model): image = models.ForeignKey(Image, models.DO_NOTHING, blank=True, null=True) site = models.ForeignKey(Site, models.DO_NOTHING, blank=True, null=True) + class Sitetreespecies(models.Model): site = models.ForeignKey(Site, models.DO_NOTHING, blank=True, null=True) - tree_type = models.ForeignKey('Treetype', models.DO_NOTHING, blank=True, null=True) + tree_type = models.ForeignKey("Treetype", models.DO_NOTHING, blank=True, null=True) quantity = models.IntegerField(blank=True, null=True) + class Sitetype(models.Model): - name = models.ForeignKey('SitetypeInternationalization', models.DO_NOTHING, blank=True, null=True) + name = models.ForeignKey("SitetypeInternationalization", models.DO_NOTHING, blank=True, null=True) + class SitetypeInternationalization(models.Model): - en = models.TextField(db_column='EN', blank=True, null=True) - fr = models.TextField(db_column='FR', blank=True, null=True) + en = models.TextField(db_column="EN", blank=True, null=True) + fr = models.TextField(db_column="FR", blank=True, null=True) + class TreespeciestypeInternationalization(models.Model): - en = models.TextField(db_column='EN', blank=True, null=True) - fr = models.TextField(db_column='FR', blank=True, null=True) + en = models.TextField(db_column="EN", blank=True, null=True) + fr = models.TextField(db_column="FR", blank=True, null=True) + class Treetype(models.Model): name = models.ForeignKey(TreespeciestypeInternationalization, models.DO_NOTHING, blank=True, null=True) + class Widget(models.Model): site = models.ForeignKey(Site, models.DO_NOTHING, blank=True, null=True) title = models.TextField(blank=True, null=True) body = models.TextField(blank=True, null=True) + class Like(models.Model): auth_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.DO_NOTHING, blank=True, null=True) post = models.ForeignKey(Post, models.DO_NOTHING, blank=True, null=True) diff --git a/canopeum_backend/canopeum_backend/serializers.py b/canopeum_backend/canopeum_backend/serializers.py index 22cc46ea5..f2cdb068e 100644 --- a/canopeum_backend/canopeum_backend/serializers.py +++ b/canopeum_backend/canopeum_backend/serializers.py @@ -1,43 +1,52 @@ -from rest_framework import serializers from django.contrib.auth.models import User -from .models import Site, Post, Batch, Announcement, Like, Comment +from rest_framework import serializers + +from .models import Announcement, Batch, Comment, Like, Post, Site + class AuthUserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ('id', 'username', 'email', 'password') + fields = ("id", "username", "email", "password") + class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = '__all__' + fields = "__all__" + class SiteSerializer(serializers.ModelSerializer): class Meta: model = Site - fields = '__all__' + fields = "__all__" + class PostSerializer(serializers.ModelSerializer): class Meta: model = Post - fields = '__all__' + fields = "__all__" + class BatchSerializer(serializers.ModelSerializer): class Meta: model = Batch - fields = '__all__' + fields = "__all__" + class AnnouncementSerializer(serializers.ModelSerializer): class Meta: model = Announcement - fields = '__all__' + fields = "__all__" + class LikeSerializer(serializers.ModelSerializer): class Meta: model = Like - fields = '__all__' + fields = "__all__" + class CommentSerializer(serializers.ModelSerializer): class Meta: model = Comment - fields = '__all__' + fields = "__all__" diff --git a/canopeum_backend/canopeum_backend/settings.py b/canopeum_backend/canopeum_backend/settings.py index 9c49e6095..090191a33 100644 --- a/canopeum_backend/canopeum_backend/settings.py +++ b/canopeum_backend/canopeum_backend/settings.py @@ -20,7 +20,7 @@ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-xy@=#v*#0yj@^gsl*0f+ci9+)8@v-x#7+npdvh50fn7^s9ow8g' +SECRET_KEY = "django-insecure-xy@=#v*#0yj@^gsl*0f+ci9+)8@v-x#7+npdvh50fn7^s9ow8g" # noqa: S105 # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -31,100 +31,99 @@ # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'rest_framework.authtoken', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'corsheaders', - 'drf_spectacular', - 'drf_spectacular_sidecar', - 'canopeum_backend', + "django.contrib.admin", + "django.contrib.auth", + "rest_framework.authtoken", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "corsheaders", + "drf_spectacular", + "drf_spectacular_sidecar", + "canopeum_backend", ] 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', - 'corsheaders.middleware.CorsMiddleware', + "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", + "corsheaders.middleware.CorsMiddleware", ] CORS_ALLOWED_ORIGINS = [ - 'http://localhost:5173', - 'http://localhost:3000', + "http://localhost:5173", + "http://localhost:3000", ] -ROOT_URLCONF = 'canopeum_backend.urls' +ROOT_URLCONF = "canopeum_backend.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', + "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 = 'canopeum_backend.wsgi.application' +WSGI_APPLICATION = "canopeum_backend.wsgi.application" REST_FRAMEWORK = { - 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', - 'DEFAULT_RENDERER_CLASSES': [ - 'rest_framework.renderers.JSONRenderer', + "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", + "DEFAULT_RENDERER_CLASSES": [ + "rest_framework.renderers.JSONRenderer", + ], + "DEFAULT_PARSER_CLASSES": [ + "rest_framework.parsers.JSONParser", ], - 'DEFAULT_PARSER_CLASSES': [ - 'rest_framework.parsers.JSONParser', - ] } SPECTACULAR_SETTINGS = { - 'SWAGGER_UI_DIST': 'SIDECAR', - 'SWAGGER_UI_FAVICON_HREF': 'SIDECAR', - 'REDOC_DIST': 'SIDECAR', - 'TITLE': 'Canopeum API', - 'DESCRIPTION': 'API for the Canopeum project', - 'VERSION': '0.0.1', - 'BASE_URL': 'http://localhost:3000', + "SWAGGER_UI_DIST": "SIDECAR", + "SWAGGER_UI_FAVICON_HREF": "SIDECAR", + "REDOC_DIST": "SIDECAR", + "TITLE": "Canopeum API", + "DESCRIPTION": "API for the Canopeum project", + "VERSION": "0.0.1", + "BASE_URL": "http://localhost:3000", "SWAGGER_UI_SETTINGS": { "deepLinking": True, "persistAuthorization": True, "displayOperationId": True, }, # Split components into request and response parts where appropriate - 'COMPONENT_SPLIT_REQUEST': False, + "COMPONENT_SPLIT_REQUEST": False, # Aid client generator targets that have trouble with read-only properties. - 'COMPONENT_NO_READ_ONLY_REQUIRED': False, + "COMPONENT_NO_READ_ONLY_REQUIRED": False, # Create separate components for PATCH endpoints (without required list) - 'COMPONENT_SPLIT_PATCH': True, + "COMPONENT_SPLIT_PATCH": True, } - # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'canopeum_db', - 'USER': 'root', - 'PASSWORD': 'canopeum', - 'HOST': 'localhost', - 'PORT': '3306', - } + "default": { + "ENGINE": "django.db.backends.mysql", + "NAME": "canopeum_db", + "USER": "root", + "PASSWORD": "canopeum", + "HOST": "localhost", + "PORT": "3306", + }, } @@ -133,16 +132,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -150,9 +149,9 @@ # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -162,9 +161,9 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" # Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/canopeum_backend/canopeum_backend/urls.py b/canopeum_backend/canopeum_backend/urls.py index 5026742fe..cce43c868 100644 --- a/canopeum_backend/canopeum_backend/urls.py +++ b/canopeum_backend/canopeum_backend/urls.py @@ -1,46 +1,38 @@ from django.contrib import admin from django.urls import path -from . import views from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView -urlpatterns = [ - path('admin/', admin.site.urls), +from canopeum_backend.canopeum_backend import views +urlpatterns = [ + path("admin/", admin.site.urls), # Auth - path('auth/login/', views.LoginAPIView.as_view(), name='login'), - path('auth/logout/', views.LogoutAPIView.as_view(), name='logout'), - path('auth/register/', views.RegisterAPIView.as_view(), name='register'), - + path("auth/login/", views.LoginAPIView.as_view(), name="login"), + path("auth/logout/", views.LogoutAPIView.as_view(), name="logout"), + path("auth/register/", views.RegisterAPIView.as_view(), name="register"), # User - path('users/', views.UserListAPIView.as_view(), name='user-list'), - path('users//', views.UserDetailAPIView.as_view(), name='user-detail'), - + path("users/", views.UserListAPIView.as_view(), name="user-list"), + path("users//", views.UserDetailAPIView.as_view(), name="user-detail"), # Announcement - path('announcements/', views.AnnouncementListAPIView.as_view(), name='announcement-list'), - path('announcements//', views.AnnouncementDetailAPIView.as_view(), name='announcement-detail'), - + path("announcements/", views.AnnouncementListAPIView.as_view(), name="announcement-list"), + path("announcements//", views.AnnouncementDetailAPIView.as_view(), name="announcement-detail"), # Site - path('sites/', views.SiteListAPIView.as_view(), name='site-list'), - path('sites//', views.SiteDetailAPIView.as_view(), name='site-detail'), - + path("sites/", views.SiteListAPIView.as_view(), name="site-list"), + path("sites//", views.SiteDetailAPIView.as_view(), name="site-detail"), # Post - path('posts/', views.PostListAPIView.as_view(), name='post-list'), - path('posts//', views.PostDetailAPIView.as_view(), name='post-detail'), - + path("posts/", views.PostListAPIView.as_view(), name="post-list"), + path("posts//", views.PostDetailAPIView.as_view(), name="post-detail"), # Batch - path('batches/', views.BatchListAPIView.as_view(), name='batch-list'), - path('batches//', views.BatchDetailAPIView.as_view(), name='batch-detail'), - + path("batches/", views.BatchListAPIView.as_view(), name="batch-list"), + path("batches//", views.BatchDetailAPIView.as_view(), name="batch-detail"), # Like - path('likes/', views.LikeListAPIView.as_view(), name='like-list'), - path('likes//', views.LikeDetailAPIView.as_view(), name='like-detail'), - + path("likes/", views.LikeListAPIView.as_view(), name="like-list"), + path("likes//", views.LikeDetailAPIView.as_view(), name="like-detail"), # Comment - path('comments/', views.CommentListAPIView.as_view(), name='comment-list'), - path('comments//', views.CommentDetailAPIView.as_view(), name='comment-detail'), - + path("comments/", views.CommentListAPIView.as_view(), name="comment-list"), + path("comments//", views.CommentDetailAPIView.as_view(), name="comment-detail"), # SWAGGER - path('api/schema/', SpectacularAPIView.as_view(), name='schema'), - path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), - path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), + path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path("api/schema/swagger-ui/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"), + path("api/schema/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), ] diff --git a/canopeum_backend/canopeum_backend/views.py b/canopeum_backend/canopeum_backend/views.py index 14baa0c4c..9235c0382 100644 --- a/canopeum_backend/canopeum_backend/views.py +++ b/canopeum_backend/canopeum_backend/views.py @@ -1,31 +1,44 @@ -from rest_framework import status -from rest_framework.views import APIView -from rest_framework.response import Response -from .models import Site, Post, Batch, Announcement, Like, Comment -from .serializers import AuthUserSerializer, UserSerializer, SiteSerializer, PostSerializer, BatchSerializer, AnnouncementSerializer, LikeSerializer, CommentSerializer +from typing import ClassVar + +from django.contrib.auth import authenticate from django.contrib.auth.models import User +from drf_spectacular.utils import extend_schema +from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.permissions import AllowAny -from drf_spectacular.utils import extend_schema -from django.contrib.auth import authenticate +from rest_framework.response import Response +from rest_framework.views import APIView + +from canopeum_backend.canopeum_backend.models import Announcement, Batch, Comment, Like, Post, Site +from canopeum_backend.canopeum_backend.serializers import ( + AnnouncementSerializer, + AuthUserSerializer, + BatchSerializer, + CommentSerializer, + LikeSerializer, + PostSerializer, + SiteSerializer, + UserSerializer, +) + class LoginAPIView(APIView): - permission_classes = [AllowAny] + permission_classes: ClassVar[list[type[AllowAny]]] = [AllowAny] # @extend_schema(request=AuthUserSerializer, responses=UserSerializer) def post(self, request): - username = request.data.get('username') - password = request.data.get('password') + username = request.data.get("username") + password = request.data.get("password") user = authenticate(username=username, password=password) if user is not None: token, _ = Token.objects.get_or_create(user=user) - return Response({'token': token.key}, status=status.HTTP_200_OK) - else: - return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED) + return Response({"token": token.key}, status=status.HTTP_200_OK) + return Response({"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED) + class RegisterAPIView(APIView): - permission_classes = [AllowAny] + permission_classes: ClassVar[list[type[AllowAny]]] = [AllowAny] @extend_schema(request=UserSerializer, responses=AuthUserSerializer) def post(self, request): @@ -33,9 +46,9 @@ def post(self, request): if serializer.is_valid(): user = serializer.save() token, _ = Token.objects.get_or_create(user=user) - return Response({'token': token.key}, status=status.HTTP_201_CREATED) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response({"token": token.key}, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class LogoutAPIView(APIView): @extend_schema(responses=status.HTTP_200_OK) @@ -43,13 +56,14 @@ def post(self, request): request.user.auth_token.delete() return Response(status=status.HTTP_200_OK) + class UserListAPIView(APIView): @extend_schema(responses=UserSerializer(many=True), operation_id="users_all") def get(self, request): users = User.objects.all() serializer = UserSerializer(users, many=True) return Response(serializer.data) - + @extend_schema(request=UserSerializer, responses=UserSerializer) def post(self, request): serializer = UserSerializer(data=request.data) @@ -58,6 +72,7 @@ def post(self, request): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class UserDetailAPIView(APIView): @extend_schema(request=UserSerializer, responses=UserSerializer) def get(self, request, pk): @@ -91,13 +106,14 @@ def delete(self, request, pk): user.delete() return Response(status=status.HTTP_204_NO_CONTENT) + class AnnouncementListAPIView(APIView): @extend_schema(responses=AnnouncementSerializer(many=True), operation_id="announcements_all") def get(self, request): announcements = Announcement.objects.all() serializer = AnnouncementSerializer(announcements, many=True) return Response(serializer.data) - + @extend_schema(request=AnnouncementSerializer, responses=AnnouncementSerializer) def post(self, request): serializer = AnnouncementSerializer(data=request.data) @@ -105,7 +121,8 @@ def post(self, request): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + + class AnnouncementDetailAPIView(APIView): @extend_schema(request=AnnouncementSerializer, responses=AnnouncementSerializer) def get(self, request, pk): @@ -139,13 +156,14 @@ def delete(self, request, pk): announcement.delete() return Response(status=status.HTTP_204_NO_CONTENT) + class SiteListAPIView(APIView): @extend_schema(responses=SiteSerializer(many=True), operation_id="sites_all") def get(self, request): sites = Site.objects.all() serializer = SiteSerializer(sites, many=True) return Response(serializer.data) - + @extend_schema(request=SiteSerializer, responses=SiteSerializer) def post(self, request): serializer = SiteSerializer(data=request.data) @@ -153,7 +171,8 @@ def post(self, request): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + + class SiteDetailAPIView(APIView): @extend_schema(request=SiteSerializer, responses=SiteSerializer) def get(self, request, pk): @@ -187,6 +206,7 @@ def delete(self, request, pk): site.delete() return Response(status=status.HTTP_204_NO_CONTENT) + class PostListAPIView(APIView): @extend_schema(responses=PostSerializer(many=True), operation_id="posts_all") def get(self, request): @@ -202,6 +222,7 @@ def post(self, request): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class PostDetailAPIView(APIView): @extend_schema(request=PostSerializer, responses=PostSerializer) def get(self, request, pk): @@ -212,7 +233,7 @@ def get(self, request, pk): serializer = PostSerializer(post) return Response(serializer.data) - + @extend_schema(request=PostSerializer, responses=PostSerializer) def put(self, request, pk): try: @@ -234,7 +255,8 @@ def delete(self, request, pk): post.delete() return Response(status=status.HTTP_204_NO_CONTENT) - + + class BatchListAPIView(APIView): @extend_schema(responses=BatchSerializer(many=True), operation_id="batches_all") def get(self, request): @@ -250,6 +272,7 @@ def post(self, request): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class BatchDetailAPIView(APIView): @extend_schema(request=BatchSerializer, responses=BatchSerializer) def get(self, request, pk): @@ -260,7 +283,7 @@ def get(self, request, pk): serializer = BatchSerializer(batch) return Response(serializer.data) - + @extend_schema(request=BatchSerializer, responses=BatchSerializer) def put(self, request, pk): try: @@ -282,7 +305,8 @@ def delete(self, request, pk): batch.delete() return Response(status=status.HTTP_204_NO_CONTENT) - + + class LikeListAPIView(APIView): @extend_schema(responses=LikeSerializer(many=True), operation_id="likes_all") def get(self, request): @@ -298,6 +322,7 @@ def post(self, request): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class LikeDetailAPIView(APIView): @extend_schema(request=LikeSerializer, responses=LikeSerializer) def get(self, request, pk): @@ -308,7 +333,7 @@ def get(self, request, pk): serializer = LikeSerializer(like) return Response(serializer.data) - + @extend_schema(request=LikeSerializer, responses=LikeSerializer) def put(self, request, pk): try: @@ -330,7 +355,8 @@ def delete(self, request, pk): like.delete() return Response(status=status.HTTP_204_NO_CONTENT) - + + class CommentListAPIView(APIView): @extend_schema(responses=CommentSerializer(many=True), operation_id="comments_all") def get(self, request): @@ -345,7 +371,8 @@ def post(self, request): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - + + class CommentDetailAPIView(APIView): @extend_schema(request=CommentSerializer, responses=CommentSerializer) def get(self, request, pk): @@ -378,4 +405,3 @@ def delete(self, request, pk): comment.delete() return Response(status=status.HTTP_204_NO_CONTENT) - \ No newline at end of file diff --git a/canopeum_backend/canopeum_backend/wsgi.py b/canopeum_backend/canopeum_backend/wsgi.py index 0057ac6ec..bef2544b6 100644 --- a/canopeum_backend/canopeum_backend/wsgi.py +++ b/canopeum_backend/canopeum_backend/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'canopeum_backend.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "canopeum_backend.settings") application = get_wsgi_application() diff --git a/canopeum_backend/manage.py b/canopeum_backend/manage.py index ca5840c1d..6a6fdbdd5 100644 --- a/canopeum_backend/manage.py +++ b/canopeum_backend/manage.py @@ -1,22 +1,24 @@ #!/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', 'canopeum_backend.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "canopeum_backend.settings") try: - from django.core.management import execute_from_command_line + # Lazy import only when this file is used as a script + from django.core.management import execute_from_command_line # noqa: PLC0415 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?" + + "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__': +if __name__ == "__main__": main() diff --git a/canopeum_backend/pyproject.toml b/canopeum_backend/pyproject.toml new file mode 100644 index 000000000..d3011249b --- /dev/null +++ b/canopeum_backend/pyproject.toml @@ -0,0 +1,131 @@ +# https://docs.astral.sh/ruff/configuration/ +[tool.ruff] +target-version = "py312" +line-length = 120 +preview = true +# Auto-generated +exclude = ["canopeum_backend/migrations/*"] + +[tool.ruff.lint] +select = ["ALL"] +# https://docs.astral.sh/ruff/rules/ +ignore = [ + ### + # Not needed or wanted + ### + "D1", # pydocstyle Missing doctring + "D401", # pydocstyle: non-imperative-mood + "EM", # flake8-errmsg + "EXE", # flake8-executable + # This is often something we can't control: https://github.com/astral-sh/ruff/issues/9497 + # Also false-positive with positional-only arguments: https://github.com/astral-sh/ruff/issues/3247 + "FBT003", # flake8-boolean-trap: boolean-positional-value-in-call + "INP", # flake8-no-pep420 + "ISC003", # flake8-implicit-str-concat: explicit-string-concatenation + # Short messages are still considered "long" messages + "TRY003", # tryceratops : raise-vanilla-args + # Don't remove commented code, also too inconsistant + "ERA001", # eradicate: commented-out-code + # contextlib.suppress is roughly 3x slower than try/except + "SIM105", # flake8-simplify: use-contextlib-suppress + # Negative performance impact + "UP038", # non-pep604-isinstance + # Checked by type-checker (pyright) + "ANN", # flake-annotations + "PGH003", # blanket-type-ignore + "TCH", # flake8-type-checking + # Already shown by Pylance, checked by pyright, and can be caused by overloads. + "ARG002", # Unused method argument + # We want D213: multi-line-summary-second-line and D211: no-blank-line-before-class + "D203", # pydocstyle: one-blank-line-before-class + "D212", # pydocstyle: multi-line-summary-first-line + # Allow differentiating between broken (FIXME) and to be done/added/completed (TODO) + "TD001", # flake8-todos: invalid-todo-tag + + ### + # Conflict with formatter + ### + "COM812", # missing-trailing-comma + "ISC001", # single-line-implicit-string-concatenation + + ### + # These should be warnings (https://github.com/astral-sh/ruff/issues/1256 & https://github.com/astral-sh/ruff/issues/1774) + ### + "FIX", # flake8-fixme + # Not all TODOs are worth an issue, this would be better as a warning + "TD003", # flake8-todos: missing-todo-link + + # False-positives + "TCH004", # https://github.com/astral-sh/ruff/issues/3821 + + ### + # Specific to this project + ### + "CPY001", # flake8-copyright, using global copyright + "D205", # Not all docstrings have a short description + desrciption + "PERF203", # try-except-in-loop, Python 3.11, introduced "zero cost" exception handling + "PLR6301", # API Views don't use "self" + "TD002", # missing-todo-author, This is a relatively small, low contributors project. Git blame suffice. + + ### FIXME/TODO: I'd normally set them as temporarily warnings, but no warnings in Ruff yet: + ### https://github.com/astral-sh/ruff/issues/1256 & https://github.com/astral-sh/ruff/issues/1774): + "DJ001", # Avoid using `null=True` on string-based fields + "DJ008", # Model does not define `__str__` method: https://docs.astral.sh/ruff/rules/django-model-without-dunder-str/ +] + +[tool.ruff.format] + +# https://docs.astral.sh/ruff/settings/#flake8-implicit-str-concat +[tool.ruff.lint.flake8-implicit-str-concat] +allow-multiline = false + +# https://docs.astral.sh/ruff/settings/#isort +[tool.ruff.lint.isort] +combine-as-imports = true +split-on-trailing-comma = false +# Unlike isort, Ruff only counts relative imports as local-folder by default for know. +# https://github.com/astral-sh/ruff/issues/3115 +# known-local-folder = [] + +# https://docs.astral.sh/ruff/settings/#mccabe +[tool.ruff.lint.mccabe] +# Arbitrary to 2 bytes, same as SonarLint +max-complexity = 15 + +[tool.ruff.lint.pylint] +# Arbitrary to 1 byte, same as SonarLint +max-args = 7 +# At least same as max-complexity +max-branches = 15 + +# https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file +[tool.pyright] +pythonVersion = "3.12" +exclude = [".venv/"] +typeCheckingMode = "standard" + +# https://mypy.readthedocs.io/en/stable/config_file.html +[tool.mypy] +show_column_numbers = true +implicit_reexport = true +python_version = "3.12" +exclude = [".venv/"] + +strict = false +# Implicit return types ! +check_untyped_defs = true +disallow_untyped_calls = false +disallow_untyped_defs = false +disallow_incomplete_defs = false +disable_error_code = [ + "return", # Implicit return types + "var-annotated", # Django models ClassVars not seen as annotated +] +# Note: mypy still has issues with some boolean infered returns like `is_valid_hwnd` +# https://github.com/python/mypy/issues/4409 +# https://github.com/python/mypy/issues/10149 + +[[tool.mypy.overrides]] +# Untyped dependencies +module = ["rest_framework.*"] +ignore_missing_imports = true diff --git a/canopeum_backend/requirements-dev.txt b/canopeum_backend/requirements-dev.txt new file mode 100644 index 000000000..b8768c064 --- /dev/null +++ b/canopeum_backend/requirements-dev.txt @@ -0,0 +1,18 @@ +-r requirements.txt +pre-commit==3.6.2 +ruff==0.3.4 # must match .pre-commit-config.yaml +mypy==1.9.0 +pyright==1.1.355 +# Stubs not yet available for 5.0: https://github.com/typeddjango/django-stubs/issues/2020 +django-stubs>=4.2.7 +# Not necessarily used directly, just taken from requirements.txt +# that are also found in https://github.com/python/typeshed/tree/main/stubs +types-colorama>=0.4.6 +types-docutils>=0.20.0 +types-jsonschema>=4.21.0 +types-pytz>=2024.1 +types-regex>=2023.12.25 +types-requests>=2.31.0 +types-setuptools>=69.2.0 +types-six>=1.16.0 +types-tqdm>=4.66.0 diff --git a/docs/VSCode_select_venv.png b/docs/VSCode_select_venv.png new file mode 100644 index 0000000000000000000000000000000000000000..a3b02c05e6a7f1171cd57cb21cc683ede02e9b13 GIT binary patch literal 24294 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 iKGPUBORD~ Date: Fri, 22 Mar 2024 18:42:46 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- canopeum_backend/.gitignore | 186 +++++++++--------- canopeum_backend/canopeum_backend/bdd.sql | 1 - canopeum_frontend/canopeum-mockoon.json | 2 +- canopeum_frontend/src/pages/Analytics.tsx | 2 +- canopeum_frontend/src/pages/Login.tsx | 2 +- canopeum_frontend/src/pages/Map.tsx | 2 +- canopeum_frontend/src/pages/Settings.tsx | 2 +- canopeum_frontend/src/pages/Utilities.tsx | 8 +- .../src/services/api-interface.ts | 2 +- canopeum_frontend/src/services/api.ts | 2 +- 10 files changed, 104 insertions(+), 105 deletions(-) diff --git a/canopeum_backend/.gitignore b/canopeum_backend/.gitignore index 1cf01105b..27ca1add6 100644 --- a/canopeum_backend/.gitignore +++ b/canopeum_backend/.gitignore @@ -6,10 +6,10 @@ __pycache__ db.sqlite3 media -# Backup files # -*.bak +# Backup files # +*.bak -# If you are using PyCharm # +# If you are using PyCharm # # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml @@ -45,94 +45,94 @@ out/ # JIRA plugin atlassian-ide-plugin.xml -# Python # -*.py[cod] -*$py.class - -# Distribution / packaging -.Python build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ +# Python # +*.py[cod] +*$py.class + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ *.whl -*.egg-info/ -.installed.cfg -*.egg -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache/ -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery -celerybeat-schedule.* - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# Sublime Text # -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache -*.sublime-workspace -*.sublime-project - -# sftp configuration file -sftp-config.json - -# Package control specific files Package -Control.last-run -Control.ca-list -Control.ca-bundle -Control.system-ca-bundle -GitHub.sublime-settings - -# Visual Studio Code # -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history \ No newline at end of file +*.egg-info/ +.installed.cfg +*.egg +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Sublime Text # +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files Package +Control.last-run +Control.ca-list +Control.ca-bundle +Control.system-ca-bundle +GitHub.sublime-settings + +# Visual Studio Code # +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history diff --git a/canopeum_backend/canopeum_backend/bdd.sql b/canopeum_backend/canopeum_backend/bdd.sql index 11ef32d12..cdb83478d 100644 --- a/canopeum_backend/canopeum_backend/bdd.sql +++ b/canopeum_backend/canopeum_backend/bdd.sql @@ -158,4 +158,3 @@ CREATE TABLE `BatchTreeType` ( `tree_type_id` integer REFERENCES `TreeType` (`id`), `quantity` integer ); - diff --git a/canopeum_frontend/canopeum-mockoon.json b/canopeum_frontend/canopeum-mockoon.json index 5915f5409..9ae00cfa2 100644 --- a/canopeum_frontend/canopeum-mockoon.json +++ b/canopeum_frontend/canopeum-mockoon.json @@ -1482,4 +1482,4 @@ } ], "callbacks": [] -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Analytics.tsx b/canopeum_frontend/src/pages/Analytics.tsx index c93715318..8985ea1dc 100644 --- a/canopeum_frontend/src/pages/Analytics.tsx +++ b/canopeum_frontend/src/pages/Analytics.tsx @@ -8,4 +8,4 @@ export default function Analytics() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Login.tsx b/canopeum_frontend/src/pages/Login.tsx index 8dbca5e35..7cbc24c4b 100644 --- a/canopeum_frontend/src/pages/Login.tsx +++ b/canopeum_frontend/src/pages/Login.tsx @@ -4,4 +4,4 @@ export default function Login() {

Login

); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Map.tsx b/canopeum_frontend/src/pages/Map.tsx index fd5d73406..4e73d7a71 100644 --- a/canopeum_frontend/src/pages/Map.tsx +++ b/canopeum_frontend/src/pages/Map.tsx @@ -6,4 +6,4 @@ export default function Map() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Settings.tsx b/canopeum_frontend/src/pages/Settings.tsx index 5eadab22b..ca655b833 100644 --- a/canopeum_frontend/src/pages/Settings.tsx +++ b/canopeum_frontend/src/pages/Settings.tsx @@ -8,4 +8,4 @@ export default function Settings() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Utilities.tsx b/canopeum_frontend/src/pages/Utilities.tsx index 92bbd0374..7cf4c8442 100644 --- a/canopeum_frontend/src/pages/Utilities.tsx +++ b/canopeum_frontend/src/pages/Utilities.tsx @@ -4,7 +4,7 @@ import facebook_logo from '@assets/icons/facebook-regular.svg'; export default function Utilities() { return (
-
+

Utilities

Icons

@@ -85,7 +85,7 @@ export default function Utilities() {
A simple secondary alert—check it out! -
+

Cards

@@ -178,7 +178,7 @@ export default function Utilities() {
- +
@@ -223,4 +223,4 @@ export default function Utilities() {
); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/services/api-interface.ts b/canopeum_frontend/src/services/api-interface.ts index ffe2a8088..a2cb52232 100644 --- a/canopeum_frontend/src/services/api-interface.ts +++ b/canopeum_frontend/src/services/api-interface.ts @@ -26,4 +26,4 @@ const api = { batchesClient: new BatchesClient(API_URL), }; -export default api; \ No newline at end of file +export default api; diff --git a/canopeum_frontend/src/services/api.ts b/canopeum_frontend/src/services/api.ts index f7928e948..a04618be9 100644 --- a/canopeum_frontend/src/services/api.ts +++ b/canopeum_frontend/src/services/api.ts @@ -2419,4 +2419,4 @@ function throwException(message: string, status: number, response: string, heade throw result; else throw new ApiException(message, status, response, headers, null); -} \ No newline at end of file +} From 9d4cfa333a1b7a7d1be53ac3afa4349581f4a814 Mon Sep 17 00:00:00 2001 From: Samuel Therrien Date: Fri, 22 Mar 2024 14:43:48 -0400 Subject: [PATCH 3/6] Fix mixed line endings --- canopeum_backend/.gitignore | 186 +++++++++--------- canopeum_backend/canopeum_backend/bdd.sql | 1 - canopeum_frontend/canopeum-mockoon.json | 2 +- canopeum_frontend/src/pages/Analytics.tsx | 2 +- canopeum_frontend/src/pages/Login.tsx | 2 +- canopeum_frontend/src/pages/Map.tsx | 2 +- canopeum_frontend/src/pages/Settings.tsx | 2 +- canopeum_frontend/src/pages/Utilities.tsx | 8 +- .../src/services/api-interface.ts | 2 +- canopeum_frontend/src/services/api.ts | 2 +- 10 files changed, 104 insertions(+), 105 deletions(-) diff --git a/canopeum_backend/.gitignore b/canopeum_backend/.gitignore index 1cf01105b..27ca1add6 100644 --- a/canopeum_backend/.gitignore +++ b/canopeum_backend/.gitignore @@ -6,10 +6,10 @@ __pycache__ db.sqlite3 media -# Backup files # -*.bak +# Backup files # +*.bak -# If you are using PyCharm # +# If you are using PyCharm # # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml @@ -45,94 +45,94 @@ out/ # JIRA plugin atlassian-ide-plugin.xml -# Python # -*.py[cod] -*$py.class - -# Distribution / packaging -.Python build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ +# Python # +*.py[cod] +*$py.class + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ *.whl -*.egg-info/ -.installed.cfg -*.egg -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache/ -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery -celerybeat-schedule.* - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# Sublime Text # -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache -*.sublime-workspace -*.sublime-project - -# sftp configuration file -sftp-config.json - -# Package control specific files Package -Control.last-run -Control.ca-list -Control.ca-bundle -Control.system-ca-bundle -GitHub.sublime-settings - -# Visual Studio Code # -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history \ No newline at end of file +*.egg-info/ +.installed.cfg +*.egg +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Sublime Text # +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files Package +Control.last-run +Control.ca-list +Control.ca-bundle +Control.system-ca-bundle +GitHub.sublime-settings + +# Visual Studio Code # +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history diff --git a/canopeum_backend/canopeum_backend/bdd.sql b/canopeum_backend/canopeum_backend/bdd.sql index 11ef32d12..cdb83478d 100644 --- a/canopeum_backend/canopeum_backend/bdd.sql +++ b/canopeum_backend/canopeum_backend/bdd.sql @@ -158,4 +158,3 @@ CREATE TABLE `BatchTreeType` ( `tree_type_id` integer REFERENCES `TreeType` (`id`), `quantity` integer ); - diff --git a/canopeum_frontend/canopeum-mockoon.json b/canopeum_frontend/canopeum-mockoon.json index 5915f5409..9ae00cfa2 100644 --- a/canopeum_frontend/canopeum-mockoon.json +++ b/canopeum_frontend/canopeum-mockoon.json @@ -1482,4 +1482,4 @@ } ], "callbacks": [] -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Analytics.tsx b/canopeum_frontend/src/pages/Analytics.tsx index c93715318..8985ea1dc 100644 --- a/canopeum_frontend/src/pages/Analytics.tsx +++ b/canopeum_frontend/src/pages/Analytics.tsx @@ -8,4 +8,4 @@ export default function Analytics() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Login.tsx b/canopeum_frontend/src/pages/Login.tsx index 8dbca5e35..7cbc24c4b 100644 --- a/canopeum_frontend/src/pages/Login.tsx +++ b/canopeum_frontend/src/pages/Login.tsx @@ -4,4 +4,4 @@ export default function Login() {

Login

); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Map.tsx b/canopeum_frontend/src/pages/Map.tsx index fd5d73406..4e73d7a71 100644 --- a/canopeum_frontend/src/pages/Map.tsx +++ b/canopeum_frontend/src/pages/Map.tsx @@ -6,4 +6,4 @@ export default function Map() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Settings.tsx b/canopeum_frontend/src/pages/Settings.tsx index 5eadab22b..ca655b833 100644 --- a/canopeum_frontend/src/pages/Settings.tsx +++ b/canopeum_frontend/src/pages/Settings.tsx @@ -8,4 +8,4 @@ export default function Settings() { ); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/pages/Utilities.tsx b/canopeum_frontend/src/pages/Utilities.tsx index 92bbd0374..7cf4c8442 100644 --- a/canopeum_frontend/src/pages/Utilities.tsx +++ b/canopeum_frontend/src/pages/Utilities.tsx @@ -4,7 +4,7 @@ import facebook_logo from '@assets/icons/facebook-regular.svg'; export default function Utilities() { return (
-
+

Utilities

Icons

@@ -85,7 +85,7 @@ export default function Utilities() {
A simple secondary alert—check it out! -
+

Cards

@@ -178,7 +178,7 @@ export default function Utilities() {
- +
@@ -223,4 +223,4 @@ export default function Utilities() {
); -} \ No newline at end of file +} diff --git a/canopeum_frontend/src/services/api-interface.ts b/canopeum_frontend/src/services/api-interface.ts index ffe2a8088..a2cb52232 100644 --- a/canopeum_frontend/src/services/api-interface.ts +++ b/canopeum_frontend/src/services/api-interface.ts @@ -26,4 +26,4 @@ const api = { batchesClient: new BatchesClient(API_URL), }; -export default api; \ No newline at end of file +export default api; diff --git a/canopeum_frontend/src/services/api.ts b/canopeum_frontend/src/services/api.ts index f7928e948..a04618be9 100644 --- a/canopeum_frontend/src/services/api.ts +++ b/canopeum_frontend/src/services/api.ts @@ -2419,4 +2419,4 @@ function throwException(message: string, status: number, response: string, heade throw result; else throw new ApiException(message, status, response, headers, null); -} \ No newline at end of file +} From 2230b16e4a6af85d639a720e7e847db7fe544cfd Mon Sep 17 00:00:00 2001 From: Samuel Therrien Date: Fri, 22 Mar 2024 14:45:51 -0400 Subject: [PATCH 4/6] Don't use pretty-format-json (doesn't support jsonc) --- .pre-commit-config.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8cdc39e8..f08968679 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,9 +3,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - - id: pretty-format-json - exclude: ".vscode/.*" # Exclude jsonc - args: [--autofix, --no-sort-keys] - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer From 3eb86fcf216d3491aa193ec316e50654aac45164 Mon Sep 17 00:00:00 2001 From: Samuel Therrien Date: Fri, 22 Mar 2024 14:54:56 -0400 Subject: [PATCH 5/6] Use relative imports --- .vscode/settings.json | 6 +++++- canopeum_backend/canopeum_backend/urls.py | 2 +- canopeum_backend/canopeum_backend/views.py | 4 ++-- canopeum_backend/pyproject.toml | 3 --- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0967a03eb..1dc28d978 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,6 +39,9 @@ "[json][jsonc]": { "editor.defaultFormatter": "vscode.json-language-features", }, + "[yaml]": { + "editor.defaultFormatter": "redhat.vscode-yaml" + }, "[python]": { "editor.defaultFormatter": "charliermarsh.ruff", "editor.tabSize": 4, @@ -61,6 +64,7 @@ "source.convertImportFormat" // Explicitly omiting "source.unusedImports", can be annoying when commenting code for debugging ], + "python.analysis.importFormat": "relative", "python.analysis.diagnosticMode": "workspace", "ruff.importStrategy": "fromEnvironment", // Use the Ruff extension instead @@ -110,5 +114,5 @@ "evenBetterToml.formatter.reorderArrays": true, "evenBetterToml.formatter.trailingNewline": true, // We like keeping TOML keys in a certain non-alphabetical order that feels more natural - "evenBetterToml.formatter.reorderKeys": false + "evenBetterToml.formatter.reorderKeys": false, } diff --git a/canopeum_backend/canopeum_backend/urls.py b/canopeum_backend/canopeum_backend/urls.py index cce43c868..af3e8fe5a 100644 --- a/canopeum_backend/canopeum_backend/urls.py +++ b/canopeum_backend/canopeum_backend/urls.py @@ -2,7 +2,7 @@ from django.urls import path from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView -from canopeum_backend.canopeum_backend import views +from . import views urlpatterns = [ path("admin/", admin.site.urls), diff --git a/canopeum_backend/canopeum_backend/views.py b/canopeum_backend/canopeum_backend/views.py index 9235c0382..842dd950c 100644 --- a/canopeum_backend/canopeum_backend/views.py +++ b/canopeum_backend/canopeum_backend/views.py @@ -9,8 +9,8 @@ from rest_framework.response import Response from rest_framework.views import APIView -from canopeum_backend.canopeum_backend.models import Announcement, Batch, Comment, Like, Post, Site -from canopeum_backend.canopeum_backend.serializers import ( +from .models import Announcement, Batch, Comment, Like, Post, Site +from .serializers import ( AnnouncementSerializer, AuthUserSerializer, BatchSerializer, diff --git a/canopeum_backend/pyproject.toml b/canopeum_backend/pyproject.toml index d3011249b..83722d4e0 100644 --- a/canopeum_backend/pyproject.toml +++ b/canopeum_backend/pyproject.toml @@ -83,9 +83,6 @@ allow-multiline = false [tool.ruff.lint.isort] combine-as-imports = true split-on-trailing-comma = false -# Unlike isort, Ruff only counts relative imports as local-folder by default for know. -# https://github.com/astral-sh/ruff/issues/3115 -# known-local-folder = [] # https://docs.astral.sh/ruff/settings/#mccabe [tool.ruff.lint.mccabe] From b288e659689ab0e7bb2a9b3c52f146b3d98cf1cf Mon Sep 17 00:00:00 2001 From: Samuel Therrien Date: Fri, 22 Mar 2024 14:57:46 -0400 Subject: [PATCH 6/6] Add CI run name --- .github/workflows/canopeum_backend.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/canopeum_backend.yml b/.github/workflows/canopeum_backend.yml index 4c49f37f2..5e5f2ac55 100644 --- a/.github/workflows/canopeum_backend.yml +++ b/.github/workflows/canopeum_backend.yml @@ -1,3 +1,5 @@ +name: canopeum_backend + on: push: pull_request: