Form / view file refactor
This commit is contained in:
parent
355f16b387
commit
abb5328feb
26 changed files with 285 additions and 143 deletions
|
|
@ -44,6 +44,8 @@ class PostModal extends React.Component {
|
||||||
const post = this.props.post;
|
const post = this.props.post;
|
||||||
const publicationDate = formatDatetime(post.publicationDate);
|
const publicationDate = formatDatetime(post.publicationDate);
|
||||||
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
|
||||||
|
|
||||||
|
// TODO add mapping & get urls from backend
|
||||||
const ruleUrl =
|
const ruleUrl =
|
||||||
this.props.rule.type === FEED
|
this.props.rule.type === FEED
|
||||||
? `/collection/rules/${this.props.rule.id}/`
|
? `/collection/rules/${this.props.rule.id}/`
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export const CATEGORY_TYPE = 'CATEGORY';
|
||||||
|
|
||||||
export const SUBREDDIT = 'subreddit';
|
export const SUBREDDIT = 'subreddit';
|
||||||
export const FEED = 'feed';
|
export const FEED = 'feed';
|
||||||
|
export const TWITTER_TIMELINE = 'twitter_timeline';
|
||||||
|
|
|
||||||
4
src/newsreader/news/collection/forms/__init__.py
Normal file
4
src/newsreader/news/collection/forms/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from newsreader.news.collection.forms.feed import FeedForm, OPMLImportForm
|
||||||
|
from newsreader.news.collection.forms.reddit import SubRedditForm
|
||||||
|
from newsreader.news.collection.forms.rules import CollectionRuleBulkForm
|
||||||
|
from newsreader.news.collection.forms.twitter import TwitterTimelineForm
|
||||||
30
src/newsreader/news/collection/forms/base.py
Normal file
30
src/newsreader/news/collection/forms/base.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
from newsreader.news.collection.models import CollectionRule
|
||||||
|
from newsreader.news.core.models import Category
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionRuleForm(forms.ModelForm):
|
||||||
|
category = forms.ModelChoiceField(required=False, queryset=Category.objects.all())
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.user = kwargs.pop("user")
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields["category"].queryset = Category.objects.filter(user=self.user)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
instance = super().save(commit=False)
|
||||||
|
instance.user = self.user
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
instance.save()
|
||||||
|
self.save_m2m()
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CollectionRule
|
||||||
|
fields = "__all__"
|
||||||
28
src/newsreader/news/collection/forms/feed.py
Normal file
28
src/newsreader/news/collection/forms/feed.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
class OPMLImportForm(forms.Form):
|
||||||
|
file = forms.FileField(allow_empty_file=False)
|
||||||
|
skip_existing = forms.BooleanField(
|
||||||
|
initial=False, required=False, widget=CheckboxInput
|
||||||
|
)
|
||||||
|
|
@ -9,6 +9,7 @@ from newsreader.core.forms import CheckboxInput
|
||||||
from newsreader.news.collection.choices import RuleTypeChoices
|
from newsreader.news.collection.choices import RuleTypeChoices
|
||||||
from newsreader.news.collection.models import CollectionRule
|
from newsreader.news.collection.models import CollectionRule
|
||||||
from newsreader.news.collection.reddit import REDDIT_API_URL
|
from newsreader.news.collection.reddit import REDDIT_API_URL
|
||||||
|
from newsreader.news.collection.forms.base import CollectionRuleForm
|
||||||
from newsreader.news.core.models import Category
|
from newsreader.news.core.models import Category
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -22,53 +23,9 @@ def get_reddit_help_text():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleForm(forms.ModelForm):
|
class SubRedditForm(CollectionRuleForm):
|
||||||
category = forms.ModelChoiceField(required=False, queryset=Category.objects.all())
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.user = kwargs.pop("user")
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields["category"].queryset = Category.objects.filter(user=self.user)
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
instance = super().save(commit=False)
|
|
||||||
instance.user = self.user
|
|
||||||
|
|
||||||
if commit:
|
|
||||||
instance.save()
|
|
||||||
self.save_m2m()
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CollectionRule
|
|
||||||
fields = ("name", "url", "timezone", "favicon", "category")
|
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleBulkForm(forms.Form):
|
|
||||||
rules = forms.ModelMultipleChoiceField(queryset=CollectionRule.objects.none())
|
|
||||||
|
|
||||||
def __init__(self, user, *args, **kwargs):
|
|
||||||
self.user = user
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields["rules"].queryset = CollectionRule.objects.filter(user=user)
|
|
||||||
|
|
||||||
|
|
||||||
class SubRedditRuleForm(CollectionRuleForm):
|
|
||||||
url = forms.URLField(max_length=1024, help_text=get_reddit_help_text)
|
url = forms.URLField(max_length=1024, help_text=get_reddit_help_text)
|
||||||
|
|
||||||
timezone = None
|
|
||||||
|
|
||||||
def clean_url(self):
|
def clean_url(self):
|
||||||
url = self.cleaned_data["url"]
|
url = self.cleaned_data["url"]
|
||||||
|
|
||||||
|
|
@ -92,10 +49,3 @@ class SubRedditRuleForm(CollectionRuleForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CollectionRule
|
model = CollectionRule
|
||||||
fields = ("name", "url", "favicon", "category")
|
fields = ("name", "url", "favicon", "category")
|
||||||
|
|
||||||
|
|
||||||
class OPMLImportForm(forms.Form):
|
|
||||||
file = forms.FileField(allow_empty_file=False)
|
|
||||||
skip_existing = forms.BooleanField(
|
|
||||||
initial=False, required=False, widget=CheckboxInput
|
|
||||||
)
|
|
||||||
15
src/newsreader/news/collection/forms/rules.py
Normal file
15
src/newsreader/news/collection/forms/rules.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
from newsreader.news.collection.models import CollectionRule
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionRuleBulkForm(forms.Form):
|
||||||
|
rules = forms.ModelMultipleChoiceField(queryset=CollectionRule.objects.none())
|
||||||
|
|
||||||
|
def __init__(self, user, *args, **kwargs):
|
||||||
|
self.user = user
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields["rules"].queryset = CollectionRule.objects.filter(user=user)
|
||||||
32
src/newsreader/news/collection/forms/twitter.py
Normal file
32
src/newsreader/news/collection/forms/twitter.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
from django import forms
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterTimelineForm(CollectionRuleForm):
|
||||||
|
screen_name = forms.CharField(
|
||||||
|
max_length=255,
|
||||||
|
label=_("Twitter profile name"),
|
||||||
|
help_text=_("Profile name without hashtags"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
instance = super().save(commit=False)
|
||||||
|
|
||||||
|
instance.type = RuleTypeChoices.twitter_timeline
|
||||||
|
instance.timezone = str(pytz.utc)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
instance.save()
|
||||||
|
self.save_m2m()
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CollectionRule
|
||||||
|
fields = ("name", "screen_name", "favicon", "category")
|
||||||
|
|
@ -70,4 +70,4 @@ class CollectionRule(TimeStampedModel):
|
||||||
if self.type == RuleTypeChoices.subreddit:
|
if self.type == RuleTypeChoices.subreddit:
|
||||||
return reverse("news:collection:subreddit-update", kwargs={"pk": self.pk})
|
return reverse("news:collection:subreddit-update", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
return reverse("news:collection:rule-update", kwargs={"pk": self.pk})
|
return reverse("news:collection:feed-update", kwargs={"pk": self.pk})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="rule--page" class="main">
|
<main id="rule--page" class="main">
|
||||||
{% url "news:collection:rules" as cancel_url %}
|
{% url "news:collection:rules" as cancel_url %}
|
||||||
{% include "components/form/form.html" with form=form title="Create rule" cancel_url=cancel_url confirm_text="Create rule" %}
|
{% include "components/form/form.html" with form=form title="Add a feed" cancel_url=cancel_url confirm_text="Add feed" %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="rule--page" class="main">
|
<main id="rule--page" class="main">
|
||||||
{% if rule.error %}
|
{% if feed.error %}
|
||||||
{% trans "Failed to retrieve posts" as title %}
|
{% trans "Failed to retrieve posts" as title %}
|
||||||
{% include "components/textbox/textbox.html" with title=title body=rule.error class="text-section--error" only %}
|
{% include "components/textbox/textbox.html" with title=title body=feed.error class="text-section--error" only %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% url "news:collection:rules" as cancel_url %}
|
{% url "news:collection:rules" as cancel_url %}
|
||||||
{% include "components/form/form.html" with form=form title="Update rule" cancel_url=cancel_url confirm_text="Save rule" only %}
|
{% include "components/form/form.html" with form=form title="Update feed" cancel_url=cancel_url confirm_text="Save feed" only %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -4,6 +4,6 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="import--page" class="main">
|
<main id="import--page" class="main">
|
||||||
{% url "news:collection:rules" as cancel_url %}
|
{% url "news:collection:rules" as cancel_url %}
|
||||||
{% include "components/form/form.html" with form=form title="Import an OPML file" cancel_url=cancel_url confirm_text="Import rules" %}
|
{% include "components/form/form.html" with form=form title="Import an OPML file" cancel_url=cancel_url confirm_text="Import feeds" %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form__actions">
|
<div class="form__actions">
|
||||||
<a class="link button button--confirm" href="{% url "news:collection:rule-create" %}">{% trans "Add a rule" %}</a>
|
<a class="link button button--confirm" href="{% url "news:collection:feed-create" %}">{% trans "Add a feed" %}</a>
|
||||||
<a class="link button button--confirm" href="{% url "news:collection:subreddit-create" %}">{% trans "Add a subreddit" %}</a>
|
<a class="link button button--reddit" href="{% url "news:collection:subreddit-create" %}">{% trans "Add a subreddit" %}</a>
|
||||||
|
<a class="link button button--twitter" href="{% url "news:collection:twitter-timeline-create" %}">{% trans "Add a Twitter profile" %}</a>
|
||||||
<a class="link button button--confirm" href="{% url "news:collection:import" %}">{% trans "Import rules" %}</a>
|
<a class="link button button--confirm" href="{% url "news:collection:import" %}">{% trans "Import rules" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main id="twitter--page" class="main">
|
||||||
|
{% url "news:collection:rules" as cancel_url %}
|
||||||
|
{% include "components/form/form.html" with form=form title="Add a Twitter profile" cancel_url=cancel_url confirm_text="Add profile" %}
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main id="twitter--page" class="main">
|
||||||
|
{% if timeline.error %}
|
||||||
|
{% trans "Failed to retrieve posts" as title %}
|
||||||
|
{% include "components/textbox/textbox.html" with title=title body=timeline.error class="text-section--error" only %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% url "news:collection:rules" as cancel_url %}
|
||||||
|
{% include "components/form/form.html" with form=form title="Update profile" cancel_url=cancel_url confirm_text="Save profile" %}
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -49,7 +49,7 @@ class CollectionRuleViewTestCase:
|
||||||
timezone=other_rule.timezone,
|
timezone=other_rule.timezone,
|
||||||
)
|
)
|
||||||
|
|
||||||
other_url = reverse("news:collection:rule-update", args=[other_rule.pk])
|
other_url = reverse("news:collection:feed-update", args=[other_rule.pk])
|
||||||
response = self.client.post(other_url, self.form_data)
|
response = self.client.post(other_url, self.form_data)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 404)
|
self.assertEquals(response.status_code, 404)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCa
|
||||||
from newsreader.news.core.tests.factories import CategoryFactory
|
from newsreader.news.core.tests.factories import CategoryFactory
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
self.url = reverse("news:collection:rule-create")
|
self.url = reverse("news:collection:feed-create")
|
||||||
|
|
||||||
self.form_data.update(
|
self.form_data.update(
|
||||||
name="new rule",
|
name="new rule",
|
||||||
|
|
@ -38,14 +38,14 @@ class CollectionRuleCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
||||||
self.assertEquals(rule.user.pk, self.user.pk)
|
self.assertEquals(rule.user.pk, self.user.pk)
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
self.rule = FeedFactory(
|
self.rule = FeedFactory(
|
||||||
name="collection rule", user=self.user, category=self.category
|
name="collection rule", user=self.user, category=self.category
|
||||||
)
|
)
|
||||||
self.url = reverse("news:collection:rule-update", kwargs={"pk": self.rule.pk})
|
self.url = reverse("news:collection:feed-update", kwargs={"pk": self.rule.pk})
|
||||||
|
|
||||||
self.form_data.update(
|
self.form_data.update(
|
||||||
name=self.rule.name,
|
name=self.rule.name,
|
||||||
|
|
@ -94,7 +94,7 @@ class CollectionRuleUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
|
||||||
category=self.category,
|
category=self.category,
|
||||||
type=RuleTypeChoices.subreddit,
|
type=RuleTypeChoices.subreddit,
|
||||||
)
|
)
|
||||||
url = reverse("news:collection:rule-update", kwargs={"pk": rule.pk})
|
url = reverse("news:collection:feed-update", kwargs={"pk": rule.pk})
|
||||||
|
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class OPMLImportTestCase(TestCase):
|
||||||
rules = CollectionRule.objects.all()
|
rules = CollectionRule.objects.all()
|
||||||
self.assertEquals(len(rules), 0)
|
self.assertEquals(len(rules), 0)
|
||||||
|
|
||||||
self.assertFormError(response, "form", "file", _("No (new) rules found"))
|
self.assertFormError(response, "form", "file", _("No (new) feeds found"))
|
||||||
|
|
||||||
def test_invalid_feeds(self):
|
def test_invalid_feeds(self):
|
||||||
file_path = self._get_file_path("invalid-url-feeds.opml")
|
file_path = self._get_file_path("invalid-url-feeds.opml")
|
||||||
|
|
@ -99,7 +99,7 @@ class OPMLImportTestCase(TestCase):
|
||||||
rules = CollectionRule.objects.all()
|
rules = CollectionRule.objects.all()
|
||||||
|
|
||||||
self.assertEquals(len(rules), 0)
|
self.assertEquals(len(rules), 0)
|
||||||
self.assertFormError(response, "form", "file", _("No (new) rules found"))
|
self.assertFormError(response, "form", "file", _("No (new) feeds found"))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
file_path = self._get_file_path("test.png")
|
file_path = self._get_file_path("test.png")
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ from newsreader.news.collection.views import (
|
||||||
CollectionRuleBulkDeleteView,
|
CollectionRuleBulkDeleteView,
|
||||||
CollectionRuleBulkDisableView,
|
CollectionRuleBulkDisableView,
|
||||||
CollectionRuleBulkEnableView,
|
CollectionRuleBulkEnableView,
|
||||||
CollectionRuleCreateView,
|
|
||||||
CollectionRuleListView,
|
CollectionRuleListView,
|
||||||
CollectionRuleUpdateView,
|
FeedCreateView,
|
||||||
|
FeedUpdateView,
|
||||||
OPMLImportView,
|
OPMLImportView,
|
||||||
SubRedditCreateView,
|
SubRedditCreateView,
|
||||||
SubRedditUpdateView,
|
SubRedditUpdateView,
|
||||||
|
TwitterTimelineCreateView,
|
||||||
|
TwitterTimelineUpdateView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,17 +30,13 @@ endpoints = [
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
# Feeds
|
||||||
|
path(
|
||||||
|
"feeds/<int:pk>/", login_required(FeedUpdateView.as_view()), name="feed-update"
|
||||||
|
),
|
||||||
|
path("feeds/create/", login_required(FeedCreateView.as_view()), name="feed-create"),
|
||||||
|
# Generic rules
|
||||||
path("rules/", login_required(CollectionRuleListView.as_view()), name="rules"),
|
path("rules/", login_required(CollectionRuleListView.as_view()), name="rules"),
|
||||||
path(
|
|
||||||
"rules/<int:pk>/",
|
|
||||||
login_required(CollectionRuleUpdateView.as_view()),
|
|
||||||
name="rule-update",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"rules/create/",
|
|
||||||
login_required(CollectionRuleCreateView.as_view()),
|
|
||||||
name="rule-create",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"rules/delete/",
|
"rules/delete/",
|
||||||
login_required(CollectionRuleBulkDeleteView.as_view()),
|
login_required(CollectionRuleBulkDeleteView.as_view()),
|
||||||
|
|
@ -54,15 +52,27 @@ urlpatterns = [
|
||||||
login_required(CollectionRuleBulkDisableView.as_view()),
|
login_required(CollectionRuleBulkDisableView.as_view()),
|
||||||
name="rules-disable",
|
name="rules-disable",
|
||||||
),
|
),
|
||||||
|
path("rules/import/", login_required(OPMLImportView.as_view()), name="import"),
|
||||||
|
# Reddit
|
||||||
path(
|
path(
|
||||||
"rules/subreddits/create/",
|
"subreddits/create/",
|
||||||
login_required(SubRedditCreateView.as_view()),
|
login_required(SubRedditCreateView.as_view()),
|
||||||
name="subreddit-create",
|
name="subreddit-create",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"rules/subreddits/<int:pk>/",
|
"subreddits/<int:pk>/",
|
||||||
login_required(SubRedditUpdateView.as_view()),
|
login_required(SubRedditUpdateView.as_view()),
|
||||||
name="subreddit-update",
|
name="subreddit-update",
|
||||||
),
|
),
|
||||||
path("rules/import/", login_required(OPMLImportView.as_view()), name="import"),
|
# Twitter
|
||||||
|
path(
|
||||||
|
"twitter/timelines/create/",
|
||||||
|
login_required(TwitterTimelineCreateView.as_view()),
|
||||||
|
name="twitter-timeline-create",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"twitter/timelines/<int:pk>/",
|
||||||
|
login_required(TwitterTimelineUpdateView.as_view()),
|
||||||
|
name="twitter-timeline-update",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
from newsreader.news.collection.views.feed import (
|
||||||
|
FeedCreateView,
|
||||||
|
FeedUpdateView,
|
||||||
|
OPMLImportView,
|
||||||
|
)
|
||||||
from newsreader.news.collection.views.reddit import (
|
from newsreader.news.collection.views.reddit import (
|
||||||
SubRedditCreateView,
|
SubRedditCreateView,
|
||||||
SubRedditUpdateView,
|
SubRedditUpdateView,
|
||||||
|
|
@ -6,8 +11,9 @@ from newsreader.news.collection.views.rules import (
|
||||||
CollectionRuleBulkDeleteView,
|
CollectionRuleBulkDeleteView,
|
||||||
CollectionRuleBulkDisableView,
|
CollectionRuleBulkDisableView,
|
||||||
CollectionRuleBulkEnableView,
|
CollectionRuleBulkEnableView,
|
||||||
CollectionRuleCreateView,
|
|
||||||
CollectionRuleListView,
|
CollectionRuleListView,
|
||||||
CollectionRuleUpdateView,
|
)
|
||||||
OPMLImportView,
|
from newsreader.news.collection.views.twitter import (
|
||||||
|
TwitterTimelineCreateView,
|
||||||
|
TwitterTimelineUpdateView,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ from django.urls import reverse_lazy
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from newsreader.news.collection.forms import CollectionRuleForm
|
|
||||||
from newsreader.news.collection.models import CollectionRule
|
from newsreader.news.collection.models import CollectionRule
|
||||||
from newsreader.news.core.models import Category
|
from newsreader.news.core.models import Category
|
||||||
|
|
||||||
|
|
@ -17,7 +16,6 @@ class CollectionRuleViewMixin:
|
||||||
|
|
||||||
class CollectionRuleDetailMixin:
|
class CollectionRuleDetailMixin:
|
||||||
success_url = reverse_lazy("news:collection:rules")
|
success_url = reverse_lazy("news:collection:rules")
|
||||||
form_class = CollectionRuleForm
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context_data = super().get_context_data(**kwargs)
|
context_data = super().get_context_data(**kwargs)
|
||||||
|
|
|
||||||
62
src/newsreader/news/collection/views/feed.py
Normal file
62
src/newsreader/news/collection/views/feed.py
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from django.views.generic.edit import CreateView, FormView, UpdateView
|
||||||
|
|
||||||
|
from newsreader.news.collection.choices import RuleTypeChoices
|
||||||
|
from newsreader.news.collection.forms import (
|
||||||
|
CollectionRuleBulkForm,
|
||||||
|
FeedForm,
|
||||||
|
OPMLImportForm,
|
||||||
|
)
|
||||||
|
from newsreader.news.collection.models import CollectionRule
|
||||||
|
from newsreader.news.collection.views.base import (
|
||||||
|
CollectionRuleDetailMixin,
|
||||||
|
CollectionRuleViewMixin,
|
||||||
|
)
|
||||||
|
from newsreader.utils.opml import parse_opml
|
||||||
|
|
||||||
|
|
||||||
|
class FeedUpdateView(CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView):
|
||||||
|
template_name = "news/collection/views/feed-update.html"
|
||||||
|
form_class = FeedForm
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
return queryset.filter(type=RuleTypeChoices.feed)
|
||||||
|
|
||||||
|
|
||||||
|
class FeedCreateView(CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView):
|
||||||
|
template_name = "news/collection/views/feed-create.html"
|
||||||
|
form_class = FeedForm
|
||||||
|
context_object_name = "feed"
|
||||||
|
|
||||||
|
|
||||||
|
class OPMLImportView(FormView):
|
||||||
|
form_class = OPMLImportForm
|
||||||
|
template_name = "news/collection/views/import.html"
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
user = self.request.user
|
||||||
|
file = form.cleaned_data["file"]
|
||||||
|
skip_existing = form.cleaned_data["skip_existing"]
|
||||||
|
|
||||||
|
instances = parse_opml(file, user, skip_existing=skip_existing)
|
||||||
|
|
||||||
|
try:
|
||||||
|
feeds = CollectionRule.objects.bulk_create(instances)
|
||||||
|
except IOError:
|
||||||
|
form.add_error("file", _("Invalid OPML file"))
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
if not feeds:
|
||||||
|
form.add_error("file", _("No (new) feeds found"))
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
message = _(f"{len(feeds)} new feeds created")
|
||||||
|
messages.success(self.request, message)
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("news:collection:rules")
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from django.views.generic.edit import CreateView, UpdateView
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
|
||||||
from newsreader.news.collection.choices import RuleTypeChoices
|
from newsreader.news.collection.choices import RuleTypeChoices
|
||||||
from newsreader.news.collection.forms import SubRedditRuleForm
|
from newsreader.news.collection.forms import SubRedditForm
|
||||||
from newsreader.news.collection.views.base import (
|
from newsreader.news.collection.views.base import (
|
||||||
CollectionRuleDetailMixin,
|
CollectionRuleDetailMixin,
|
||||||
CollectionRuleViewMixin,
|
CollectionRuleViewMixin,
|
||||||
|
|
@ -11,14 +11,14 @@ from newsreader.news.collection.views.base import (
|
||||||
class SubRedditCreateView(
|
class SubRedditCreateView(
|
||||||
CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView
|
CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView
|
||||||
):
|
):
|
||||||
form_class = SubRedditRuleForm
|
form_class = SubRedditForm
|
||||||
template_name = "news/collection/views/subreddit-create.html"
|
template_name = "news/collection/views/subreddit-create.html"
|
||||||
|
|
||||||
|
|
||||||
class SubRedditUpdateView(
|
class SubRedditUpdateView(
|
||||||
CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView
|
CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView
|
||||||
):
|
):
|
||||||
form_class = SubRedditRuleForm
|
form_class = SubRedditForm
|
||||||
template_name = "news/collection/views/subreddit-update.html"
|
template_name = "news/collection/views/subreddit-update.html"
|
||||||
context_object_name = "subreddit"
|
context_object_name = "subreddit"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,14 @@ from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic.edit import CreateView, FormView, UpdateView
|
from django.views.generic.edit import FormView
|
||||||
from django.views.generic.list import ListView
|
from django.views.generic.list import ListView
|
||||||
|
|
||||||
from newsreader.news.collection.choices import RuleTypeChoices
|
from newsreader.news.collection.forms import CollectionRuleBulkForm
|
||||||
from newsreader.news.collection.forms import CollectionRuleBulkForm, OPMLImportForm
|
|
||||||
from newsreader.news.collection.models import CollectionRule
|
|
||||||
from newsreader.news.collection.views.base import (
|
from newsreader.news.collection.views.base import (
|
||||||
CollectionRuleDetailMixin,
|
CollectionRuleDetailMixin,
|
||||||
CollectionRuleViewMixin,
|
CollectionRuleViewMixin,
|
||||||
)
|
)
|
||||||
from newsreader.utils.opml import parse_opml
|
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleListView(CollectionRuleViewMixin, ListView):
|
class CollectionRuleListView(CollectionRuleViewMixin, ListView):
|
||||||
|
|
@ -21,23 +18,6 @@ class CollectionRuleListView(CollectionRuleViewMixin, ListView):
|
||||||
context_object_name = "rules"
|
context_object_name = "rules"
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleUpdateView(
|
|
||||||
CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView
|
|
||||||
):
|
|
||||||
template_name = "news/collection/views/rule-update.html"
|
|
||||||
context_object_name = "rule"
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset()
|
|
||||||
return queryset.filter(type=RuleTypeChoices.feed)
|
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleCreateView(
|
|
||||||
CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView
|
|
||||||
):
|
|
||||||
template_name = "news/collection/views/rule-create.html"
|
|
||||||
|
|
||||||
|
|
||||||
class CollectionRuleBulkView(FormView):
|
class CollectionRuleBulkView(FormView):
|
||||||
form_class = CollectionRuleBulkForm
|
form_class = CollectionRuleBulkForm
|
||||||
|
|
||||||
|
|
@ -90,33 +70,3 @@ class CollectionRuleBulkDeleteView(CollectionRuleBulkView):
|
||||||
rule.delete()
|
rule.delete()
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class OPMLImportView(FormView):
|
|
||||||
form_class = OPMLImportForm
|
|
||||||
template_name = "news/collection/views/import.html"
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
user = self.request.user
|
|
||||||
file = form.cleaned_data["file"]
|
|
||||||
skip_existing = form.cleaned_data["skip_existing"]
|
|
||||||
|
|
||||||
instances = parse_opml(file, user, skip_existing=skip_existing)
|
|
||||||
|
|
||||||
try:
|
|
||||||
rules = CollectionRule.objects.bulk_create(instances)
|
|
||||||
except IOError:
|
|
||||||
form.add_error("file", _("Invalid OPML file"))
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
if not rules:
|
|
||||||
form.add_error("file", _("No (new) rules found"))
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
message = _(f"{len(rules)} new rules created")
|
|
||||||
messages.success(self.request, message)
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("news:collection:rules")
|
|
||||||
|
|
|
||||||
29
src/newsreader/news/collection/views/twitter.py
Normal file
29
src/newsreader/news/collection/views/twitter.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
from django.views.generic.edit import CreateView, UpdateView
|
||||||
|
|
||||||
|
from newsreader.news.collection.choices import RuleTypeChoices
|
||||||
|
from newsreader.news.collection.forms import TwitterTimelineForm
|
||||||
|
from newsreader.news.collection.views.base import (
|
||||||
|
CollectionRuleDetailMixin,
|
||||||
|
CollectionRuleViewMixin,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO add tests
|
||||||
|
class TwitterTimelineCreateView(
|
||||||
|
CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView
|
||||||
|
):
|
||||||
|
form_class = TwitterTimelineForm
|
||||||
|
template_name = "news/collection/views/twitter/timeline-create.html"
|
||||||
|
|
||||||
|
|
||||||
|
# TODO add tests
|
||||||
|
class TwitterTimelineUpdateView(
|
||||||
|
CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView
|
||||||
|
):
|
||||||
|
form_class = TwitterTimelineForm
|
||||||
|
template_name = "news/collection/views/twitter/timeline-update.html"
|
||||||
|
context_object_name = "timeline"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
return queryset.filter(type=RuleTypeChoices.twitter_timeline)
|
||||||
|
|
@ -38,4 +38,5 @@ def parse_opml(file, user, skip_existing=False):
|
||||||
logging.info(f"Skipped due to invalid URL: {e}")
|
logging.info(f"Skipped due to invalid URL: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# TODO create feed type rules
|
||||||
yield CollectionRule(url=feed_url, name=name, user=user)
|
yield CollectionRule(url=feed_url, name=name, user=user)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue