astro_avtourist/frontend/scripts/analyze-posts.ts
2026-05-07 17:16:25 +05:00

158 lines
No EOL
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import PocketBase from 'pocketbase';
const PB_URL = 'http://127.0.0.1:8090';
const pb = new PocketBase(PB_URL);
interface Post {
id: string;
title: string;
content: string;
slug: string;
category: string;
draft: boolean;
}
interface AnalysisResult {
title: string;
slug: string;
category: string;
hasClaim: boolean;
hasTakeaway: boolean;
hasPronouns: boolean;
hasProof: boolean;
h2Issues: string[];
problems: string[];
}
function analyzePost(post: Post): AnalysisResult {
const result: AnalysisResult = {
title: post.title,
slug: post.slug,
category: post.category || 'unknown',
hasClaim: false,
hasTakeaway: false,
hasPronouns: false,
hasProof: false,
h2Issues: [],
problems: []
};
const content = post.content || '';
// Check for Claim patterns directly in content (including HTML)
// Look for new first paragraph that starts with Claim-like content
// Check first 1500 chars to catch posts where Claim may have been inserted
const claimPatterns = [
{ pattern: /За\s+\d+/i, name: 'За год' },
{ pattern: /В\s+\d+\s+(год|месяц)/i, name: 'В год/месяц' },
{ pattern: /\d+%/i, name: 'Процент' },
{ pattern: /Вы можете/i, name: 'Вы можете' },
];
const prefix = content.substring(0, 3000);
result.hasClaim = claimPatterns.some(p => p.pattern.test(prefix));
// Proof: contains numbers
const numberPatterns = [/\d+%/g, /\d+\s+(лет|месяцев|дней|тысяч|рублей)/gi];
result.hasProof = numberPatterns.some(p => p.test(content));
// Pronouns check
const pronouns = /\b(мы|наш|наша|наши|их|он|она|оно|этот|эта|эти)\b/gi;
result.hasPronouns = pronouns.test(content);
// Takeaway check
const takeawayPatterns = [
/что\s+делать/i,
/обращайтесь/i,
/позвоните/i,
/закажите/i,
/получите/i,
/напишите/i,
/свяжитесь/i
];
result.hasTakeaway = takeawayPatterns.some(p => p.test(content));
// H2 analysis - check all H2s in content
const h2Matches = content.match(/<h2[^>]*>[^<]+<\/h2>/gi) || [];
const badH2s = ['проблема', 'подход', 'ситуация', 'что было', 'решение', 'как помочь', 'поможет'];
h2Matches.forEach(h2 => {
const h2Text = h2.toLowerCase();
if (badH2s.some(b => h2Text.includes(b))) {
result.h2Issues.push(h2.replace(/<[^>]+>/g, ''));
}
});
// Collect problems
if (!result.hasClaim) result.problems.push('Нет Claim в первом абзаце');
if (!result.hasTakeaway) result.problems.push('Нет Takeaway');
if (result.hasPronouns) result.problems.push('Есть местоимения');
if (!result.hasProof) result.problems.push('Нет конкретных цифр');
if (result.h2Issues.length > 0) result.problems.push(`Размытые H2: ${result.h2Issues.join(', ')}`);
return result;
}
async function main() {
console.log('📊 Анализ постов блога по методологии Answer Unit\n');
console.log('='.repeat(60));
try {
const postsResult = await pb.collection('posts').getList(1, 500);
const posts = postsResult.items as unknown as Post[];
console.log(`Найдено постов: ${posts.length}\n`);
// Filter for non-draft posts
const activePosts = posts.filter(p => !p.draft);
console.log(`Опубликованных постов: ${activePosts.length}\n`);
const results = activePosts.map(analyzePost);
// Stats
const withClaim = results.filter(r => r.hasClaim).length;
const withTakeaway = results.filter(r => r.hasTakeaway).length;
const withPronouns = results.filter(r => r.hasPronouns).length;
const withProof = results.filter(r => r.hasProof).length;
const withH2Issues = results.filter(r => r.h2Issues.length > 0).length;
const problemCount = results.filter(r => r.problems.length > 0).length;
const n = activePosts.length;
console.log('📈 ОБЩАЯ СТАТИСТИКА:');
console.log('-'.repeat(40));
console.log(` ✓ Есть Claim: ${withClaim}/${n} (${n > 0 ? Math.round(withClaim/n*100) : 0}%)`);
console.log(` ✓ Есть Takeaway: ${withTakeaway}/${n} (${n > 0 ? Math.round(withTakeaway/n*100) : 0}%)`);
console.log(` ✗ Есть местоимения: ${withPronouns}/${n} (${n > 0 ? Math.round(withPronouns/n*100) : 0}%)`);
console.log(` ✓ Есть Proof (цифры): ${withProof}/${n} (${n > 0 ? Math.round(withProof/n*100) : 0}%)`);
console.log(` ✗ Проблемы с H2: ${withH2Issues}/${n}`);
console.log(` ⚠️ Постов с проблемами: ${problemCount}/${n}\n`);
// Problem posts
const problemPosts = results.filter(r => r.problems.length > 0);
if (problemPosts.length > 0) {
console.log('❌ ПОСТЫ С ПРОБЛЕМАМИ:');
console.log('-'.repeat(40));
problemPosts.forEach((r, i) => {
console.log(`\n${i+1}. ${r.title}`);
console.log(` slug: ${r.slug}`);
console.log(` category: ${r.category}`);
r.problems.forEach(p => console.log(` - ${p}`));
});
}
// Good posts
const goodPosts = results.filter(r => r.problems.length === 0);
if (goodPosts.length > 0) {
console.log('\n\n✅ ПОСТЫ БЕЗ ПРОБЛЕМ:');
console.log('-'.repeat(40));
goodPosts.forEach(r => {
console.log(`${r.title}`);
});
}
} catch (error) {
console.error('Ошибка:', error);
}
}
main();