您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/env python3
  2. # Kosmorro - Compute The Next Ephemerides
  3. # Copyright (C) 2019 Jérôme Deuchnord <jerome@deuchnord.fr>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. import argparse
  18. import sys
  19. from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase
  20. from kosmorrolib.__version__ import __version__ as kosmorrolib_version
  21. from kosmorrolib.exceptions import OutOfRangeDateError
  22. from datetime import date
  23. from . import dumper, environment, debug
  24. from .date import parse_date
  25. from .utils import colored, set_colors_activated
  26. from .__version__ import __version__ as kosmorro_version
  27. from .exceptions import UnavailableFeatureError, OutOfRangeDateError as DateRangeError
  28. from _kosmorro.i18n.utils import _, SHORT_DATE_FORMAT
  29. def main():
  30. env_vars = environment.get_env_vars()
  31. output_formats = get_dumpers()
  32. args = get_args(list(output_formats.keys()))
  33. debug.show_debug_messages = args.show_debug_messages
  34. output_format = args.format
  35. set_colors_activated(args.colors)
  36. if args.special_action is not None:
  37. return 0 if args.special_action() else 1
  38. try:
  39. compute_date = parse_date(args.date)
  40. except ValueError as error:
  41. print(colored(error.args[0], color="red", attrs=["bold"]))
  42. return -1
  43. position = None
  44. if args.latitude is not None or args.longitude is not None:
  45. position = Position(args.latitude, args.longitude)
  46. elif env_vars.latitude is not None and env_vars.longitude is not None:
  47. position = Position(float(env_vars.latitude), float(env_vars.longitude))
  48. if output_format == "pdf":
  49. print(
  50. _(
  51. "Save the planet and paper!\n"
  52. "Consider printing your PDF document only if really necessary, and use the other side of the sheet."
  53. )
  54. )
  55. if position is None:
  56. print()
  57. print(
  58. colored(
  59. _(
  60. "PDF output will not contain the ephemerides, because you didn't provide the observation "
  61. "coordinates."
  62. ),
  63. "yellow",
  64. )
  65. )
  66. timezone = args.timezone
  67. if timezone is None and env_vars.timezone is not None:
  68. timezone = int(env_vars.timezone)
  69. elif timezone is None:
  70. timezone = 0
  71. try:
  72. output = get_information(
  73. compute_date,
  74. position,
  75. timezone,
  76. output_format,
  77. args.colors,
  78. args.show_graph,
  79. )
  80. except UnavailableFeatureError as error:
  81. print(colored(error.msg, "red"))
  82. debug.debug_print(error)
  83. return 2
  84. except DateRangeError as error:
  85. print(colored(error.msg, "red"))
  86. debug.debug_print(error)
  87. return 1
  88. if args.output is not None:
  89. try:
  90. pdf_content = output.to_string()
  91. with open(args.output, "wb") as output_file:
  92. output_file.write(pdf_content)
  93. except UnavailableFeatureError as error:
  94. print(colored(error.msg, "red"))
  95. debug.debug_print(error)
  96. return 2
  97. except OSError as error:
  98. print(
  99. colored(
  100. _('The file could not be saved in "{path}": {error}').format(
  101. path=args.output, error=error.strerror
  102. ),
  103. "red",
  104. )
  105. )
  106. debug.debug_print(error)
  107. return 3
  108. elif not output.is_file_output_needed():
  109. print(output)
  110. else:
  111. print(
  112. colored(
  113. _("Please provide a file path to export in this format (--output)."),
  114. color="red",
  115. )
  116. )
  117. return 1
  118. return 0
  119. def get_information(
  120. compute_date: date,
  121. position: Position,
  122. timezone: int,
  123. output_format: str,
  124. colors: bool,
  125. show_graph: bool,
  126. ) -> dumper.Dumper:
  127. if position is not None:
  128. try:
  129. eph = get_ephemerides(
  130. for_date=compute_date, position=position, timezone=timezone
  131. )
  132. except OutOfRangeDateError as error:
  133. raise DateRangeError(error.min_date, error.max_date)
  134. else:
  135. eph = []
  136. try:
  137. moon_phase = get_moon_phase(for_date=compute_date, timezone=timezone)
  138. except OutOfRangeDateError as error:
  139. moon_phase = None
  140. print(
  141. colored(
  142. _(
  143. "Moon phase can only be displayed between {min_date} and {max_date}"
  144. ).format(
  145. min_date=error.min_date.strftime(SHORT_DATE_FORMAT),
  146. max_date=error.max_date.strftime(SHORT_DATE_FORMAT),
  147. ),
  148. "yellow",
  149. )
  150. )
  151. events_list = get_events(compute_date, timezone)
  152. return get_dumpers()[output_format](
  153. ephemerides=eph,
  154. moon_phase=moon_phase,
  155. events=events_list,
  156. date=compute_date,
  157. timezone=timezone,
  158. with_colors=colors,
  159. show_graph=show_graph,
  160. )
  161. def get_dumpers() -> {str: dumper.Dumper}:
  162. return {
  163. "text": dumper.TextDumper,
  164. "json": dumper.JsonDumper,
  165. "pdf": dumper.PdfDumper,
  166. }
  167. def output_version() -> bool:
  168. python_version = "%d.%d.%d" % (
  169. sys.version_info[0],
  170. sys.version_info[1],
  171. sys.version_info[2],
  172. )
  173. print("Kosmorro %s" % kosmorro_version)
  174. print(
  175. _(
  176. "Running on Python {python_version} "
  177. "with Kosmorrolib v{kosmorrolib_version}"
  178. ).format(python_version=python_version, kosmorrolib_version=kosmorrolib_version)
  179. )
  180. return True
  181. def get_args(output_formats: [str]):
  182. today = date.today()
  183. parser = argparse.ArgumentParser(
  184. description=_(
  185. "Compute the ephemerides and the events for a given date and a given position on Earth."
  186. ),
  187. epilog=_(
  188. "By default, only the events will be computed for today.\n"
  189. "To compute also the ephemerides, latitude and longitude arguments are needed."
  190. ).format(date=today.strftime(dumper.FULL_DATE_FORMAT)),
  191. )
  192. parser.add_argument(
  193. "--version",
  194. "-v",
  195. dest="special_action",
  196. action="store_const",
  197. const=output_version,
  198. default=None,
  199. help=_("Show the program version"),
  200. )
  201. parser.add_argument(
  202. "--format",
  203. "-f",
  204. type=str,
  205. default=output_formats[0],
  206. choices=output_formats,
  207. help=_("The format to output the information to"),
  208. )
  209. parser.add_argument(
  210. "--latitude",
  211. "-lat",
  212. type=float,
  213. default=None,
  214. help=_(
  215. "The observer's latitude on Earth. Can also be set in the KOSMORRO_LATITUDE environment "
  216. "variable."
  217. ),
  218. )
  219. parser.add_argument(
  220. "--longitude",
  221. "-lon",
  222. type=float,
  223. default=None,
  224. help=_(
  225. "The observer's longitude on Earth. Can also be set in the KOSMORRO_LONGITUDE "
  226. "environment variable."
  227. ),
  228. )
  229. parser.add_argument(
  230. "--date",
  231. "-d",
  232. type=str,
  233. default=today.strftime("%Y-%m-%d"),
  234. help=_(
  235. "The date for which the ephemerides must be calculated. Can be in the YYYY-MM-DD format "
  236. 'or an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers). '
  237. "Defaults to current date."
  238. ).format(default_date=today.strftime("%Y-%m-%d")),
  239. )
  240. parser.add_argument(
  241. "--timezone",
  242. "-t",
  243. type=int,
  244. default=None,
  245. help=_(
  246. "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
  247. "Can also be set in the KOSMORRO_TIMEZONE environment variable."
  248. ),
  249. )
  250. parser.add_argument(
  251. "--no-colors",
  252. dest="colors",
  253. action="store_false",
  254. help=_("Disable the colors in the console."),
  255. )
  256. parser.add_argument(
  257. "--output",
  258. "-o",
  259. type=str,
  260. default=None,
  261. help=_(
  262. "A file to export the output to. If not given, the standard output is used. "
  263. "This argument is needed for PDF format."
  264. ),
  265. )
  266. parser.add_argument(
  267. "--no-graph",
  268. dest="show_graph",
  269. action="store_false",
  270. help=_(
  271. "Do not generate a graph to represent the rise and set times in the PDF format."
  272. ),
  273. )
  274. parser.add_argument(
  275. "--debug",
  276. dest="show_debug_messages",
  277. action="store_true",
  278. help=_("Show debugging messages"),
  279. )
  280. return parser.parse_args()