klio 1.4.3 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/package.json +1 -1
- package/src/astrology/astrologyService.js +13 -13
- package/src/cli/cli.js +115 -0
- package/src/wikidata/wikidataService.js +228 -0
package/README.md
CHANGED
|
@@ -129,6 +129,15 @@ Then, instead using `--i` for the commands from above you can use `--wp <id>` i.
|
|
|
129
129
|
- Example: `klio saturn --in 2` - Shows next 2 Saturn ingresses (note: slow planets may have limited future data)
|
|
130
130
|
- Example: `klio mercury --in --d "15.03.2026"` - Shows next Mercury ingress from a specific date
|
|
131
131
|
|
|
132
|
+
### Wikidata Integration
|
|
133
|
+
|
|
134
|
+
- **Search for people with specific aspects**: `[planet1] [aspect-type] [planet2] --wiki <occupation> [limit]` - Searches Wikidata for people with specific astrological aspects
|
|
135
|
+
- **Available occupations**: authors, scientists, artists, musicians, politicians, actors, philosophers
|
|
136
|
+
- **Aspect types**: c (conjunction), o (opposition), s (square), t (trine), se (sextile), q (quincunx)
|
|
137
|
+
- **Example**: `klio moon c neptune --wiki authors 50` - Tries to find authors with Moon conjunct Neptune aspect with a limit of 50. Is faster but less common aspects are maybe not found with 50
|
|
138
|
+
- **Example**: `klio saturn s pluto --wiki scientists 100` - Finds scientists with Saturn square Pluto aspect
|
|
139
|
+
- **Example**: `klio sun t moon --wiki artists 200` - Finds artists with Sun trine Moon aspect
|
|
140
|
+
|
|
132
141
|
### AI Integration
|
|
133
142
|
|
|
134
143
|
- **AI model selection**: `--ai <model>` - Sets a specific AI model (e.g., "google/gemma-3n-e4b") for LM Studio ([lmstudio.ai](https://lmstudio.ai))
|
package/package.json
CHANGED
|
@@ -2544,19 +2544,16 @@ function calculateNextPlanetIngress(planetName, startDate = null, count = 1) {
|
|
|
2544
2544
|
isMediumSlowPlanet ? 6 :
|
|
2545
2545
|
1; // Daily for very slow, 6-hourly for medium, hourly for fast
|
|
2546
2546
|
|
|
2547
|
-
// Maximum search window (in days)
|
|
2548
|
-
//
|
|
2549
|
-
//
|
|
2550
|
-
|
|
2551
|
-
// Uranus: ~7 years per sign, so we need ~15-20 years
|
|
2552
|
-
// Neptune: ~13.75 years per sign, so we need ~25-30 years
|
|
2553
|
-
// Pluto: ~20.7 years per sign, so we need ~35-40 years
|
|
2554
|
-
// For fast planets (Moon, Mercury, Venus, Mars, Sun), we need a reasonable window per ingress
|
|
2555
|
-
const fastPlanets = ['moon', 'mercury', 'venus', 'mars', 'sun'];
|
|
2547
|
+
// Maximum search window (in days) - calculate based on planet and requested count
|
|
2548
|
+
// Mars: ~2 months per sign, so for multiple ingresses we need ~count * 60 days
|
|
2549
|
+
// Other fast planets (Moon, Mercury, Venus, Sun): 90 days is sufficient
|
|
2550
|
+
const fastPlanets = ['moon', 'mercury', 'venus', 'sun'];
|
|
2556
2551
|
const isFastPlanet = fastPlanets.includes(planetName);
|
|
2552
|
+
const isMars = planetName === 'mars';
|
|
2557
2553
|
|
|
2558
2554
|
const maxDays = isVerySlowPlanet ? 365 * 90 :
|
|
2559
2555
|
isMediumSlowPlanet ? 365 * 90 :
|
|
2556
|
+
isMars ? Math.min(365 * 5, 90 * count) : // Mars: up to 5 years or count * 3 months
|
|
2560
2557
|
isFastPlanet ? 90 : // 30 days for fast planets (enough for multiple moon ingresses)
|
|
2561
2558
|
365 * 90; // Default for other planets
|
|
2562
2559
|
const maxAttempts = maxDays * 24 / timeStepHours;
|
|
@@ -2599,15 +2596,18 @@ function calculateNextPlanetIngress(planetName, startDate = null, count = 1) {
|
|
|
2599
2596
|
currentDate.setHours(currentDate.getHours() + 6); // Start 6 hours after ingress
|
|
2600
2597
|
}
|
|
2601
2598
|
} else {
|
|
2602
|
-
const daysLimit = isVerySlowPlanet ? 365 *
|
|
2603
|
-
isMediumSlowPlanet ? 365 *
|
|
2604
|
-
|
|
2605
|
-
|
|
2599
|
+
const daysLimit = isVerySlowPlanet ? 365 * 90 :
|
|
2600
|
+
isMediumSlowPlanet ? 365 * 90 :
|
|
2601
|
+
isMars ? Math.min(365 * 5, 90 * count) :
|
|
2602
|
+
isFastPlanet ? 90 :
|
|
2603
|
+
365 * 90;
|
|
2606
2604
|
console.log(`Could not find ${planetName} ingress to ${signs[targetSignIndex]} within ${daysLimit} days`);
|
|
2607
2605
|
if (isVerySlowPlanet) {
|
|
2608
2606
|
console.log(`💡 Note: For very slow planets like ${planetName}, the ephemeris data may not extend far enough into the future.`);
|
|
2609
2607
|
} else if (isMediumSlowPlanet) {
|
|
2610
2608
|
console.log(`💡 Note: For medium-slow planets like ${planetName}, the ephemeris data may not extend far enough into the future.`);
|
|
2609
|
+
} else if (isMars) {
|
|
2610
|
+
console.log(`💡 Note: For Mars, the requested number of ingresses may be too high for the current search window.`);
|
|
2611
2611
|
} else {
|
|
2612
2612
|
console.log(`💡 Note: For ${planetName}, the requested number of ingresses may be too high.`);
|
|
2613
2613
|
}
|
package/src/cli/cli.js
CHANGED
|
@@ -11,6 +11,15 @@ const swisseph = require('swisseph');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
|
|
14
|
+
// Wikidata service import
|
|
15
|
+
let wikidataService = null;
|
|
16
|
+
try {
|
|
17
|
+
wikidataService = require('../wikidata/wikidataService');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
// Wikidata service not available
|
|
20
|
+
console.debug('Wikidata service not available');
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
// GUI Server import
|
|
15
24
|
let guiServer = null;
|
|
16
25
|
try {
|
|
@@ -255,6 +264,7 @@ program
|
|
|
255
264
|
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
256
265
|
.option('--csv <filepath>', 'Analyzes a CSV file with ISO-Datetime values or Unix timestamps')
|
|
257
266
|
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
267
|
+
.option('--wiki <occupation>', 'Fetches people from Wikidata by occupation and checks for specific aspects (Format: planet1 aspectType planet2 --wiki <occupation> [limit])')
|
|
258
268
|
.option('--gui', 'Launches the web interface for command history (port 37421)')
|
|
259
269
|
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
260
270
|
.description('Shows astrological data for a planet')
|
|
@@ -1112,6 +1122,111 @@ program
|
|
|
1112
1122
|
return;
|
|
1113
1123
|
}
|
|
1114
1124
|
|
|
1125
|
+
// Handle Wikidata people search if --wiki option is specified
|
|
1126
|
+
if (options.wiki) {
|
|
1127
|
+
// For the wiki command, the format is: planet1 aspectType planet2 --wiki <occupation> [limit]
|
|
1128
|
+
// But the argument parsing is: planetArg=planet1, planet2=aspectType, and options.wiki=planet2
|
|
1129
|
+
// We need to extract the occupation and planet2 from the remaining arguments
|
|
1130
|
+
|
|
1131
|
+
if (!planetArg || !planet2 || !options.wiki) {
|
|
1132
|
+
console.error('Error: Wikidata search requires format: planet1 aspectType planet2 --wiki <occupation> [limit]');
|
|
1133
|
+
console.error('Example: klio saturn conjunction neptune --wiki authors');
|
|
1134
|
+
console.error('Available occupations:', Object.keys(wikidataService.PREDEFINED_OCCUPATIONS || {}).join(', '));
|
|
1135
|
+
process.exit(1);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const planet1 = planetArg.toLowerCase();
|
|
1139
|
+
const aspectType = planet2.toLowerCase();
|
|
1140
|
+
|
|
1141
|
+
// The occupation is in options.wiki, but we need to find the actual planet2
|
|
1142
|
+
// Let's look at the original arguments to parse this correctly
|
|
1143
|
+
const args = process.argv.slice(2); // Skip 'node' and script name
|
|
1144
|
+
|
|
1145
|
+
// Find the position of --wiki
|
|
1146
|
+
const wikiIndex = args.findIndex(arg => arg === '--wiki');
|
|
1147
|
+
if (wikiIndex === -1 || wikiIndex + 1 >= args.length) {
|
|
1148
|
+
console.error('Error: Missing occupation after --wiki flag');
|
|
1149
|
+
process.exit(1);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const occupation = args[wikiIndex + 1].toLowerCase();
|
|
1153
|
+
|
|
1154
|
+
// The planet2 should be the argument before --wiki
|
|
1155
|
+
const planet2Name = args[wikiIndex - 1].toLowerCase();
|
|
1156
|
+
|
|
1157
|
+
// Validate planets
|
|
1158
|
+
if (planets[planet1] === undefined) {
|
|
1159
|
+
console.error(`Error: Invalid planet '${planet1}'. Available planets: ${Object.keys(planets).join(', ')}`);
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if (planets[planet2Name] === undefined) {
|
|
1164
|
+
console.error(`Error: Invalid planet '${planet2Name}'. Available planets: ${Object.keys(planets).join(', ')}`);
|
|
1165
|
+
process.exit(1);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Validate aspect type (including shorthands)
|
|
1169
|
+
const validAspects = ['conjunction', 'opposition', 'square', 'trine', 'sextile', 'quincunx', 'c', 'con', 'opp', 'sq', 'tri', 'sex', 'qui'];
|
|
1170
|
+
if (!validAspects.includes(aspectType)) {
|
|
1171
|
+
console.error(`Error: Invalid aspect type '${aspectType}'. Available aspects: ${['conjunction', 'opposition', 'square', 'trine', 'sextile', 'quincunx'].join(', ')}`);
|
|
1172
|
+
console.error('Shorthands: c/con, opp, sq, tri, sex, qui');
|
|
1173
|
+
process.exit(1);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Parse limit if provided (look for arguments after occupation)
|
|
1177
|
+
let limit = 500;
|
|
1178
|
+
if (wikiIndex + 2 < args.length) {
|
|
1179
|
+
const limitArg = args[wikiIndex + 2];
|
|
1180
|
+
if (!isNaN(limitArg) && parseInt(limitArg) > 0) {
|
|
1181
|
+
limit = parseInt(limitArg);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
console.log(`🔍 Searching Wikidata for ${occupation} with ${planet1} ${aspectType} ${planet2Name} aspect...`);
|
|
1186
|
+
console.log(`📊 Limit: ${limit} people`);
|
|
1187
|
+
|
|
1188
|
+
if (!wikidataService) {
|
|
1189
|
+
console.error('❌ Wikidata service is not available.');
|
|
1190
|
+
process.exit(1);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
try {
|
|
1194
|
+
const results = await wikidataService.findPeopleWithAspect(occupation, planet1, aspectType, planet2Name, limit);
|
|
1195
|
+
|
|
1196
|
+
if (results.length === 0) {
|
|
1197
|
+
console.log('No results found.');
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// Display results in a table
|
|
1202
|
+
console.log('\n📋 Results (sorted by orb, closest aspects first):');
|
|
1203
|
+
console.log('================================================================================');
|
|
1204
|
+
console.log('| # | Name | Birth Date | Aspect Type | Orb | Wikidata Link |');
|
|
1205
|
+
console.log('================================================================================');
|
|
1206
|
+
|
|
1207
|
+
results.forEach((result, index) => {
|
|
1208
|
+
const name = result.name.padEnd(28, ' ').substring(0, 28);
|
|
1209
|
+
const birthDate = result.birthDate ? result.birthDate.substring(0, 10) : 'Unknown';
|
|
1210
|
+
const aspectTypeDisplay = result.aspect.type.padEnd(11, ' ');
|
|
1211
|
+
const orb = result.aspect.orb.padEnd(4, ' ');
|
|
1212
|
+
const linkUrl = result.linkUrl || 'N/A';
|
|
1213
|
+
|
|
1214
|
+
console.log(`| ${(index + 1).toString().padEnd(2)} | ${name} | ${birthDate} | ${aspectTypeDisplay} | ${orb}° | ${linkUrl} |`);
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
console.log('================================================================================');
|
|
1218
|
+
console.log(`\n📊 Found ${results.length} ${occupation} with ${planet1} ${aspectType} ${planet2Name} aspect.`);
|
|
1219
|
+
console.log('🔗 Wikipedia search links are provided for each person.');
|
|
1220
|
+
console.log('🎯 Results are sorted by orb (closest aspects first).');
|
|
1221
|
+
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
console.error('❌ Error searching Wikidata:', error.message);
|
|
1224
|
+
process.exit(1);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1115
1230
|
// For other options, a planet is required (except for --a or --s without planet)
|
|
1116
1231
|
if (!planetArg && !options.a && !options.s) {
|
|
1117
1232
|
console.error('Error: Planet is required for this operation.');
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Wikidata Service for fetching author data and checking astrological aspects
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { calculatePlanetComboAspects } = require('../astrology/astrologyService');
|
|
4
|
+
|
|
5
|
+
// Wikidata SPARQL endpoint
|
|
6
|
+
const WIKIDATA_SPARQL_ENDPOINT = 'https://query.wikidata.org/sparql';
|
|
7
|
+
|
|
8
|
+
// Predefined occupations with their Wikidata Q IDs
|
|
9
|
+
const PREDEFINED_OCCUPATIONS = {
|
|
10
|
+
'authors': 'wd:Q36180', // Writer
|
|
11
|
+
'scientists': 'wd:Q901', // Scientist
|
|
12
|
+
'artists': 'wd:Q1028181', // Visual artist
|
|
13
|
+
'musicians': 'wd:Q639669', // Musician
|
|
14
|
+
'politicians': 'wd:Q82955', // Politician
|
|
15
|
+
'actors': 'wd:Q33999', // Actor
|
|
16
|
+
'philosophers': 'wd:Q4964182' // Philosopher
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Function to fetch people by occupation from Wikidata
|
|
20
|
+
async function fetchPeopleByOccupation(occupation, limit = 500) {
|
|
21
|
+
try {
|
|
22
|
+
// Get the Wikidata Q ID for the occupation
|
|
23
|
+
const occupationQid = PREDEFINED_OCCUPATIONS[occupation.toLowerCase()];
|
|
24
|
+
|
|
25
|
+
if (!occupationQid) {
|
|
26
|
+
console.error(`Error: Unknown occupation '${occupation}'. Available occupations: ${Object.keys(PREDEFINED_OCCUPATIONS).join(', ')}`);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const query = `
|
|
31
|
+
SELECT DISTINCT ?person ?personLabel ?birthDate ?wikipediaLink WHERE {
|
|
32
|
+
?person wdt:P31 wd:Q5; # Instance of human
|
|
33
|
+
wdt:P106 ${occupationQid}. # Specific occupation
|
|
34
|
+
|
|
35
|
+
# Make birth date optional
|
|
36
|
+
OPTIONAL { ?person wdt:P569 ?birthDate. }
|
|
37
|
+
|
|
38
|
+
# Get English Wikipedia article link
|
|
39
|
+
OPTIONAL { ?wikipediaArticle schema:about ?person ;
|
|
40
|
+
schema:isPartOf <https://en.wikipedia.org/> ;
|
|
41
|
+
schema:url ?wikipediaLink . }
|
|
42
|
+
|
|
43
|
+
# Get label
|
|
44
|
+
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
|
|
45
|
+
}
|
|
46
|
+
LIMIT ${limit}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const response = await axios.get(WIKIDATA_SPARQL_ENDPOINT, {
|
|
50
|
+
params: {
|
|
51
|
+
query: query,
|
|
52
|
+
format: 'json'
|
|
53
|
+
},
|
|
54
|
+
headers: {
|
|
55
|
+
'Accept': 'application/sparql-results+json'
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (response.data && response.data.results && response.data.results.bindings) {
|
|
60
|
+
return response.data.results.bindings.map(binding => ({
|
|
61
|
+
name: binding.personLabel?.value || 'Unknown',
|
|
62
|
+
birthDate: binding.birthDate?.value || null,
|
|
63
|
+
wikidataId: binding.person.value.split('/').pop()
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return [];
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Error fetching people from Wikidata:', error.message);
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Backward compatibility function
|
|
75
|
+
async function fetchAuthorsFromWikidata(limit = 500) {
|
|
76
|
+
return fetchPeopleByOccupation('authors', limit);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Function to parse birth date and extract astrological data
|
|
80
|
+
function parseBirthDate(birthDateString) {
|
|
81
|
+
if (!birthDateString) return null;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Parse ISO date string (format: +YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ssZ or +YYYY-MM-DD or YYYY-MM-DD or +YYYY-MM or YYYY-MM or +YYYY or YYYY)
|
|
85
|
+
const dateMatch = birthDateString.match(/^\+?(\d{4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?/);
|
|
86
|
+
if (!dateMatch) return null;
|
|
87
|
+
|
|
88
|
+
const year = parseInt(dateMatch[1]);
|
|
89
|
+
const month = dateMatch[2] ? parseInt(dateMatch[2]) : 1; // Default to January if month not specified
|
|
90
|
+
const day = dateMatch[3] ? parseInt(dateMatch[3]) : 1; // Default to 1st if day not specified
|
|
91
|
+
|
|
92
|
+
// Handle invalid month/day values (like 00 for century-level dates)
|
|
93
|
+
const validMonth = month === 0 ? 1 : month;
|
|
94
|
+
const validDay = day === 0 ? 1 : day;
|
|
95
|
+
|
|
96
|
+
// Default time to noon if not specified
|
|
97
|
+
return {
|
|
98
|
+
year: year,
|
|
99
|
+
month: validMonth,
|
|
100
|
+
day: validDay,
|
|
101
|
+
hour: 12,
|
|
102
|
+
minute: 0
|
|
103
|
+
};
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('Error parsing birth date:', error.message);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Aspect type mapping for shorthand support
|
|
111
|
+
const ASPECT_SHORTHAND = {
|
|
112
|
+
'c': 'conjunction',
|
|
113
|
+
'o': 'opposition',
|
|
114
|
+
's': 'square',
|
|
115
|
+
't': 'trine',
|
|
116
|
+
'se': 'sextile',
|
|
117
|
+
'q': 'quincunx'
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Function to create a Wikipedia search URL from a person's name
|
|
121
|
+
function createWikipediaSearchUrl(personName) {
|
|
122
|
+
// Create a Wikipedia search URL using the person's name
|
|
123
|
+
// This will take users to a search page where they can find the correct article
|
|
124
|
+
const encodedName = encodeURIComponent(personName);
|
|
125
|
+
return `https://en.wikipedia.org/wiki/Special:Search?search=${encodedName}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Function to normalize aspect type (support shorthands)
|
|
129
|
+
function normalizeAspectType(aspectType) {
|
|
130
|
+
const normalized = aspectType.toLowerCase();
|
|
131
|
+
return ASPECT_SHORTHAND[normalized] || normalized;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Function to check if an author has a specific aspect in their natal chart
|
|
135
|
+
function checkAuthorAspect(authorData, planet1, aspectType, planet2) {
|
|
136
|
+
if (!authorData.birthDateData) return null;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Normalize aspect type to handle shorthands
|
|
140
|
+
const normalizedAspectType = normalizeAspectType(aspectType);
|
|
141
|
+
|
|
142
|
+
// Calculate aspects for this author's birth chart
|
|
143
|
+
const aspects = calculatePlanetComboAspects([planet1, planet2], authorData.birthDateData, true);
|
|
144
|
+
|
|
145
|
+
// Look for the specific aspect type
|
|
146
|
+
const matchingAspect = aspects.find(aspect =>
|
|
147
|
+
((aspect.planet1 === planet1 && aspect.planet2 === planet2) ||
|
|
148
|
+
(aspect.planet1 === planet2 && aspect.planet2 === planet1)) &&
|
|
149
|
+
aspect.type.toLowerCase() === normalizedAspectType.toLowerCase()
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return matchingAspect || null;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error('Error checking aspect for author:', authorData.name, error.message);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Main function to find people with specific aspects by occupation
|
|
160
|
+
async function findPeopleWithAspect(occupation, planet1, aspectType, planet2, limit = 500) {
|
|
161
|
+
// Fetch people by occupation from Wikidata
|
|
162
|
+
const people = await fetchPeopleByOccupation(occupation, limit);
|
|
163
|
+
|
|
164
|
+
if (people.length === 0) {
|
|
165
|
+
console.log(`No ${occupation} found in Wikidata.`);
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log(`Fetched ${people.length} ${occupation} from Wikidata. Checking aspects...`);
|
|
170
|
+
|
|
171
|
+
// Process each person to check for the specified aspect
|
|
172
|
+
const results = [];
|
|
173
|
+
let processedCount = 0;
|
|
174
|
+
|
|
175
|
+
for (const person of people) {
|
|
176
|
+
// Parse birth date
|
|
177
|
+
const birthDateData = parseBirthDate(person.birthDate);
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
// Add birth date data to person object
|
|
181
|
+
person.birthDateData = birthDateData;
|
|
182
|
+
|
|
183
|
+
// Check for the specific aspect
|
|
184
|
+
const aspect = checkAuthorAspect(person, planet1, aspectType, planet2);
|
|
185
|
+
|
|
186
|
+
if (aspect) {
|
|
187
|
+
// Create Wikipedia search URL using the person's name
|
|
188
|
+
const linkUrl = createWikipediaSearchUrl(person.name);
|
|
189
|
+
|
|
190
|
+
results.push({
|
|
191
|
+
name: person.name,
|
|
192
|
+
birthDate: person.birthDate,
|
|
193
|
+
linkUrl: linkUrl,
|
|
194
|
+
aspect: aspect,
|
|
195
|
+
birthDateData: person.birthDateData
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
processedCount++;
|
|
200
|
+
if (processedCount % 50 === 0) {
|
|
201
|
+
console.log(`Processed ${processedCount}/${people.length} ${occupation}...`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`Found ${results.length} ${occupation} with ${planet1} ${aspectType} ${planet2} aspect.`);
|
|
206
|
+
|
|
207
|
+
// Sort by orb (closest aspects first)
|
|
208
|
+
results.sort((a, b) => parseFloat(a.aspect.orb) - parseFloat(b.aspect.orb));
|
|
209
|
+
|
|
210
|
+
return results;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Backward compatibility function
|
|
214
|
+
async function findAuthorsWithAspect(planet1, aspectType, planet2, limit = 500) {
|
|
215
|
+
return findPeopleWithAspect('authors', planet1, aspectType, planet2, limit);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = {
|
|
219
|
+
fetchAuthorsFromWikidata,
|
|
220
|
+
findAuthorsWithAspect,
|
|
221
|
+
fetchPeopleByOccupation,
|
|
222
|
+
findPeopleWithAspect,
|
|
223
|
+
parseBirthDate,
|
|
224
|
+
checkAuthorAspect,
|
|
225
|
+
normalizeAspectType,
|
|
226
|
+
ASPECT_SHORTHAND,
|
|
227
|
+
PREDEFINED_OCCUPATIONS
|
|
228
|
+
};
|