klio 1.5.4 → 1.5.6
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 +4 -0
- package/package.json +1 -1
- package/src/astrology/astrologyConstants.js +4 -2
- package/src/astrology/astrologyService.js +81 -13
- package/src/cli/cli.js +9 -4
package/README.md
CHANGED
|
@@ -118,8 +118,12 @@ It's possible to analyze a csv with a column of either ISO date time or unix tim
|
|
|
118
118
|
|
|
119
119
|
- **Show house and sign distribution of the datetime column**: `klio [planet] --csv <file-path>`
|
|
120
120
|
- **Show aspect type distribution between two planets:** `klio [planet1] [planet2] --csv <file-path> --a`
|
|
121
|
+
- **Specify date column name:** `klio [planet] --csv <file-path> --date-col "Buchungsdatum"` (e.g., `--date-col "Date"`)
|
|
121
122
|
- **Filter CSV data by column value:** `klio [planet] --csv <file-path> --filter "column:value"` (e.g., `--filter "Item:coffee"`)
|
|
123
|
+
- **Filter CSV data by contains operator:** `klio [planet] --csv <file-path> --filter "column*text"` (e.g., `--filter "Buchungstext*Gutschrift"`)
|
|
124
|
+
- **Filter CSV data by excludes operator:** `klio [planet] --csv <file-path> --filter "column!text"` (e.g., `--filter "Buchungstext!Gutschrift"`)
|
|
122
125
|
- **Filter CSV data by multiple conditions:** `klio [planet] --csv <file-path> --filter "column1:value1,column2:value2"` (e.g., `--filter "FTR:H,HomeTeam:Liverpool"`)
|
|
126
|
+
- **Filter CSV data with comparison operators:** `klio [planet] --csv <file-path> --filter "column>value"` (e.g., `--filter "Amount>100"`)
|
|
123
127
|
- **Creating a bar chart**: Create a bar chart and save the image to the downloads' folder. The image shows the aspect distribution of your csv datetime values: `klio moon sun --csv /home/user/Downloads/coffee.csv --filter "Item:cookie" --a --title "Eaten cookies during sun-moon aspects"`
|
|
124
128
|
|
|
125
129
|
- The command also returns a Chi-Square.
|
package/package.json
CHANGED
|
@@ -9,7 +9,8 @@ const planets = {
|
|
|
9
9
|
saturn: 6,
|
|
10
10
|
uranus: 7,
|
|
11
11
|
neptune: 8,
|
|
12
|
-
pluto: 9
|
|
12
|
+
pluto: 9,
|
|
13
|
+
chiron: 15
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
// Zodiac signs (English)
|
|
@@ -49,7 +50,8 @@ const dignities = {
|
|
|
49
50
|
6: { sign: 'Capricorn', exaltation: 'Libra', fall: 'Aries', detriment: 'Cancer' }, // Saturn
|
|
50
51
|
7: { sign: 'Aquarius', exaltation: 'Sagittarius', fall: 'Leo', detriment: 'Taurus' }, // Uranus
|
|
51
52
|
8: { sign: 'Pisces', exaltation: 'Leo', fall: 'Aquarius', detriment: 'Virgo' }, // Neptune
|
|
52
|
-
9: { sign: 'Scorpio', exaltation: 'Aries', fall: 'Taurus', detriment: 'Scorpio' } // Pluto
|
|
53
|
+
9: { sign: 'Scorpio', exaltation: 'Aries', fall: 'Taurus', detriment: 'Scorpio' }, // Pluto
|
|
54
|
+
15: { sign: 'Virgo', exaltation: 'Pisces', fall: 'Virgo', detriment: 'Pisces' } // Chiron
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
module.exports = {
|
|
@@ -1859,7 +1859,7 @@ function parseFilterCriteria(filterString) {
|
|
|
1859
1859
|
const filterConditions = filterString.split(',').map(cond => cond.trim());
|
|
1860
1860
|
|
|
1861
1861
|
const criteria = [];
|
|
1862
|
-
const operatorRegex = /^(.+?)\s*(
|
|
1862
|
+
const operatorRegex = /^(.+?)\s*(>=|<=|!=|=|>|<|\*|!)\s*(.+)$/;
|
|
1863
1863
|
|
|
1864
1864
|
for (const condition of filterConditions) {
|
|
1865
1865
|
const operatorMatch = condition.match(operatorRegex);
|
|
@@ -1872,6 +1872,27 @@ function parseFilterCriteria(filterString) {
|
|
|
1872
1872
|
continue;
|
|
1873
1873
|
}
|
|
1874
1874
|
|
|
1875
|
+
// Check for contains (*) or excludes (!) operators in the value part
|
|
1876
|
+
const containsMatch = condition.match(/^(.+?):\*(.+)$/);
|
|
1877
|
+
if (containsMatch) {
|
|
1878
|
+
criteria.push({
|
|
1879
|
+
column: containsMatch[1].trim(),
|
|
1880
|
+
operator: '*',
|
|
1881
|
+
value: containsMatch[2].trim()
|
|
1882
|
+
});
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
const excludesMatch = condition.match(/^(.+?):!(.+)$/);
|
|
1887
|
+
if (excludesMatch) {
|
|
1888
|
+
criteria.push({
|
|
1889
|
+
column: excludesMatch[1].trim(),
|
|
1890
|
+
operator: '!',
|
|
1891
|
+
value: excludesMatch[2].trim()
|
|
1892
|
+
});
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1875
1896
|
const parts = condition.split(':');
|
|
1876
1897
|
if (parts.length !== 2) {
|
|
1877
1898
|
console.warn(`Invalid filter format. Expected "column:value" or "column > value" but got "${condition}"`);
|
|
@@ -1919,6 +1940,12 @@ function applyFilter(records, filterCriteria) {
|
|
|
1919
1940
|
return bothNumeric ? recordNum < valueNum : false;
|
|
1920
1941
|
case '<=':
|
|
1921
1942
|
return bothNumeric ? recordNum <= valueNum : false;
|
|
1943
|
+
case '*':
|
|
1944
|
+
// Contains operator - check if record value contains the search string
|
|
1945
|
+
return recordValueStr.includes(valueStr);
|
|
1946
|
+
case '!':
|
|
1947
|
+
// Excludes operator - check if record value does NOT contain the search string
|
|
1948
|
+
return !recordValueStr.includes(valueStr);
|
|
1922
1949
|
default:
|
|
1923
1950
|
return false;
|
|
1924
1951
|
}
|
|
@@ -1941,7 +1968,7 @@ function applyFilter(records, filterCriteria) {
|
|
|
1941
1968
|
}
|
|
1942
1969
|
}
|
|
1943
1970
|
|
|
1944
|
-
function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'koch', analyzeAspects = false, partnerPlanet = null, filterCriteria = null) {
|
|
1971
|
+
function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'koch', analyzeAspects = false, partnerPlanet = null, filterCriteria = null, dateColumn = null) {
|
|
1945
1972
|
return new Promise(async (resolve, reject) => {
|
|
1946
1973
|
const results = [];
|
|
1947
1974
|
let pendingOperations = 0;
|
|
@@ -2014,8 +2041,25 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2014
2041
|
|
|
2015
2042
|
// Process each row
|
|
2016
2043
|
for (const data of filteredRecords) {
|
|
2017
|
-
//
|
|
2018
|
-
|
|
2044
|
+
// Use specified date column if provided, otherwise auto-detect
|
|
2045
|
+
let datetimeColumns = [];
|
|
2046
|
+
let datetimeValue = null;
|
|
2047
|
+
|
|
2048
|
+
if (dateColumn) {
|
|
2049
|
+
// Use the specified date column
|
|
2050
|
+
if (data.hasOwnProperty(dateColumn)) {
|
|
2051
|
+
datetimeValue = data[dateColumn];
|
|
2052
|
+
datetimeColumns = [dateColumn];
|
|
2053
|
+
} else {
|
|
2054
|
+
console.warn(`⚠️ Specified date column "${dateColumn}" not found in CSV. Trying auto-detection...`);
|
|
2055
|
+
// Fall back to auto-detection
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
// If no date column was specified or it wasn't found, auto-detect
|
|
2060
|
+
if (datetimeColumns.length === 0) {
|
|
2061
|
+
// Look for a column with datetime values, prioritizing specific date formats
|
|
2062
|
+
datetimeColumns = [];
|
|
2019
2063
|
|
|
2020
2064
|
// First pass: look for YYYY-MM-DD format (most specific)
|
|
2021
2065
|
const yyyyMmDdColumns = Object.keys(data).filter(key => {
|
|
@@ -2049,11 +2093,15 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2049
2093
|
return moment(trimmed, moment.ISO_8601, true).isValid();
|
|
2050
2094
|
});
|
|
2051
2095
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2096
|
+
// Prioritize columns: YYYY-MM-DD, DD/MM/YYYY, Unix timestamps, then ISO dates
|
|
2097
|
+
datetimeColumns.push(...yyyyMmDdColumns, ...ddMmYyyyColumns, ...unixTimestampColumns, ...isoDateColumns);
|
|
2098
|
+
|
|
2099
|
+
if (datetimeColumns.length > 0) {
|
|
2100
|
+
datetimeValue = data[datetimeColumns[0]];
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2054
2103
|
|
|
2055
|
-
if (datetimeColumns.length > 0) {
|
|
2056
|
-
const datetimeValue = data[datetimeColumns[0]];
|
|
2104
|
+
if (datetimeColumns.length > 0 && datetimeValue !== null) {
|
|
2057
2105
|
const datetimeValueTrimmed = datetimeValue != null ? datetimeValue.toString().trim() : '';
|
|
2058
2106
|
let datetime;
|
|
2059
2107
|
|
|
@@ -2249,9 +2297,25 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2249
2297
|
|
|
2250
2298
|
// Process each record
|
|
2251
2299
|
for (const data of filteredRecords) {
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2300
|
+
// Use specified date column if provided, otherwise auto-detect
|
|
2301
|
+
let datetimeColumns = [];
|
|
2302
|
+
let datetimeValue = null;
|
|
2303
|
+
|
|
2304
|
+
if (dateColumn) {
|
|
2305
|
+
// Use the specified date column
|
|
2306
|
+
if (data.hasOwnProperty(dateColumn)) {
|
|
2307
|
+
datetimeValue = data[dateColumn];
|
|
2308
|
+
datetimeColumns = [dateColumn];
|
|
2309
|
+
} else {
|
|
2310
|
+
console.warn(`⚠️ Specified date column "${dateColumn}" not found in CSV. Trying auto-detection...`);
|
|
2311
|
+
// Fall back to auto-detection
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// If no date column was specified or it wasn't found, auto-detect
|
|
2316
|
+
if (datetimeColumns.length === 0) {
|
|
2317
|
+
// Look for a column with ISO-Datetime values, YYYY-MM-DD dates, DD/MM/YYYY dates (optional time), or Unix-Timestamps
|
|
2318
|
+
datetimeColumns = Object.keys(data).filter(key => {
|
|
2255
2319
|
const value = data[key];
|
|
2256
2320
|
const trimmed = value != null ? value.toString().trim() : '';
|
|
2257
2321
|
// Check for ISO-8601 date
|
|
@@ -2265,8 +2329,12 @@ function analyzeCSVWithDatetime(filePath, planetName = 'moon', houseSystem = 'ko
|
|
|
2265
2329
|
return isISO || isYYYYMMDD || isDDMMYYYY || isUnixTimestamp;
|
|
2266
2330
|
});
|
|
2267
2331
|
|
|
2268
|
-
|
|
2269
|
-
|
|
2332
|
+
if (datetimeColumns.length > 0) {
|
|
2333
|
+
datetimeValue = data[datetimeColumns[0]];
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
if (datetimeColumns.length > 0 && datetimeValue !== null) {
|
|
2270
2338
|
const datetimeValueTrimmed = datetimeValue != null ? datetimeValue.toString().trim() : '';
|
|
2271
2339
|
let datetime;
|
|
2272
2340
|
|
package/src/cli/cli.js
CHANGED
|
@@ -392,6 +392,7 @@ program
|
|
|
392
392
|
.option('--v <count>', 'Shows past aspects between two planets (Format: --v <count> planet1 aspectType planet2)')
|
|
393
393
|
.option('--z <count>', 'Shows future aspects between two planets (Format: --z <count> planet1 aspectType planet2)')
|
|
394
394
|
.option('--csv <filepath>', 'Analyzes a CSV file with ISO-Datetime values or Unix timestamps')
|
|
395
|
+
.option('--date-col <column>', 'Specifies the column name containing date values (e.g., --date-col "Buchungsdatum")')
|
|
395
396
|
.option('--filter <column:value>', 'Filters CSV data by column:value (e.g., --filter "Item:coffee")')
|
|
396
397
|
.option('--title <title>', 'Title for the chart image (generates PNG image when provided)')
|
|
397
398
|
.option('--in [count]', 'Shows next planet ingress (entering new sign). Optional count for multiple ingresses')
|
|
@@ -1560,8 +1561,8 @@ program
|
|
|
1560
1561
|
};
|
|
1561
1562
|
}
|
|
1562
1563
|
|
|
1563
|
-
// Check if we should use natal positions (when --i is used without custom date)
|
|
1564
|
-
if (shouldUseBirthData(options) && !options.d) {
|
|
1564
|
+
// Check if we should use natal positions (when --i, --p1, --p2, --wp, or --up is used without custom date)
|
|
1565
|
+
if ((shouldUseBirthData(options) || shouldUsePersonData(options)) && !options.d) {
|
|
1565
1566
|
useNatalPositions = true;
|
|
1566
1567
|
customDate = birthData; // Use birth data for planet positions
|
|
1567
1568
|
console.log('Using natal positions from birth data.');
|
|
@@ -1742,7 +1743,9 @@ program
|
|
|
1742
1743
|
const analyzeAspects = actualOptions.a || false;
|
|
1743
1744
|
const filterCriteria = actualOptions.filter;
|
|
1744
1745
|
|
|
1745
|
-
|
|
1746
|
+
const dateColumn = actualOptions.dateCol;
|
|
1747
|
+
|
|
1748
|
+
analyzeCSVWithDatetime(actualOptions.csv, planet, houseSystem, analyzeAspects, planet2, filterCriteria, dateColumn)
|
|
1746
1749
|
.then(({ results, houseDistribution, signDistribution, aspectStatistics }) => {
|
|
1747
1750
|
if (results.length === 0) {
|
|
1748
1751
|
console.log('No valid ISO-Datetime values found in the CSV file.');
|
|
@@ -2785,7 +2788,9 @@ program
|
|
|
2785
2788
|
const analyzeAspects = actualOptions.a || false;
|
|
2786
2789
|
const filterCriteria = actualOptions.filter;
|
|
2787
2790
|
|
|
2788
|
-
|
|
2791
|
+
const dateColumn = actualOptions.dateCol;
|
|
2792
|
+
|
|
2793
|
+
analyzeCSVWithDatetime(actualOptions.csv, planet, houseSystem, analyzeAspects, planet2, filterCriteria, dateColumn)
|
|
2789
2794
|
.then(({ results, houseDistribution, signDistribution, aspectStatistics }) => {
|
|
2790
2795
|
if (results.length === 0) {
|
|
2791
2796
|
console.log('No valid ISO-Datetime values found in the CSV file.');
|