mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 16:11:08 -05:00
Add comments app with models, views, and tests; integrate comments into existing models
This commit is contained in:
0
comments/__init__.py
Normal file
0
comments/__init__.py
Normal file
3
comments/admin.py
Normal file
3
comments/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
comments/apps.py
Normal file
6
comments/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "comments"
|
||||||
0
comments/migrations/__init__.py
Normal file
0
comments/migrations/__init__.py
Normal file
73
comments/models.py
Normal file
73
comments/models.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
class CommentThread(models.Model):
|
||||||
|
"""
|
||||||
|
A generic comment thread that can be attached to any model instance.
|
||||||
|
Used for tracking discussions on various objects across the platform.
|
||||||
|
"""
|
||||||
|
content_type = models.ForeignKey(
|
||||||
|
ContentType,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='comment_threads'
|
||||||
|
)
|
||||||
|
object_id = models.PositiveIntegerField()
|
||||||
|
content_object = GenericForeignKey('content_type', 'object_id')
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255, blank=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
created_by = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
related_name='created_comment_threads'
|
||||||
|
)
|
||||||
|
is_locked = models.BooleanField(default=False)
|
||||||
|
is_hidden = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=['content_type', 'object_id']),
|
||||||
|
]
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Comment Thread on {self.content_object} - {self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(models.Model):
|
||||||
|
"""
|
||||||
|
Individual comment within a comment thread.
|
||||||
|
"""
|
||||||
|
thread = models.ForeignKey(
|
||||||
|
CommentThread,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='comments'
|
||||||
|
)
|
||||||
|
author = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
related_name='comments'
|
||||||
|
)
|
||||||
|
content = models.TextField()
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
is_edited = models.BooleanField(default=False)
|
||||||
|
is_hidden = models.BooleanField(default=False)
|
||||||
|
parent = models.ForeignKey(
|
||||||
|
'self',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='replies'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Comment by {self.author} on {self.created_at}"
|
||||||
3
comments/tests.py
Normal file
3
comments/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
3
comments/views.py
Normal file
3
comments/views.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -2,6 +2,7 @@ from django.db import models
|
|||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
from typing import Tuple, Optional, ClassVar, TYPE_CHECKING
|
from typing import Tuple, Optional, ClassVar, TYPE_CHECKING
|
||||||
from history_tracking.models import HistoricalModel, VersionBranch, ChangeSet
|
from history_tracking.models import HistoricalModel, VersionBranch, ChangeSet
|
||||||
from history_tracking.signals import get_current_branch, ChangesetContextManager
|
from history_tracking.signals import get_current_branch, ChangesetContextManager
|
||||||
@@ -19,6 +20,10 @@ class Company(HistoricalModel):
|
|||||||
total_rides = models.IntegerField(default=0)
|
total_rides = models.IntegerField(default=0)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='company_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
objects: ClassVar[models.Manager['Company']]
|
objects: ClassVar[models.Manager['Company']]
|
||||||
|
|
||||||
@@ -101,6 +106,10 @@ class Manufacturer(HistoricalModel):
|
|||||||
total_roller_coasters = models.IntegerField(default=0)
|
total_roller_coasters = models.IntegerField(default=0)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='manufacturer_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
objects: ClassVar[models.Manager['Manufacturer']]
|
objects: ClassVar[models.Manager['Manufacturer']]
|
||||||
|
|
||||||
@@ -181,6 +190,10 @@ class Designer(HistoricalModel):
|
|||||||
total_roller_coasters = models.IntegerField(default=0)
|
total_roller_coasters = models.IntegerField(default=0)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='designer_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
objects: ClassVar[models.Manager['Designer']]
|
objects: ClassVar[models.Manager['Designer']]
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from django.http import HttpRequest, HttpResponse, Http404
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
from .models import ChangeSet, CommentThread, Comment
|
from .models import ChangeSet, HistoricalCommentThread, Comment
|
||||||
from .notifications import NotificationDispatcher
|
from .notifications import NotificationDispatcher
|
||||||
from .state_machine import ApprovalStateMachine
|
from .state_machine import ApprovalStateMachine
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ def get_comments(request: HttpRequest) -> HttpResponse:
|
|||||||
if not anchor:
|
if not anchor:
|
||||||
raise Http404("Anchor parameter is required")
|
raise Http404("Anchor parameter is required")
|
||||||
|
|
||||||
thread = CommentThread.objects.filter(anchor__id=anchor).first()
|
thread = HistoricalCommentThread.objects.filter(anchor__id=anchor).first()
|
||||||
comments = thread.comments.all() if thread else []
|
comments = thread.comments.all() if thread else []
|
||||||
|
|
||||||
return render(request, 'history_tracking/partials/comments_list.html', {
|
return render(request, 'history_tracking/partials/comments_list.html', {
|
||||||
@@ -44,7 +44,7 @@ def add_comment(request: HttpRequest) -> HttpResponse:
|
|||||||
if not content:
|
if not content:
|
||||||
return HttpResponse("Comment content is required", status=400)
|
return HttpResponse("Comment content is required", status=400)
|
||||||
|
|
||||||
thread, created = CommentThread.objects.get_or_create(
|
thread, created = HistoricalCommentThread.objects.get_or_create(
|
||||||
anchor={'id': anchor},
|
anchor={'id': anchor},
|
||||||
defaults={'created_by': request.user}
|
defaults={'created_by': request.user}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ class HistoricalModel(models.Model):
|
|||||||
id = models.BigAutoField(primary_key=True)
|
id = models.BigAutoField(primary_key=True)
|
||||||
history: HistoricalRecords = HistoricalRecords(
|
history: HistoricalRecords = HistoricalRecords(
|
||||||
inherit=True,
|
inherit=True,
|
||||||
bases=(HistoricalChangeMixin,)
|
bases=(HistoricalChangeMixin,),
|
||||||
|
excluded_fields=['comments', 'photos', 'reviews'] # Exclude all generic relations
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -116,8 +117,8 @@ class VersionTag(models.Model):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.name} ({self.branch.name})"
|
return f"{self.name} ({self.branch.name})"
|
||||||
|
|
||||||
class CommentThread(models.Model):
|
class HistoricalCommentThread(models.Model):
|
||||||
"""Represents a thread of comments on a historical record"""
|
"""Represents a thread of comments specific to historical records and version control"""
|
||||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||||
object_id = models.PositiveIntegerField()
|
object_id = models.PositiveIntegerField()
|
||||||
content_object = GenericForeignKey('content_type', 'object_id')
|
content_object = GenericForeignKey('content_type', 'object_id')
|
||||||
@@ -149,7 +150,7 @@ class CommentThread(models.Model):
|
|||||||
|
|
||||||
class Comment(models.Model):
|
class Comment(models.Model):
|
||||||
"""Individual comment within a thread"""
|
"""Individual comment within a thread"""
|
||||||
thread = models.ForeignKey(CommentThread, on_delete=models.CASCADE, related_name='comments')
|
thread = models.ForeignKey(HistoricalCommentThread, on_delete=models.CASCADE, related_name='comments')
|
||||||
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from django.views.decorators.http import require_http_methods
|
|||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
from .models import VersionBranch, ChangeSet, VersionTag, CommentThread
|
from .models import VersionBranch, ChangeSet, VersionTag, HistoricalCommentThread
|
||||||
from .managers import ChangeTracker
|
from .managers import ChangeTracker
|
||||||
from .comparison import ComparisonEngine
|
from .comparison import ComparisonEngine
|
||||||
from .state_machine import ApprovalStateMachine
|
from .state_machine import ApprovalStateMachine
|
||||||
@@ -42,7 +42,7 @@ def version_comparison(request: HttpRequest) -> HttpResponse:
|
|||||||
# Add comments to changes
|
# Add comments to changes
|
||||||
for change in diff_result['changes']:
|
for change in diff_result['changes']:
|
||||||
anchor_id = change['metadata']['comment_anchor_id']
|
anchor_id = change['metadata']['comment_anchor_id']
|
||||||
change['comments'] = CommentThread.objects.filter(
|
change['comments'] = HistoricalCommentThread.objects.filter(
|
||||||
anchor__contains={'id': anchor_id}
|
anchor__contains={'id': anchor_id}
|
||||||
).prefetch_related('comments')
|
).prefetch_related('comments')
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ class Park(HistoricalModel):
|
|||||||
Company, on_delete=models.SET_NULL, null=True, blank=True, related_name="parks"
|
Company, on_delete=models.SET_NULL, null=True, blank=True, related_name="parks"
|
||||||
)
|
)
|
||||||
photos = GenericRelation(Photo, related_query_name="park")
|
photos = GenericRelation(Photo, related_query_name="park")
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='park_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
areas: models.Manager['ParkArea'] # Type hint for reverse relation
|
areas: models.Manager['ParkArea'] # Type hint for reverse relation
|
||||||
rides: models.Manager['Ride'] # Type hint for reverse relation from rides app
|
rides: models.Manager['Ride'] # Type hint for reverse relation from rides app
|
||||||
|
|
||||||
@@ -164,6 +168,12 @@ class ParkArea(HistoricalModel):
|
|||||||
opening_date = models.DateField(null=True, blank=True)
|
opening_date = models.DateField(null=True, blank=True)
|
||||||
closing_date = models.DateField(null=True, blank=True)
|
closing_date = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='park_area_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
created_at = models.DateTimeField(auto_now_add=True, null=True)
|
created_at = models.DateTimeField(auto_now_add=True, null=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|||||||
@@ -55,5 +55,7 @@ dependencies = [
|
|||||||
"django-simple-history>=3.5.0",
|
"django-simple-history>=3.5.0",
|
||||||
"django-tailwind-cli>=2.21.1",
|
"django-tailwind-cli>=2.21.1",
|
||||||
"playwright>=1.41.0",
|
"playwright>=1.41.0",
|
||||||
"pytest-playwright>=0.4.3"
|
"pytest-playwright>=0.4.3",
|
||||||
|
"celery>=5.4.0",
|
||||||
|
"django-redis>=5.4.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pyjwt==2.10.1
|
|||||||
# Database
|
# Database
|
||||||
psycopg2-binary==2.9.10
|
psycopg2-binary==2.9.10
|
||||||
dj-database-url==2.3.0
|
dj-database-url==2.3.0
|
||||||
|
django-redis==5.4.0
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
requests==2.32.3 # For ForwardEmail.net API
|
requests==2.32.3 # For ForwardEmail.net API
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from history_tracking.models import HistoricalModel, VersionBranch, ChangeSet
|
from history_tracking.models import HistoricalModel, VersionBranch, ChangeSet
|
||||||
@@ -41,6 +41,12 @@ class Review(HistoricalModel):
|
|||||||
)
|
)
|
||||||
moderated_at = models.DateTimeField(null=True, blank=True)
|
moderated_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
|
# Comments
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='review_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-created_at']
|
ordering = ['-created_at']
|
||||||
indexes = [
|
indexes = [
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class RideModel(HistoricalModel):
|
|||||||
)
|
)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='ride_model_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['manufacturer', 'name']
|
ordering = ['manufacturer', 'name']
|
||||||
@@ -179,6 +183,10 @@ class Ride(HistoricalModel):
|
|||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
photos = GenericRelation('media.Photo')
|
photos = GenericRelation('media.Photo')
|
||||||
reviews = GenericRelation('reviews.Review')
|
reviews = GenericRelation('reviews.Review')
|
||||||
|
comments = GenericRelation('comments.CommentThread',
|
||||||
|
related_name='ride_threads',
|
||||||
|
related_query_name='comments_thread'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ INSTALLED_APPS = [
|
|||||||
"designers",
|
"designers",
|
||||||
"analytics",
|
"analytics",
|
||||||
"location",
|
"location",
|
||||||
|
"comments",
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -109,14 +110,22 @@ DATABASES = {
|
|||||||
# Cache settings
|
# Cache settings
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
"LOCATION": "unique-snowflake",
|
"LOCATION": "redis://192.168.86.3:6379/1",
|
||||||
|
"OPTIONS": {
|
||||||
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
|
"SOCKET_CONNECT_TIMEOUT": 5,
|
||||||
|
"SOCKET_TIMEOUT": 5,
|
||||||
|
"RETRY_ON_TIMEOUT": True,
|
||||||
|
"MAX_CONNECTIONS": 1000,
|
||||||
|
"PARSER_CLASS": "redis.connection.HiredisParser",
|
||||||
|
},
|
||||||
|
"KEY_PREFIX": "thrillwiki",
|
||||||
"TIMEOUT": 300, # 5 minutes
|
"TIMEOUT": 300, # 5 minutes
|
||||||
"OPTIONS": {"MAX_ENTRIES": 1000},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHE_MIDDLEWARE_SECONDS = 1 # 5 minutes
|
CACHE_MIDDLEWARE_SECONDS = 300 # 5 minutes
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX = "thrillwiki"
|
CACHE_MIDDLEWARE_KEY_PREFIX = "thrillwiki"
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
|
|||||||
161
uv.lock
generated
161
uv.lock
generated
@@ -1,6 +1,18 @@
|
|||||||
version = 1
|
version = 1
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "amqp"
|
||||||
|
version = "5.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "vine" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]amqp-5.3.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]de74d170a6f10ab044739432", size = 129013 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]amqp-5.3.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]32d479b98661a95f117880a2", size = 50944 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asgiref"
|
name = "asgiref"
|
||||||
version = "3.8.1"
|
version = "3.8.1"
|
||||||
@@ -43,6 +55,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]Automat-24.8.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]0ab63ff808566ce90551e02a", size = 42585 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]Automat-24.8.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]0ab63ff808566ce90551e02a", size = 42585 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "billiard"
|
||||||
|
version = "4.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]billiard-4.2.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]33c7c1fcd66a7e677c4fb36f", size = 155031 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]billiard-4.2.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]227baffd928c644d15d8f3cb", size = 86766 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "24.10.0"
|
version = "24.10.0"
|
||||||
@@ -63,6 +84,26 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]black-24.10.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]9853e47a294a3dd963c1dd7d", size = 206898 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]black-24.10.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]9853e47a294a3dd963c1dd7d", size = 206898 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "celery"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "billiard" },
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "click-didyoumean" },
|
||||||
|
{ name = "click-plugins" },
|
||||||
|
{ name = "click-repl" },
|
||||||
|
{ name = "kombu" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
{ name = "tzdata" },
|
||||||
|
{ name = "vine" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]celery-5.4.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]89d85f94756762d8bca7e706", size = 1575692 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]celery-5.4.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]37edde2dfc0dacd73ed97f64", size = 425983 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.12.14"
|
version = "2024.12.14"
|
||||||
@@ -156,6 +197,43 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click-8.1.8-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]d329b7903a866228027263b2", size = 98188 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click-8.1.8-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]d329b7903a866228027263b2", size = 98188 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-didyoumean"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click_didyoumean-0.3.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]05aa09cbfc07c9d7fbb5a463", size = 3089 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]957e63d0fac41c10e7c3117c", size = 3631 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-plugins"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click-plugins-1.1.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]df9a3a3742e9ed63645f264b", size = 8164 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]dc815c06b442aa3c02889fc8", size = 7497 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-repl"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "prompt-toolkit" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click-repl-0.3.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]0fe37474f3feebb69ced26a9", size = 10449 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]click_repl-0.3.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]4c6899382da7feeeeb51b812", size = 10289 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -328,6 +406,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_oauth_toolkit-3.0.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]221bbb1cffcb50b8932e55ed", size = 77299 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_oauth_toolkit-3.0.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]221bbb1cffcb50b8932e55ed", size = 77299 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-redis"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "redis" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django-redis-5.4.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]50b2db93602e6cb292818c42", size = 52567 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_redis-5.4.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]319df4c6da6ca9a2942edd5b", size = 31119 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-simple-history"
|
name = "django-simple-history"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
@@ -483,6 +574,20 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]8be71f67f03566692fd55789", size = 92520 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]8be71f67f03566692fd55789", size = 92520 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kombu"
|
||||||
|
version = "5.4.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "amqp" },
|
||||||
|
{ name = "tzdata" },
|
||||||
|
{ name = "vine" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]kombu-5.4.2.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]ff31e7fba89138cdb406f2cf", size = 442858 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]kombu-5.4.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]8c49ea866884047d66e14763", size = 201349 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mccabe"
|
name = "mccabe"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -610,6 +715,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pluggy-1.5.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]4d554740532335b9d75ea669", size = 20556 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pluggy-1.5.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]4d554740532335b9d75ea669", size = 20556 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prompt-toolkit"
|
||||||
|
version = "3.0.50"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "wcwidth" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]prompt_toolkit-3.0.50.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]c9728359013f79877fc89bab", size = 429087 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]89e6d88ebbb1b509d9779198", size = 387816 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg2-binary"
|
name = "psycopg2-binary"
|
||||||
version = "2.9.10"
|
version = "2.9.10"
|
||||||
@@ -626,6 +743,7 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:[AWS-SECRET-REMOVED]07c6df12b7737febc40f0909", size = 2822712 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:[AWS-SECRET-REMOVED]07c6df12b7737febc40f0909", size = 2822712 },
|
||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:[AWS-SECRET-REMOVED]860ff3bbe1384130828714b1", size = 2920155 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:[AWS-SECRET-REMOVED]860ff3bbe1384130828714b1", size = 2920155 },
|
||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:[AWS-SECRET-REMOVED]998122abe1dce6428bd86567", size = 2959356 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:[AWS-SECRET-REMOVED]998122abe1dce6428bd86567", size = 2959356 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:[AWS-SECRET-REMOVED]9b46e6fd07c3eb46e4535142", size = 2569224 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -773,6 +891,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pytest_playwright-0.6.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]1f8b3acf575beed84e7e9043", size = 16436 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pytest_playwright-0.6.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]1f8b3acf575beed84e7e9043", size = 16436 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.9.0.post0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]f90fb72437e91a35459a0ad3", size = 342432 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]6dfe83f850eea9a5f7470427", size = 229892 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -851,6 +981,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]56c89140852d1120324e8686", size = 9755 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]56c89140852d1120324e8686", size = 9755 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]six-1.17.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]63acc5171de367e834932a81", size = 34031 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]six-1.17.0-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]b2e1e8eda2be8970586c3274", size = 11050 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparse"
|
name = "sqlparse"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -875,6 +1014,7 @@ version = "0.1.0"
|
|||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "black" },
|
{ name = "black" },
|
||||||
|
{ name = "celery" },
|
||||||
{ name = "channels" },
|
{ name = "channels" },
|
||||||
{ name = "channels-redis" },
|
{ name = "channels-redis" },
|
||||||
{ name = "daphne" },
|
{ name = "daphne" },
|
||||||
@@ -887,6 +1027,7 @@ dependencies = [
|
|||||||
{ name = "django-filter" },
|
{ name = "django-filter" },
|
||||||
{ name = "django-htmx" },
|
{ name = "django-htmx" },
|
||||||
{ name = "django-oauth-toolkit" },
|
{ name = "django-oauth-toolkit" },
|
||||||
|
{ name = "django-redis" },
|
||||||
{ name = "django-simple-history" },
|
{ name = "django-simple-history" },
|
||||||
{ name = "django-tailwind-cli" },
|
{ name = "django-tailwind-cli" },
|
||||||
{ name = "django-webpack-loader" },
|
{ name = "django-webpack-loader" },
|
||||||
@@ -908,6 +1049,7 @@ dependencies = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "black", specifier = ">=24.1.0" },
|
{ name = "black", specifier = ">=24.1.0" },
|
||||||
|
{ name = "celery", specifier = ">=5.4.0" },
|
||||||
{ name = "channels", specifier = ">=4.2.0" },
|
{ name = "channels", specifier = ">=4.2.0" },
|
||||||
{ name = "channels-redis", specifier = ">=4.2.1" },
|
{ name = "channels-redis", specifier = ">=4.2.1" },
|
||||||
{ name = "daphne", specifier = ">=4.1.2" },
|
{ name = "daphne", specifier = ">=4.1.2" },
|
||||||
@@ -920,6 +1062,7 @@ requires-dist = [
|
|||||||
{ name = "django-filter", specifier = ">=23.5" },
|
{ name = "django-filter", specifier = ">=23.5" },
|
||||||
{ name = "django-htmx", specifier = ">=1.17.2" },
|
{ name = "django-htmx", specifier = ">=1.17.2" },
|
||||||
{ name = "django-oauth-toolkit", specifier = ">=3.0.1" },
|
{ name = "django-oauth-toolkit", specifier = ">=3.0.1" },
|
||||||
|
{ name = "django-redis", specifier = ">=5.4.0" },
|
||||||
{ name = "django-simple-history", specifier = ">=3.5.0" },
|
{ name = "django-simple-history", specifier = ">=3.5.0" },
|
||||||
{ name = "django-tailwind-cli", specifier = ">=2.21.1" },
|
{ name = "django-tailwind-cli", specifier = ">=2.21.1" },
|
||||||
{ name = "django-webpack-loader", specifier = ">=3.1.1" },
|
{ name = "django-webpack-loader", specifier = ">=3.1.1" },
|
||||||
@@ -1012,6 +1155,24 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]urllib3-2.3.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]710050facf0dd6911440e3df", size = 128369 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]urllib3-2.3.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]710050facf0dd6911440e3df", size = 128369 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vine"
|
||||||
|
version = "5.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]vine-5.1.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]15bb196ce38aefd6799e61e0", size = 48980 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]vine-5.1.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]810050a728bd7413811fb1dc", size = 9636 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wcwidth"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]wcwidth-0.2.13.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]6ce63e9b4e64fc18303972b5", size = 101301 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]6bdb8d6102117aac784f6859", size = 34166 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whitenoise"
|
name = "whitenoise"
|
||||||
version = "6.8.2"
|
version = "6.8.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user