horoscope: add
This commit is contained in:
4
horoscope/default.nix
Normal file
4
horoscope/default.nix
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{poetry2nix}:
|
||||||
|
poetry2nix.mkPoetryApplication {
|
||||||
|
projectDir = ./.;
|
||||||
|
}
|
||||||
54
horoscope/horoscope.py
Normal file
54
horoscope/horoscope.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
import click
|
||||||
|
from flatlib.datetime import Datetime
|
||||||
|
from flatlib.geopos import GeoPos
|
||||||
|
from flatlib.chart import Chart
|
||||||
|
import flatlib.const
|
||||||
|
|
||||||
|
sign_symbols = {
|
||||||
|
flatlib.const.ARIES: "♈",
|
||||||
|
flatlib.const.TAURUS: "♉",
|
||||||
|
flatlib.const.GEMINI: "♊",
|
||||||
|
flatlib.const.CANCER: "♋",
|
||||||
|
flatlib.const.LEO: "♌",
|
||||||
|
flatlib.const.VIRGO: "♍",
|
||||||
|
flatlib.const.LIBRA: "♎",
|
||||||
|
flatlib.const.SCORPIO: "♏",
|
||||||
|
flatlib.const.SAGITTARIUS: "♐",
|
||||||
|
flatlib.const.CAPRICORN: "♑",
|
||||||
|
flatlib.const.AQUARIUS: "♒",
|
||||||
|
flatlib.const.PISCES: "♓",
|
||||||
|
}
|
||||||
|
|
||||||
|
planet_symbols = {
|
||||||
|
flatlib.const.SUN: "☉",
|
||||||
|
flatlib.const.MOON: "☽",
|
||||||
|
flatlib.const.MERCURY: "☿",
|
||||||
|
flatlib.const.VENUS: "♀",
|
||||||
|
flatlib.const.MARS: "♂",
|
||||||
|
flatlib.const.JUPITER: "♃",
|
||||||
|
flatlib.const.SATURN: "♄",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_into_stupid_flatlib_format(dt):
|
||||||
|
return Datetime(dt.strftime("%Y/%m/%d"), dt.strftime("%H:%M"))
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--latitude", type=click.FLOAT, required=True)
|
||||||
|
@click.option("--longitude", type=click.FLOAT, required=True)
|
||||||
|
@click.option("--date", type=click.DateTime(), default=datetime.now())
|
||||||
|
def main(latitude: float, longitude: float, date: datetime):
|
||||||
|
flatlib_datetime = convert_into_stupid_flatlib_format(date)
|
||||||
|
position = GeoPos(latitude, longitude)
|
||||||
|
chart = Chart(flatlib_datetime, position)
|
||||||
|
for planet in planet_symbols.keys():
|
||||||
|
planet_position = chart.getObject(planet)
|
||||||
|
print(
|
||||||
|
planet_symbols[planet],
|
||||||
|
sign_symbols[planet_position.sign],
|
||||||
|
"℞" if planet_position.movement() == flatlib.const.RETROGRADE else "",
|
||||||
|
end="",
|
||||||
|
)
|
||||||
|
print()
|
||||||
81
horoscope/poetry.lock
generated
Normal file
81
horoscope/poetry.lock
generated
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.1.3"
|
||||||
|
description = "Composable command line interface toolkit"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.5"
|
||||||
|
description = "Cross-platform colored terminal text."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flatlib"
|
||||||
|
version = "0.2.3"
|
||||||
|
description = "Python library for Traditional Astrology"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pyswisseph = "2.08.00-1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numpy"
|
||||||
|
version = "1.23.1"
|
||||||
|
description = "NumPy is the fundamental package for array computing with Python."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyswisseph"
|
||||||
|
version = "2.08.00-1"
|
||||||
|
description = "Python extension to the Swiss Ephemeris"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytz"
|
||||||
|
version = "2021.3"
|
||||||
|
description = "World timezone definitions, modern and historical"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "timezonefinder"
|
||||||
|
version = "5.2.0"
|
||||||
|
description = "fast python package for finding the timezone of any point on earth (coordinates) offline"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
numpy = ">=1.16"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
numba = ["numba (>=0.48)"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "1.1"
|
||||||
|
python-versions = "^3.8"
|
||||||
|
content-hash = "657742383232643f2fa13df5686de0cc79c624f9ae9bdb2f0fc96c7a94b5b8bf"
|
||||||
|
|
||||||
|
[metadata.files]
|
||||||
|
click = []
|
||||||
|
colorama = []
|
||||||
|
flatlib = []
|
||||||
|
numpy = []
|
||||||
|
pyswisseph = []
|
||||||
|
pytz = []
|
||||||
|
timezonefinder = []
|
||||||
21
horoscope/pyproject.toml
Normal file
21
horoscope/pyproject.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "horoscope"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Kierán Meinhardt <kmein@posteo.de>"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.8"
|
||||||
|
flatlib = "^0.2.3"
|
||||||
|
click = "^8.0.3"
|
||||||
|
timezonefinder = "^5.2.0"
|
||||||
|
pytz = "^2021.3"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
horoscope = "horoscope:main"
|
||||||
|
transits-current = "transits:current"
|
||||||
|
transits-forecast = "transits:forecast"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
181
horoscope/transits.py
Normal file
181
horoscope/transits.py
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
from flatlib import aspects, const
|
||||||
|
from flatlib.chart import Chart
|
||||||
|
from flatlib.datetime import Datetime
|
||||||
|
import pytz
|
||||||
|
from flatlib.geopos import GeoPos
|
||||||
|
import timezonefinder
|
||||||
|
import operator
|
||||||
|
import click
|
||||||
|
import itertools
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
tf = timezonefinder.TimezoneFinder()
|
||||||
|
|
||||||
|
planets = [
|
||||||
|
const.SUN,
|
||||||
|
const.MOON,
|
||||||
|
const.MERCURY,
|
||||||
|
const.VENUS,
|
||||||
|
const.MARS,
|
||||||
|
const.JUPITER,
|
||||||
|
const.SATURN,
|
||||||
|
const.URANUS,
|
||||||
|
const.NEPTUNE,
|
||||||
|
const.PLUTO,
|
||||||
|
]
|
||||||
|
|
||||||
|
planet_symbols = {
|
||||||
|
const.SUN: "☉",
|
||||||
|
const.MOON: "☽",
|
||||||
|
const.MERCURY: "☿",
|
||||||
|
const.VENUS: "♀",
|
||||||
|
const.MARS: "♂",
|
||||||
|
const.JUPITER: "♃",
|
||||||
|
const.SATURN: "♄",
|
||||||
|
const.URANUS: "♅",
|
||||||
|
const.NEPTUNE: "♆",
|
||||||
|
const.PLUTO: "⯓",
|
||||||
|
}
|
||||||
|
|
||||||
|
aspect_symbols = {
|
||||||
|
const.NO_ASPECT: " ",
|
||||||
|
const.CONJUNCTION: "☌",
|
||||||
|
const.SEXTILE: "⚹",
|
||||||
|
const.SQUARE: "□",
|
||||||
|
const.TRINE: "△",
|
||||||
|
const.OPPOSITION: "☍",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_into_stupid_flatlib_format(dt):
|
||||||
|
return Datetime(
|
||||||
|
dt.strftime("%Y/%m/%d"),
|
||||||
|
dt.strftime("%H:%M"),
|
||||||
|
dt.utcoffset().total_seconds() / 3600,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
here_latitude = 52.52
|
||||||
|
here_longitude = 13.4
|
||||||
|
|
||||||
|
|
||||||
|
def get_aspects(chart1, chart2, *, threshold):
|
||||||
|
for planet1 in chart1.objects:
|
||||||
|
for planet2 in chart2.objects:
|
||||||
|
aspect = aspects.getAspect(planet1, planet2, const.MAJOR_ASPECTS)
|
||||||
|
if aspect.exists() and aspect.orb <= threshold:
|
||||||
|
yield aspect
|
||||||
|
|
||||||
|
|
||||||
|
def get_chart(position, dt_naive):
|
||||||
|
timezone = pytz.timezone(tf.timezone_at(lat=position.lat, lng=position.lon))
|
||||||
|
dt_aware = timezone.localize(dt_naive)
|
||||||
|
return Chart(convert_into_stupid_flatlib_format(dt_aware), position, IDs=planets)
|
||||||
|
|
||||||
|
|
||||||
|
def show_aspect(aspect):
|
||||||
|
return " ".join(
|
||||||
|
[
|
||||||
|
planet_symbols[aspect.active.id],
|
||||||
|
aspect_symbols[aspect.type],
|
||||||
|
planet_symbols[aspect.passive.id],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--natal-latitude", type=click.FLOAT, default=here_latitude)
|
||||||
|
@click.option("--natal-longitude", type=click.FLOAT, default=here_longitude)
|
||||||
|
@click.option("--natal-date", type=click.DateTime(), default=datetime.now())
|
||||||
|
@click.option("--transit-latitude", type=click.FLOAT, default=here_latitude)
|
||||||
|
@click.option("--transit-longitude", type=click.FLOAT, default=here_longitude)
|
||||||
|
@click.option("--transit-date", type=click.DateTime(), default=datetime.now())
|
||||||
|
@click.option("--threshold", type=click.FLOAT, default=5)
|
||||||
|
def forecast(
|
||||||
|
natal_latitude: float,
|
||||||
|
natal_longitude: float,
|
||||||
|
natal_date: datetime,
|
||||||
|
transit_latitude: float,
|
||||||
|
transit_longitude: float,
|
||||||
|
transit_date: datetime,
|
||||||
|
threshold: float,
|
||||||
|
):
|
||||||
|
transit_position = GeoPos(transit_latitude, transit_longitude)
|
||||||
|
natal_position = GeoPos(natal_latitude, natal_longitude)
|
||||||
|
natal_chart = get_chart(natal_position, natal_date)
|
||||||
|
transit_chart = get_chart(transit_position, transit_date)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
previous_aspects = set(
|
||||||
|
show_aspect(a)
|
||||||
|
for a in get_aspects(natal_chart, transit_chart, threshold=threshold)
|
||||||
|
)
|
||||||
|
while True:
|
||||||
|
then = transit_date + timedelta(minutes=offset)
|
||||||
|
current_chart = get_chart(transit_position, then)
|
||||||
|
current_aspects = set(
|
||||||
|
show_aspect(a)
|
||||||
|
for a in get_aspects(natal_chart, current_chart, threshold=threshold)
|
||||||
|
)
|
||||||
|
entered = current_aspects - previous_aspects
|
||||||
|
exited = previous_aspects - current_aspects
|
||||||
|
if entered or exited:
|
||||||
|
print(
|
||||||
|
then.strftime("%Y-%m-%d %H:%M"),
|
||||||
|
"".join([" | +" + a for a in entered] + [" | -" + a for a in exited]),
|
||||||
|
sep="",
|
||||||
|
)
|
||||||
|
previous_aspects = current_aspects
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option("--natal-latitude", type=click.FLOAT, default=here_latitude)
|
||||||
|
@click.option("--natal-longitude", type=click.FLOAT, default=here_longitude)
|
||||||
|
@click.option("--natal-date", "-D", type=click.DateTime(), default=datetime.now())
|
||||||
|
@click.option("--transit-latitude", type=click.FLOAT, default=here_latitude)
|
||||||
|
@click.option("--transit-longitude", type=click.FLOAT, default=here_longitude)
|
||||||
|
@click.option("--transit-date", "-d", type=click.DateTime(), default=datetime.now())
|
||||||
|
@click.option("--threshold", "-t", type=click.FLOAT, default=5)
|
||||||
|
def current(
|
||||||
|
natal_latitude: float,
|
||||||
|
natal_longitude: float,
|
||||||
|
natal_date: datetime,
|
||||||
|
transit_latitude: float,
|
||||||
|
transit_longitude: float,
|
||||||
|
transit_date: datetime,
|
||||||
|
threshold: float,
|
||||||
|
):
|
||||||
|
transit_position = GeoPos(transit_latitude, transit_longitude)
|
||||||
|
natal_position = GeoPos(natal_latitude, natal_longitude)
|
||||||
|
natal_chart = get_chart(natal_position, natal_date)
|
||||||
|
transit_chart = get_chart(transit_position, transit_date)
|
||||||
|
|
||||||
|
relevant_aspects = list(
|
||||||
|
get_aspects(natal_chart, transit_chart, threshold=threshold)
|
||||||
|
)
|
||||||
|
|
||||||
|
def aspect_switch_date(aspect, *, direction=1, threshold):
|
||||||
|
offset = 0
|
||||||
|
while True:
|
||||||
|
then = transit_date + direction * timedelta(days=offset)
|
||||||
|
current_chart = get_chart(transit_position, then)
|
||||||
|
aspects = [
|
||||||
|
show_aspect(a)
|
||||||
|
for a in get_aspects(natal_chart, current_chart, threshold=threshold)
|
||||||
|
]
|
||||||
|
if aspect not in aspects:
|
||||||
|
return then.date()
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
for aspect in sorted(relevant_aspects, key=operator.attrgetter("orb")):
|
||||||
|
aspect_string = show_aspect(aspect)
|
||||||
|
print(
|
||||||
|
aspect_switch_date(
|
||||||
|
aspect_string, direction=-1, threshold=threshold
|
||||||
|
).isoformat(),
|
||||||
|
aspect_switch_date(
|
||||||
|
aspect_string, direction=1, threshold=threshold
|
||||||
|
).isoformat(),
|
||||||
|
aspect_string,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user