Add and use pre-commit hooks
This commit is contained in:
parent
eab379a028
commit
b2eef830d8
10 changed files with 66 additions and 18 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -182,9 +182,9 @@ cython_debug/
|
||||||
.abstra/
|
.abstra/
|
||||||
|
|
||||||
# Visual Studio Code
|
# Visual Studio Code
|
||||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||||
# you could uncomment the following to ignore the entire vscode folder
|
# you could uncomment the following to ignore the entire vscode folder
|
||||||
# .vscode/
|
# .vscode/
|
||||||
|
|
||||||
|
|
|
||||||
39
.pre-commit-config.yaml
Normal file
39
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/asottile/add-trailing-comma
|
||||||
|
rev: v4.0.0
|
||||||
|
hooks:
|
||||||
|
- id: add-trailing-comma
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: check-ast
|
||||||
|
- id: check-builtin-literals
|
||||||
|
- id: check-docstring-first
|
||||||
|
- id: check-executables-have-shebangs
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: check-toml
|
||||||
|
- id: check-vcs-permalinks
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: mixed-line-ending
|
||||||
|
- id: name-tests-test
|
||||||
|
args: [--pytest-test-first]
|
||||||
|
- id: trailing-whitespace
|
||||||
|
|
||||||
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.14.6
|
||||||
|
hooks:
|
||||||
|
- id: ruff-check
|
||||||
|
args: ["--fix", "--exit-non-zero-on-fix"]
|
||||||
|
- id: ruff-format
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v3.21.2
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ["--py311-plus"]
|
||||||
|
|
||||||
|
- repo: https://github.com/rhysd/actionlint
|
||||||
|
rev: v1.7.9
|
||||||
|
hooks:
|
||||||
|
- id: actionlint
|
||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -25,4 +25,4 @@
|
||||||
"program": "${workspaceFolder}/manage.py"
|
"program": "${workspaceFolder}/manage.py"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -38,4 +38,4 @@
|
||||||
"wrongpassword",
|
"wrongpassword",
|
||||||
"xdist"
|
"xdist"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,4 @@ uv run python manage.py migrate
|
||||||
uv run python manage.py collectstatic
|
uv run python manage.py collectstatic
|
||||||
uv run python manage.py runserver
|
uv run python manage.py runserver
|
||||||
uv run pytest
|
uv run pytest
|
||||||
```
|
```
|
||||||
|
|
|
||||||
0
manage.py
Normal file → Executable file
0
manage.py
Normal file → Executable file
|
|
@ -42,17 +42,17 @@
|
||||||
|
|
||||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.4; padding: 0; font-size: 115%; max-width: 75%; margin: 0 auto;}
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.4; padding: 0; font-size: 115%; max-width: 75%; margin: 0 auto;}
|
||||||
@media (max-width: 900px) { body { max-width: 95%; } }
|
@media (max-width: 900px) { body { max-width: 95%; } }
|
||||||
|
|
||||||
table { width: 100%; }
|
table { width: 100%; }
|
||||||
th, td { padding: 8px; text-align: left; vertical-align: middle; }
|
th, td { padding: 8px; text-align: left; vertical-align: middle; }
|
||||||
th {background-color: Canvas; color: CanvasText; font-weight: bold; }
|
th {background-color: Canvas; color: CanvasText; font-weight: bold; }
|
||||||
tr:nth-child(even) { background-color: color-mix(in srgb, Canvas 95%, CanvasText 5%); }
|
tr:nth-child(even) { background-color: color-mix(in srgb, Canvas 95%, CanvasText 5%); }
|
||||||
td > img { display: block; height: 160px; width: 160px; object-fit: cover; border-radius: 4px; }
|
td > img { display: block; height: 160px; width: 160px; object-fit: cover; border-radius: 4px; }
|
||||||
|
|
||||||
.campaign-benefits { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 8px; }
|
.campaign-benefits { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
.benefit-item { display: inline-flex; align-items: center; padding: 2px 6px; background-color: color-mix(in srgb, Canvas 90%, CanvasText 10%); border-radius: 4px; font-size: 0.9em; }
|
.benefit-item { display: inline-flex; align-items: center; padding: 2px 6px; background-color: color-mix(in srgb, Canvas 90%, CanvasText 10%); border-radius: 4px; font-size: 0.9em; }
|
||||||
.benefit-item img { width: 24px !important; height: 24px !important; display: inline-block !important; margin-right: 4px; border-radius: 2px; }
|
.benefit-item img { width: 24px !important; height: 24px !important; display: inline-block !important; margin-right: 4px; border-radius: 2px; }
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.highlight { background: #0d1117; color: #E6EDF3; }
|
.highlight { background: #0d1117; color: #E6EDF3; }
|
||||||
.highlight .p { color: #E6EDF3; }
|
.highlight .p { color: #E6EDF3; }
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ class Command(BaseCommand):
|
||||||
f"{len(self._organization_cache)} orgs, "
|
f"{len(self._organization_cache)} orgs, "
|
||||||
f"{len(self._drop_campaign_cache)} campaigns, "
|
f"{len(self._drop_campaign_cache)} campaigns, "
|
||||||
f"{len(self._channel_cache)} channels, "
|
f"{len(self._channel_cache)} channels, "
|
||||||
f"{len(self._benefit_cache)} benefits."
|
f"{len(self._benefit_cache)} benefits.",
|
||||||
)
|
)
|
||||||
except (FileNotFoundError, OSError, RuntimeError):
|
except (FileNotFoundError, OSError, RuntimeError):
|
||||||
# If preload fails for any reason, continue without it
|
# If preload fails for any reason, continue without it
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,10 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"id",
|
"id",
|
||||||
models.TextField(
|
models.TextField(
|
||||||
help_text="The unique Twitch identifier for the organization.", primary_key=True, serialize=False, verbose_name="Organization ID"
|
help_text="The unique Twitch identifier for the organization.",
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="Organization ID",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("name", models.TextField(help_text="Display name of the organization.", unique=True, verbose_name="Name")),
|
("name", models.TextField(help_text="Display name of the organization.", unique=True, verbose_name="Name")),
|
||||||
|
|
@ -179,7 +182,10 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"benefits",
|
"benefits",
|
||||||
models.ManyToManyField(
|
models.ManyToManyField(
|
||||||
help_text="Benefits unlocked by this drop.", related_name="drops", through="twitch.DropBenefitEdge", to="twitch.dropbenefit"
|
help_text="Benefits unlocked by this drop.",
|
||||||
|
related_name="drops",
|
||||||
|
through="twitch.DropBenefitEdge",
|
||||||
|
to="twitch.dropbenefit",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -200,7 +206,9 @@ class Migration(migrations.Migration):
|
||||||
model_name="dropbenefitedge",
|
model_name="dropbenefitedge",
|
||||||
name="drop",
|
name="drop",
|
||||||
field=models.ForeignKey(
|
field=models.ForeignKey(
|
||||||
help_text="The time-based drop in this relationship.", on_delete=django.db.models.deletion.CASCADE, to="twitch.timebaseddrop"
|
help_text="The time-based drop in this relationship.",
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="twitch.timebaseddrop",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
|
|
||||||
|
|
@ -434,8 +434,9 @@ class GameDetailView(DetailView):
|
||||||
.select_related("game__owner")
|
.select_related("game__owner")
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
Prefetch(
|
Prefetch(
|
||||||
"time_based_drops", queryset=TimeBasedDrop.objects.prefetch_related(Prefetch("benefits", queryset=DropBenefit.objects.order_by("name")))
|
"time_based_drops",
|
||||||
)
|
queryset=TimeBasedDrop.objects.prefetch_related(Prefetch("benefits", queryset=DropBenefit.objects.order_by("name"))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.order_by("-end_at")
|
.order_by("-end_at")
|
||||||
)
|
)
|
||||||
|
|
@ -580,12 +581,12 @@ def debug_view(request: HttpRequest) -> HttpResponse:
|
||||||
|
|
||||||
# Campaigns with missing or obviously broken images (empty or not starting with http)
|
# Campaigns with missing or obviously broken images (empty or not starting with http)
|
||||||
broken_image_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
broken_image_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
||||||
Q(image_url__isnull=True) | Q(image_url__exact="") | ~Q(image_url__startswith="http")
|
Q(image_url__isnull=True) | Q(image_url__exact="") | ~Q(image_url__startswith="http"),
|
||||||
).select_related("game")
|
).select_related("game")
|
||||||
|
|
||||||
# Benefits with missing images
|
# Benefits with missing images
|
||||||
broken_benefit_images: QuerySet[DropBenefit] = DropBenefit.objects.annotate(trimmed_url=Trim("image_asset_url")).filter(
|
broken_benefit_images: QuerySet[DropBenefit] = DropBenefit.objects.annotate(trimmed_url=Trim("image_asset_url")).filter(
|
||||||
Q(image_asset_url__isnull=True) | Q(trimmed_url__exact="") | ~Q(image_asset_url__startswith="http")
|
Q(image_asset_url__isnull=True) | Q(trimmed_url__exact="") | ~Q(image_asset_url__startswith="http"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Time-based drops without any benefits
|
# Time-based drops without any benefits
|
||||||
|
|
@ -593,7 +594,7 @@ def debug_view(request: HttpRequest) -> HttpResponse:
|
||||||
|
|
||||||
# Campaigns with invalid dates (start after end or missing either)
|
# Campaigns with invalid dates (start after end or missing either)
|
||||||
invalid_date_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
invalid_date_campaigns: QuerySet[DropCampaign] = DropCampaign.objects.filter(
|
||||||
Q(start_at__gt=F("end_at")) | Q(start_at__isnull=True) | Q(end_at__isnull=True)
|
Q(start_at__gt=F("end_at")) | Q(start_at__isnull=True) | Q(end_at__isnull=True),
|
||||||
).select_related("game")
|
).select_related("game")
|
||||||
|
|
||||||
# Duplicate campaign names per game. We retrieve the game's name for user-friendly display.
|
# Duplicate campaign names per game. We retrieve the game's name for user-friendly display.
|
||||||
|
|
@ -729,7 +730,7 @@ class ChannelDetailView(DetailView):
|
||||||
queryset=TimeBasedDrop.objects.prefetch_related(
|
queryset=TimeBasedDrop.objects.prefetch_related(
|
||||||
Prefetch("benefits", queryset=DropBenefit.objects.order_by("name")),
|
Prefetch("benefits", queryset=DropBenefit.objects.order_by("name")),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.order_by("-start_at")
|
.order_by("-start_at")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue