feat: SQLAlchemy models for Article, Image, Issue, Setting

This commit is contained in:
cottongin
2026-04-06 14:56:45 -04:00
parent 88e359069d
commit b0e1ed20bd
2 changed files with 160 additions and 1 deletions

View File

@@ -1 +1,74 @@
pass import json
from datetime import datetime, date, timezone
from app import db
class Article(db.Model):
__tablename__ = "articles"
id = db.Column(db.Integer, primary_key=True)
guid = db.Column(db.Text, unique=True, nullable=False)
title = db.Column(db.Text, nullable=False)
author = db.Column(db.Text, nullable=False)
pub_date = db.Column(db.DateTime, nullable=False)
categories = db.Column(db.Text, nullable=False, default="[]")
link = db.Column(db.Text, nullable=False)
content_html = db.Column(db.Text, nullable=False, default="")
fetched_at = db.Column(
db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc)
)
images = db.relationship("Image", backref="article", lazy=True,
cascade="all, delete-orphan")
class Image(db.Model):
__tablename__ = "images"
id = db.Column(db.Integer, primary_key=True)
article_id = db.Column(db.Integer, db.ForeignKey("articles.id"), nullable=False)
original_url = db.Column(db.Text, nullable=False)
local_path = db.Column(db.Text, nullable=False)
width = db.Column(db.Integer, nullable=False)
height = db.Column(db.Integer, nullable=False)
class Issue(db.Model):
__tablename__ = "issues"
id = db.Column(db.Integer, primary_key=True)
week_start = db.Column(db.Date, nullable=False)
week_end = db.Column(db.Date, nullable=False)
cover_method = db.Column(db.Text, nullable=False)
cover_path = db.Column(db.Text, nullable=False)
epub_path = db.Column(db.Text, nullable=False)
article_ids = db.Column(db.Text, nullable=False, default="[]")
excluded_article_ids = db.Column(db.Text, nullable=False, default="[]")
created_at = db.Column(
db.DateTime, nullable=False, default=lambda: datetime.now(timezone.utc)
)
status = db.Column(db.Text, nullable=False, default="draft")
class Setting(db.Model):
__tablename__ = "settings"
key = db.Column(db.Text, primary_key=True)
value = db.Column(db.Text, nullable=False)
@staticmethod
def get(key, default=None):
row = Setting.query.get(key)
if row is None:
return default
return json.loads(row.value)
@staticmethod
def set(key, value):
row = Setting.query.get(key)
if row is None:
row = Setting(key=key, value=json.dumps(value))
db.session.add(row)
else:
row.value = json.dumps(value)
db.session.commit()

86
tests/test_models.py Normal file
View File

@@ -0,0 +1,86 @@
import json
from datetime import datetime, date
from src.models import Article, Image, Issue, Setting
def test_create_article(db):
article = Article(
guid="https://example.com/?p=100",
title="Test Article",
author="Test Author",
pub_date=datetime(2026, 4, 6, 12, 0, 0),
categories=json.dumps(["Government"]),
link="https://example.com/test",
content_html="<p>Test content</p>",
)
db.session.add(article)
db.session.commit()
saved = Article.query.filter_by(guid="https://example.com/?p=100").first()
assert saved is not None
assert saved.title == "Test Article"
assert saved.author == "Test Author"
assert json.loads(saved.categories) == ["Government"]
assert saved.fetched_at is not None
def test_article_guid_unique(db):
a1 = Article(guid="dup", title="A", author="X", pub_date=datetime.now(),
categories="[]", link="http://a", content_html="")
a2 = Article(guid="dup", title="B", author="Y", pub_date=datetime.now(),
categories="[]", link="http://b", content_html="")
db.session.add(a1)
db.session.commit()
db.session.add(a2)
try:
db.session.commit()
assert False, "Should have raised IntegrityError"
except Exception:
db.session.rollback()
def test_create_image(db):
article = Article(guid="img-test", title="A", author="X",
pub_date=datetime.now(), categories="[]",
link="http://a", content_html="")
db.session.add(article)
db.session.commit()
img = Image(
article_id=article.id,
original_url="https://example.com/photo.jpg",
local_path="data/images/abc123.jpg",
width=800,
height=450,
)
db.session.add(img)
db.session.commit()
assert img.id is not None
assert img.article.guid == "img-test"
def test_create_issue(db):
issue = Issue(
week_start=date(2026, 4, 6),
week_end=date(2026, 4, 12),
cover_method="text",
cover_path="data/issues/cover.jpg",
epub_path="data/issues/test.epub",
article_ids=json.dumps([1, 2, 3]),
excluded_article_ids=json.dumps([]),
status="published",
)
db.session.add(issue)
db.session.commit()
assert issue.id is not None
assert issue.created_at is not None
def test_setting_crud(db):
Setting.set("fetch_interval", 2)
assert Setting.get("fetch_interval") == 2
assert Setting.get("nonexistent", default="fallback") == "fallback"
Setting.set("fetch_interval", 4)
assert Setting.get("fetch_interval") == 4