Skip to content

Commit b4d07fa

Browse files
committed
Initial commit
0 parents  commit b4d07fa

27 files changed

+1605
-0
lines changed

.github/workflows/tests.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Unit tests
2+
3+
on:
4+
push:
5+
branches: ["*"]
6+
pull_request:
7+
branches: ["*"]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
max-parallel: 4
14+
15+
steps:
16+
- uses: actions/checkout@v3
17+
- uses: astral-sh/setup-uv@v3
18+
- run: uv python install
19+
- run: uv sync --group dev
20+
- run: uv run pytest

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
.venv
9+
example/db.sqlite3

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) Loopwerk
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# django-rss-filter
2+
3+
This is a Django app that creates filtered RSS feeds. It does this by filtering exsiting RSS feeds, removing articles that match filtered words and/or filtered categories. It's what powers https://www.rssfilter.com.
4+
5+
It comes with one view that returns the filtered feed XML, as well as Django Admin configuration to make it possible and easy to self-host your own instance of django-rss-filter. It does not come with views to create or edit filtered feeds; this can be done using the Django Admin.
6+
7+
## Getting started
8+
9+
All instructions use [uv](https://docs.astral.sh/uv/), but should work just as well with pip or Poetry for example.
10+
11+
In an existing Django project:
12+
13+
1. Install: `uv add django-rss-filter`
14+
2. Add `rssfilter` to `INSTALLED_APPS`
15+
3. `uv run ./manage.py migrate`
16+
4. Include the URL config: `path("", include("rssfilter.urls"))`
17+
18+
If you don't have a Django project yet, these are the steps to get started:
19+
20+
1. `uv init my-django-project`
21+
2. `cd my-django-project`
22+
3. `uv add django`
23+
4. `uv run django-admin startproject my_django_app .`
24+
5. `uv run ./manage.py migrate`
25+
6. `uv run ./manage.py createsuperuser`
26+
7. `uv run ./manage.py runserver`
27+
28+
This will get you up and running with a working Admin site at http://127.0.0.1:8000/admin/.
29+
30+
Then follow the first four steps to add django-rss-filter to your Django project.
31+
32+
## Example project
33+
34+
There's an example Django project using django-rss-filter under `example/`.
35+
36+
1. `cd example`
37+
2. `uv run ./manage.py migrate`
38+
3. `uv run ./manage.py createsuperuser`
39+
4. `uv run ./manage.py runserver`
40+
41+
You can then go to http://127.0.0.1:8000/admin/ to create a feed, which you can then access at http://127.0.0.1:8000/[feed_uuid]/.
42+
43+
## Settings
44+
45+
There is one setting that can be configured: `RSS_FILTER_CACHE_SECONDS`. The default value is 300 (5 minutes).
46+
47+
This setting controls how long a feed will be cached before it's fetched, filtered, and stored again.
48+
49+
## Tests
50+
51+
Unit tests can be run with `uv run pytest`.

example/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

example/config/__init__.py

Whitespace-only changes.

example/config/asgi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
ASGI config for config project.
3+
4+
It exposes the ASGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.asgi import get_asgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
15+
16+
application = get_asgi_application()

example/config/settings.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""
2+
Django settings for config project.
3+
4+
Generated by 'django-admin startproject' using Django 5.1.7.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/5.1/topics/settings/
8+
9+
For the full list of settings and their values, see
10+
https://docs.djangoproject.com/en/5.1/ref/settings/
11+
"""
12+
13+
from pathlib import Path
14+
15+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
16+
BASE_DIR = Path(__file__).resolve().parent.parent
17+
18+
19+
# Quick-start development settings - unsuitable for production
20+
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
21+
22+
# SECURITY WARNING: keep the secret key used in production secret!
23+
SECRET_KEY = "django-insecure-26+#r2)29f3+2-%_%5_9=4&-h-b#2e0wa7@xj8q($c(92r)-_x"
24+
25+
# SECURITY WARNING: don't run with debug turned on in production!
26+
DEBUG = True
27+
28+
ALLOWED_HOSTS = []
29+
30+
31+
# Application definition
32+
33+
INSTALLED_APPS = [
34+
"django.contrib.admin",
35+
"django.contrib.auth",
36+
"django.contrib.contenttypes",
37+
"django.contrib.sessions",
38+
"django.contrib.messages",
39+
"django.contrib.staticfiles",
40+
"rssfilter",
41+
]
42+
43+
MIDDLEWARE = [
44+
"django.middleware.security.SecurityMiddleware",
45+
"django.contrib.sessions.middleware.SessionMiddleware",
46+
"django.middleware.common.CommonMiddleware",
47+
"django.middleware.csrf.CsrfViewMiddleware",
48+
"django.contrib.auth.middleware.AuthenticationMiddleware",
49+
"django.contrib.messages.middleware.MessageMiddleware",
50+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
51+
]
52+
53+
ROOT_URLCONF = "config.urls"
54+
55+
TEMPLATES = [
56+
{
57+
"BACKEND": "django.template.backends.django.DjangoTemplates",
58+
"DIRS": [],
59+
"APP_DIRS": True,
60+
"OPTIONS": {
61+
"context_processors": [
62+
"django.template.context_processors.debug",
63+
"django.template.context_processors.request",
64+
"django.contrib.auth.context_processors.auth",
65+
"django.contrib.messages.context_processors.messages",
66+
],
67+
},
68+
},
69+
]
70+
71+
WSGI_APPLICATION = "config.wsgi.application"
72+
73+
74+
# Database
75+
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
76+
77+
DATABASES = {
78+
"default": {
79+
"ENGINE": "django.db.backends.sqlite3",
80+
"NAME": BASE_DIR / "db.sqlite3",
81+
}
82+
}
83+
84+
85+
# Password validation
86+
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
87+
88+
AUTH_PASSWORD_VALIDATORS = [
89+
{
90+
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
91+
},
92+
{
93+
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
94+
},
95+
{
96+
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
97+
},
98+
{
99+
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
100+
},
101+
]
102+
103+
104+
# Internationalization
105+
# https://docs.djangoproject.com/en/5.1/topics/i18n/
106+
107+
LANGUAGE_CODE = "en-us"
108+
109+
TIME_ZONE = "UTC"
110+
111+
USE_I18N = True
112+
113+
USE_TZ = True
114+
115+
116+
# Static files (CSS, JavaScript, Images)
117+
# https://docs.djangoproject.com/en/5.1/howto/static-files/
118+
119+
STATIC_URL = "static/"
120+
121+
# Default primary key field type
122+
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
123+
124+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

example/config/urls.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
URL configuration for config project.
3+
4+
The `urlpatterns` list routes URLs to views. For more information please see:
5+
https://docs.djangoproject.com/en/5.1/topics/http/urls/
6+
Examples:
7+
Function views
8+
1. Add an import: from my_app import views
9+
2. Add a URL to urlpatterns: path('', views.home, name='home')
10+
Class-based views
11+
1. Add an import: from other_app.views import Home
12+
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13+
Including another URLconf
14+
1. Import the include() function: from django.urls import include, path
15+
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16+
"""
17+
18+
from django.contrib import admin
19+
from django.urls import include, path
20+
21+
urlpatterns = [
22+
path("", include("rssfilter.urls")),
23+
path("admin/", admin.site.urls),
24+
]

example/config/wsgi.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
WSGI config for config project.
3+
4+
It exposes the WSGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.wsgi import get_wsgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
15+
16+
application = get_wsgi_application()

0 commit comments

Comments
 (0)