klio 1.4.6 → 1.4.8
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 +14 -2
- package/package.json +1 -1
- package/src/astrology/astrologyService.js +87 -1
- package/src/cli/cli.js +116 -9
- package/src/health/healthAnalysis.js +216 -1
package/README.md
CHANGED
|
@@ -86,9 +86,11 @@ klio --gui --gui-port 8080
|
|
|
86
86
|
### Health Analysis
|
|
87
87
|
It's possible to analyze Apple Health data. This happens on the device and no server is involved like everything from this CLI. It just parses the export XML.
|
|
88
88
|
|
|
89
|
-
- **
|
|
89
|
+
- **Sleep analysis**: `klio [planet] --apple <filepath> --sleep ` - Analyzes Apple Health Export XML file and shows the most frequent aspects during sleep deprivation (Top 10):
|
|
90
90
|
- **Step analysis**: `klio [planet] --apple <filepath> --steps` - Analyzes step patterns of any planet
|
|
91
91
|
- **Stress analysis**: `klio [planet] --apple <filepath> --stress` - Analyzes stress patterns of planet sign stress based on HRV
|
|
92
|
+
- **Late night analysis**: `klio [planet] --apple <filepath> --night` - Analyzes late night sleep patterns (after 2am) and shows the most frequent aspects during these times
|
|
93
|
+
- **All-nighter analysis**: `klio [planet] --apple <filepath> --night-all` - Analyzes all-nighter sleep patterns (starting at 04:00 or 06:00) and shows the most frequent aspects during these times
|
|
92
94
|
|
|
93
95
|
### CSV Analysis
|
|
94
96
|
It's possible to analyze a csv with a column of either ISO date time or unix time stamp.
|
|
@@ -116,12 +118,22 @@ Then, instead using `--i` for the commands from above you can use `--wp <id>` i.
|
|
|
116
118
|
- **Element distribution**: `klio --el` - Shows element distribution of planets in a horizontal chart. combine with `--wp <id>` or `--i`
|
|
117
119
|
- **Aspect figures**: `klio --af` - Shows active aspect figures like T-squares, Grand Trines, etc. Combine with `--wp <id>` or `--i` or `--d <"DD.MM.YYYY HH:MM>` or `--hs <house-system>`
|
|
118
120
|
- **Transits**: `klio --tr` - Shows personal transits based on birth data. Combine with `--wp <id or --i>` and optional `--d <"DD.MM.YYYY HH:MM>`
|
|
121
|
+
- **Transit Houses**: `klio --s --tra --i`
|
|
122
|
+
|
|
123
|
+
|
|
119
124
|
|
|
120
125
|
### Past and Future Aspects
|
|
121
126
|
|
|
122
127
|
- **Past aspects**: `klio --v <count> [planet1] [aspect-type] [planet2]` - Shows past aspects (Format: --v <count> planet1 aspectType planet2) Available aspect types: c, o, s, t, se (conjunction, opposition, square, trine, sextile)
|
|
123
128
|
- **Future aspects**: `klio --z <count> [planet1] [aspect-type] [planet2]` - Shows future aspects (Format: --z <count> planet1 aspectType planet2)
|
|
124
129
|
|
|
130
|
+
### Reference
|
|
131
|
+
- **Transit frequency**: `[planet1] [aspect-type] [planet2] --o` - Calculates how often a transit occurs in the sky using astronomical synodic periods
|
|
132
|
+
- **Example**: `klio saturn c neptune --o` - Shows Saturn-Neptune conjunction frequency
|
|
133
|
+
- **Example**: `klio jupiter s pluto --o` - Shows Jupiter-Pluto square frequency
|
|
134
|
+
- **Aspect types**: c (conjunction), o (opposition), s (square), t (trine), se (sextile), q (quincunx)
|
|
135
|
+
- **Output**: Shows frequency in readable format (e.g., "every 36 years") with approximate
|
|
136
|
+
|
|
125
137
|
### Planet Ingresses
|
|
126
138
|
|
|
127
139
|
- **Planet ingress**: `klio [planet] --in [count]` - Shows when a planet enters a new zodiac sign. Optional count parameter for multiple ingresses (default: 1). Works with any planet (moon, mercury, venus, etc.)
|
|
@@ -132,7 +144,7 @@ Then, instead using `--i` for the commands from above you can use `--wp <id>` i.
|
|
|
132
144
|
### Wikidata Integration
|
|
133
145
|
|
|
134
146
|
- **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
|
|
147
|
+
- **Available occupations for now**: authors, scientists, artists, musicians, politicians, actors, philosophers
|
|
136
148
|
- **Aspect types**: c (conjunction), o (opposition), s (square), t (trine), se (sextile), q (quincunx)
|
|
137
149
|
- **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
150
|
- **Example**: `klio saturn s pluto --wiki scientists 100` - Finds scientists with Saturn square Pluto aspect
|
package/package.json
CHANGED
|
@@ -2780,5 +2780,91 @@ module.exports = {
|
|
|
2780
2780
|
showPlanetComboAspects,
|
|
2781
2781
|
calculateNextPlanetIngress,
|
|
2782
2782
|
calculateAstrologicalAngles,
|
|
2783
|
-
longitudeToSignDegree
|
|
2783
|
+
longitudeToSignDegree,
|
|
2784
|
+
calculateTransitFrequency
|
|
2784
2785
|
};
|
|
2786
|
+
|
|
2787
|
+
// Function to calculate how often a transit occurs between two planets
|
|
2788
|
+
function calculateTransitFrequency(planet1, planet2, aspectType) {
|
|
2789
|
+
// Use astronomical synodic period calculation for more accurate results
|
|
2790
|
+
const orbitalPeriods = {
|
|
2791
|
+
'sun': 1, // Not applicable for conjunctions, but included for completeness
|
|
2792
|
+
'moon': 0.0748, // 27.3 days
|
|
2793
|
+
'mercury': 0.2408, // 87.97 days
|
|
2794
|
+
'venus': 0.6152, // 224.7 days
|
|
2795
|
+
'mars': 1.8808, // 686.98 days
|
|
2796
|
+
'jupiter': 11.862, // 11.862 years
|
|
2797
|
+
'saturn': 29.457, // 29.457 years
|
|
2798
|
+
'uranus': 84.016, // 84.016 years
|
|
2799
|
+
'neptune': 164.79, // 164.79 years
|
|
2800
|
+
'pluto': 248.09 // 248.09 years
|
|
2801
|
+
};
|
|
2802
|
+
|
|
2803
|
+
// Get orbital periods for the two planets
|
|
2804
|
+
const period1 = orbitalPeriods[planet1];
|
|
2805
|
+
const period2 = orbitalPeriods[planet2];
|
|
2806
|
+
|
|
2807
|
+
if (!period1 || !period2) {
|
|
2808
|
+
return {
|
|
2809
|
+
frequency: 'Cannot calculate frequency: unknown orbital periods',
|
|
2810
|
+
averageInterval: null,
|
|
2811
|
+
aspects: []
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
// Calculate synodic period using the formula: 1/S = |1/P1 - 1/P2|
|
|
2816
|
+
// For conjunctions, we use the absolute difference
|
|
2817
|
+
const synodicPeriod = 1 / Math.abs(1/period1 - 1/period2);
|
|
2818
|
+
|
|
2819
|
+
// Convert to days
|
|
2820
|
+
const averageIntervalDays = synodicPeriod * 365.25;
|
|
2821
|
+
|
|
2822
|
+
// Format the frequency in a more readable way
|
|
2823
|
+
const formattedFrequency = formatFrequency(averageIntervalDays);
|
|
2824
|
+
|
|
2825
|
+
// Also calculate some actual aspects for reference
|
|
2826
|
+
const futureAspects = getFutureAspects(planet1, planet2, aspectType, 5);
|
|
2827
|
+
|
|
2828
|
+
return {
|
|
2829
|
+
frequency: formattedFrequency,
|
|
2830
|
+
frequencyShort: `Approximately every ${synodicPeriod.toFixed(1)} years`,
|
|
2831
|
+
averageInterval: averageIntervalDays,
|
|
2832
|
+
averageIntervalYears: synodicPeriod,
|
|
2833
|
+
intervals: [], // Not applicable for this calculation method
|
|
2834
|
+
aspects: futureAspects,
|
|
2835
|
+
note: `Note: Frequency calculated using astronomical synodic period (1/S = |1/${period1} - 1/${period2}| years)`
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
// Helper function to format frequency in a readable way
|
|
2840
|
+
function formatFrequency(days) {
|
|
2841
|
+
if (days < 30) {
|
|
2842
|
+
return `Approximately every ${Math.round(days)} days`;
|
|
2843
|
+
} else if (days < 90) {
|
|
2844
|
+
const months = days / 30;
|
|
2845
|
+
return `Approximately every ${Math.round(months)} months`;
|
|
2846
|
+
} else if (days < 365) {
|
|
2847
|
+
const months = days / 30;
|
|
2848
|
+
return `Approximately every ${Math.round(months)} months`;
|
|
2849
|
+
} else if (days < 730) {
|
|
2850
|
+
const years = days / 365.25;
|
|
2851
|
+
const months = (years - Math.floor(years)) * 12;
|
|
2852
|
+
if (months >= 9) {
|
|
2853
|
+
return `Approximately every ${Math.floor(years) + 1} years`;
|
|
2854
|
+
} else if (months >= 1) {
|
|
2855
|
+
return `Approximately every ${Math.floor(years)} years, ${Math.round(months)} months`;
|
|
2856
|
+
} else {
|
|
2857
|
+
return `Approximately every ${Math.floor(years)} years`;
|
|
2858
|
+
}
|
|
2859
|
+
} else {
|
|
2860
|
+
const years = days / 365.25;
|
|
2861
|
+
const months = (years - Math.floor(years)) * 12;
|
|
2862
|
+
if (months >= 9) {
|
|
2863
|
+
return `Approximately every ${Math.floor(years) + 1} years`;
|
|
2864
|
+
} else if (months >= 1) {
|
|
2865
|
+
return `Approximately every ${Math.floor(years)} years, ${Math.round(months)} months`;
|
|
2866
|
+
} else {
|
|
2867
|
+
return `Approximately every ${Math.round(years)} years`;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
}
|
package/src/cli/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { Command } = require('commander');
|
|
2
2
|
const { planets, signs } = require('../astrology/astrologyConstants');
|
|
3
3
|
const { showRetrogradePlanets } = require('../astrology/retrogradeService');
|
|
4
|
-
const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, getPersonDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, findExactAspectTime, findLastExactAspectTime, getAspectAngle, getFutureAspects, getPastAspects, analyzeCSVWithDatetime, analyzeHouseDistributionSignificance, analyzeAspectDistributionSignificance, analyzeSignDistributionSignificance, calculateAspectStatistics, calculatePlanetComboAspects, showPlanetComboAspects, getCriticalPlanets, getHouseSystemCode, calculateNextPlanetIngress, calculateAstrologicalAngles, longitudeToSignDegree } = require('../astrology/astrologyService');
|
|
4
|
+
const { getCurrentTimeInTimezone, showAspectFigures, analyzeElementDistribution, getTimezoneOffset, calculateJulianDayUTC, calculateHouses, getAstrologicalData, getPlanetHouse, showPlanetAspects, calculatePlanetAspects, getAllActiveAspects, showAllActiveAspects, getBirthDataFromConfig, getPersonDataFromConfig, detectAspectFigures, calculatePersonalTransits, showPersonalTransitAspects, showCombinedAnalysis, calculatePersonalTransitAspects, determineAspectPhase, findExactAspectTime, findLastExactAspectTime, getAspectAngle, getFutureAspects, getPastAspects, analyzeCSVWithDatetime, analyzeHouseDistributionSignificance, analyzeAspectDistributionSignificance, analyzeSignDistributionSignificance, calculateAspectStatistics, calculatePlanetComboAspects, showPlanetComboAspects, getCriticalPlanets, getHouseSystemCode, calculateNextPlanetIngress, calculateAstrologicalAngles, longitudeToSignDegree, calculateTransitFrequency } = require('../astrology/astrologyService');
|
|
5
5
|
const { performSetup, showConfigStatus, loadConfig, setAIModel, askAIModel, setPerson1, setPerson2, setPerson, getPersonData, listPeople, deletePerson } = require('../config/configService');
|
|
6
6
|
const { parseAppleHealthXML } = require('../health/healthService');
|
|
7
|
-
const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep } = require('../health/healthAnalysis');
|
|
7
|
+
const { analyzeStepsByPlanetSign, analyzeStressByPlanetAspects, analyzePlanetAspectsForSleep, analyzeLateNightAspects, analyzeAllNighterAspects } = require('../health/healthAnalysis');
|
|
8
8
|
const { analyzeHouseDistribution, filterFilesByHouse } = require('../health/fileAnalysis');
|
|
9
9
|
const { getFileCreationDate, parseDateToComponents } = require('../utils/fileUtils');
|
|
10
10
|
const swisseph = require('swisseph');
|
|
@@ -232,6 +232,8 @@ program
|
|
|
232
232
|
.option('--sleep', 'Analyzes sleep patterns with moon aspects')
|
|
233
233
|
.option('--steps', 'Analyzes step patterns with moon signs')
|
|
234
234
|
.option('--stress', 'Analyzes stress patterns with moon aspects based on HRV')
|
|
235
|
+
.option('--night', 'Analyzes late night sleep patterns (after 2am) and common aspects')
|
|
236
|
+
.option('--night-all', 'Analyzes all-nighter sleep patterns (04:00 and 06:00 starts)')
|
|
235
237
|
.option('--setup', 'Configures location and birth data for personalized calculations')
|
|
236
238
|
.option('--ai <model>', 'Sets a specific AI model (e.g. "google/gemma-3n-e4b")')
|
|
237
239
|
.option('--system <prompt>', 'Sets a custom system prompt for all AI requests')
|
|
@@ -257,7 +259,7 @@ program
|
|
|
257
259
|
.option('--p <prompt>', 'Asks a question to the AI model (e.g. "Which careers suit this position?")')
|
|
258
260
|
.option('--el', 'Shows the element distribution of planets in a horizontal chart')
|
|
259
261
|
.option('--af', 'Shows active aspect figures like T-squares, Grand Trines, etc.')
|
|
260
|
-
.option('--
|
|
262
|
+
.option('--tra', 'Shows personal transits based on birth data')
|
|
261
263
|
.option('--transit-aspekte', 'Shows personal transit aspects (Transit → Radix)')
|
|
262
264
|
.option('--tr', 'Shows personal transit aspects (Transit → Radix) (short form)')
|
|
263
265
|
.option('--v <count>', 'Shows past aspects between two planets (Format: --v <count> planet1 aspectType planet2)')
|
|
@@ -267,6 +269,7 @@ program
|
|
|
267
269
|
.option('--wiki <occupation>', 'Fetches people from Wikidata by occupation and checks for specific aspects (Format: planet1 aspectType planet2 --wiki <occupation> [limit])')
|
|
268
270
|
.option('--gui', 'Launches the web interface for command history (port 37421)')
|
|
269
271
|
.option('--gui-port <port>', 'Specify custom port for GUI server')
|
|
272
|
+
.option('--o', 'Shows how often this transit occurs in the sky (frequency calculation)')
|
|
270
273
|
.description('Shows astrological data for a planet')
|
|
271
274
|
.action(async (planetArg, planet2Arg, options) => {
|
|
272
275
|
// If planet2Arg is an object, it contains the options (commander behavior)
|
|
@@ -516,8 +519,8 @@ program
|
|
|
516
519
|
return;
|
|
517
520
|
}
|
|
518
521
|
|
|
519
|
-
// Show personal transits if --
|
|
520
|
-
if (options.
|
|
522
|
+
// Show personal transits if --tra option is specified (no planet required)
|
|
523
|
+
if (options.tra) {
|
|
521
524
|
// Person data is required for transits
|
|
522
525
|
const personDataToUse = getPersonDataToUse(options);
|
|
523
526
|
if (!personDataToUse.data) {
|
|
@@ -604,10 +607,7 @@ program
|
|
|
604
607
|
}
|
|
605
608
|
|
|
606
609
|
console.log('================================================================================');
|
|
607
|
-
|
|
608
|
-
console.log('Example: If Jupiter is in Cancer and you have Scorpio ASC, this shows in which');
|
|
609
|
-
console.log('of your birth houses the transit Jupiter is located.');
|
|
610
|
-
|
|
610
|
+
|
|
611
611
|
// Send question to AI model if --p option is specified
|
|
612
612
|
if (options.p) {
|
|
613
613
|
// Create a data object for AI with transit information
|
|
@@ -1949,6 +1949,44 @@ program
|
|
|
1949
1949
|
};
|
|
1950
1950
|
await handleAIRequest(options, healthDataSummary);
|
|
1951
1951
|
}
|
|
1952
|
+
} else if (options.night) {
|
|
1953
|
+
console.log(`Apple Health late night sleep analysis with ${pLabel} aspects`);
|
|
1954
|
+
console.log('========================================================\n');
|
|
1955
|
+
await analyzeLateNightAspects(planet, healthData);
|
|
1956
|
+
|
|
1957
|
+
// Handle AI request for night analysis
|
|
1958
|
+
if (options.p) {
|
|
1959
|
+
const healthDataSummary = {
|
|
1960
|
+
planet: planet,
|
|
1961
|
+
sign: 'Multiple',
|
|
1962
|
+
degreeInSign: 'Multiple',
|
|
1963
|
+
dignity: `Apple Health late night sleep analysis with ${planet} aspects`,
|
|
1964
|
+
element: 'Multiple',
|
|
1965
|
+
decan: 'Multiple',
|
|
1966
|
+
healthAnalysisType: 'night',
|
|
1967
|
+
planetName: planet
|
|
1968
|
+
};
|
|
1969
|
+
await handleAIRequest(options, healthDataSummary);
|
|
1970
|
+
}
|
|
1971
|
+
} else if (options.nightAll) {
|
|
1972
|
+
console.log(`Apple Health all-nighter sleep analysis with ${pLabel} aspects`);
|
|
1973
|
+
console.log('============================================================\n');
|
|
1974
|
+
await analyzeAllNighterAspects(planet, healthData);
|
|
1975
|
+
|
|
1976
|
+
// Handle AI request for all-nighter analysis
|
|
1977
|
+
if (options.p) {
|
|
1978
|
+
const healthDataSummary = {
|
|
1979
|
+
planet: planet,
|
|
1980
|
+
sign: 'Multiple',
|
|
1981
|
+
degreeInSign: 'Multiple',
|
|
1982
|
+
dignity: `Apple Health all-nighter sleep analysis with ${planet} aspects`,
|
|
1983
|
+
element: 'Multiple',
|
|
1984
|
+
decan: 'Multiple',
|
|
1985
|
+
healthAnalysisType: 'night-all',
|
|
1986
|
+
planetName: planet
|
|
1987
|
+
};
|
|
1988
|
+
await handleAIRequest(options, healthDataSummary);
|
|
1989
|
+
}
|
|
1952
1990
|
} else if (hypothesis === 'stress-moon') {
|
|
1953
1991
|
console.log(`Apple Health stress analysis with ${pLabel} aspects`);
|
|
1954
1992
|
console.log('==========================================\n');
|
|
@@ -2397,6 +2435,75 @@ program
|
|
|
2397
2435
|
return;
|
|
2398
2436
|
}
|
|
2399
2437
|
|
|
2438
|
+
// Show transit frequency if --o option is specified
|
|
2439
|
+
if (options.o) {
|
|
2440
|
+
// We expect a specific format: planet1 aspectType planet2 --o
|
|
2441
|
+
// Example: "saturn k neptun --o" for Saturn conjunction Neptune
|
|
2442
|
+
|
|
2443
|
+
// Parse planet and aspect information from positional arguments
|
|
2444
|
+
// For the format: planet1 aspectType planet2 --o
|
|
2445
|
+
// planetArg = planet1, planet2Arg = aspectType, and we need to get planet2 from excess arguments
|
|
2446
|
+
const planet1 = planetArg ? planetArg.toLowerCase() : null;
|
|
2447
|
+
const aspectType = planet2Arg ? planet2Arg.toLowerCase() : null;
|
|
2448
|
+
|
|
2449
|
+
// Get the third argument (planet2) from excess arguments
|
|
2450
|
+
const excessArgs = program.args;
|
|
2451
|
+
const planet2 = excessArgs.length >= 3 ? excessArgs[2].toLowerCase() : null;
|
|
2452
|
+
|
|
2453
|
+
// Check if we have all required arguments
|
|
2454
|
+
if (!planet1 || !aspectType || !planet2) {
|
|
2455
|
+
console.error('Error: Transit frequency requires two planets and an aspect type.');
|
|
2456
|
+
console.error('Format: planet1 aspectType planet2 --o');
|
|
2457
|
+
console.error('Example: saturn c neptune --o');
|
|
2458
|
+
console.error('Aspect types: c=conjunction, o=opposition, s=square, t=trine, se=sextile, q=quincunx');
|
|
2459
|
+
process.exit(1);
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// Check if planets are valid
|
|
2463
|
+
if (!planets[planet1] || !planets[planet2]) {
|
|
2464
|
+
console.error('Error: Invalid planets.');
|
|
2465
|
+
console.error('Available planets:', Object.keys(planets).join(', '));
|
|
2466
|
+
process.exit(1);
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
// Check if aspect type is valid
|
|
2470
|
+
const targetAngle = getAspectAngle(aspectType);
|
|
2471
|
+
if (targetAngle === null) {
|
|
2472
|
+
console.error('Error: Invalid aspect type.');
|
|
2473
|
+
console.error('Available aspect types: c, o, s, t, se (conjunction, opposition, square, trine, sextile)');
|
|
2474
|
+
process.exit(1);
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
// Calculate transit frequency
|
|
2478
|
+
const frequencyData = calculateTransitFrequency(planet1, planet2, aspectType);
|
|
2479
|
+
|
|
2480
|
+
// Show results
|
|
2481
|
+
const aspectTypeFull = getAspectTypeFullName(aspectType);
|
|
2482
|
+
console.log(`Frequency: ${frequencyData.frequency}`);
|
|
2483
|
+
console.log(`(Average interval: ${frequencyData.averageInterval.toFixed(1)} days / ${frequencyData.averageIntervalYears.toFixed(1)} years)`);
|
|
2484
|
+
|
|
2485
|
+
// Show note if available
|
|
2486
|
+
if (frequencyData.note) {
|
|
2487
|
+
console.log(`${frequencyData.note}`);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// Handle AI request for transit frequency
|
|
2491
|
+
if (options.p) {
|
|
2492
|
+
const frequencyDataForAI = {
|
|
2493
|
+
planet: `${planet1}-${planet2}`,
|
|
2494
|
+
sign: 'Multiple',
|
|
2495
|
+
degreeInSign: 'Multiple',
|
|
2496
|
+
dignity: `Transit frequency: ${frequencyData.frequency} for ${aspectTypeFull} between ${planet1} and ${planet2}`,
|
|
2497
|
+
element: 'Multiple',
|
|
2498
|
+
decan: 'Multiple',
|
|
2499
|
+
frequencyData: frequencyData
|
|
2500
|
+
};
|
|
2501
|
+
await handleAIRequest(options, frequencyDataForAI);
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2400
2507
|
// Analyze CSV file if --csv option is provided
|
|
2401
2508
|
if (actualOptions.csv) {
|
|
2402
2509
|
const planet = planetArg ? planetArg.toLowerCase() : 'moon';
|
|
@@ -1,6 +1,219 @@
|
|
|
1
1
|
const { getAstrologicalData, calculatePlanetAspects } = require('../astrology/astrologyService');
|
|
2
2
|
const { extractStepData, extractHRVData, extractSleepData, extractSleepGoal } = require('./healthService');
|
|
3
3
|
|
|
4
|
+
// Function to analyze all-nighter sleep patterns (04:00 and 06:00 starts)
|
|
5
|
+
async function analyzeAllNighterAspects(planetName, healthData) {
|
|
6
|
+
const sleepRecords = extractSleepData(healthData);
|
|
7
|
+
|
|
8
|
+
if (sleepRecords.length === 0) {
|
|
9
|
+
console.log('No sleep data found to analyze.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Filter sleep periods that start at typical all-nighter hours (04:00 and 06:00)
|
|
14
|
+
const allNighterHours = [4, 6];
|
|
15
|
+
const allNighterSleepRecords = sleepRecords.filter(record => {
|
|
16
|
+
const startHour = record.startDate.getHours();
|
|
17
|
+
return allNighterHours.includes(startHour);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log(`All-nighter sleep periods (starting at 04:00 or 06:00): ${allNighterSleepRecords.length} of ${sleepRecords.length} total sleep periods`);
|
|
21
|
+
|
|
22
|
+
if (allNighterSleepRecords.length === 0) {
|
|
23
|
+
console.log('No all-nighter sleep periods found (starting at 04:00 or 06:00).');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Analyze planet aspects for each all-nighter sleep period
|
|
28
|
+
const aspectStats = {};
|
|
29
|
+
|
|
30
|
+
for (const night of allNighterSleepRecords) {
|
|
31
|
+
const midSleepTime = new Date(night.startDate.getTime() +
|
|
32
|
+
(night.endDate.getTime() - night.startDate.getTime()) / 2);
|
|
33
|
+
const dateComponents = {
|
|
34
|
+
year: midSleepTime.getFullYear(),
|
|
35
|
+
month: midSleepTime.getMonth() + 1,
|
|
36
|
+
day: midSleepTime.getDate(),
|
|
37
|
+
hour: midSleepTime.getHours(),
|
|
38
|
+
minute: midSleepTime.getMinutes()
|
|
39
|
+
};
|
|
40
|
+
const planetAspects = calculatePlanetAspects(planetName, dateComponents);
|
|
41
|
+
|
|
42
|
+
// Count aspects
|
|
43
|
+
for (const aspect of planetAspects) {
|
|
44
|
+
const aspectKey = `${aspect.type} with ${aspect.planet}`;
|
|
45
|
+
aspectStats[aspectKey] = (aspectStats[aspectKey] || 0) + 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Show statistics (Top 10 planet aspects)
|
|
50
|
+
console.log(`\nMost frequent ${planetName.charAt(0).toUpperCase() + planetName.slice(1)} aspects during all-nighters (Top 10):`);
|
|
51
|
+
console.log('Aspect | Frequency | Percent | Active today');
|
|
52
|
+
console.log('-------|-----------|--------|------------');
|
|
53
|
+
|
|
54
|
+
const totalAllNighters = allNighterSleepRecords.length;
|
|
55
|
+
const sortedAspects = Object.entries(aspectStats)
|
|
56
|
+
.sort((a, b) => b[1] - a[1])
|
|
57
|
+
.slice(0, 10); // Limit to Top 10
|
|
58
|
+
|
|
59
|
+
// Calculate current planet aspects for comparison
|
|
60
|
+
const currentDate = new Date();
|
|
61
|
+
const currentDateComponents = {
|
|
62
|
+
year: currentDate.getFullYear(),
|
|
63
|
+
month: currentDate.getMonth() + 1,
|
|
64
|
+
day: currentDate.getDate(),
|
|
65
|
+
hour: currentDate.getHours(),
|
|
66
|
+
minute: currentDate.getMinutes()
|
|
67
|
+
};
|
|
68
|
+
const currentPlanetAspects = calculatePlanetAspects(planetName, currentDateComponents);
|
|
69
|
+
const currentAspectSet = new Set(currentPlanetAspects.map(a => `${a.type} with ${a.planet}`));
|
|
70
|
+
|
|
71
|
+
for (const [aspect, count] of sortedAspects) {
|
|
72
|
+
const percentage = totalAllNighters > 0 ? ((count / totalAllNighters) * 100).toFixed(1) : 0;
|
|
73
|
+
const isActiveToday = currentAspectSet.has(aspect) ? '✓' : '✗';
|
|
74
|
+
console.log(`${aspect.padEnd(25)} | ${count.toString().padStart(2)} | ${percentage}% | ${isActiveToday}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (sortedAspects.length === 0) {
|
|
78
|
+
console.log(`No ${planetName.charAt(0).toUpperCase() + planetName.slice(1)} aspects found in all-nighter sleep periods.`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Show all-nighter statistics
|
|
82
|
+
console.log(`\nAll-nighter sleep statistics:`);
|
|
83
|
+
const totalAllNighterDuration = allNighterSleepRecords.reduce((sum, record) => sum + record.duration, 0);
|
|
84
|
+
const avgAllNighterDuration = totalAllNighterDuration / allNighterSleepRecords.length;
|
|
85
|
+
|
|
86
|
+
console.log(`Average all-nighter sleep duration: ${avgAllNighterDuration.toFixed(2)} hours`);
|
|
87
|
+
console.log(`Total all-nighter sleep periods: ${allNighterSleepRecords.length}`);
|
|
88
|
+
|
|
89
|
+
// Show breakdown by all-nighter start hours
|
|
90
|
+
const hourCounts = {};
|
|
91
|
+
allNighterSleepRecords.forEach(record => {
|
|
92
|
+
const hour = record.startDate.getHours();
|
|
93
|
+
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
console.log(`All-nighter start hour breakdown:`);
|
|
97
|
+
allNighterHours.forEach(hour => {
|
|
98
|
+
if (hourCounts[hour]) {
|
|
99
|
+
console.log(` ${hour}:00 - ${hourCounts[hour]} times`);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Function to analyze late night sleep patterns (after 2am) and common aspects
|
|
105
|
+
async function analyzeLateNightAspects(planetName, healthData) {
|
|
106
|
+
const sleepRecords = extractSleepData(healthData);
|
|
107
|
+
|
|
108
|
+
if (sleepRecords.length === 0) {
|
|
109
|
+
console.log('No sleep data found to analyze.');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Filter sleep periods that start after 2am (went to bed later than 2am)
|
|
114
|
+
const lateNightSleepRecords = sleepRecords.filter(record => {
|
|
115
|
+
const startHour = record.startDate.getHours();
|
|
116
|
+
return startHour >= 2; // Started after 2am
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
console.log(`Late night sleep periods (starting after 2am): ${lateNightSleepRecords.length} of ${sleepRecords.length} total sleep periods`);
|
|
120
|
+
|
|
121
|
+
if (lateNightSleepRecords.length === 0) {
|
|
122
|
+
console.log('No late night sleep periods found (starting after 2am).');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Analyze planet aspects for each late night sleep period
|
|
127
|
+
const aspectStats = {};
|
|
128
|
+
|
|
129
|
+
for (const night of lateNightSleepRecords) {
|
|
130
|
+
const midSleepTime = new Date(night.startDate.getTime() +
|
|
131
|
+
(night.endDate.getTime() - night.startDate.getTime()) / 2);
|
|
132
|
+
const dateComponents = {
|
|
133
|
+
year: midSleepTime.getFullYear(),
|
|
134
|
+
month: midSleepTime.getMonth() + 1,
|
|
135
|
+
day: midSleepTime.getDate(),
|
|
136
|
+
hour: midSleepTime.getHours(),
|
|
137
|
+
minute: midSleepTime.getMinutes()
|
|
138
|
+
};
|
|
139
|
+
const planetAspects = calculatePlanetAspects(planetName, dateComponents);
|
|
140
|
+
|
|
141
|
+
// Count aspects
|
|
142
|
+
for (const aspect of planetAspects) {
|
|
143
|
+
const aspectKey = `${aspect.type} with ${aspect.planet}`;
|
|
144
|
+
aspectStats[aspectKey] = (aspectStats[aspectKey] || 0) + 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Show statistics (Top 10 planet aspects)
|
|
149
|
+
console.log(`\nMost frequent ${planetName.charAt(0).toUpperCase() + planetName.slice(1)} aspects during late night sleep (Top 10):`);
|
|
150
|
+
console.log('Aspect | Frequency | Percent | Active today');
|
|
151
|
+
console.log('-------|-----------|--------|------------');
|
|
152
|
+
|
|
153
|
+
const totalLateNights = lateNightSleepRecords.length;
|
|
154
|
+
const sortedAspects = Object.entries(aspectStats)
|
|
155
|
+
.sort((a, b) => b[1] - a[1])
|
|
156
|
+
.slice(0, 10); // Limit to Top 10
|
|
157
|
+
|
|
158
|
+
// Calculate current planet aspects for comparison
|
|
159
|
+
const currentDate = new Date();
|
|
160
|
+
const currentDateComponents = {
|
|
161
|
+
year: currentDate.getFullYear(),
|
|
162
|
+
month: currentDate.getMonth() + 1,
|
|
163
|
+
day: currentDate.getDate(),
|
|
164
|
+
hour: currentDate.getHours(),
|
|
165
|
+
minute: currentDate.getMinutes()
|
|
166
|
+
};
|
|
167
|
+
const currentPlanetAspects = calculatePlanetAspects(planetName, currentDateComponents);
|
|
168
|
+
const currentAspectSet = new Set(currentPlanetAspects.map(a => `${a.type} with ${a.planet}`));
|
|
169
|
+
|
|
170
|
+
for (const [aspect, count] of sortedAspects) {
|
|
171
|
+
const percentage = totalLateNights > 0 ? ((count / totalLateNights) * 100).toFixed(1) : 0;
|
|
172
|
+
const isActiveToday = currentAspectSet.has(aspect) ? '✓' : '✗';
|
|
173
|
+
console.log(`${aspect.padEnd(25)} | ${count.toString().padStart(2)} | ${percentage}% | ${isActiveToday}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (sortedAspects.length === 0) {
|
|
177
|
+
console.log(`No ${planetName.charAt(0).toUpperCase() + planetName.slice(1)} aspects found in late night sleep periods.`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Show some statistics about the late night sleep patterns
|
|
181
|
+
console.log(`\nLate night sleep statistics:`);
|
|
182
|
+
const totalLateNightDuration = lateNightSleepRecords.reduce((sum, record) => sum + record.duration, 0);
|
|
183
|
+
const avgLateNightDuration = totalLateNightDuration / lateNightSleepRecords.length;
|
|
184
|
+
|
|
185
|
+
console.log(`Average late night sleep duration: ${avgLateNightDuration.toFixed(2)} hours`);
|
|
186
|
+
console.log(`Total late night sleep periods: ${lateNightSleepRecords.length}`);
|
|
187
|
+
|
|
188
|
+
// Show the most common late night sleep start times
|
|
189
|
+
const startHours = lateNightSleepRecords.map(record => record.startDate.getHours());
|
|
190
|
+
const hourCounts = {};
|
|
191
|
+
startHours.forEach(hour => {
|
|
192
|
+
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const sortedHours = Object.entries(hourCounts).sort((a, b) => b[1] - a[1]);
|
|
196
|
+
console.log(`Most common late night sleep start hours:`);
|
|
197
|
+
|
|
198
|
+
// Show top 3 most common hours
|
|
199
|
+
sortedHours.slice(0, 3).forEach(([hour, count]) => {
|
|
200
|
+
console.log(` ${hour}:00 - ${count} times`);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Check for all-nighter patterns (04:00 and 06:00)
|
|
204
|
+
const allNighterHours = [4, 6];
|
|
205
|
+
const allNighterFound = allNighterHours.some(hour => hourCounts[hour] && hourCounts[hour] > 0);
|
|
206
|
+
|
|
207
|
+
if (allNighterFound) {
|
|
208
|
+
console.log(`\nAll-nighter patterns detected:`);
|
|
209
|
+
allNighterHours.forEach(hour => {
|
|
210
|
+
if (hourCounts[hour] && hourCounts[hour] > 0) {
|
|
211
|
+
console.log(` ${hour}:00 - ${hourCounts[hour]} times (potential all-nighter)`);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
4
217
|
// Function to analyze steps by planet sign
|
|
5
218
|
async function analyzeStepsByPlanetSign(planetName, healthData, timeLimitDays = null) {
|
|
6
219
|
let stepRecords = extractStepData(healthData);
|
|
@@ -421,5 +634,7 @@ async function analyzePlanetAspectsForSleep(planetName, healthData, sleepGoal =
|
|
|
421
634
|
module.exports = {
|
|
422
635
|
analyzeStepsByPlanetSign,
|
|
423
636
|
analyzeStressByPlanetAspects,
|
|
424
|
-
analyzePlanetAspectsForSleep
|
|
637
|
+
analyzePlanetAspectsForSleep,
|
|
638
|
+
analyzeLateNightAspects,
|
|
639
|
+
analyzeAllNighterAspects
|
|
425
640
|
};
|