forked from astropy/astropy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcalculation.py
More file actions
235 lines (198 loc) · 6.98 KB
/
calculation.py
File metadata and controls
235 lines (198 loc) · 6.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# Licensed under a 3-clause BSD style license - see LICENSE.rst
# Standard library
import re
import textwrap
import warnings
from datetime import datetime
from urllib.request import Request, urlopen
# Third-party
from astropy import time as atime
from astropy.utils.console import _color_text, color_print
from .funcs import get_sun
__all__ = []
class HumanError(ValueError):
pass
class CelestialError(ValueError):
pass
def get_sign(dt):
""" """
if (int(dt.month) == 12 and int(dt.day) >= 22) or (
int(dt.month) == 1 and int(dt.day) <= 19
):
zodiac_sign = "capricorn"
elif (int(dt.month) == 1 and int(dt.day) >= 20) or (
int(dt.month) == 2 and int(dt.day) <= 17
):
zodiac_sign = "aquarius"
elif (int(dt.month) == 2 and int(dt.day) >= 18) or (
int(dt.month) == 3 and int(dt.day) <= 19
):
zodiac_sign = "pisces"
elif (int(dt.month) == 3 and int(dt.day) >= 20) or (
int(dt.month) == 4 and int(dt.day) <= 19
):
zodiac_sign = "aries"
elif (int(dt.month) == 4 and int(dt.day) >= 20) or (
int(dt.month) == 5 and int(dt.day) <= 20
):
zodiac_sign = "taurus"
elif (int(dt.month) == 5 and int(dt.day) >= 21) or (
int(dt.month) == 6 and int(dt.day) <= 20
):
zodiac_sign = "gemini"
elif (int(dt.month) == 6 and int(dt.day) >= 21) or (
int(dt.month) == 7 and int(dt.day) <= 22
):
zodiac_sign = "cancer"
elif (int(dt.month) == 7 and int(dt.day) >= 23) or (
int(dt.month) == 8 and int(dt.day) <= 22
):
zodiac_sign = "leo"
elif (int(dt.month) == 8 and int(dt.day) >= 23) or (
int(dt.month) == 9 and int(dt.day) <= 22
):
zodiac_sign = "virgo"
elif (int(dt.month) == 9 and int(dt.day) >= 23) or (
int(dt.month) == 10 and int(dt.day) <= 22
):
zodiac_sign = "libra"
elif (int(dt.month) == 10 and int(dt.day) >= 23) or (
int(dt.month) == 11 and int(dt.day) <= 21
):
zodiac_sign = "scorpio"
elif (int(dt.month) == 11 and int(dt.day) >= 22) or (
int(dt.month) == 12 and int(dt.day) <= 21
):
zodiac_sign = "sagittarius"
return zodiac_sign
_VALID_SIGNS = [
"capricorn",
"aquarius",
"pisces",
"aries",
"taurus",
"gemini",
"cancer",
"leo",
"virgo",
"libra",
"scorpio",
"sagittarius",
]
# Some of the constellation names map to different astrological "sign names".
# Astrologers really needs to talk to the IAU...
_CONST_TO_SIGNS = {"capricornus": "capricorn", "scorpius": "scorpio"}
_ZODIAC = (
(1900, "rat"),
(1901, "ox"),
(1902, "tiger"),
(1903, "rabbit"),
(1904, "dragon"),
(1905, "snake"),
(1906, "horse"),
(1907, "goat"),
(1908, "monkey"),
(1909, "rooster"),
(1910, "dog"),
(1911, "pig"),
)
# https://stackoverflow.com/questions/12791871/chinese-zodiac-python-program
def _get_zodiac(yr):
return _ZODIAC[(yr - _ZODIAC[0][0]) % 12][1]
def horoscope(birthday, corrected=True, chinese=False):
"""
Enter your birthday as an `astropy.time.Time` object and
receive a mystical horoscope about things to come.
Parameters
----------
birthday : `astropy.time.Time` or str
Your birthday as a `datetime.datetime` or `astropy.time.Time` object
or "YYYY-MM-DD"string.
corrected : bool
Whether to account for the precession of the Earth instead of using the
ancient Greek dates for the signs. After all, you do want your *real*
horoscope, not a cheap inaccurate approximation, right?
chinese : bool
Chinese annual zodiac wisdom instead of Western one.
Returns
-------
Infinite wisdom, condensed into astrologically precise prose.
Notes
-----
This function was implemented on April 1. Take note of that date.
"""
from bs4 import BeautifulSoup
today = datetime.now()
err_msg = "Invalid response from celestial gods (failed to load horoscope)."
headers = {"User-Agent": "foo/bar"}
special_words = {
"([sS]tar[s^ ]*)": "yellow",
"([yY]ou[^ ]*)": "magenta",
"([pP]lay[^ ]*)": "blue",
"([hH]eart)": "red",
"([fF]ate)": "lightgreen",
}
if isinstance(birthday, str):
birthday = datetime.strptime(birthday, "%Y-%m-%d")
if chinese:
# TODO: Make this more accurate by using the actual date, not just year
# Might need third-party tool like https://pypi.org/project/lunardate
zodiac_sign = _get_zodiac(birthday.year)
url = (
"https://www.horoscope.com/us/horoscopes/yearly/"
f"{today.year}-chinese-horoscope-{zodiac_sign}.aspx"
)
summ_title_sfx = f"in {today.year}"
try:
res = Request(url, headers=headers)
with urlopen(res) as f:
try:
doc = BeautifulSoup(f, "html.parser")
# TODO: Also include Love, Family & Friends, Work, Money, More?
item = doc.find(id="overview")
desc = item.getText()
except Exception:
raise CelestialError(err_msg)
except Exception:
raise CelestialError(err_msg)
else:
birthday = atime.Time(birthday)
if corrected:
with warnings.catch_warnings():
warnings.simplefilter("ignore") # Ignore ErfaWarning
zodiac_sign = get_sun(birthday).get_constellation().lower()
zodiac_sign = _CONST_TO_SIGNS.get(zodiac_sign, zodiac_sign)
if zodiac_sign not in _VALID_SIGNS:
raise HumanError(
f"On your birthday the sun was in {zodiac_sign.title()}, which is"
" not a sign of the zodiac. You must not exist. Or maybe you can"
" settle for corrected=False."
)
else:
zodiac_sign = get_sign(birthday.to_datetime())
url = f"https://astrology.com/horoscope/daily/{zodiac_sign}.html"
summ_title_sfx = f"on {today.strftime('%Y-%m-%d')}"
res = Request(url, headers=headers)
with urlopen(res) as f:
try:
doc = BeautifulSoup(f, "html.parser")
item = doc.find("div", {"id": "content"})
desc = item.getText()
except Exception:
raise CelestialError(err_msg)
print("*" * 79)
color_print(f"Horoscope for {zodiac_sign.capitalize()} {summ_title_sfx}:", "green")
print("*" * 79)
for block in textwrap.wrap(desc, 79):
split_block = block.split()
for i, word in enumerate(split_block):
for re_word in special_words.keys():
match = re.search(re_word, word)
if match is None:
continue
split_block[i] = _color_text(match.groups()[0], special_words[re_word])
print(" ".join(split_block))
def inject_horoscope():
import astropy
astropy._yourfuture = horoscope
inject_horoscope()