[update] added migration scripts for migrating news
data to articles
, geographic and category tagging, and default sharing templates
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
-- Drop articles table and its indexes
|
||||
DROP INDEX IF EXISTS idx_articles_read_at;
|
||||
DROP INDEX IF EXISTS idx_articles_source_type;
|
||||
DROP INDEX IF EXISTS idx_articles_processing_status;
|
||||
DROP INDEX IF EXISTS idx_articles_added_at;
|
||||
DROP INDEX IF EXISTS idx_articles_published_at;
|
||||
DROP TABLE IF EXISTS articles;
|
27
backend-rust/migrations/003_create_articles_table.up.sql
Normal file
27
backend-rust/migrations/003_create_articles_table.up.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
-- Create enhanced articles table to replace news table structure
|
||||
CREATE TABLE IF NOT EXISTS articles
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL DEFAULT 'rss', -- 'rss', 'manual'
|
||||
rss_content TEXT, -- RSS description/excerpt
|
||||
full_content TEXT, -- Scraped full content
|
||||
summary TEXT, -- AI-generated summary
|
||||
processing_status TEXT NOT NULL DEFAULT 'pending', -- 'pending', 'processing', 'completed', 'failed'
|
||||
published_at TIMESTAMP NOT NULL,
|
||||
added_at TIMESTAMP NOT NULL DEFAULT (datetime('now')),
|
||||
read_at TIMESTAMP,
|
||||
read_count INTEGER NOT NULL DEFAULT 0,
|
||||
reading_time INTEGER, -- in seconds
|
||||
ai_enabled BOOLEAN NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_published_at ON articles (published_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_added_at ON articles (added_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_processing_status ON articles (processing_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_source_type ON articles (source_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_read_at ON articles (read_at);
|
9
backend-rust/migrations/004_create_tags_table.down.sql
Normal file
9
backend-rust/migrations/004_create_tags_table.down.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Drop tag system tables and indexes
|
||||
DROP INDEX IF EXISTS idx_article_tags_ai_generated;
|
||||
DROP INDEX IF EXISTS idx_article_tags_tag_id;
|
||||
DROP INDEX IF EXISTS idx_article_tags_article_id;
|
||||
DROP INDEX IF EXISTS idx_tags_usage_count;
|
||||
DROP INDEX IF EXISTS idx_tags_parent_id;
|
||||
DROP INDEX IF EXISTS idx_tags_category;
|
||||
DROP TABLE IF EXISTS article_tags;
|
||||
DROP TABLE IF EXISTS tags;
|
31
backend-rust/migrations/004_create_tags_table.up.sql
Normal file
31
backend-rust/migrations/004_create_tags_table.up.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
-- Create tags table with hierarchical support
|
||||
CREATE TABLE IF NOT EXISTS tags
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
category TEXT NOT NULL, -- 'geographic', 'content', 'source', 'custom'
|
||||
description TEXT,
|
||||
color TEXT, -- Hex color for UI display
|
||||
usage_count INTEGER NOT NULL DEFAULT 0,
|
||||
parent_id INTEGER REFERENCES tags (id), -- For hierarchical tags (e.g., Country -> Region -> City)
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create article_tags junction table
|
||||
CREATE TABLE IF NOT EXISTS article_tags
|
||||
(
|
||||
article_id INTEGER NOT NULL REFERENCES articles (id) ON DELETE CASCADE,
|
||||
tag_id INTEGER NOT NULL REFERENCES tags (id) ON DELETE CASCADE,
|
||||
confidence_score REAL DEFAULT 1.0, -- AI confidence (0.0-1.0)
|
||||
ai_generated BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (article_id, tag_id)
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_tags_category ON tags (category);
|
||||
CREATE INDEX IF NOT EXISTS idx_tags_parent_id ON tags (parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tags_usage_count ON tags (usage_count DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_tags_article_id ON article_tags (article_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_tags_tag_id ON article_tags (tag_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_article_tags_ai_generated ON article_tags (ai_generated);
|
11
backend-rust/migrations/005_create_statistics_table.down.sql
Normal file
11
backend-rust/migrations/005_create_statistics_table.down.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Drop analytics system tables and indexes
|
||||
DROP INDEX IF EXISTS idx_legacy_migration_old_filter_type;
|
||||
DROP INDEX IF EXISTS idx_share_templates_format;
|
||||
DROP INDEX IF EXISTS idx_filter_presets_user_id;
|
||||
DROP INDEX IF EXISTS idx_reading_stats_read_at;
|
||||
DROP INDEX IF EXISTS idx_reading_stats_article_id;
|
||||
DROP INDEX IF EXISTS idx_reading_stats_user_id;
|
||||
DROP TABLE IF EXISTS legacy_migration;
|
||||
DROP TABLE IF EXISTS share_templates;
|
||||
DROP TABLE IF EXISTS filter_presets;
|
||||
DROP TABLE IF EXISTS reading_stats;
|
50
backend-rust/migrations/005_create_statistics_table.up.sql
Normal file
50
backend-rust/migrations/005_create_statistics_table.up.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
-- Create reading statistics table
|
||||
CREATE TABLE IF NOT EXISTS reading_stats
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER DEFAULT 1, -- For future multi-user support
|
||||
article_id INTEGER NOT NULL REFERENCES articles (id) ON DELETE CASCADE,
|
||||
read_at TIMESTAMP NOT NULL,
|
||||
reading_time INTEGER, -- in seconds
|
||||
completion_rate REAL DEFAULT 1.0, -- 0.0-1.0, how much of the article was read
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create filter presets table
|
||||
CREATE TABLE IF NOT EXISTS filter_presets
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
filter_criteria TEXT NOT NULL, -- JSON string of filter parameters
|
||||
user_id INTEGER DEFAULT 1, -- For future multi-user support
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create share templates table
|
||||
CREATE TABLE IF NOT EXISTS share_templates
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
format TEXT NOT NULL, -- 'text', 'markdown', 'html', 'json'
|
||||
template_content TEXT NOT NULL,
|
||||
is_default BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create legacy migration tracking table
|
||||
CREATE TABLE IF NOT EXISTS legacy_migration
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
old_filter_type TEXT NOT NULL, -- 'country', 'category', etc.
|
||||
old_value TEXT NOT NULL,
|
||||
new_tag_ids TEXT, -- JSON array of tag IDs
|
||||
migrated_at TIMESTAMP NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_reading_stats_user_id ON reading_stats (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_reading_stats_article_id ON reading_stats (article_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_reading_stats_read_at ON reading_stats (read_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_filter_presets_user_id ON filter_presets (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_share_templates_format ON share_templates (format);
|
||||
CREATE INDEX IF NOT EXISTS idx_legacy_migration_old_filter_type ON legacy_migration (old_filter_type);
|
18
backend-rust/migrations/006_update_settings_table.down.sql
Normal file
18
backend-rust/migrations/006_update_settings_table.down.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Remove enhanced settings columns and indexes
|
||||
DROP INDEX IF EXISTS idx_settings_user_id;
|
||||
DROP INDEX IF EXISTS idx_settings_category;
|
||||
|
||||
-- Note: SQLite doesn't support DROP COLUMN, so we recreate the table
|
||||
CREATE TABLE settings_backup AS
|
||||
SELECT key, val
|
||||
FROM settings;
|
||||
DROP TABLE settings;
|
||||
CREATE TABLE settings
|
||||
(
|
||||
key TEXT PRIMARY KEY,
|
||||
val TEXT NOT NULL
|
||||
);
|
||||
INSERT INTO settings
|
||||
SELECT key, val
|
||||
FROM settings_backup;
|
||||
DROP TABLE settings_backup;
|
74
backend-rust/migrations/006_update_settings_table.up.sql
Normal file
74
backend-rust/migrations/006_update_settings_table.up.sql
Normal file
@@ -0,0 +1,74 @@
|
||||
-- Enhance settings table to support more structured configuration
|
||||
ALTER TABLE settings
|
||||
ADD COLUMN category TEXT DEFAULT 'general';
|
||||
ALTER TABLE settings
|
||||
ADD COLUMN user_id INTEGER DEFAULT 1;
|
||||
ALTER TABLE settings
|
||||
ADD COLUMN updated_at TIMESTAMP DEFAULT (datetime('now'));
|
||||
|
||||
-- Create index for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_settings_category ON settings (category);
|
||||
CREATE INDEX IF NOT EXISTS idx_settings_user_id ON settings (user_id);
|
||||
|
||||
-- Insert default settings based on roadmap configuration
|
||||
INSERT OR IGNORE INTO settings (key, val, category)
|
||||
VALUES
|
||||
-- Display settings
|
||||
('default_view', 'compact', 'display'),
|
||||
('articles_per_page', '50', 'display'),
|
||||
('show_reading_time', '1', 'display'),
|
||||
('show_word_count', '0', 'display'),
|
||||
('highlight_unread', '1', 'display'),
|
||||
('theme', 'auto', 'display'),
|
||||
|
||||
-- Analytics settings
|
||||
('analytics_enabled', '1', 'analytics'),
|
||||
('track_reading_time', '1', 'analytics'),
|
||||
('track_scroll_position', '1', 'analytics'),
|
||||
('retention_days', '365', 'analytics'),
|
||||
('aggregate_older_data', '1', 'analytics'),
|
||||
|
||||
-- Filtering settings
|
||||
('enable_smart_suggestions', '1', 'filtering'),
|
||||
('max_recent_filters', '10', 'filtering'),
|
||||
('auto_save_filters', '1', 'filtering'),
|
||||
('default_sort', 'added_desc', 'filtering'),
|
||||
('enable_geographic_hierarchy', '1', 'filtering'),
|
||||
('auto_migrate_country_filters', '1', 'filtering'),
|
||||
|
||||
-- Sharing settings
|
||||
('default_share_format', 'text', 'sharing'),
|
||||
('include_summary', '1', 'sharing'),
|
||||
('include_tags', '1', 'sharing'),
|
||||
('include_source', '1', 'sharing'),
|
||||
('copy_to_clipboard', '1', 'sharing'),
|
||||
|
||||
-- AI settings
|
||||
('ai_enabled', '1', 'ai'),
|
||||
('ai_provider', 'ollama', 'ai'),
|
||||
('ai_timeout_seconds', '120', 'ai'),
|
||||
('ai_summary_enabled', '1', 'ai'),
|
||||
('ai_summary_temperature', '0.1', 'ai'),
|
||||
('ai_summary_max_tokens', '1000', 'ai'),
|
||||
('ai_tagging_enabled', '1', 'ai'),
|
||||
('ai_tagging_temperature', '0.3', 'ai'),
|
||||
('ai_tagging_max_tokens', '200', 'ai'),
|
||||
('max_tags_per_article', '10', 'ai'),
|
||||
('min_confidence_threshold', '0.7', 'ai'),
|
||||
('enable_geographic_tagging', '1', 'ai'),
|
||||
('enable_category_tagging', '1', 'ai'),
|
||||
('geographic_hierarchy_levels', '3', 'ai'),
|
||||
|
||||
-- Scraping settings
|
||||
('scraping_timeout_seconds', '30', 'scraping'),
|
||||
('scraping_max_retries', '3', 'scraping'),
|
||||
('max_content_length', '50000', 'scraping'),
|
||||
('respect_robots_txt', '1', 'scraping'),
|
||||
('rate_limit_delay_ms', '1000', 'scraping'),
|
||||
|
||||
-- Processing settings
|
||||
('batch_size', '10', 'processing'),
|
||||
('max_concurrent', '5', 'processing'),
|
||||
('retry_attempts', '3', 'processing'),
|
||||
('priority_manual', '1', 'processing'),
|
||||
('auto_mark_read_on_view', '0', 'processing');
|
@@ -0,0 +1,39 @@
|
||||
-- Remove migrated data (this will remove all articles and tags created from migration)
|
||||
-- WARNING: This will delete all migrated data
|
||||
|
||||
-- Remove legacy migration records
|
||||
DELETE
|
||||
FROM legacy_migration
|
||||
WHERE old_filter_type IN ('country', 'category');
|
||||
|
||||
-- Remove article-tag associations for migrated data (non-AI generated)
|
||||
DELETE
|
||||
FROM article_tags
|
||||
WHERE ai_generated = 0;
|
||||
|
||||
-- Remove migrated geographic tags (only those created from country data)
|
||||
DELETE
|
||||
FROM tags
|
||||
WHERE tags.category = 'geographic'
|
||||
AND EXISTS (SELECT 1 FROM news WHERE news.country = tags.name);
|
||||
|
||||
-- Remove migrated content tags (only those created from category data)
|
||||
DELETE
|
||||
FROM tags
|
||||
WHERE tags.category = 'content'
|
||||
AND EXISTS (SELECT 1 FROM news WHERE news.category = tags.name);
|
||||
|
||||
-- Remove migrated articles (only those that match news entries)
|
||||
DELETE
|
||||
FROM articles
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM news
|
||||
WHERE news.url = articles.url
|
||||
AND news.title = articles.title
|
||||
AND articles.source_type = 'rss');
|
||||
|
||||
-- Reset tag usage counts
|
||||
UPDATE tags
|
||||
SET usage_count = (SELECT COUNT(*)
|
||||
FROM article_tags
|
||||
WHERE tag_id = tags.id);
|
@@ -0,0 +1,84 @@
|
||||
|
||||
-- Migrate data from old news table to new articles table
|
||||
INSERT INTO articles (title, url, summary, published_at, added_at, source_type, processing_status)
|
||||
SELECT title,
|
||||
url,
|
||||
summary,
|
||||
published,
|
||||
datetime(created_at, 'unixepoch'),
|
||||
'rss',
|
||||
CASE
|
||||
WHEN summary IS NOT NULL AND summary != '' THEN 'completed'
|
||||
ELSE 'pending'
|
||||
END
|
||||
FROM news;
|
||||
|
||||
-- Create geographic tags from existing country data
|
||||
INSERT OR IGNORE INTO tags (name, category, description, usage_count)
|
||||
SELECT DISTINCT country,
|
||||
'geographic',
|
||||
'Geographic location: ' || country,
|
||||
COUNT(*)
|
||||
FROM news
|
||||
WHERE country IS NOT NULL
|
||||
AND country != ''
|
||||
GROUP BY country;
|
||||
|
||||
-- Link articles to their geographic tags
|
||||
INSERT OR IGNORE INTO article_tags (article_id, tag_id, ai_generated, confidence_score)
|
||||
SELECT a.id,
|
||||
t.id,
|
||||
0, -- Not AI generated, migrated from legacy data
|
||||
1.0 -- Full confidence for existing data
|
||||
FROM articles a
|
||||
JOIN news n ON a.url = n.url AND a.title = n.title
|
||||
JOIN tags t ON t.name = n.country AND t.category = 'geographic'
|
||||
WHERE n.country IS NOT NULL
|
||||
AND n.country != '';
|
||||
|
||||
-- Create category tags if category column exists in news table
|
||||
INSERT OR IGNORE INTO tags (name, category, description, usage_count)
|
||||
SELECT DISTINCT n.category,
|
||||
'content',
|
||||
'Content category: ' || n.category,
|
||||
COUNT(*)
|
||||
FROM news n
|
||||
WHERE n.category IS NOT NULL
|
||||
AND n.category != ''
|
||||
GROUP BY n.category;
|
||||
|
||||
-- Link articles to their category tags
|
||||
INSERT OR IGNORE INTO article_tags (article_id, tag_id, ai_generated, confidence_score)
|
||||
SELECT a.id,
|
||||
t.id,
|
||||
0, -- Not AI generated, migrated from legacy data
|
||||
1.0 -- Full confidence for existing data
|
||||
FROM articles a
|
||||
JOIN news n ON a.url = n.url AND a.title = n.title
|
||||
JOIN tags t ON t.name = n.category AND t.category = 'content'
|
||||
WHERE n.category IS NOT NULL
|
||||
AND n.category != '';
|
||||
|
||||
-- Record migration in legacy_migration table for countries
|
||||
INSERT INTO legacy_migration (old_filter_type, old_value, new_tag_ids)
|
||||
SELECT 'country',
|
||||
n.country,
|
||||
'[' || GROUP_CONCAT(t.id) || ']'
|
||||
FROM (SELECT DISTINCT country FROM news WHERE country IS NOT NULL AND country != '') n
|
||||
JOIN tags t ON t.name = n.country AND t.category = 'geographic'
|
||||
GROUP BY n.country;
|
||||
|
||||
-- Record migration in legacy_migration table for categories (if they exist)
|
||||
INSERT INTO legacy_migration (old_filter_type, old_value, new_tag_ids)
|
||||
SELECT 'category',
|
||||
n.category,
|
||||
'[' || GROUP_CONCAT(t.id) || ']'
|
||||
FROM (SELECT DISTINCT category FROM news WHERE category IS NOT NULL AND category != '') n
|
||||
JOIN tags t ON t.name = n.category AND t.category = 'content'
|
||||
GROUP BY n.category;
|
||||
|
||||
-- Update tag usage counts
|
||||
UPDATE tags
|
||||
SET usage_count = (SELECT COUNT(*)
|
||||
FROM article_tags
|
||||
WHERE tag_id = tags.id);
|
@@ -0,0 +1,4 @@
|
||||
-- Remove default sharing templates
|
||||
DELETE
|
||||
FROM share_templates
|
||||
WHERE name IN ('Default Text', 'Markdown', 'Simple Text', 'HTML Email');
|
@@ -0,0 +1,39 @@
|
||||
-- Insert default sharing templates
|
||||
INSERT INTO share_templates (name, format, template_content, is_default)
|
||||
VALUES ('Default Text', 'text', '📰 {title}
|
||||
|
||||
{summary}
|
||||
|
||||
🏷️ Tags: {tags}
|
||||
🌍 Location: {geographic_tags}
|
||||
🔗 Source: {url}
|
||||
📅 Published: {published_at}
|
||||
|
||||
Shared via Owly News Summariser', 1),
|
||||
|
||||
('Markdown', 'markdown', '# {title}
|
||||
|
||||
{summary}
|
||||
|
||||
**Tags:** {tags}
|
||||
**Location:** {geographic_tags}
|
||||
**Source:** [{url}]({url})
|
||||
**Published:** {published_at}
|
||||
|
||||
---
|
||||
*Shared via Owly News Summariser*', 1),
|
||||
|
||||
('Simple Text', 'text', '{title}
|
||||
|
||||
{summary}
|
||||
|
||||
Source: {url}', 0),
|
||||
|
||||
('HTML Email', 'html', '<h2>{title}</h2>
|
||||
<p>{summary}</p>
|
||||
<p><strong>Tags:</strong> {tags}<br>
|
||||
<strong>Location:</strong> {geographic_tags}<br>
|
||||
<strong>Source:</strong> <a href="{url}">{url}</a><br>
|
||||
<strong>Published:</strong> {published_at}</p>
|
||||
<hr>
|
||||
<small>Shared via Owly News Summariser</small>', 0);
|
Reference in New Issue
Block a user