Skip to content

ff-698 (gh-225) — Collect and visualize feed activity statistics. - #527

Merged
Tiendil merged 73 commits into
mainfrom
feature/consistency-experiment
May 30, 2026
Merged

ff-698 (gh-225) — Collect and visualize feed activity statistics.#527
Tiendil merged 73 commits into
mainfrom
feature/consistency-experiment

Conversation

@Tiendil

@Tiendil Tiendil commented May 30, 2026

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings May 30, 2026 10:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces backend collection and frontend visualization of per-feed activity statistics (daily loaded entries, average news/day, "young feed" flag, site URL). It also restructures the Feeds view UI to mirror the News view (clickable rows with detail bodies, a 30-day activity chart using chart.js/vue-chartjs), refactors product-specific user settings out of ffun.user_settings into ffun.product, introduces a SettingKind NewType, and updates feed parsing/storage to capture a feed's site URL. A new l_feed_entries_count table tracks per-day per-feed entry counts, and a new /get-feeds-by-ids private API serves on-demand full feed details. Specs (tests.md, modules_layout.md, errors.md) are updated to formalize the new module structure and rules. A new Donna inconsistency-check workflow and supporting tooling are added.

Changes:

  • Backend: new site_url, daily entries counts, feed statistics utilities, SettingKind typing, refactor of ffun.product (UserSetting/Resource moved to entities.py), new get-feeds-by-ids endpoint.
  • Frontend: new Vue components (feed_list/Columns.vue, FeedEntriesPerDayChart.vue), feeds store rewritten for on-demand full-feed loading, new tests, chart.js/vue-chartjs dependency, dropped "show feed descriptions" toggle.
  • Spec/tooling: updated specs/backend_architecture/*.md, new Donna workflow + consistency.toml, changelog.

Reviewed changes

Copilot reviewed 75 out of 77 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
ffun/ffun/library/migrations/20260527_01_c0DeX-feed-entry-counts.py New l_feed_entries_count table.
ffun/ffun/feeds/migrations/20260529_01_c0DeX-feed-site-url.py Adds site_url column to f_feeds.
ffun/ffun/library/operations.py _increment_feed_entries_count, remove_feed_entries_count, entries_in_period(_details).
ffun/ffun/library/domain.py / tests Re-exports plus test coverage for new operations.
ffun/ffun/feeds/operations.py / entities.py / utils.py / domain.py + tests New site_url/created_at columns, entries_per_day/is_young helpers.
ffun/ffun/parsers/feed.py / opml.py / entities.py + tests + fixtures Extracts feed site URL from RSS/Atom/OPML.
ffun/ffun/loader/domain.py + tests Syncs site_url along with title/description.
ffun/ffun/meta/domain.py + tests Removes per-feed counts when orphaned feeds are cleaned.
ffun/ffun/product/entities.py / user_settings.py / resources.py Moves UserSetting and Resource enums into entities.py.
ffun/ffun/user_settings/entities.py / types.py / operations.py / domain.py / values.py + tests Introduces SettingKind NewType and TypeId enum relocation.
ffun/ffun/feeds_collections/domain.py New thin facade over the collections singleton.
ffun/ffun/llms_framework/{entities,domain,keys_rotator}.py + tests/fixtures Adds LLMCostPoints NewType and public cost_points_to_usd_cost.
ffun/ffun/api/spa/{entities,http_handlers,settings}.py + tests New get-feeds-by-ids endpoint, _external_feeds, feed metrics fields, settings refactor.
ffun/ffun/core/tests/test_middlewares.py Registers new endpoint path.
ffun/ffun/cli/commands/metrics.py Updated import path.
ffun/ffun/domain/entities.py Adds Days NewType.
site/src/logic/types.ts + tests Adds siteUrl, young, entriesPerDay, entriesLoadedDetails; nullable linkedAt.
site/src/logic/enums.ts Adds EntriesPerDay order.
site/src/logic/api.ts New getFeedsByIds client.
site/src/stores/feeds.ts + tests Store rewritten: on-demand full-feed loading via Timer queue.
site/src/stores/globalSettings.ts Removes showFeedsDescriptions.
site/src/views/FeedsView.vue Side menu reshuffled; remove descriptions toggle.
site/src/components/FeedsList.vue / FeedForList.vue New column layout, click-to-expand details body.
site/src/components/feed_list/{Columns.vue,FeedEntriesPerDayChart.vue} New components.
site/src/components/body_list/EntryBody.vue Adds body-prefix slot.
site/package.json / package-lock.json Adds chart.js + vue-chartjs deps.
specs/backend_architecture/{tests,modules_layout,errors}.md Doc updates: time-baseline rule, foundational/edge module taxonomy, expected-vs-unexpected error guidance.
consistency.toml / workflows/inconsistency-check.donna.md / AGENTS.md / .session/inconsistency-check-plan.md New Donna consistency-check workflow + planning artifacts.
changes/next_release.md / unreleased.md Changelog entry.
Files not reviewed (1)
  • site/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread site/src/stores/feeds.ts
Comment on lines +99 to +114
async function loadFullFeeds() {
const ids: t.FeedId[] = Object.keys(requestedFeeds.value).map((key) => t.toFeedId(key));

if (ids.length === 0) {
return;
}

const loadedFeeds = await api.getFeedsByIds({ids: ids});

registerFeeds({
newFeeds: loadedFeeds,
replace: false
});

requestedFeeds.value = {};
}
Comment thread ffun/ffun/feeds/utils.py
Comment on lines +9 to +30
def entries_per_day(feed: Feed, entries_loaded: int, period: Days, now: datetime.datetime | None = None) -> int:
assert feed.created_at is not None

if now is None:
now = core_utils.now()

feed_age = max(datetime.timedelta(), now - feed.created_at)
feed_age_days = math.ceil(feed_age / datetime.timedelta(days=1))
days = max(1, min(int(period), feed_age_days))

return math.ceil(entries_loaded / days)


def is_young(feed: Feed, period: Days, now: datetime.datetime | None = None) -> bool:
assert feed.created_at is not None

if now is None:
now = core_utils.now()

feed_age = max(datetime.timedelta(), now - feed.created_at)

return feed_age < datetime.timedelta(days=int(period))
@Tiendil
Tiendil merged commit fb64fe0 into main May 30, 2026
3 checks passed
@Tiendil
Tiendil deleted the feature/consistency-experiment branch May 30, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants