Django 4.2 upgrade

This commit is contained in:
Sonny Bakker 2024-09-07 20:50:38 +02:00
parent b78f03d3b0
commit dfb049ae14
32 changed files with 345 additions and 414 deletions

View file

@ -5,23 +5,22 @@ authors = [{name = 'Sonny', email= 'sonnyba871@gmail.com'}]
license = {text = 'GPL-3.0'}
requires-python = '>=3.11'
dependencies = [
'django~=3.2',
'celery~=5.0',
'psycopg2',
'django~=4.2',
'celery~=5.4',
'psycopg',
'django-axes',
'django-celery-beat~=2.5.0',
'django-celery-beat~=2.7.0',
'django-registration-redux~=2.7',
'django-rest-framework',
"python-memcached<=1.59",
'python-dotenv~=0.12',
'ftfy~=5.8',
'pymemcache',
'python-dotenv~=1.0.1',
'ftfy~=6.2',
'requests',
'requests_oauthlib',
'feedparser',
'bleach',
'beautifulsoup4',
'lxml',
"setuptools>=74.0.0",
]
[project.optional-dependencies]
@ -33,10 +32,11 @@ testing = [
]
development = [
'django-debug-toolbar',
'django-stubs',
'django-extensions',
]
ci = ['coverage>=5.3.1']
production = ['gunicorn~=20.0', 'sentry-sdk~=1.0']
ci = ['coverage~=7.6.1']
production = ['gunicorn~=23.0', 'sentry-sdk~=2.0']
[tool.uv]
environments = ["sys_platform == 'linux'"]

View file

@ -2,7 +2,7 @@ from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from newsreader.accounts.models import User

View file

@ -106,11 +106,11 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211",
},
"axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211",
},
}

View file

@ -22,11 +22,11 @@ AXES_ENABLED = False
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211",
},
"axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211",
},
}

View file

@ -23,7 +23,6 @@ class App extends React.Component {
<PostList
feedUrl={this.props.feedUrl}
subredditUrl={this.props.subredditUrl}
timelineUrl={this.props.timelineUrl}
timezone={this.props.timezone}
/>
@ -35,7 +34,6 @@ class App extends React.Component {
selectedType={this.props.selectedType}
feedUrl={this.props.feedUrl}
subredditUrl={this.props.subredditUrl}
timelineUrl={this.props.timelineUrl}
categoriesUrl={this.props.categoriesUrl}
timezone={this.props.timezone}
autoMarking={this.props.autoMarking}

View file

@ -3,14 +3,7 @@ import { connect } from 'react-redux';
import Cookies from 'js-cookie';
import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js';
import {
CATEGORY_TYPE,
RULE_TYPE,
SAVED_TYPE,
FEED,
SUBREDDIT,
TWITTER_TIMELINE,
} from '../constants.js';
import { SAVED_TYPE, SUBREDDIT } from '../constants.js';
import { formatDatetime } from '../../../utils.js';
class PostModal extends React.Component {
@ -62,9 +55,6 @@ class PostModal extends React.Component {
case SUBREDDIT:
ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`;
break;
case TWITTER_TIMELINE:
ruleUrl = '#';
break;
default:
ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`;
break;

View file

@ -4,11 +4,8 @@ import Cookies from 'js-cookie';
import {
CATEGORY_TYPE,
RULE_TYPE,
SAVED_TYPE,
FEED,
SUBREDDIT,
TWITTER_TIMELINE,
} from '../../constants.js';
import { selectPost, toggleSaved } from '../../actions/posts.js';
import { formatDatetime } from '../../../../utils.js';
@ -28,8 +25,6 @@ class PostItem extends React.Component {
let ruleUrl = '';
if (rule.type === SUBREDDIT) {
ruleUrl = `${this.props.subredditUrl}/${rule.id}/`;
} else if (rule.type === TWITTER_TIMELINE) {
ruleUrl = `${this.props.timelineUrl}/${rule.id}/`;
} else {
ruleUrl = `${this.props.feedUrl}/${rule.id}/`;
}

View file

@ -65,7 +65,6 @@ class PostList extends React.Component {
selected: this.props.selected,
feedUrl: this.props.feedUrl,
subredditUrl: this.props.subredditUrl,
timelineUrl: this.props.timelineUrl,
timezone: this.props.timezone,
};

View file

@ -4,4 +4,3 @@ export const SAVED_TYPE = 'SAVED';
export const SUBREDDIT = 'subreddit';
export const FEED = 'feed';
export const TWITTER_TIMELINE = 'twitter_timeline';

View file

@ -12,14 +12,13 @@ if (page) {
const store = configureStore();
const settings = JSON.parse(document.getElementById('homepageSettings').textContent);
const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings;
const { feedUrl, subredditUrl, categoriesUrl } = settings;
const app = (
<Provider store={store}>
<App
feedUrl={feedUrl.substring(1, feedUrl.length - 3)}
subredditUrl={subredditUrl.substring(1, subredditUrl.length - 3)}
timelineUrl={timelineUrl.substring(1, timelineUrl.length - 3)}
categoriesUrl={categoriesUrl.substring(1, categoriesUrl.length - 3)}
timezone={settings.timezone}
autoMarking={settings.autoMarking}

View file

@ -6,8 +6,6 @@ from datetime import timedelta
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.utils import timezone
import pytz
from feedparser import parse
from newsreader.news.collection.base import (
@ -58,7 +56,6 @@ class FeedBuilder(PostBuilder):
"published_parsed": "publication_date",
"author": "author",
}
tz = pytz.timezone(self.stream.rule.timezone)
data = {"rule_id": self.stream.rule.pk}
for field, model_field in field_mapping.items():
@ -68,7 +65,7 @@ class FeedBuilder(PostBuilder):
value = truncate_text(Post, model_field, entry[field])
if field == "published_parsed":
data[model_field] = build_publication_date(value, tz)
data[model_field] = build_publication_date(value)
elif field == "summary":
data[model_field] = self.sanitize_fragment(value)
else:

View file

@ -1,24 +1,15 @@
from django import forms
from django.utils.translation import gettext_lazy as _
import pytz
from newsreader.core.forms import CheckboxInput
from newsreader.news.collection.forms.base import CollectionRuleForm
from newsreader.news.collection.models import CollectionRule
class FeedForm(CollectionRuleForm):
timezone = forms.ChoiceField(
widget=forms.Select(attrs={"size": len(pytz.all_timezones)}),
choices=((timezone, timezone) for timezone in pytz.all_timezones),
help_text=_("The timezone which the feed uses"),
initial=pytz.utc,
)
class Meta:
model = CollectionRule
fields = ("name", "url", "timezone", "favicon", "category")
fields = ("name", "url", "favicon", "category")
class OPMLImportForm(forms.Form):

View file

@ -3,8 +3,6 @@ from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
import pytz
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.forms.base import CollectionRuleForm
from newsreader.news.collection.models import CollectionRule
@ -36,7 +34,6 @@ class SubRedditForm(CollectionRuleForm):
instance = super().save(commit=False)
instance.type = RuleTypeChoices.subreddit
instance.timezone = str(pytz.utc)
if commit:
instance.save()

View file

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-09-07 17:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('collection', '0016_alter_collectionrule_timezone'),
]
operations = [
migrations.RemoveField(
model_name='collectionrule',
name='timezone',
),
]

View file

@ -3,8 +3,6 @@ from django.conf import settings
from django.urls import reverse
from django.utils.translation import gettext as _
import pytz
from newsreader.core.models import TimeStampedModel
from newsreader.news.collection.choices import RuleTypeChoices
@ -26,12 +24,6 @@ class CollectionRule(TimeStampedModel):
)
favicon = models.URLField(blank=True, null=True)
timezone = models.CharField(
choices=((timezone, timezone) for timezone in pytz.all_timezones),
max_length=100,
default=str(pytz.utc),
)
category = models.ForeignKey(
"core.Category",
blank=True,

View file

@ -1,7 +1,7 @@
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from html import unescape
from json.decoder import JSONDecodeError
from urllib.parse import urlencode
@ -9,10 +9,8 @@ from uuid import uuid4
from django.conf import settings
from django.core.cache import cache
from django.utils import timezone
from django.utils.html import format_html
import pytz
import requests
from newsreader.news.collection.base import (
@ -214,8 +212,8 @@ class RedditBuilder(PostBuilder):
body = self.get_url_post(title, direct_url)
try:
parsed_date = datetime.fromtimestamp(entry_data["created_utc"])
created_date = pytz.utc.localize(parsed_date)
_created_date = datetime.fromtimestamp(entry_data["created_utc"])
created_date = _created_date.replace(tzinfo=timezone.utc)
except (OverflowError, OSError) as e:
raise BuilderParseException(payload=entry) from e
except KeyError as e:
@ -370,7 +368,7 @@ class RedditClient(PostClient):
continue
finally:
stream.rule.last_run = timezone.now()
stream.rule.last_run = datetime.now(tz=timezone.utc)
stream.rule.save()

View file

@ -1,7 +1,7 @@
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
import requests

View file

@ -1,12 +1,10 @@
import json
from datetime import date, datetime, time
from datetime import date, datetime, time, timezone
from django.test import TestCase
from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -153,22 +151,22 @@ class NestedRuleListViewTestCase(TestCase):
FeedPostFactory(
title="I'm the first post",
rule=rule,
publication_date=datetime.combine(
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
),
publication_date=(
datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc)
)
),
FeedPostFactory(
title="I'm the second post",
rule=rule,
publication_date=datetime.combine(
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc
publication_date=(
datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc)
),
),
FeedPostFactory(
title="I'm the third post",
rule=rule,
publication_date=datetime.combine(
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc
publication_date=(
datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc)
),
),
]

View file

@ -1,10 +1,7 @@
from datetime import datetime
from datetime import datetime, timezone
from unittest.mock import Mock
from django.test import TestCase
from django.utils import timezone
import pytz
from freezegun import freeze_time
@ -51,7 +48,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0]
publication_date = datetime(
2019, 5, 20, hour=16, minute=32, second=38, tzinfo=pytz.utc
2019, 5, 20, hour=16, minute=32, second=38, tzinfo=timezone.utc
)
self.assertEqual(
@ -75,7 +72,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1]
publication_date = datetime(
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
)
self.assertEqual(
@ -110,7 +107,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0]
publication_date = datetime(
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
)
self.assertEqual(post.publication_date, publication_date)
@ -125,7 +122,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1]
publication_date = datetime(
2019, 5, 20, hour=12, minute=19, second=19, tzinfo=pytz.utc
2019, 5, 20, hour=12, minute=19, second=19, tzinfo=timezone.utc
)
self.assertEqual(post.publication_date, publication_date)
@ -149,7 +146,7 @@ class FeedBuilderTestCase(TestCase):
self.assertEqual(
post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
)
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -160,7 +157,7 @@ class FeedBuilderTestCase(TestCase):
self.assertEqual(
post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
)
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
)
@ -178,7 +175,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0]
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -186,7 +183,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1]
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
)
@ -238,7 +235,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0]
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -247,7 +244,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1]
self.assertEqual(post.created, timezone.now())
self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
)

View file

@ -1,11 +1,8 @@
from datetime import date, datetime, time
from datetime import datetime, timezone
from time import struct_time
from unittest.mock import Mock, patch
from django.test import TestCase
from django.utils import timezone
import pytz
from freezegun import freeze_time
@ -47,10 +44,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now())
self.assertEquals(rule.error, None)
self.assertEqual(Post.objects.count(), 3)
self.assertEqual(rule.succeeded, True)
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEqual(rule.error, None)
def test_emtpy_batch(self):
self.mocked_fetch.return_value = Mock()
@ -63,10 +60,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, True)
self.assertEquals(rule.error, None)
self.assertEquals(rule.last_run, timezone.now())
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, True)
self.assertEqual(rule.error, None)
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_not_found(self):
self.mocked_fetch.side_effect = StreamNotFoundException
@ -77,14 +74,14 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream not found")
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream not found")
def test_denied(self):
self.mocked_fetch.side_effect = StreamDeniedException
old_run = timezone.make_aware(datetime(2019, 10, 30, 12, 30))
old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
rule = FeedFactory(last_run=old_run)
collector = FeedCollector()
@ -92,15 +89,15 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream does not have sufficient permissions")
self.assertEquals(rule.last_run, timezone.now())
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream does not have sufficient permissions")
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_forbidden(self):
self.mocked_fetch.side_effect = StreamForbiddenException
old_run = pytz.utc.localize(datetime(2019, 10, 30, 12, 30))
old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
rule = FeedFactory(last_run=old_run)
collector = FeedCollector()
@ -108,17 +105,15 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream forbidden")
self.assertEquals(rule.last_run, timezone.now())
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream forbidden")
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_timed_out(self):
self.mocked_fetch.side_effect = StreamTimeOutException
last_run = timezone.make_aware(
datetime.combine(date=date(2019, 10, 30), time=time(12, 30))
)
last_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
rule = FeedFactory(last_run=last_run)
collector = FeedCollector()
@ -126,11 +121,11 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream timed out")
self.assertEquals(
rule.last_run, pytz.utc.localize(datetime(2019, 10, 30, 12, 30))
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream timed out")
self.assertEqual(
rule.last_run, datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
)
def test_duplicates(self):
@ -139,7 +134,7 @@ class FeedCollectorTestCase(TestCase):
rule = FeedFactory()
aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc
struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0))
)
FeedPostFactory(
@ -152,7 +147,7 @@ class FeedCollectorTestCase(TestCase):
)
aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc
struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0))
)
FeedPostFactory(
@ -165,7 +160,7 @@ class FeedCollectorTestCase(TestCase):
)
aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc
struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0))
)
FeedPostFactory(
@ -183,10 +178,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now())
self.assertEquals(rule.error, None)
self.assertEqual(Post.objects.count(), 3)
self.assertEqual(rule.succeeded, True)
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEqual(rule.error, None)
def test_items_with_identifiers_get_updated(self):
self.mocked_parse.return_value = multiple_update_mock
@ -197,7 +192,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/",
title="Trump",
body="Foreign Minister Mohammad Javad Zarif",
publication_date=timezone.now(),
publication_date=datetime.now(tz=timezone.utc),
rule=rule,
)
@ -206,7 +201,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/",
title="Huawei's Android loss: How it affects you",
body="Google's move to end business ties with Huawei will",
publication_date=timezone.now(),
publication_date=datetime.now(tz=timezone.utc),
rule=rule,
)
@ -215,7 +210,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Police are investigating the messages while an MP",
publication_date=timezone.now(),
publication_date=datetime.now(tz=timezone.utc),
rule=rule,
)
@ -227,19 +222,19 @@ class FeedCollectorTestCase(TestCase):
second_post.refresh_from_db()
third_post.refresh_from_db()
self.assertEquals(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now())
self.assertEquals(rule.error, None)
self.assertEqual(Post.objects.count(), 3)
self.assertEqual(rule.succeeded, True)
self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEqual(rule.error, None)
self.assertEquals(
self.assertEqual(
first_post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif"
)
self.assertEquals(
self.assertEqual(
second_post.title, "Huawei's Android loss: How it affects you"
)
self.assertEquals(
self.assertEqual(
third_post.title, "Birmingham head teacher threatened over LGBT lessons"
)

View file

@ -1,10 +1,8 @@
from datetime import datetime
from datetime import datetime, timezone
from unittest.mock import Mock
from django.test import TestCase
import pytz
from newsreader.news.collection.reddit import RedditBuilder
from newsreader.news.collection.tests.factories import SubredditFactory
from newsreader.news.collection.tests.reddit.builder.mocks import (
@ -53,8 +51,8 @@ class RedditBuilderTestCase(TestCase):
post = posts["hm0qct"]
self.assertEquals(post.rule, subreddit)
self.assertEquals(
self.assertEqual(post.rule, subreddit)
self.assertEqual(
post.title,
"Linux Experiences/Rants or Education/Certifications thread - July 06, 2020",
)
@ -72,13 +70,13 @@ class RedditBuilderTestCase(TestCase):
post.body,
)
self.assertEquals(post.author, "AutoModerator")
self.assertEquals(
self.assertEqual(post.author, "AutoModerator")
self.assertEqual(
post.url,
"https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/",
)
self.assertEquals(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22))
self.assertEqual(
post.publication_date, datetime(2020, 7, 6, 6, 11, 22, tzinfo=timezone.utc)
)
def test_empty_data(self):
@ -91,7 +89,7 @@ class RedditBuilderTestCase(TestCase):
builder.build()
builder.save()
self.assertEquals(Post.objects.count(), 0)
self.assertEqual(Post.objects.count(), 0)
def test_unknown_mock(self):
builder = RedditBuilder
@ -103,7 +101,7 @@ class RedditBuilderTestCase(TestCase):
builder.build()
builder.save()
self.assertEquals(Post.objects.count(), 0)
self.assertEqual(Post.objects.count(), 0)
def test_html_sanitizing(self):
builder = RedditBuilder
@ -121,7 +119,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"]
self.assertEquals(post.body, "<article></article>")
self.assertEqual(post.body, "<article></article>")
def test_long_author_text_is_truncated(self):
builder = RedditBuilder
@ -139,7 +137,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"]
self.assertEquals(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…")
self.assertEqual(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…")
def test_long_title_text_is_truncated(self):
builder = RedditBuilder
@ -157,7 +155,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"]
self.assertEquals(
self.assertEqual(
post.title,
'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on t…',
)
@ -174,7 +172,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 2)
self.assertEqual(Post.objects.count(), 2)
self.assertCountEqual(("hm0qct", "hna75r"), posts.keys())
def test_duplicate_in_database(self):
@ -191,7 +189,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 5)
self.assertEqual(Post.objects.count(), 5)
self.assertCountEqual(
("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys()
)
@ -220,11 +218,11 @@ class RedditBuilderTestCase(TestCase):
)
url = "https://i.redd.it/cm2qybia1va51.jpg"
self.assertEquals(
self.assertEqual(
"https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/",
post.url,
)
self.assertEquals(
self.assertEqual(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
)
@ -247,11 +245,11 @@ class RedditBuilderTestCase(TestCase):
url = "http://gfycat.com/thatalivedogwoodclubgall"
title = "Excited cows have a new brush!"
self.assertEquals(
self.assertEqual(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body,
)
self.assertEquals(
self.assertEqual(
"https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/",
post.url,
)
@ -261,10 +259,10 @@ class RedditBuilderTestCase(TestCase):
url = "https://i.imgur.com/usfMVUJ.jpg"
title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallass cat kittens"
self.assertEquals(
self.assertEqual(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
)
self.assertEquals(
self.assertEqual(
"https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/",
post.url,
)
@ -287,11 +285,11 @@ class RedditBuilderTestCase(TestCase):
url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback"
self.assertEquals(
self.assertEqual(
post.url,
"https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/",
)
self.assertEquals(
self.assertEqual(
f"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
post.body,
)
@ -308,9 +306,9 @@ class RedditBuilderTestCase(TestCase):
post = Post.objects.get()
self.assertEquals(post.remote_identifier, "hulh8k")
self.assertEqual(post.remote_identifier, "hulh8k")
self.assertEquals(
self.assertEqual(
post.url,
"https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/",
)
@ -318,7 +316,7 @@ class RedditBuilderTestCase(TestCase):
title = "Dog splashing in water"
url = "https://gfycat.com/excellentinfantileamericanwigeon"
self.assertEquals(
self.assertEqual(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body,
)
@ -335,13 +333,13 @@ class RedditBuilderTestCase(TestCase):
post = Post.objects.get()
self.assertEquals(post.remote_identifier, "humdlf")
self.assertEqual(post.remote_identifier, "humdlf")
self.assertEquals(
self.assertEqual(
post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/"
)
self.assertEquals(
self.assertEqual(
"<div><video controls muted><source src='https://i.imgur.com/grVh2AG.mp4' type='video/mp4' /></video></div>",
post.body,
)
@ -366,7 +364,7 @@ class RedditBuilderTestCase(TestCase):
post.body,
)
self.assertEquals(
self.assertEqual(
post.url,
"https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/",
)
@ -381,7 +379,7 @@ class RedditBuilderTestCase(TestCase):
builder.build()
builder.save()
self.assertEquals(Post.objects.count(), 0)
self.assertEqual(Post.objects.count(), 0)
def test_nsfw_not_allowed(self):
builder = RedditBuilder
@ -395,7 +393,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys())
def test_spoiler_not_allowed(self):
@ -410,7 +408,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys())
def test_already_seen_not_allowed(self):
@ -425,7 +423,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys())
def test_upvote_minimum(self):
@ -440,7 +438,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys())
def test_comments_minimum(self):
@ -455,7 +453,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys())
def test_downvote_maximum(self):
@ -470,5 +468,5 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1)
self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys())

View file

@ -1,11 +1,8 @@
from datetime import datetime
from datetime import datetime, timezone
from unittest.mock import patch
from uuid import uuid4
from django.test import TestCase
from django.utils import timezone
import pytz
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.exceptions import (
@ -73,23 +70,23 @@ class RedditCollectorTestCase(TestCase):
for subreddit in rules:
with self.subTest(subreddit=subreddit):
self.assertEquals(subreddit.succeeded, True)
self.assertEquals(subreddit.last_run, timezone.now())
self.assertEquals(subreddit.error, None)
self.assertEqual(subreddit.succeeded, True)
self.assertEqual(subreddit.last_run, datetime.now(tz=timezone.utc))
self.assertEqual(subreddit.error, None)
post = Post.objects.get(
remote_identifier="hph00n", rule__type=RuleTypeChoices.subreddit
)
self.assertEquals(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 11, 22, 23, 24))
self.assertEqual(
post.publication_date, datetime(2020, 7, 11, 22, 23, 24, tzinfo=timezone.utc)
)
self.assertEquals(post.author, "HannahB888")
self.assertEquals(
self.assertEqual(post.author, "HannahB888")
self.assertEqual(
post.title, "Drake Interplanetary Smartkey thing that I made!"
)
self.assertEquals(
self.assertEqual(
post.url,
"https://www.reddit.com/r/starcitizen/comments/hph00n/drake_interplanetary_smartkey_thing_that_i_made/",
)
@ -98,16 +95,16 @@ class RedditCollectorTestCase(TestCase):
remote_identifier="hpr28u", rule__type=RuleTypeChoices.subreddit
)
self.assertEquals(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 12, 10, 29, 10))
self.assertEqual(
post.publication_date, datetime(2020, 7, 12, 10, 29, 10, tzinfo=timezone.utc)
)
self.assertEquals(post.author, "Sebaron")
self.assertEquals(
self.assertEqual(post.author, "Sebaron")
self.assertEqual(
post.title,
"I am a medical student, and I recently programmed an open-source eye-tracker for brain research",
)
self.assertEquals(
self.assertEqual(
post.url,
"https://www.reddit.com/r/Python/comments/hpr28u/i_am_a_medical_student_and_i_recently_programmed/",
)
@ -128,13 +125,13 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector()
collector.collect(rules=rules)
self.assertEquals(Post.objects.count(), 0)
self.assertEqual(Post.objects.count(), 0)
for subreddit in rules:
with self.subTest(subreddit=subreddit):
self.assertEquals(subreddit.succeeded, True)
self.assertEquals(subreddit.last_run, timezone.now())
self.assertEquals(subreddit.error, None)
self.assertEqual(subreddit.succeeded, True)
self.assertEqual(subreddit.last_run, datetime.now(tz=timezone.utc))
self.assertEqual(subreddit.error, None)
def test_not_found(self):
self.mocked_fetch.side_effect = StreamNotFoundException
@ -148,9 +145,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector()
collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream not found")
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream not found")
@patch("newsreader.news.collection.reddit.RedditTokenTask")
def test_denied(self, mocked_task):
@ -165,9 +162,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector()
collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream does not have sufficient permissions")
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream does not have sufficient permissions")
mocked_task.delay.assert_called_once_with(rule.user.pk)
@ -183,9 +180,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector()
collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream forbidden")
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream forbidden")
def test_timed_out(self):
self.mocked_fetch.side_effect = StreamTimeOutException
@ -199,6 +196,6 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector()
collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False)
self.assertEquals(rule.error, "Stream timed out")
self.assertEqual(Post.objects.count(), 0)
self.assertEqual(rule.succeeded, False)
self.assertEqual(rule.error, "Stream timed out")

View file

@ -46,7 +46,6 @@ class CollectionRuleViewTestCase:
name="new name",
category=other_rule.category,
url=other_rule.url,
timezone=other_rule.timezone,
)
other_url = reverse("news:collection:feed-update", args=[other_rule.pk])

View file

@ -1,8 +1,6 @@
from django.test import TestCase
from django.urls import reverse
import pytz
from django_celery_beat.models import PeriodicTask
from newsreader.news.collection.choices import RuleTypeChoices
@ -21,23 +19,21 @@ class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(
name="new rule",
url="https://www.rss.com/rss",
timezone=pytz.utc,
category=str(self.category.pk),
)
def test_creation(self):
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="new rule")
self.assertEquals(rule.type, RuleTypeChoices.feed)
self.assertEquals(rule.url, "https://www.rss.com/rss")
self.assertEquals(rule.timezone, str(pytz.utc))
self.assertEquals(rule.favicon, None)
self.assertEquals(rule.category.pk, self.category.pk)
self.assertEquals(rule.user.pk, self.user.pk)
self.assertEqual(rule.type, RuleTypeChoices.feed)
self.assertEqual(rule.url, "https://www.rss.com/rss")
self.assertEqual(rule.favicon, None)
self.assertEqual(rule.category.pk, self.category.pk)
self.assertEqual(rule.user.pk, self.user.pk)
self.assertTrue(
PeriodicTask.objects.get(
@ -59,18 +55,17 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
name=self.rule.name,
category=self.rule.category.pk,
url=self.rule.url,
timezone=self.rule.timezone,
)
def test_name_change(self):
self.form_data.update(name="new name")
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db()
self.assertEquals(self.rule.name, "new name")
self.assertEqual(self.rule.name, "new name")
def test_category_change(self):
new_category = CategoryFactory(user=self.user)
@ -78,21 +73,21 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(category=new_category.pk)
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db()
self.assertEquals(self.rule.category.pk, new_category.pk)
self.assertEqual(self.rule.category.pk, new_category.pk)
def test_category_removal(self):
self.form_data.update(category="")
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db()
self.assertEquals(self.rule.category, None)
self.assertEqual(self.rule.category, None)
def test_rules_only(self):
rule = FeedFactory(
@ -106,4 +101,4 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
self.assertEqual(response.status_code, 404)

View file

@ -2,8 +2,6 @@ from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext as _
import pytz
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL
@ -32,16 +30,15 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
def test_creation(self):
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="new rule")
self.assertEquals(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc))
self.assertEquals(rule.favicon, None)
self.assertEquals(rule.category.pk, self.category.pk)
self.assertEquals(rule.user.pk, self.user.pk)
self.assertEqual(rule.type, RuleTypeChoices.subreddit)
self.assertEqual(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEqual(rule.favicon, None)
self.assertEqual(rule.category.pk, self.category.pk)
self.assertEqual(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww")
@ -70,7 +67,6 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
"name": self.rule.name,
"url": self.rule.url,
"category": str(self.category.pk),
"timezone": pytz.utc,
"reddit_allow_nfsw": False,
"reddit_allow_spoiler": False,
"reddit_allow_viewed": True,
@ -82,11 +78,11 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(name="Python 2")
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db()
self.assertEquals(self.rule.name, "Python 2")
self.assertEqual(self.rule.name, "Python 2")
def test_category_change(self):
new_category = CategoryFactory(user=self.user)
@ -94,11 +90,11 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(category=new_category.pk)
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db()
self.assertEquals(self.rule.category.pk, new_category.pk)
self.assertEqual(self.rule.category.pk, new_category.pk)
def test_subreddit_rules_only(self):
rule = SubredditFactory(
@ -112,23 +108,22 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
self.assertEqual(response.status_code, 404)
def test_url_change(self):
self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww")
response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302)
self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="aww")
self.assertEquals(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc))
self.assertEquals(rule.favicon, None)
self.assertEquals(rule.category.pk, self.category.pk)
self.assertEquals(rule.user.pk, self.user.pk)
self.assertEqual(rule.type, RuleTypeChoices.subreddit)
self.assertEqual(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEqual(rule.favicon, None)
self.assertEqual(rule.category.pk, self.category.pk)
self.assertEqual(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww")

View file

@ -1,10 +1,8 @@
from datetime import datetime
from datetime import datetime, timezone
from django.conf import settings
from django.db.models.fields import CharField, TextField
from django.utils import timezone
import pytz
import requests
from requests.exceptions import RequestException
@ -15,14 +13,11 @@ from newsreader.news.collection.response_handler import ResponseHandler
DEFAULT_HEADERS = {"User-Agent": f"linux:rss.fudiggity.nl:{settings.VERSION}"}
def build_publication_date(dt, tz):
def build_publication_date(_datetime_info: tuple) -> datetime:
try:
naive_datetime = datetime(*dt[:6])
published_parsed = timezone.make_aware(naive_datetime, timezone=tz)
return datetime(*_datetime_info[:6], tzinfo=timezone.utc)
except (TypeError, ValueError):
return timezone.now()
return published_parsed.astimezone(pytz.utc)
return datetime.now(tz=timezone.utc)
def fetch(url, auth=None, headers={}):

View file

@ -1,8 +1,8 @@
import json
from django.urls import reverse_lazy
from zoneinfo import available_timezones
import pytz
from django.urls import reverse_lazy
from django_celery_beat.models import IntervalSchedule, PeriodicTask
@ -25,7 +25,7 @@ class CollectionRuleDetailMixin:
context_data = super().get_context_data(**kwargs)
categories = Category.objects.filter(user=self.request.user).order_by("name")
timezones = [timezone for timezone in pytz.all_timezones]
timezones = available_timezones()
context_data["categories"] = categories
context_data["timezones"] = timezones

View file

@ -1,5 +1,4 @@
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from rest_framework import filters
from rest_framework.compat import coreapi, coreschema
@ -25,8 +24,8 @@ class ReadFilter(filters.BaseFilterBackend):
required=False,
location="query",
schema=coreschema.String(
title=force_text(self.query_param),
description=force_text(_("Wether posts should be read or not")),
title=str(self.query_param),
description=str(_("Wether posts should be read or not")),
),
)
]
@ -52,8 +51,8 @@ class SavedFilter(filters.BaseFilterBackend):
required=False,
location="query",
schema=coreschema.String(
title=force_text(self.query_param),
description=force_text(_("Wether posts should be saved or not")),
title=str(self.query_param),
description=str(_("Wether posts should be saved or not")),
),
)
]

View file

@ -1,12 +1,10 @@
import json
from datetime import datetime
from datetime import datetime, timezone
from django.test import TestCase
from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -29,15 +27,15 @@ class CategoryListViewTestCase(TestCase):
def test_ordering(self):
categories = [
CategoryFactory(
created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc),
created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc),
user=self.user,
),
CategoryFactory(
created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc),
created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc),
user=self.user,
),
CategoryFactory(
created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc),
created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc),
user=self.user,
),
]
@ -428,37 +426,37 @@ class NestedCategoryPostView(TestCase):
FeedPostFactory.create(
title="Second Reuters post",
rule=reuters_rule,
publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 21, 15, tzinfo=timezone.utc),
),
FeedPostFactory.create(
title="First Reuters post",
rule=reuters_rule,
publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 12, tzinfo=timezone.utc),
),
]
FeedPostFactory.create(
title="Second Guardian post",
rule=guardian_rule,
publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 21, 14, tzinfo=timezone.utc),
)
FeedPostFactory.create(
title="First Guardian post",
rule=guardian_rule,
publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 11, tzinfo=timezone.utc),
)
FeedPostFactory.create(
title="Second BBC post",
rule=bbc_rule,
publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 21, 16, tzinfo=timezone.utc),
)
FeedPostFactory.create(
title="First BBC post",
rule=bbc_rule,
publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 13, tzinfo=timezone.utc),
)
response = self.client.get(

View file

@ -1,10 +1,8 @@
from datetime import datetime
from datetime import datetime, timezone
from django.test import TestCase
from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -22,8 +20,8 @@ class PostListViewTestCase(TestCase):
response = self.client.get(reverse("api:news:core:posts-list"))
data = response.json()
self.assertEquals(response.status_code, 200)
self.assertEquals(len(data["results"]), 3)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(data["results"]), 3)
def test_ordering(self):
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
@ -32,24 +30,24 @@ class PostListViewTestCase(TestCase):
FeedPostFactory(
title="I'm the first post",
rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=timezone.utc),
),
FeedPostFactory(
title="I'm the second post",
rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc),
),
FeedPostFactory(
title="I'm the third post",
rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=pytz.utc),
publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=timezone.utc),
),
]
response = self.client.get(reverse("api:news:core:posts-list"))
data = response.json()
self.assertEquals(response.status_code, 200)
self.assertEqual(response.status_code, 200)
for index, post in enumerate(posts, start=0):
with self.subTest(post=post):
@ -68,8 +66,8 @@ class PostListViewTestCase(TestCase):
data = response.json()
posts = data["results"]
self.assertEquals(response.status_code, 200)
self.assertEquals(len(data["results"]), 10)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(data["results"]), 10)
for post in posts:
with self.subTest(post=post):
@ -88,8 +86,8 @@ class PostListViewTestCase(TestCase):
data = response.json()
posts = data["results"]
self.assertEquals(response.status_code, 200)
self.assertEquals(len(data["results"]), 10)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(data["results"]), 10)
for post in posts:
with self.subTest(post=post):

View file

@ -1,6 +1,7 @@
from datetime import timezone
import factory
import factory.fuzzy
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.reddit import REDDIT_API_URL
@ -19,7 +20,7 @@ class PostFactory(factory.django.DjangoModelFactory):
title = factory.Faker("sentence")
body = factory.Faker("paragraph")
author = factory.Faker("name")
publication_date = factory.Faker("date_time_this_year", tzinfo=pytz.utc)
publication_date = factory.Faker("date_time_this_year", tzinfo=timezone.utc)
url = factory.Faker("url")
remote_identifier = factory.Faker("uuid4")

222
uv.lock generated
View file

@ -97,9 +97,6 @@ version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
{ url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
{ url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
{ url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
{ url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
{ url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
@ -110,11 +107,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
{ url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
{ url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
{ url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
{ url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
@ -125,8 +117,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
]
@ -194,46 +184,30 @@ version = "7.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 },
{ url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 },
{ url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 },
{ url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 },
{ url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 },
{ url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 },
{ url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 },
{ url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 },
{ url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348 },
{ url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230 },
{ url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 },
{ url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 },
{ url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 },
{ url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 },
{ url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 },
{ url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 },
{ url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 },
{ url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 },
{ url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 },
{ url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 },
{ url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 },
{ url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 },
{ url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 },
{ url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 },
{ url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 },
{ url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 },
{ url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 },
{ url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 },
{ url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 },
{ url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 },
{ url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 },
{ url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 },
{ url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 },
{ url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 },
{ url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 },
{ url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 },
{ url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 },
{ url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 },
{ url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 },
{ url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 },
]
[[package]]
@ -247,16 +221,15 @@ wheels = [
[[package]]
name = "django"
version = "3.2.25"
version = "4.2.16"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref", marker = "sys_platform == 'linux'" },
{ name = "pytz", marker = "sys_platform == 'linux'" },
{ name = "sqlparse", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ec/68/0e744f07b57bfdf99abbb6b3eb14fcba188867021c05f4a104e04f6d56b8/Django-3.2.25.tar.gz", hash = "sha256:7ca38a78654aee72378594d63e51636c04b8e28574f5505dff630895b5472777", size = 9836336 }
sdist = { url = "https://files.pythonhosted.org/packages/65/d8/a607ee443b54a4db4ad28902328b906ae6218aa556fb9b3ac45c0bcb313d/Django-4.2.16.tar.gz", hash = "sha256:6f1616c2786c408ce86ab7e10f792b8f15742f7b7b7460243929cb371e7f1dad", size = 10436023 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/8e/cc23c762c5dcd1d367d73cf006a326e0df2bd0e785cba18b658b39904c1e/Django-3.2.25-py3-none-any.whl", hash = "sha256:a52ea7fcf280b16f7b739cec38fa6d3f8953a5456986944c3ca97e79882b4e38", size = 7890550 },
{ url = "https://files.pythonhosted.org/packages/94/2c/6b6c7e493d5ea789416918658ebfa16be7a64c77610307497ed09a93c8c4/Django-4.2.16-py3-none-any.whl", hash = "sha256:1ddc333a16fc139fd253035a1606bb24261951bbc3a6ca256717fa06cc41a898", size = 7992936 },
]
[[package]]
@ -274,7 +247,7 @@ wheels = [
[[package]]
name = "django-celery-beat"
version = "2.5.0"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "celery", marker = "sys_platform == 'linux'" },
@ -284,9 +257,9 @@ dependencies = [
{ name = "python-crontab", marker = "sys_platform == 'linux'" },
{ name = "tzdata", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/97/ca63898f76dd43fc91f4791b05dbbecb60dc99215f16b270e9b1e29af974/django-celery-beat-2.5.0.tar.gz", hash = "sha256:cd0a47f5958402f51ac0c715bc942ae33d7b50b4e48cba91bc3f2712be505df1", size = 159635 }
sdist = { url = "https://files.pythonhosted.org/packages/03/8f/8a18f234173001bd7a7d63826d2d7f456b38031c892514d27c0f7aea10be/django_celery_beat-2.7.0.tar.gz", hash = "sha256:8482034925e09b698c05ad61c36ed2a8dbc436724a3fe119215193a4ca6dc967", size = 163472 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/92/fa53396870566276357bb81e3fece5b7f8a00f99c91689ff777c481d40e0/django_celery_beat-2.5.0-py3-none-any.whl", hash = "sha256:ae460faa5ea142fba0875409095d22f6bd7bcc7377889b85e8cab5c0dfb781fe", size = 97223 },
{ url = "https://files.pythonhosted.org/packages/29/f8/f5a25472222b19258c3a53ce71c4efd171a12ab3c988bb3026dec0522a64/django_celery_beat-2.7.0-py3-none-any.whl", hash = "sha256:851c680d8fbf608ca5fecd5836622beea89fa017bc2b3f94a5b8c648c32d84b1", size = 94097 },
]
[[package]]
@ -332,6 +305,35 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/d2/61159bc6efd1bf16adc4a2a48f7ace2080d1f7aef054f606d1857cab490c/django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a", size = 969 }
[[package]]
name = "django-stubs"
version = "5.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref", marker = "sys_platform == 'linux'" },
{ name = "django", marker = "sys_platform == 'linux'" },
{ name = "django-stubs-ext", marker = "sys_platform == 'linux'" },
{ name = "types-pyyaml", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/36/f4/2dfc77809e4d04164ec614755e2359ec2e68a32f7b5428909fa0b7f8f4e0/django_stubs-5.0.4.tar.gz", hash = "sha256:78e3764488fdfd2695f12502136548ec22f8d4b1780541a835042b8238d11514", size = 262238 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/f0/36c0f82ed7b4ef630b39e165590645c4fe4361f52d41bca5001327d62f57/django_stubs-5.0.4-py3-none-any.whl", hash = "sha256:c2502f5ecbae50c68f9a86d52b5b2447d8648fd205036dad0ccb41e19a445927", size = 466530 },
]
[[package]]
name = "django-stubs-ext"
version = "5.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/77/ef453a8286fff87db8efd7fe93c1a86f05aeddcc78973c883af91b667f74/django_stubs_ext-5.0.4.tar.gz", hash = "sha256:85da065224204774208be29c7d02b4482d5a69218a728465c2fbe41725fdc819", size = 9410 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/54/66a7ccb1f4e4a8e37e0881a3dfdcabaee9fc0c0c91cbe64170e794acebd7/django_stubs_ext-5.0.4-py3-none-any.whl", hash = "sha256:910cbaff3d1e8e806a5c27d5ddd4088535aae8371ea921b7fd680fdfa5f14e30", size = 8954 },
]
[[package]]
name = "django-timezone-field"
version = "7.0"
@ -406,23 +408,26 @@ wheels = [
[[package]]
name = "ftfy"
version = "5.9"
version = "6.2.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "wcwidth", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/06/e5c80e2e0f979628d47345efba51f7ba386fe95963b11c594209085f5a9b/ftfy-5.9.tar.gz", hash = "sha256:8c4fb2863c0b82eae2ab3cf353d9ade268dfbde863d322f78d6a9fd5cefb31e9", size = 66049 }
sdist = { url = "https://files.pythonhosted.org/packages/da/a9/59f4354257e8350a25be1774021991fb3a99a2fb87d0c1f367592548aed3/ftfy-6.2.3.tar.gz", hash = "sha256:79b505988f29d577a58a9069afe75553a02a46e42de6091c0660cdc67812badc", size = 64165 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/46/14d230ad057048aea7ccd2f96a80905830866d281ea90a6662a825490659/ftfy-6.2.3-py3-none-any.whl", hash = "sha256:f15761b023f3061a66207d33f0c0149ad40a8319fd16da91796363e2c049fdf8", size = 43011 },
]
[[package]]
name = "gunicorn"
version = "20.1.0"
version = "23.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "setuptools", marker = "sys_platform == 'linux'" },
{ name = "packaging", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/28/5b/0d1f0296485a6af03366604142ea8f19f0833894db3512a40ed07b2a56dd/gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8", size = 370601 }
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/dd/5b190393e6066286773a67dfcc2f9492058e9b57c4867a95f1ba5caf0a83/gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", size = 79531 },
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 },
]
[[package]]
@ -453,8 +458,6 @@ version = "5.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056 },
{ url = "https://files.pythonhosted.org/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238 },
{ url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 },
{ url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 },
{ url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 },
@ -468,10 +471,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 },
{ url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 },
{ url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 },
{ url = "https://files.pythonhosted.org/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226 },
{ url = "https://files.pythonhosted.org/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971 },
{ url = "https://files.pythonhosted.org/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753 },
{ url = "https://files.pythonhosted.org/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955 },
{ url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 },
{ url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 },
{ url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 },
@ -485,10 +484,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 },
{ url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 },
{ url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 },
{ url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 },
{ url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 },
{ url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 },
{ url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 },
{ url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 },
{ url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 },
{ url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 },
@ -502,14 +497,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 },
{ url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 },
{ url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 },
{ url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 },
{ url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 },
]
[[package]]
name = "newsreader"
version = "0.4.4"
source = { editable = "." }
source = { virtual = "." }
dependencies = [
{ name = "beautifulsoup4", marker = "sys_platform == 'linux'" },
{ name = "bleach", marker = "sys_platform == 'linux'" },
@ -522,12 +515,11 @@ dependencies = [
{ name = "feedparser", marker = "sys_platform == 'linux'" },
{ name = "ftfy", marker = "sys_platform == 'linux'" },
{ name = "lxml", marker = "sys_platform == 'linux'" },
{ name = "psycopg2", marker = "sys_platform == 'linux'" },
{ name = "psycopg", marker = "sys_platform == 'linux'" },
{ name = "pymemcache", marker = "sys_platform == 'linux'" },
{ name = "python-dotenv", marker = "sys_platform == 'linux'" },
{ name = "python-memcached", marker = "sys_platform == 'linux'" },
{ name = "requests", marker = "sys_platform == 'linux'" },
{ name = "requests-oauthlib", marker = "sys_platform == 'linux'" },
{ name = "setuptools", marker = "sys_platform == 'linux'" },
]
[package.optional-dependencies]
@ -537,6 +529,7 @@ ci = [
development = [
{ name = "django-debug-toolbar", marker = "sys_platform == 'linux'" },
{ name = "django-extensions", marker = "sys_platform == 'linux'" },
{ name = "django-stubs", marker = "sys_platform == 'linux'" },
]
production = [
{ name = "gunicorn", marker = "sys_platform == 'linux'" },
@ -553,29 +546,29 @@ testing = [
requires-dist = [
{ name = "beautifulsoup4" },
{ name = "bleach" },
{ name = "celery", specifier = "~=5.0" },
{ name = "coverage", marker = "extra == 'ci'", specifier = ">=5.3.1" },
{ name = "django", specifier = "~=3.2" },
{ name = "celery", specifier = "~=5.4" },
{ name = "coverage", marker = "extra == 'ci'", specifier = "~=7.6.1" },
{ name = "django", specifier = "~=4.2" },
{ name = "django-axes" },
{ name = "django-celery-beat", specifier = "~=2.5.0" },
{ name = "django-celery-beat", specifier = "~=2.7.0" },
{ name = "django-debug-toolbar", marker = "extra == 'development'" },
{ name = "django-extensions", marker = "extra == 'development'" },
{ name = "django-registration-redux", specifier = "~=2.7" },
{ name = "django-rest-framework" },
{ name = "django-stubs", marker = "extra == 'development'" },
{ name = "factory-boy", marker = "extra == 'testing'" },
{ name = "feedparser" },
{ name = "freezegun", marker = "extra == 'testing'" },
{ name = "ftfy", specifier = "~=5.8" },
{ name = "gunicorn", marker = "extra == 'production'", specifier = "~=20.0" },
{ name = "ftfy", specifier = "~=6.2" },
{ name = "gunicorn", marker = "extra == 'production'", specifier = "~=23.0" },
{ name = "lxml" },
{ name = "psycopg2" },
{ name = "python-dotenv", specifier = "~=0.12" },
{ name = "python-memcached", specifier = "<=1.59" },
{ name = "psycopg" },
{ name = "pymemcache" },
{ name = "python-dotenv", specifier = "~=1.0.1" },
{ name = "requests" },
{ name = "requests-oauthlib" },
{ name = "ruff", marker = "extra == 'testing'", specifier = ">=0.6.3" },
{ name = "sentry-sdk", marker = "extra == 'production'", specifier = "~=1.0" },
{ name = "setuptools", specifier = ">=74.0.0" },
{ name = "sentry-sdk", marker = "extra == 'production'", specifier = "~=2.0" },
{ name = "tblib", marker = "extra == 'testing'" },
]
@ -588,6 +581,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 },
]
[[package]]
name = "packaging"
version = "24.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
]
[[package]]
name = "prompt-toolkit"
version = "3.0.47"
@ -601,15 +603,24 @@ wheels = [
]
[[package]]
name = "psycopg2"
version = "2.9.9"
name = "psycopg"
version = "3.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/5e/dc6acaf46d78979d6b03458b7a1618a68e152a6776fce95daac5e0f0301b/psycopg2-2.9.9.tar.gz", hash = "sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156", size = 384926 }
dependencies = [
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/8e/f176997fd790d3dce9fa0ca695391beaeee39af7ecd6d426c4c063cf6744/psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7", size = 155313 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/91/2c/1fc5b9d33cd248c548ba19f2cef8e89cabaafab9858a602868a592cdc1b0/psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372", size = 1024163 },
{ url = "https://files.pythonhosted.org/packages/37/2c/5133dd3183a3bd82371569f0dd783e6927672de7e671b278ce248810b7f7/psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981", size = 1163579 },
{ url = "https://files.pythonhosted.org/packages/13/13/f74ffe6b6f58822e807c70391dc5679a53feb92ce119ccb8a6546c3fb893/psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024", size = 1024634 },
{ url = "https://files.pythonhosted.org/packages/58/4b/c4a26e191882b60150bfcb639e416524ae7f8249ab7ee854fb5247f16c40/psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693", size = 1163789 },
{ url = "https://files.pythonhosted.org/packages/8a/0e/0f755db36f47f96464463385552f8f132a981731356837c9a30a11ab2d35/psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175", size = 197743 },
]
[[package]]
name = "pymemcache"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d9/b6/4541b664aeaad025dfb8e851dcddf8e25ab22607e674dd2b562ea3e3586f/pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94", size = 70176 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/41/ba/2f7b22d8135b51c4fefb041461f8431e1908778e6539ff5af6eeaaee367a/pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab", size = 60772 },
]
[[package]]
@ -638,32 +649,11 @@ wheels = [
[[package]]
name = "python-dotenv"
version = "0.21.1"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/d7/d548e0d5a68b328a8d69af833a861be415a17cb15ce3d8f0cd850073d2e1/python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49", size = 35930 }
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/62/f19d1e9023aacb47241de3ab5a5d5fedf32c78a71a9e365bb2153378c141/python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a", size = 19284 },
]
[[package]]
name = "python-memcached"
version = "1.59"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz", hash = "sha256:a2e28637be13ee0bf1a8b6843e7490f9456fd3f2a4cb60471733c7b5d5557e4f", size = 22210 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/90/19d3908048f70c120ec66a39e61b92c253e834e6e895cd104ce5e46cbe53/python_memcached-1.59-py2.py3-none-any.whl", hash = "sha256:4dac64916871bd3550263323fc2ce18e1e439080a2d5670c594cf3118d99b594", size = 16200 },
]
[[package]]
name = "pytz"
version = "2024.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 },
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
]
[[package]]
@ -701,8 +691,6 @@ source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5d/f9/0b32e5d1c6f957df49398cd882a011e9488fcbca0d6acfeeea50ccd37a4d/ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", size = 2463514 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/72/68/1da6a1e39a03a229ea57c511691d6225072759cc7764206c3f0989521194/ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", size = 9696928 },
{ url = "https://files.pythonhosted.org/packages/6e/59/3b8b1d3a4271c6eb6ceecd3cef19a6d881639a0f18ad651563d6f619aaae/ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc", size = 9448462 },
{ url = "https://files.pythonhosted.org/packages/35/4f/b942ecb8bbebe53aa9b33e9b96df88acd50b70adaaed3070f1d92131a1cb/ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1", size = 9176190 },
{ url = "https://files.pythonhosted.org/packages/a0/20/b0bcb29d4ee437f3567b73b6905c034e2e94d29b9b826c66daecc1cf6388/ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", size = 10108892 },
{ url = "https://files.pythonhosted.org/packages/9c/e3/211bc759f424e8823a9937e0f678695ca02113c621dfde1fa756f9f26f6d/ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", size = 9476471 },
{ url = "https://files.pythonhosted.org/packages/b2/a3/2ec35a2d7a554364864206f0e46812b92a074ad8a014b923d821ead532aa/ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", size = 10294802 },
@ -714,31 +702,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a8/9f/f801a1619f5549e552f1f722f1db57eb39e7e1d83d482133142781d450de/ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", size = 9563859 },
{ url = "https://files.pythonhosted.org/packages/0b/4d/fb2424faf04ffdb960ae2b3a1d991c5183dd981003de727d2d5cc38abc98/ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", size = 9914291 },
{ url = "https://files.pythonhosted.org/packages/2e/dd/94fddf002a8f6152e8ebfbb51d3f93febc415c1fe694345623c31ce8b33b/ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8", size = 10331549 },
{ url = "https://files.pythonhosted.org/packages/b4/73/ca9c2f9237a430ca423b6dca83b77e9a428afeb7aec80596e86c369123fe/ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521", size = 7962163 },
{ url = "https://files.pythonhosted.org/packages/55/ce/061c605b1dfb52748d59bc0c7a8507546c178801156415773d18febfd71d/ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb", size = 8800901 },
{ url = "https://files.pythonhosted.org/packages/63/28/ae4ffe7d3b6134ca6d31ebef07447ef70097c4a9e8fbbc519b374c5c1559/ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82", size = 8229171 },
]
[[package]]
name = "sentry-sdk"
version = "1.45.1"
version = "2.13.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi", marker = "sys_platform == 'linux'" },
{ name = "urllib3", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c8/28/02c0cd9184f9108e3c52519f9628b215077a3854240e0b17ae845e664855/sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26", size = 244774 }
sdist = { url = "https://files.pythonhosted.org/packages/bb/41/97f673384dae5ed81cc2a568cc5c28e76deee85f8ba50def862e86150a5a/sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260", size = 279937 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/9f/105366a122efa93f0cb1914f841747d160788e4d022d0488d2d44c2ba26c/sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1", size = 267163 },
]
[[package]]
name = "setuptools"
version = "74.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6a/21/8fd457d5a979109603e0e460c73177c3a9b6b7abcd136d0146156da95895/setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e", size = 1389536 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/b5/168cec9a10bf93b60b8f9af7f4e61d526e31e1aad8b9be0e30837746d700/setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f", size = 1301729 },
{ url = "https://files.pythonhosted.org/packages/ad/7e/e9ca09f24a6c334286631a2d32c267cdc5edad5ac03fd9d20a01a82f1c35/sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6", size = 309078 },
]
[[package]]
@ -783,6 +759,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/87/ce70db7cae60e67851eb94e1a2127d4abb573d3866d2efd302ceb0d4d2a5/tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", size = 12478 },
]
[[package]]
name = "types-pyyaml"
version = "6.0.12.20240808"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/dd/08/6f5737f645571b7a0b1ebd2fe8b5cf1ee4ec3e707866ca96042a86fc1d10/types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", size = 12359 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/ad/ffbad24e2bc8f20bf047ec22af0c0a92f6ce2071eb21c9103df600cda6de/types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35", size = 15298 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]]
name = "tzdata"
version = "2024.1"