mirror of
https://github.com/boostorg/website-v2.git
synced 2026-01-19 04:42:17 +00:00
Upgrade django to 5.2, python to 3.13 (#1915)
This commit is contained in:
10
.github/workflows/actions-gcp.yaml
vendored
10
.github/workflows/actions-gcp.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -44,10 +44,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.11
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.13
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
@@ -133,10 +133,10 @@ jobs:
|
||||
run: |
|
||||
git fetch --depth=1 origin +refs/tags/*:refs/tags/* || true
|
||||
|
||||
- name: Set up Python 3.11
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.13
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
|
||||
10
.github/workflows/actions.yml
vendored
10
.github/workflows/actions.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -34,10 +34,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.11
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.13
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
@@ -85,10 +85,10 @@ jobs:
|
||||
run: |
|
||||
git fetch --depth=1 origin +refs/tags/*:refs/tags/* || true
|
||||
|
||||
- name: Set up Python 3.11
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.13
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
default_language_version:
|
||||
python: python3.11
|
||||
python: python3.13
|
||||
|
||||
exclude: .*migrations\/.*|static\/img\/.*|static\/animations\/.*|static\/js\/boost-gecko\/.*|kube\/boost\/templates\/.*\.yaml
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: "1.27.0"
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: [--target-version, "5.2"] # Replace with Django version
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
|
||||
@@ -13,7 +13,7 @@ Links:
|
||||
|
||||
## Local Development Setup
|
||||
|
||||
This project will use Python 3.11, Docker, and Docker Compose.
|
||||
This project will use Python 3.13, Docker, and Docker Compose.
|
||||
|
||||
Instructions to install those packages are included in [development_setup_notes.md](docs/development_setup_notes.md).
|
||||
|
||||
|
||||
@@ -20,10 +20,8 @@ def get_calendar(min_time=None, single_events=True, order_by="startTime"):
|
||||
https://developers.google.com/calendar/api/v3/reference/events/list
|
||||
"""
|
||||
if not min_time:
|
||||
min_time = (
|
||||
datetime.datetime.utcnow().isoformat() + "Z"
|
||||
) # 'Z' indicates UTC time
|
||||
|
||||
# 'Z' indicates UTC time
|
||||
min_time = datetime.datetime.now(datetime.timezone.utc).isoformat() + "Z"
|
||||
url = f"https://www.googleapis.com/calendar/v3/calendars/{settings.BOOST_CALENDAR}/events?key={settings.CALENDAR_API_KEY}&timeMin={min_time}&singleEvents={single_events}&orderBy={order_by}"
|
||||
|
||||
headers = {"Accept": "application/json"}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddConstraint(
|
||||
model_name="sitesettings",
|
||||
constraint=models.CheckConstraint(
|
||||
check=models.Q(("id", 1)), name="core_sitesettings_single_instance"
|
||||
condition=models.Q(("id", 1)), name="core_sitesettings_single_instance"
|
||||
),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
|
||||
@@ -72,7 +72,7 @@ class SiteSettings(models.Model):
|
||||
# check constraint to only allow id=1 to exist
|
||||
models.CheckConstraint(
|
||||
name="%(app_label)s_%(class)s_single_instance",
|
||||
check=models.Q(id=1),
|
||||
condition=models.Q(id=1),
|
||||
),
|
||||
]
|
||||
verbose_name_plural = "Site Settings"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
|
||||
import requests
|
||||
from django.utils import timezone
|
||||
|
||||
from urllib.parse import urljoin
|
||||
@@ -16,11 +17,14 @@ from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseRedirect,
|
||||
HttpRequest,
|
||||
)
|
||||
from django.shortcuts import redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from config.settings import ENABLE_DB_CACHE
|
||||
@@ -942,3 +946,57 @@ class RedirectToLibrariesView(BaseRedirectView):
|
||||
if requested_version == "release":
|
||||
new_path = "/libraries/"
|
||||
return HttpResponseRedirect(new_path)
|
||||
|
||||
|
||||
@method_decorator(never_cache, name="dispatch")
|
||||
class QRCodeView(View):
|
||||
"""Handles QR code urls, sending them to Plausible, then redirecting to the desired url.
|
||||
|
||||
QR code urls are formatted /qrc/<campaign_identifier>/desired/path/to/content/, and will
|
||||
result in a redirect to /desired/path/to/content/.
|
||||
|
||||
E.g. https://www.boost.org/qrc/pv-01/library/latest/beast/ will send this full url to Plausible,
|
||||
then redirect to https://www.boost.org/library/latest/beast/
|
||||
"""
|
||||
|
||||
def get(self, request: HttpRequest, campaign_identifier: str, main_path: str = ""):
|
||||
absolute_url = request.build_absolute_uri(request.path)
|
||||
referrer = request.headers.get("referer", "")
|
||||
user_agent = request.headers.get("user-agent", "")
|
||||
|
||||
plausible_payload = {
|
||||
"name": "pageview",
|
||||
"domain": "qrc.boost.org",
|
||||
"url": absolute_url,
|
||||
"referrer": referrer,
|
||||
}
|
||||
|
||||
headers = {"Content-Type": "application/json", "User-Agent": user_agent}
|
||||
|
||||
client_ip = request.headers.get("x-forwarded-for", "").split(",")[0].strip()
|
||||
client_ip = client_ip or request.META.get("REMOTE_ADDR")
|
||||
|
||||
if client_ip:
|
||||
headers["X-Forwarded-For"] = client_ip
|
||||
|
||||
try:
|
||||
requests.post(
|
||||
"https://plausible.io/api/event",
|
||||
json=plausible_payload,
|
||||
headers=headers,
|
||||
timeout=2.0,
|
||||
)
|
||||
except Exception as e:
|
||||
# Don’t interrupt the redirect - just log it
|
||||
logger.error(f"Plausible event post failed: {e}")
|
||||
|
||||
# Now that we've sent the request url to plausible, we can redirect to the main_path
|
||||
# Preserve the original querystring, if any.
|
||||
# Example: /qrc/3/library/latest/algorithm/?x=1 -> /library/latest/algorithm/?x=1
|
||||
# `main_path` is everything after qrc/<campaign>/ thanks to <path:main_path>.
|
||||
redirect_path = "/" + main_path if main_path else "/"
|
||||
qs = request.META.get("QUERY_STRING")
|
||||
if qs:
|
||||
redirect_path = f"{redirect_path}?{qs}"
|
||||
|
||||
return HttpResponseRedirect(redirect_path)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax = docker/dockerfile:experimental
|
||||
|
||||
FROM python:3.11-slim AS builder-py
|
||||
FROM python:3.13-slim AS builder-py
|
||||
|
||||
ARG LOCAL_DEVELOPMENT
|
||||
|
||||
@@ -42,7 +42,7 @@ RUN yarn build
|
||||
|
||||
|
||||
# Final image.
|
||||
FROM python:3.11-slim AS release
|
||||
FROM python:3.13-slim AS release
|
||||
|
||||
RUN apt update && apt install -y git libpq-dev ruby ruby-dev && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -7,3 +7,14 @@
|
||||
1. Run `just pip-compile`, which will add the dependency to `requirements.txt`
|
||||
1. Run `just rebuild` to rebuild your Docker image to include the new dependencies
|
||||
2. Run `just up` and continue with development
|
||||
|
||||
## Upgrading dependencies
|
||||
|
||||
To upgrade all dependencies to their latest versions, run:
|
||||
|
||||
1. `just pip-compile-upgrade`.
|
||||
2. Get the django version from requirements.txt and set the `DJANGO_VERSION` value in /justfile
|
||||
3. Update the `--target-version` args value for django-upgrade in .pre-commit-config.yaml to match
|
||||
3. In a venv with installed packages run `just run-django-upgrade` to upgrade python code.
|
||||
4. `just build` to create new docker images.
|
||||
5. Tear down docker containers and restart with the newly built images, then test.
|
||||
|
||||
5
justfile
5
justfile
@@ -1,6 +1,7 @@
|
||||
set dotenv-load := false
|
||||
COMPOSE_FILE := "docker-compose.yml"
|
||||
ENV_FILE := ".env"
|
||||
DJANGO_VERSION := "5.2"
|
||||
|
||||
@_default:
|
||||
just --list
|
||||
@@ -122,6 +123,10 @@ alias shell := console
|
||||
fi
|
||||
@cd development-tofu; direnv allow && tofu destroy
|
||||
|
||||
@run-django-upgrade:
|
||||
[ -n "${VIRTUAL_ENV-}" ] || { echo "❌ Activate your venv first."; exit 1; }
|
||||
-git ls-files -z -- '*.py' | xargs -0r django-upgrade --target {{DJANGO_VERSION}}
|
||||
|
||||
# Dependency management
|
||||
@pip-compile ARGS='': ## rebuilds our pip requirements
|
||||
docker compose run --rm web uv pip compile {{ ARGS }} ./requirements.in --no-strip-extras --output-file ./requirements.txt
|
||||
|
||||
@@ -28,8 +28,8 @@ class PlausibleRedirectView(View):
|
||||
|
||||
def get(self, request: HttpRequest, campaign_identifier: str, main_path: str = ""):
|
||||
absolute_url = request.build_absolute_uri(request.path)
|
||||
referrer = request.META.get("HTTP_REFERER", "")
|
||||
user_agent = request.META.get("HTTP_USER_AGENT", "")
|
||||
referrer = request.headers.get("referer", "")
|
||||
user_agent = request.headers.get("user-agent", "")
|
||||
|
||||
plausible_payload = {
|
||||
"name": "pageview",
|
||||
@@ -40,7 +40,7 @@ class PlausibleRedirectView(View):
|
||||
|
||||
headers = {"Content-Type": "application/json", "User-Agent": user_agent}
|
||||
|
||||
client_ip = request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[0].strip()
|
||||
client_ip = request.headers.get("x-forwarded-for", "").split(",")[0].strip()
|
||||
client_ip = client_ip or request.META.get("REMOTE_ADDR")
|
||||
|
||||
if client_ip:
|
||||
@@ -85,7 +85,7 @@ class WhitePaperView(SuccessMessageMixin, CreateView):
|
||||
if original_referrer := self.request.session.get("original_referrer", ""):
|
||||
self.referrer = original_referrer
|
||||
else:
|
||||
self.referrer = self.request.META.get("HTTP_REFERER", "")
|
||||
self.referrer = self.request.headers.get("referer", "")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_template_names(self):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.utils.feedgenerator import Atom1Feed
|
||||
from django.utils.timezone import make_aware, utc
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.html import urlize, linebreaks
|
||||
|
||||
from .models import Entry
|
||||
@@ -22,7 +22,7 @@ class RSSNewsFeed(Feed):
|
||||
publish_date = item.publish_at
|
||||
if publish_date:
|
||||
datetime_obj = datetime.combine(publish_date, datetime.min.time())
|
||||
aware_datetime_obj = make_aware(datetime_obj, timezone=utc)
|
||||
aware_datetime_obj = make_aware(datetime_obj, timezone=timezone.utc)
|
||||
return aware_datetime_obj
|
||||
|
||||
def item_description(self, item):
|
||||
|
||||
@@ -60,7 +60,7 @@ def generate_magic_approval_link(entry_slug: str, moderator_id: int):
|
||||
def send_email_news_needs_moderation(request, entry):
|
||||
recipient_list = [
|
||||
u
|
||||
for u in moderators().select_related("preferences").only("email")
|
||||
for u in moderators().select_related("preferences").only("email", "preferences")
|
||||
if entry.tag in u.preferences.allow_notification_others_news_needs_moderation
|
||||
]
|
||||
if not recipient_list:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils.timezone import make_aware, now, utc
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from django.utils.timezone import make_aware, now
|
||||
from model_bakery import baker
|
||||
from ..feeds import RSSNewsFeed, AtomNewsFeed
|
||||
|
||||
@@ -22,7 +22,8 @@ def test_item_pubdate(make_entry):
|
||||
feed = RSSNewsFeed()
|
||||
published_entry = make_entry(moderator=baker.make("users.User"), approved_at=now())
|
||||
expected_datetime = make_aware(
|
||||
datetime.combine(published_entry.publish_at, datetime.min.time()), timezone=utc
|
||||
datetime.combine(published_entry.publish_at, datetime.min.time()),
|
||||
timezone=timezone.utc,
|
||||
)
|
||||
assert feed.item_pubdate(published_entry) == expected_datetime
|
||||
|
||||
@@ -51,6 +52,7 @@ def test_item_pubdate_atom(make_entry):
|
||||
feed = AtomNewsFeed()
|
||||
published_entry = make_entry(moderator=baker.make("users.User"), approved_at=now())
|
||||
expected_datetime = make_aware(
|
||||
datetime.combine(published_entry.publish_at, datetime.min.time()), timezone=utc
|
||||
datetime.combine(published_entry.publish_at, datetime.min.time()),
|
||||
timezone=timezone.utc,
|
||||
)
|
||||
assert feed.item_pubdate(published_entry) == expected_datetime
|
||||
|
||||
@@ -6,7 +6,7 @@ whitelist-regex = ["test_.*"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = "py311"
|
||||
target-version = "py313"
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
-c requirements.txt
|
||||
django-debug-toolbar
|
||||
pydevd-pycharm==243.26053.29 # pinned to appropriate version for current pycharm
|
||||
pydevd-pycharm==252.26830.99 # pinned to appropriate version for current pycharm
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile ./requirements-dev.in --no-strip-extras --output-file ./requirements-dev.txt
|
||||
asgiref==3.9.1
|
||||
asgiref==3.10.0
|
||||
# via
|
||||
# -c ./requirements.txt
|
||||
# django
|
||||
django==4.2.24
|
||||
django==5.2.7
|
||||
# via
|
||||
# -c ./requirements.txt
|
||||
# django-debug-toolbar
|
||||
django-debug-toolbar==6.0.0
|
||||
# via -r ./requirements-dev.in
|
||||
pydevd-pycharm==243.26053.29
|
||||
pydevd-pycharm==252.26830.99
|
||||
# via -r ./requirements-dev.in
|
||||
sqlparse==0.5.3
|
||||
# via
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Django>=4.0, <5.0
|
||||
Django>=5.0, <6.0
|
||||
bumpversion
|
||||
django-admin-env-notice
|
||||
django-allauth
|
||||
@@ -12,7 +12,7 @@ django-health-check
|
||||
django-imagekit
|
||||
django-oauth-toolkit
|
||||
django-redis
|
||||
django-rest-auth
|
||||
django-upgrade
|
||||
django-widget-tweaks
|
||||
djangorestframework
|
||||
environs[django]
|
||||
|
||||
150
requirements.txt
150
requirements.txt
@@ -2,17 +2,17 @@
|
||||
# uv pip compile ./requirements.in --no-strip-extras --output-file ./requirements.txt
|
||||
aiohappyeyeballs==2.6.1
|
||||
# via aiohttp
|
||||
aiohttp==3.12.15
|
||||
aiohttp==3.13.1
|
||||
# via algoliasearch
|
||||
aiosignal==1.4.0
|
||||
# via aiohttp
|
||||
algoliasearch==4.27.0
|
||||
algoliasearch==4.30.0
|
||||
# via -r ./requirements.in
|
||||
amqp==5.3.1
|
||||
# via kombu
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.10.0
|
||||
anyio==4.11.0
|
||||
# via
|
||||
# httpx
|
||||
# openai
|
||||
@@ -22,7 +22,7 @@ argon2-cffi==25.1.0
|
||||
# via minio
|
||||
argon2-cffi-bindings==25.1.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.9.1
|
||||
asgiref==3.10.0
|
||||
# via
|
||||
# django
|
||||
# django-allauth
|
||||
@@ -32,21 +32,21 @@ asttokens==3.0.0
|
||||
# via stack-data
|
||||
async-timeout==5.0.1
|
||||
# via algoliasearch
|
||||
attrs==25.3.0
|
||||
attrs==25.4.0
|
||||
# via
|
||||
# aiohttp
|
||||
# interrogate
|
||||
beautifulsoup4==4.13.5
|
||||
beautifulsoup4==4.14.2
|
||||
# via -r ./requirements.in
|
||||
billiard==4.2.1
|
||||
billiard==4.2.2
|
||||
# via celery
|
||||
black==25.1.0
|
||||
black==25.9.0
|
||||
# via -r ./requirements.in
|
||||
boto3==1.40.24
|
||||
boto3==1.40.56
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# django-bakery
|
||||
botocore==1.40.24
|
||||
botocore==1.40.56
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
@@ -56,14 +56,14 @@ bumpversion==0.6.0
|
||||
# via -r ./requirements.in
|
||||
celery==5.5.3
|
||||
# via -r ./requirements.in
|
||||
certifi==2025.8.3
|
||||
certifi==2025.10.5
|
||||
# via
|
||||
# elasticsearch
|
||||
# httpcore
|
||||
# httpx
|
||||
# minio
|
||||
# requests
|
||||
cffi==1.17.1
|
||||
cffi==2.0.0
|
||||
# via
|
||||
# argon2-cffi-bindings
|
||||
# cryptography
|
||||
@@ -71,9 +71,9 @@ cfgv==3.4.0
|
||||
# via pre-commit
|
||||
chardet==5.2.0
|
||||
# via -r ./requirements.in
|
||||
charset-normalizer==3.4.3
|
||||
charset-normalizer==3.4.4
|
||||
# via requests
|
||||
click==8.2.1
|
||||
click==8.3.0
|
||||
# via
|
||||
# black
|
||||
# celery
|
||||
@@ -92,9 +92,9 @@ colorama==0.4.6
|
||||
# via interrogate
|
||||
contourpy==1.3.3
|
||||
# via matplotlib
|
||||
coverage[toml]==7.10.6
|
||||
coverage[toml]==7.11.0
|
||||
# via pytest-cov
|
||||
cryptography==45.0.7
|
||||
cryptography==46.0.3
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# jwcrypto
|
||||
@@ -107,11 +107,11 @@ distlib==0.4.0
|
||||
# via virtualenv
|
||||
distro==1.9.0
|
||||
# via openai
|
||||
dj-database-url==2.2.0
|
||||
dj-database-url==3.0.1
|
||||
# via environs
|
||||
dj-email-url==1.0.6
|
||||
# via environs
|
||||
django==4.2.24
|
||||
django==5.2.7
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# dj-database-url
|
||||
@@ -126,13 +126,12 @@ django==4.2.24
|
||||
# django-js-asset
|
||||
# django-oauth-toolkit
|
||||
# django-redis
|
||||
# django-rest-auth
|
||||
# django-storages
|
||||
# djangorestframework
|
||||
# model-bakery
|
||||
django-admin-env-notice==1.0.1
|
||||
# via -r ./requirements.in
|
||||
django-allauth[socialaccount]==65.11.1
|
||||
django-allauth[socialaccount]==65.12.1
|
||||
# via -r ./requirements.in
|
||||
django-anymail[mailgun]==13.1
|
||||
# via -r ./requirements.in
|
||||
@@ -144,7 +143,7 @@ django-cache-url==3.4.5
|
||||
# via environs
|
||||
django-click==2.4.1
|
||||
# via -r ./requirements.in
|
||||
django-cors-headers==4.7.0
|
||||
django-cors-headers==4.9.0
|
||||
# via -r ./requirements.in
|
||||
django-countries==7.6.1
|
||||
# via -r ./requirements.in
|
||||
@@ -156,53 +155,51 @@ django-haystack==3.3.0
|
||||
# via -r ./requirements.in
|
||||
django-health-check==3.20.0
|
||||
# via -r ./requirements.in
|
||||
django-imagekit==5.0.0
|
||||
django-imagekit==6.0.0
|
||||
# via -r ./requirements.in
|
||||
django-js-asset==3.1.2
|
||||
# via django-mptt
|
||||
django-mptt==0.14.0
|
||||
# via -r ./requirements.in
|
||||
django-oauth-toolkit==3.0.1
|
||||
django-oauth-toolkit==3.1.0
|
||||
# via -r ./requirements.in
|
||||
django-redis==6.0.0
|
||||
# via -r ./requirements.in
|
||||
django-rest-auth==0.9.5
|
||||
# via -r ./requirements.in
|
||||
django-storages==1.14.6
|
||||
# via -r ./requirements.in
|
||||
django-test-plus==2.3.0
|
||||
# via -r ./requirements.in
|
||||
django-tracer==0.9.3
|
||||
# via -r ./requirements.in
|
||||
django-upgrade==1.29.0
|
||||
# via -r ./requirements.in
|
||||
django-widget-tweaks==1.5.0
|
||||
# via -r ./requirements.in
|
||||
djangorestframework==3.16.1
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# django-rest-auth
|
||||
elasticsearch==7.17.12
|
||||
# via -r ./requirements.in
|
||||
elasticsearch==7.9.1
|
||||
# via -r ./requirements.in
|
||||
environs[django]==14.3.0
|
||||
# via -r ./requirements.in
|
||||
executing==2.2.1
|
||||
# via stack-data
|
||||
faker==37.6.0
|
||||
faker==37.11.0
|
||||
# via -r ./requirements.in
|
||||
fastcore==1.8.8
|
||||
fastcore==1.8.13
|
||||
# via ghapi
|
||||
filelock==3.19.1
|
||||
filelock==3.20.0
|
||||
# via virtualenv
|
||||
fonttools==4.59.2
|
||||
fonttools==4.60.1
|
||||
# via matplotlib
|
||||
frozenlist==1.7.0
|
||||
frozenlist==1.8.0
|
||||
# via
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
fs==2.4.16
|
||||
# via django-bakery
|
||||
gevent==25.8.2
|
||||
gevent==25.9.1
|
||||
# via -r ./requirements.in
|
||||
ghapi==1.0.6
|
||||
ghapi==1.0.8
|
||||
# via -r ./requirements.in
|
||||
greenlet==3.2.4
|
||||
# via
|
||||
@@ -216,19 +213,19 @@ httpcore==1.0.9
|
||||
# via httpx
|
||||
httpx==0.28.1
|
||||
# via openai
|
||||
identify==2.6.1
|
||||
identify==2.6.15
|
||||
# via pre-commit
|
||||
idna==3.10
|
||||
idna==3.11
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
# requests
|
||||
# yarl
|
||||
iniconfig==2.1.0
|
||||
iniconfig==2.3.0
|
||||
# via pytest
|
||||
interrogate==1.7.0
|
||||
# via -r ./requirements.in
|
||||
ipython==9.5.0
|
||||
ipython==9.6.0
|
||||
# via -r ./requirements.in
|
||||
ipython-pygments-lexers==1.1.1
|
||||
# via ipython
|
||||
@@ -236,7 +233,7 @@ itsdangerous==2.2.0
|
||||
# via -r ./requirements.in
|
||||
jedi==0.19.2
|
||||
# via ipython
|
||||
jiter==0.10.0
|
||||
jiter==0.11.1
|
||||
# via openai
|
||||
jmespath==1.0.1
|
||||
# via
|
||||
@@ -252,21 +249,21 @@ kiwisolver==1.4.9
|
||||
# via matplotlib
|
||||
kombu==5.5.4
|
||||
# via celery
|
||||
lxml==6.0.1
|
||||
lxml==6.0.2
|
||||
# via -r ./requirements.in
|
||||
marshmallow==4.0.1
|
||||
# via environs
|
||||
matplotlib==3.10.6
|
||||
matplotlib==3.10.7
|
||||
# via wordcloud
|
||||
matplotlib-inline==0.1.7
|
||||
# via ipython
|
||||
minio==7.2.16
|
||||
minio==7.2.18
|
||||
# via -r ./requirements.in
|
||||
mistletoe==1.4.0
|
||||
mistletoe==1.5.0
|
||||
# via -r ./requirements.in
|
||||
model-bakery==1.20.5
|
||||
# via -r ./requirements.in
|
||||
multidict==6.6.4
|
||||
multidict==6.7.0
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
@@ -274,7 +271,7 @@ mypy-extensions==1.1.0
|
||||
# via black
|
||||
nodeenv==1.9.1
|
||||
# via pre-commit
|
||||
numpy==2.3.2
|
||||
numpy==2.3.4
|
||||
# via
|
||||
# contourpy
|
||||
# matplotlib
|
||||
@@ -283,9 +280,9 @@ oauthlib==3.3.1
|
||||
# via
|
||||
# django-allauth
|
||||
# django-oauth-toolkit
|
||||
openai==1.102.0
|
||||
openai==2.6.0
|
||||
# via -r ./requirements.in
|
||||
packaging==24.1
|
||||
packaging==25.0
|
||||
# via
|
||||
# black
|
||||
# django-haystack
|
||||
@@ -303,13 +300,13 @@ pexpect==4.9.0
|
||||
# via ipython
|
||||
pilkit==3.0
|
||||
# via django-imagekit
|
||||
pillow==11.3.0
|
||||
pillow==12.0.0
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# matplotlib
|
||||
# pilkit
|
||||
# wordcloud
|
||||
platformdirs==4.4.0
|
||||
platformdirs==4.5.0
|
||||
# via
|
||||
# black
|
||||
# virtualenv
|
||||
@@ -323,13 +320,13 @@ prompt-toolkit==3.0.52
|
||||
# via
|
||||
# click-repl
|
||||
# ipython
|
||||
propcache==0.3.2
|
||||
propcache==0.4.1
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
psycogreen==1.0.2
|
||||
# via -r ./requirements.in
|
||||
psycopg2-binary==2.9.10
|
||||
psycopg2-binary==2.9.11
|
||||
# via -r ./requirements.in
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
@@ -337,33 +334,33 @@ pure-eval==0.2.3
|
||||
# via stack-data
|
||||
py==1.11.0
|
||||
# via interrogate
|
||||
pycparser==2.22
|
||||
pycparser==2.23
|
||||
# via cffi
|
||||
pycryptodome==3.23.0
|
||||
# via minio
|
||||
pydantic==2.11.9
|
||||
pydantic==2.12.3
|
||||
# via
|
||||
# algoliasearch
|
||||
# openai
|
||||
pydantic-core==2.33.2
|
||||
pydantic-core==2.41.4
|
||||
# via pydantic
|
||||
pygments==2.19.2
|
||||
# via
|
||||
# ipython
|
||||
# ipython-pygments-lexers
|
||||
# pytest
|
||||
pyjwt[crypto]==2.9.0
|
||||
pyjwt[crypto]==2.10.1
|
||||
# via
|
||||
# django-allauth
|
||||
# redis
|
||||
pyparsing==3.2.0
|
||||
pyparsing==3.2.5
|
||||
# via matplotlib
|
||||
pytest==8.4.2
|
||||
# via
|
||||
# -r ./requirements.in
|
||||
# pytest-cov
|
||||
# pytest-django
|
||||
pytest-cov==6.2.1
|
||||
pytest-cov==7.0.0
|
||||
# via -r ./requirements.in
|
||||
pytest-django==4.11.1
|
||||
# via -r ./requirements.in
|
||||
@@ -378,9 +375,11 @@ python-dotenv==1.1.1
|
||||
# via environs
|
||||
python-frontmatter==1.1.0
|
||||
# via -r ./requirements.in
|
||||
python-json-logger==3.3.0
|
||||
python-json-logger==4.0.0
|
||||
# via -r ./requirements.in
|
||||
pyyaml==6.0.2
|
||||
pytokens==0.2.0
|
||||
# via black
|
||||
pyyaml==6.0.3
|
||||
# via
|
||||
# pre-commit
|
||||
# python-frontmatter
|
||||
@@ -399,26 +398,24 @@ requests==2.32.5
|
||||
# responses
|
||||
responses==0.25.8
|
||||
# via -r ./requirements.in
|
||||
s3transfer==0.13.1
|
||||
s3transfer==0.14.0
|
||||
# via boto3
|
||||
setuptools==80.9.0
|
||||
# via
|
||||
# fs
|
||||
# zope-event
|
||||
# zope-interface
|
||||
six==1.17.0
|
||||
# via
|
||||
# django-bakery
|
||||
# django-rest-auth
|
||||
# fs
|
||||
# python-dateutil
|
||||
slack-sdk==3.36.0
|
||||
slack-sdk==3.37.0
|
||||
# via -r ./requirements.in
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
# anyio
|
||||
# openai
|
||||
soupsieve==2.6
|
||||
soupsieve==2.8
|
||||
# via beautifulsoup4
|
||||
sqlparse==0.5.3
|
||||
# via django
|
||||
@@ -428,6 +425,8 @@ structlog==25.4.0
|
||||
# via -r ./requirements.in
|
||||
tabulate==0.9.0
|
||||
# via interrogate
|
||||
tokenize-rt==6.2.0
|
||||
# via django-upgrade
|
||||
tqdm==4.67.1
|
||||
# via openai
|
||||
traitlets==5.14.3
|
||||
@@ -439,7 +438,6 @@ typing-extensions==4.15.0
|
||||
# aiosignal
|
||||
# anyio
|
||||
# beautifulsoup4
|
||||
# dj-database-url
|
||||
# django-countries
|
||||
# ipython
|
||||
# jwcrypto
|
||||
@@ -448,7 +446,7 @@ typing-extensions==4.15.0
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# typing-inspection
|
||||
typing-inspection==0.4.1
|
||||
typing-inspection==0.4.2
|
||||
# via pydantic
|
||||
tzdata==2025.2
|
||||
# via
|
||||
@@ -456,7 +454,7 @@ tzdata==2025.2
|
||||
# kombu
|
||||
unidecode==1.4.0
|
||||
# via -r ./requirements.in
|
||||
urllib3==1.26.20
|
||||
urllib3==2.5.0
|
||||
# via
|
||||
# algoliasearch
|
||||
# botocore
|
||||
@@ -465,26 +463,26 @@ urllib3==1.26.20
|
||||
# minio
|
||||
# requests
|
||||
# responses
|
||||
uv==0.8.15
|
||||
uv==0.9.5
|
||||
# via -r ./requirements.in
|
||||
vine==5.1.0
|
||||
# via
|
||||
# amqp
|
||||
# celery
|
||||
# kombu
|
||||
virtualenv==20.34.0
|
||||
virtualenv==20.35.3
|
||||
# via pre-commit
|
||||
wcwidth==0.2.13
|
||||
wcwidth==0.2.14
|
||||
# via prompt-toolkit
|
||||
wheel==0.45.1
|
||||
# via -r ./requirements.in
|
||||
whitenoise==6.9.0
|
||||
whitenoise==6.11.0
|
||||
# via -r ./requirements.in
|
||||
wordcloud==1.9.4
|
||||
# via -r ./requirements.in
|
||||
yarl==1.20.1
|
||||
yarl==1.22.0
|
||||
# via aiohttp
|
||||
zope-event==5.1.1
|
||||
zope-event==6.0
|
||||
# via gevent
|
||||
zope-interface==7.2
|
||||
zope-interface==8.0.1
|
||||
# via gevent
|
||||
|
||||
@@ -146,7 +146,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddConstraint(
|
||||
model_name="thread",
|
||||
constraint=models.CheckConstraint(
|
||||
check=django.db.models.lookups.GreaterThanOrEqual(
|
||||
condition=django.db.models.lookups.GreaterThanOrEqual(
|
||||
django.db.models.functions.comparison.Cast(
|
||||
"last_update_ts", output_field=models.FloatField()
|
||||
),
|
||||
@@ -172,7 +172,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddConstraint(
|
||||
model_name="channelupdategap",
|
||||
constraint=models.CheckConstraint(
|
||||
check=django.db.models.lookups.GreaterThan(
|
||||
condition=django.db.models.lookups.GreaterThan(
|
||||
django.db.models.functions.comparison.Cast(
|
||||
"newest_message_ts", output_field=models.FloatField()
|
||||
),
|
||||
|
||||
@@ -61,7 +61,7 @@ class ChannelUpdateGap(models.Model):
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=models.lookups.GreaterThan(
|
||||
condition=models.lookups.GreaterThan(
|
||||
models.functions.Cast(
|
||||
"newest_message_ts", output_field=models.FloatField()
|
||||
),
|
||||
@@ -110,7 +110,7 @@ class Thread(models.Model):
|
||||
unique_together = [("channel", "thread_ts")]
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=models.lookups.GreaterThanOrEqual(
|
||||
condition=models.lookups.GreaterThanOrEqual(
|
||||
models.functions.Cast(
|
||||
"last_update_ts", output_field=models.FloatField()
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from .models import User
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class EmailUserAdmin(UserAdmin):
|
||||
fieldsets = (
|
||||
(None, {"fields": ("email", "password")}),
|
||||
@@ -59,6 +60,3 @@ class EmailUserAdmin(UserAdmin):
|
||||
"claimed",
|
||||
)
|
||||
search_fields = ("email", "display_name__unaccent")
|
||||
|
||||
|
||||
admin.site.register(User, EmailUserAdmin)
|
||||
|
||||
@@ -330,7 +330,7 @@ class UserAvatar(TemplateView):
|
||||
):
|
||||
# check if user is on pages that require CSRF but don't require login
|
||||
# (auth pages where anonymous users submit forms)
|
||||
referer = self.request.META.get("HTTP_REFERER", "")
|
||||
referer = self.request.headers.get("referer", "")
|
||||
current_path = self.request.path
|
||||
|
||||
# paths that anonymous users can access and have forms
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.utils.feedgenerator import Atom1Feed
|
||||
from django.utils.timezone import make_aware, utc
|
||||
from django.utils.timezone import make_aware
|
||||
|
||||
from core.models import RenderedContent
|
||||
from .models import Version
|
||||
@@ -24,7 +24,7 @@ class RSSVersionFeed(Feed):
|
||||
release_date = item.release_date
|
||||
if release_date:
|
||||
datetime_obj = datetime.combine(release_date, datetime.min.time())
|
||||
aware_datetime_obj = make_aware(datetime_obj, timezone=utc)
|
||||
aware_datetime_obj = make_aware(datetime_obj, timezone=timezone.utc)
|
||||
return aware_datetime_obj
|
||||
|
||||
def item_description(self, item):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
from django.utils.timezone import make_aware, utc
|
||||
from datetime import datetime, timezone
|
||||
from django.utils.timezone import make_aware
|
||||
from ..feeds import RSSVersionFeed, AtomVersionFeed
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ def test_items(version, old_version):
|
||||
def test_item_pubdate(version):
|
||||
feed = RSSVersionFeed()
|
||||
expected_datetime = make_aware(
|
||||
datetime.combine(version.release_date, datetime.min.time()), timezone=utc
|
||||
datetime.combine(version.release_date, datetime.min.time()),
|
||||
timezone=timezone.utc,
|
||||
)
|
||||
assert feed.item_pubdate(version) == expected_datetime
|
||||
|
||||
@@ -51,6 +52,7 @@ def test_items_atom(version, old_version):
|
||||
def test_item_pubdate_atom(version):
|
||||
feed = AtomVersionFeed()
|
||||
expected_datetime = make_aware(
|
||||
datetime.combine(version.release_date, datetime.min.time()), timezone=utc
|
||||
datetime.combine(version.release_date, datetime.min.time()),
|
||||
timezone=timezone.utc,
|
||||
)
|
||||
assert feed.item_pubdate(version) == expected_datetime
|
||||
|
||||
Reference in New Issue
Block a user