Add Google Trends API integration with real-time keyword fetching

This commit is contained in:
Imtiaz Ali
2025-10-29 23:15:56 +03:00
parent df24db865b
commit a6e19b662f
4 changed files with 218 additions and 214 deletions

100
app.js
View File

@@ -43,15 +43,28 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
// Import Google Trends API functions
import { fetchGoogleTrends, getTrendingDate } from './trends-api.js';
// Fetch trending topics from multiple sources for diversity
const fetchTrendingTopics = async () => {
try {
// Source 1: Wikipedia trending articles
let combinedTopics = [];
// Source 1: Google Trends API
console.log('Fetching trending topics from Google Trends...');
const trendingData = await fetchGoogleTrends();
if (trendingData && trendingData.length > 0) {
combinedTopics = [...combinedTopics, ...trendingData];
}
// Update trending date display
updateTrendingDateDisplay();
// Source 2: Wikipedia trending articles (as backup)
const wikiResponse = await fetch('https://wikimedia.org/api/rest_v1/metrics/pageviews/top/en.wikipedia/all-access/2023/01/all-days');
const wikiData = await wikiResponse.json();
let combinedTopics = [];
// Process Wikipedia data
if (wikiData && wikiData.items && wikiData.items[0] && wikiData.items[0].articles) {
const wikiTopics = wikiData.items[0].articles
@@ -68,23 +81,6 @@ document.addEventListener('DOMContentLoaded', () => {
combinedTopics = [...combinedTopics, ...wikiTopics];
}
// Source 2: Use Google Trends API-like data (simulated)
// In a real implementation, you would use Google Trends API
const techTrendingTopics = [
{ keyword: 'quantum computing applications', searchVolume: 85000 },
{ keyword: 'edge computing use cases', searchVolume: 62000 },
{ keyword: 'zero-knowledge proofs', searchVolume: 45000 },
{ keyword: 'synthetic data generation', searchVolume: 73000 },
{ keyword: 'federated learning models', searchVolume: 58000 },
{ keyword: 'serverless architecture patterns', searchVolume: 67000 },
{ keyword: 'homomorphic encryption', searchVolume: 41000 },
{ keyword: 'computer vision in healthcare', searchVolume: 89000 },
{ keyword: 'explainable ai techniques', searchVolume: 76000 },
{ keyword: 'graph neural networks', searchVolume: 52000 }
].map(item => ({ ...item, source: 'tech_trends' }));
combinedTopics = [...combinedTopics, ...techTrendingTopics];
// Source 3: Emerging research topics (simulated)
const emergingTopics = [
{ keyword: 'biomimetic materials science', searchVolume: 32000 },
@@ -106,6 +102,18 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
// Update trending date display in the UI
const updateTrendingDateDisplay = () => {
const dateInfo = getTrendingDate();
const dateDisplay = document.getElementById('trending-date');
if (dateDisplay) {
const formattedDate = new Date(dateInfo.timestamp).toLocaleString();
dateDisplay.textContent = `Trending as of: ${formattedDate}`;
dateDisplay.style.display = 'block';
}
};
// Enrich keywords with search result counts
const enrichKeywordsWithOpportunityData = async (keywords) => {
// In a production environment, you would use a real search API
@@ -154,17 +162,35 @@ document.addEventListener('DOMContentLoaded', () => {
const maxResultCount = Math.max(...keywords.map(k => k.resultCount));
const normalizedResultCount = 100 - ((keyword.resultCount / maxResultCount) * 100);
// Calculate SR (Search Relevance) - how relevant the keyword is based on search volume
const searchRelevance = Math.round(normalizedSearchVolume);
// Calculate DR (Difficulty Rating) - how difficult it is to rank for this keyword
// Lower result count means easier to rank (lower difficulty)
const difficultyRating = Math.round(100 - normalizedResultCount);
// Calculate opportunity score (weighted average)
// 60% weight to search volume, 40% weight to inverse result count
const opportunityScore = (normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4);
const opportunityScore = Math.round((normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4));
// Calculate competition level (lower is better)
const competitionLevel = keyword.resultCount / keyword.searchVolume;
const competitionLevel = Math.round(keyword.resultCount / keyword.searchVolume);
// Calculate keyword quality score (0-100) for color coding
// Higher is better - combines all metrics
const qualityScore = Math.round(
(opportunityScore * 0.5) +
(searchRelevance * 0.3) +
((100 - difficultyRating) * 0.2)
);
return {
...keyword,
opportunityScore: Math.round(opportunityScore),
competitionLevel: Math.round(competitionLevel)
opportunityScore,
searchRelevance,
difficultyRating,
competitionLevel,
qualityScore
};
});
@@ -207,13 +233,19 @@ document.addEventListener('DOMContentLoaded', () => {
const resultCount = keyword.resultCount ? `${(keyword.resultCount/1000).toFixed(1)}K` : 'N/A';
const competitionLevel = keyword.competitionLevel || 'N/A';
// Determine color based on quality score
const qualityScore = keyword.qualityScore || score;
const qualityColor = getQualityColor(qualityScore);
keywordCard.innerHTML = `
<div class="keyword-name">${keyword.keyword}</div>
<div class="keyword-metrics">
<div class="opportunity-score">Opportunity: <span class="highlight">${score}</span></div>
<div class="keyword-metrics" style="border-left: 4px solid ${qualityColor}">
<div class="opportunity-score">Opportunity: <span class="highlight" style="background-color: ${qualityColor}">${score}</span></div>
<div class="search-volume">Search: ${searchVolume}</div>
<div class="result-count">Results: ${resultCount}</div>
<div class="competition">Competition: ${competitionLevel}</div>
${keyword.searchRelevance ? `<div class="search-relevance">SR: ${keyword.searchRelevance}</div>` : ''}
${keyword.difficultyRating ? `<div class="difficulty-rating">DR: ${keyword.difficultyRating}</div>` : ''}
</div>
`;
@@ -223,6 +255,22 @@ document.addEventListener('DOMContentLoaded', () => {
});
};
// Get color based on quality score
const getQualityColor = (score) => {
// Green gradient for good keywords (score > 70)
if (score > 85) return '#00a651'; // Bright green
if (score > 70) return '#4caf50'; // Green
// Yellow/Orange for medium quality keywords (score 50-70)
if (score > 60) return '#8bc34a'; // Light green
if (score > 50) return '#ffeb3b'; // Yellow
// Red gradient for poor keywords (score < 50)
if (score > 40) return '#ffc107'; // Amber
if (score > 30) return '#ff9800'; // Orange
return '#f44336'; // Red
};
// Select a keyword
const selectKeyword = (keyword, card) => {
// Remove selected class from all cards