You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

313 lines
9.2 KiB

  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 termcolor import colored
  24. from . import dumper, environment, debug
  25. from .date import parse_date
  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. if args.special_action is not None:
  36. return 0 if args.special_action() else 1
  37. try:
  38. compute_date = parse_date(args.date)
  39. except ValueError as error:
  40. print(colored(error.args[0], color="red", attrs=["bold"]))
  41. return -1
  42. position = None
  43. if args.latitude is not None or args.longitude is not None:
  44. position = Position(args.latitude, args.longitude)
  45. elif env_vars.latitude is not None and env_vars.longitude is not None:
  46. position = Position(float(env_vars.latitude), float(env_vars.longitude))
  47. if output_format == "pdf":
  48. print(
  49. _(
  50. "Save the planet and paper!\n"
  51. "Consider printing your PDF document only if really necessary, and use the other side of the sheet."
  52. )
  53. )
  54. if position is None:
  55. print()
  56. print(
  57. colored(
  58. _(
  59. "PDF output will not contain the ephemerides, because you didn't provide the observation "
  60. "coordinates."
  61. ),
  62. "yellow",
  63. )
  64. )
  65. timezone = args.timezone
  66. if timezone is None and env_vars.timezone is not None:
  67. timezone = int(env_vars.timezone)
  68. elif timezone is None:
  69. timezone = 0
  70. try:
  71. output = get_information(
  72. compute_date,
  73. position,
  74. timezone,
  75. output_format,
  76. args.colors,
  77. args.show_graph,
  78. )
  79. except UnavailableFeatureError as error:
  80. print(colored(error.msg, "red"))
  81. debug.debug_print(error)
  82. return 2
  83. except DateRangeError as error:
  84. print(colored(error.msg, "red"))
  85. debug.debug_print(error)
  86. return 1
  87. if args.output is not None:
  88. try:
  89. pdf_content = output.to_string()
  90. with open(args.output, "wb") as output_file:
  91. output_file.write(pdf_content)
  92. except UnavailableFeatureError as error:
  93. print(colored(error.msg, "red"))
  94. debug.debug_print(error)
  95. return 2
  96. except OSError as error:
  97. print(
  98. colored(
  99. _('The file could not be saved in "{path}": {error}').format(
  100. path=args.output, error=error.strerror
  101. ),
  102. "red",
  103. )
  104. )
  105. debug.debug_print(error)
  106. return 3
  107. elif not output.is_file_output_needed():
  108. print(output)
  109. else:
  110. print(
  111. colored(
  112. _("Please provide a file path to export in this format (--output)."),
  113. color="red",
  114. )
  115. )
  116. return 1
  117. return 0
  118. def get_information(
  119. compute_date: date,
  120. position: Position,
  121. timezone: int,
  122. output_format: str,
  123. colors: bool,
  124. show_graph: bool,
  125. ) -> dumper.Dumper:
  126. if position is not None:
  127. try:
  128. eph = get_ephemerides(
  129. for_date=compute_date, position=position, timezone=timezone
  130. )
  131. except OutOfRangeDateError as error:
  132. raise DateRangeError(error.min_date, error.max_date)
  133. else:
  134. eph = []
  135. try:
  136. moon_phase = get_moon_phase(for_date=compute_date, timezone=timezone)
  137. except OutOfRangeDateError as error:
  138. moon_phase = None
  139. print(
  140. colored(
  141. _(
  142. "Moon phase can only be displayed between {min_date} and {max_date}"
  143. ).format(
  144. min_date=error.min_date.strftime(SHORT_DATE_FORMAT),
  145. max_date=error.max_date.strftime(SHORT_DATE_FORMAT),
  146. ),
  147. "yellow",
  148. )
  149. )
  150. events_list = get_events(compute_date, timezone)
  151. return get_dumpers()[output_format](
  152. ephemerides=eph,
  153. moon_phase=moon_phase,
  154. events=events_list,
  155. date=compute_date,
  156. timezone=timezone,
  157. with_colors=colors,
  158. show_graph=show_graph,
  159. )
  160. def get_dumpers() -> {str: dumper.Dumper}:
  161. return {
  162. "text": dumper.TextDumper,
  163. "json": dumper.JsonDumper,
  164. "pdf": dumper.PdfDumper,
  165. }
  166. def output_version() -> bool:
  167. python_version = "%d.%d.%d" % (
  168. sys.version_info[0],
  169. sys.version_info[1],
  170. sys.version_info[2],
  171. )
  172. print("Kosmorro %s" % kosmorro_version)
  173. print(
  174. _(
  175. "Running on Python {python_version} "
  176. "with Kosmorrolib v{kosmorrolib_version}"
  177. ).format(python_version=python_version, kosmorrolib_version=kosmorrolib_version)
  178. )
  179. return True
  180. def get_args(output_formats: [str]):
  181. today = date.today()
  182. parser = argparse.ArgumentParser(
  183. description=_(
  184. "Compute the ephemerides and the events for a given date and a given position on Earth."
  185. ),
  186. epilog=_(
  187. "By default, only the events will be computed for today ({date}).\n"
  188. "To compute also the ephemerides, latitude and longitude arguments"
  189. " 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 today ({default_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()