Update
This commit is contained in:
@ -18,6 +18,7 @@ class BatchScheduler {
|
||||
};
|
||||
|
||||
private currentAbortController?: AbortController;
|
||||
private migrationCompleted = false;
|
||||
|
||||
private readonly SIX_HOURS_MS = 6 * 60 * 60 * 1000; // 6 hours in milliseconds
|
||||
|
||||
@ -84,6 +85,28 @@ class BatchScheduler {
|
||||
|
||||
try {
|
||||
console.log("🔄 Running scheduled batch process...");
|
||||
|
||||
// Run migration for feeds without categories (only once)
|
||||
if (!this.migrationCompleted) {
|
||||
try {
|
||||
const { migrateFeedsWithCategories, getFeedCategoryMigrationStatus } = await import("./database.js");
|
||||
const migrationStatus = await getFeedCategoryMigrationStatus();
|
||||
|
||||
if (!migrationStatus.migrationComplete) {
|
||||
console.log("🔄 Running feed category migration...");
|
||||
await migrateFeedsWithCategories();
|
||||
this.migrationCompleted = true;
|
||||
} else {
|
||||
console.log("✅ Feed category migration already complete");
|
||||
this.migrationCompleted = true;
|
||||
}
|
||||
} catch (migrationError) {
|
||||
console.error("❌ Error during feed category migration:", migrationError);
|
||||
// Don't fail the entire batch process due to migration error
|
||||
this.migrationCompleted = true; // Mark as completed to avoid retrying every batch
|
||||
}
|
||||
}
|
||||
|
||||
await batchProcess(this.currentAbortController.signal);
|
||||
console.log("✅ Scheduled batch process completed");
|
||||
} catch (error) {
|
||||
|
@ -131,6 +131,7 @@ function initializeDatabase(): Database {
|
||||
url TEXT NOT NULL UNIQUE,
|
||||
title TEXT,
|
||||
description TEXT,
|
||||
category TEXT,
|
||||
last_updated TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
active BOOLEAN DEFAULT 1
|
||||
@ -267,6 +268,7 @@ export interface EpisodeWithFeedInfo {
|
||||
feedId: string;
|
||||
feedTitle?: string;
|
||||
feedUrl: string;
|
||||
feedCategory?: string;
|
||||
}
|
||||
|
||||
// Feed management functions
|
||||
@ -280,11 +282,12 @@ export async function saveFeed(
|
||||
if (existingFeed) {
|
||||
// Update existing feed
|
||||
const updateStmt = db.prepare(
|
||||
"UPDATE feeds SET title = ?, description = ?, last_updated = ?, active = ? WHERE url = ?",
|
||||
"UPDATE feeds SET title = ?, description = ?, category = ?, last_updated = ?, active = ? WHERE url = ?",
|
||||
);
|
||||
updateStmt.run(
|
||||
feed.title || null,
|
||||
feed.description || null,
|
||||
feed.category || null,
|
||||
feed.lastUpdated || null,
|
||||
feed.active !== undefined ? (feed.active ? 1 : 0) : 1,
|
||||
feed.url,
|
||||
@ -296,13 +299,14 @@ export async function saveFeed(
|
||||
const createdAt = new Date().toISOString();
|
||||
|
||||
const insertStmt = db.prepare(
|
||||
"INSERT INTO feeds (id, url, title, description, last_updated, created_at, active) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
"INSERT INTO feeds (id, url, title, description, category, last_updated, created_at, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
);
|
||||
insertStmt.run(
|
||||
id,
|
||||
feed.url,
|
||||
feed.title || null,
|
||||
feed.description || null,
|
||||
feed.category || null,
|
||||
feed.lastUpdated || null,
|
||||
createdAt,
|
||||
feed.active !== undefined ? (feed.active ? 1 : 0) : 1,
|
||||
@ -407,7 +411,8 @@ export async function fetchEpisodesWithFeedInfo(): Promise<
|
||||
a.pub_date as articlePubDate,
|
||||
f.id as feedId,
|
||||
f.title as feedTitle,
|
||||
f.url as feedUrl
|
||||
f.url as feedUrl,
|
||||
f.category as feedCategory
|
||||
FROM episodes e
|
||||
JOIN articles a ON e.article_id = a.id
|
||||
JOIN feeds f ON a.feed_id = f.id
|
||||
@ -432,6 +437,7 @@ export async function fetchEpisodesWithFeedInfo(): Promise<
|
||||
feedId: row.feedId,
|
||||
feedTitle: row.feedTitle,
|
||||
feedUrl: row.feedUrl,
|
||||
feedCategory: row.feedCategory,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error fetching episodes with feed info:", error);
|
||||
@ -459,7 +465,8 @@ export async function fetchEpisodesByFeedId(
|
||||
a.pub_date as articlePubDate,
|
||||
f.id as feedId,
|
||||
f.title as feedTitle,
|
||||
f.url as feedUrl
|
||||
f.url as feedUrl,
|
||||
f.category as feedCategory
|
||||
FROM episodes e
|
||||
JOIN articles a ON e.article_id = a.id
|
||||
JOIN feeds f ON a.feed_id = f.id
|
||||
@ -484,6 +491,7 @@ export async function fetchEpisodesByFeedId(
|
||||
feedId: row.feedId,
|
||||
feedTitle: row.feedTitle,
|
||||
feedUrl: row.feedUrl,
|
||||
feedCategory: row.feedCategory,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error fetching episodes by feed ID:", error);
|
||||
@ -511,7 +519,8 @@ export async function fetchEpisodeWithSourceInfo(
|
||||
a.pub_date as articlePubDate,
|
||||
f.id as feedId,
|
||||
f.title as feedTitle,
|
||||
f.url as feedUrl
|
||||
f.url as feedUrl,
|
||||
f.category as feedCategory
|
||||
FROM episodes e
|
||||
JOIN articles a ON e.article_id = a.id
|
||||
JOIN feeds f ON a.feed_id = f.id
|
||||
@ -536,6 +545,7 @@ export async function fetchEpisodeWithSourceInfo(
|
||||
feedId: row.feedId,
|
||||
feedTitle: row.feedTitle,
|
||||
feedUrl: row.feedUrl,
|
||||
feedCategory: row.feedCategory,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching episode with source info:", error);
|
||||
@ -1104,6 +1114,100 @@ export async function updateFeedRequestStatus(
|
||||
}
|
||||
}
|
||||
|
||||
// Migration function to classify existing feeds without categories
|
||||
export async function migrateFeedsWithCategories(): Promise<void> {
|
||||
try {
|
||||
console.log("🔄 Starting feed category migration...");
|
||||
|
||||
// Get all feeds without categories
|
||||
const stmt = db.prepare("SELECT * FROM feeds WHERE category IS NULL OR category = ''");
|
||||
const feedsWithoutCategories = stmt.all() as any[];
|
||||
|
||||
if (feedsWithoutCategories.length === 0) {
|
||||
console.log("✅ All feeds already have categories assigned");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📋 Found ${feedsWithoutCategories.length} feeds without categories`);
|
||||
|
||||
// Import LLM service
|
||||
const { openAI_ClassifyFeed } = await import("./llm.js");
|
||||
|
||||
let processedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const feed of feedsWithoutCategories) {
|
||||
try {
|
||||
// Use title for classification, fallback to URL if no title
|
||||
const titleForClassification = feed.title || feed.url;
|
||||
|
||||
console.log(`🔍 Classifying feed: ${titleForClassification}`);
|
||||
|
||||
// Classify the feed
|
||||
const category = await openAI_ClassifyFeed(titleForClassification);
|
||||
|
||||
// Update the feed with the category
|
||||
const updateStmt = db.prepare("UPDATE feeds SET category = ? WHERE id = ?");
|
||||
updateStmt.run(category, feed.id);
|
||||
|
||||
console.log(`✅ Assigned category "${category}" to feed: ${titleForClassification}`);
|
||||
processedCount++;
|
||||
|
||||
// Add a small delay to avoid rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to classify feed ${feed.title || feed.url}:`, error);
|
||||
errorCount++;
|
||||
|
||||
// Set a default category for failed classifications
|
||||
const defaultCategory = "その他";
|
||||
const updateStmt = db.prepare("UPDATE feeds SET category = ? WHERE id = ?");
|
||||
updateStmt.run(defaultCategory, feed.id);
|
||||
console.log(`⚠️ Assigned default category "${defaultCategory}" to feed: ${feed.title || feed.url}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Feed category migration completed`);
|
||||
console.log(`📊 Processed: ${processedCount}, Errors: ${errorCount}, Total: ${feedsWithoutCategories.length}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error("❌ Error during feed category migration:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get migration status
|
||||
export async function getFeedCategoryMigrationStatus(): Promise<{
|
||||
totalFeeds: number;
|
||||
feedsWithCategories: number;
|
||||
feedsWithoutCategories: number;
|
||||
migrationComplete: boolean;
|
||||
}> {
|
||||
try {
|
||||
const totalStmt = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 1");
|
||||
const totalResult = totalStmt.get() as any;
|
||||
const totalFeeds = totalResult.count;
|
||||
|
||||
const withCategoriesStmt = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 1 AND category IS NOT NULL AND category != ''");
|
||||
const withCategoriesResult = withCategoriesStmt.get() as any;
|
||||
const feedsWithCategories = withCategoriesResult.count;
|
||||
|
||||
const feedsWithoutCategories = totalFeeds - feedsWithCategories;
|
||||
const migrationComplete = feedsWithoutCategories === 0;
|
||||
|
||||
return {
|
||||
totalFeeds,
|
||||
feedsWithCategories,
|
||||
feedsWithoutCategories,
|
||||
migrationComplete,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error getting migration status:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function closeDatabase(): void {
|
||||
db.close();
|
||||
}
|
||||
|
Reference in New Issue
Block a user