Add /add page and cli.py for updating feeds
This commit is contained in:
parent
3f3ad42acd
commit
8cea0bd347
8 changed files with 130 additions and 57 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -159,3 +159,10 @@ cython_debug/
|
|||
data/
|
||||
media/
|
||||
staticfiles/
|
||||
|
||||
# https://github.com/lemon24/reader
|
||||
*.sqlite.search
|
||||
*.sqlite
|
||||
|
||||
# When running the cli.py script, the following files are created
|
||||
broken_feeds.csv
|
||||
|
|
|
|||
41
app/cli.py
41
app/cli.py
|
|
@ -1,9 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import click
|
||||
from reader import Reader, UpdateError
|
||||
from reader import Feed, Reader, UpdateError, UpdateResult
|
||||
from reader.types import UpdatedFeed
|
||||
|
||||
from app.dependencies import get_reader
|
||||
|
||||
|
|
@ -11,19 +13,44 @@ if TYPE_CHECKING:
|
|||
from reader import UpdatedFeed
|
||||
|
||||
|
||||
def add_broken_feed_to_csv(feed: Feed | UpdateResult | None) -> None:
|
||||
"""Add a broken feed to a CSV file."""
|
||||
if feed is None:
|
||||
click.echo("Feed is None.", err=True)
|
||||
return
|
||||
|
||||
with Path("broken_feeds.csv").open("a", encoding="utf-8") as f:
|
||||
f.write(f"{feed.url}\n")
|
||||
|
||||
|
||||
@click.command()
|
||||
def update_feeds() -> None:
|
||||
"""Update all the feeds."""
|
||||
click.echo("Updating feeds...")
|
||||
reader: Reader = get_reader()
|
||||
for feed in reader.update_feeds_iter():
|
||||
click.echo("Updating feeds...")
|
||||
|
||||
for feed in reader.update_feeds_iter(updates_enabled=True, workers=100):
|
||||
url: str = feed.url
|
||||
value: UpdatedFeed | None | UpdateError = feed.value
|
||||
if value is not None and isinstance(value, UpdateError):
|
||||
click.echo(f"Error updating {feed.url}: {value}")
|
||||
else:
|
||||
click.echo(f"Updated {feed.url}.")
|
||||
|
||||
if isinstance(value, UpdateError):
|
||||
add_broken_feed_to_csv(feed)
|
||||
reader.disable_feed_updates(url)
|
||||
continue
|
||||
|
||||
if value is None:
|
||||
click.echo(f"Feed not updated: {url}")
|
||||
continue
|
||||
|
||||
click.echo(f"Updated feed: {url}")
|
||||
|
||||
click.echo("Feeds updated.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
reader: Reader = get_reader()
|
||||
|
||||
for feed in reader.get_feeds(updates_enabled=False):
|
||||
reader.enable_feed_updates(feed)
|
||||
|
||||
update_feeds()
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import time
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from fastapi import APIRouter, File, Request, UploadFile
|
||||
from fastapi import APIRouter, File, Form, Request, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from reader import FeedExistsError, InvalidFeedURLError
|
||||
|
||||
from app.dependencies import CommonReader, CommonStats # noqa: TCH001
|
||||
from app.settings import MEDIA_ROOT
|
||||
|
|
@ -131,3 +132,40 @@ async def upload_page(request: Request, stats: CommonStats):
|
|||
async def contact(request: Request, stats: CommonStats):
|
||||
"""Contact page."""
|
||||
return templates.TemplateResponse(request=request, name="contact.html", context={"stats": stats})
|
||||
|
||||
|
||||
@static_router.post(path="/contact", summary="Contact page.", tags=["HTML"])
|
||||
async def contact_form(request: Request, stats: CommonStats, message: str = Form(...)):
|
||||
"""Contact page."""
|
||||
# TODO(TheLovinator): Send the message to the admin. # noqa: TD003
|
||||
return {
|
||||
"message": message,
|
||||
"stats": stats,
|
||||
}
|
||||
|
||||
|
||||
@static_router.get(path="/add", summary="Add feeds page.", tags=["HTML"])
|
||||
async def add_page(request: Request, stats: CommonStats):
|
||||
"""Add feeds page."""
|
||||
return templates.TemplateResponse(request=request, name="add.html", context={"stats": stats})
|
||||
|
||||
|
||||
@static_router.post(path="/add", summary="Add feeds page.", tags=["HTML"])
|
||||
async def add_feed(reader: CommonReader, stats: CommonStats, feed_urls: str = Form(...)):
|
||||
"""Add feeds page."""
|
||||
feed_info = []
|
||||
# Each line is a feed URL.
|
||||
for feed_url in feed_urls.split("\n"):
|
||||
try:
|
||||
reader.add_feed(feed_url.strip())
|
||||
feed_info.append({"url": feed_url.strip(), "status": "Added"})
|
||||
except FeedExistsError as e:
|
||||
feed_info.append({"url": feed_url.strip(), "status": str(e)})
|
||||
except InvalidFeedURLError as e:
|
||||
feed_info.append({"url": feed_url.strip(), "status": str(e)})
|
||||
|
||||
return {
|
||||
"feed_urls": feed_urls,
|
||||
"stats": stats,
|
||||
"feed_info": feed_info,
|
||||
}
|
||||
|
|
|
|||
5
app/testboi.py
Normal file
5
app/testboi.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from reader import Reader, make_reader
|
||||
|
||||
reader: Reader = make_reader(url="testboi.sqlite")
|
||||
reader.add_feed("http://485i.com/feed/")
|
||||
reader.update_feeds()
|
||||
20
manage.py
20
manage.py
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault(key="DJANGO_SETTINGS_MODULE", value="feedvault.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line # noqa: PLC0415
|
||||
except ImportError as exc:
|
||||
msg = "Couldn't import Django. Have you run `poetry install` or `poetry shell`?"
|
||||
raise ImportError(msg) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
49
poetry.lock
generated
49
poetry.lock
generated
|
|
@ -1185,45 +1185,44 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
|||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.3.7"
|
||||
version = "0.4.4"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"},
|
||||
{file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"},
|
||||
{file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"},
|
||||
{file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"},
|
||||
{file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"},
|
||||
{file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"},
|
||||
{file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"},
|
||||
{file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"},
|
||||
{file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"},
|
||||
{file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"},
|
||||
{file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"},
|
||||
{file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"},
|
||||
{file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"},
|
||||
{file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"},
|
||||
{file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"},
|
||||
{file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"},
|
||||
{file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"},
|
||||
{file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"},
|
||||
{file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.5.1"
|
||||
version = "70.0.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
|
||||
{file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
|
||||
{file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
|
||||
{file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "sgmllib3k"
|
||||
|
|
@ -1725,4 +1724,4 @@ watchdog = ["watchdog (>=2.3)"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "1306d3bb68f23a7887a7cb506ea7095ab0d66a779b0ccfaf36ab886b552cd186"
|
||||
content-hash = "069d3a9e95892d8e057e97858f342469cb3543f8bde8d893c3592583b0d36948"
|
||||
|
|
|
|||
|
|
@ -7,23 +7,24 @@ readme = "README.md"
|
|||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
python-dotenv = "^1.0.1"
|
||||
reader = "^3.12"
|
||||
fastapi = "^0.111.0"
|
||||
jinja2 = "^3.1.4"
|
||||
python-multipart = "^0.0.9"
|
||||
humanize = "^4.9.0"
|
||||
jinja2 = "^3.1.4"
|
||||
python-dotenv = "^1.0.1"
|
||||
python-multipart = "^0.0.9"
|
||||
reader = "^3.12"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.3.0"
|
||||
djlint = "^1.34.1"
|
||||
pre-commit = "^3.7.1"
|
||||
ruff = "^0.4.4"
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["poetry-core"]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
fix = true
|
||||
unsafe-fixes = true
|
||||
preview = true
|
||||
|
|
|
|||
16
templates/add.html
Normal file
16
templates/add.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>Add feeds</h2>
|
||||
<form method="post" action='{{ url_for("add_feed") }}'>
|
||||
<p>
|
||||
<label for="feed_urls">Feed URLs</label>
|
||||
<textarea id="feed_urls"
|
||||
name="feed_urls"
|
||||
rows="4"
|
||||
cols="50"
|
||||
required
|
||||
placeholder="Enter the URLs of the feeds you want to add"></textarea>
|
||||
</p>
|
||||
<button type="submit">Add feeds</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue