Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

335 righe
9.7 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. import os.path
  20. from babel.dates import format_date
  21. from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase
  22. from kosmorrolib.exceptions import OutOfRangeDateError
  23. from datetime import date
  24. from . import dumper, environment, debug
  25. from .date import parse_date
  26. from .geolocation import get_position
  27. from .utils import (
  28. KOSMORRO_VERSION,
  29. KOSMORROLIB_VERSION,
  30. colored,
  31. set_colors_activated,
  32. print_stderr,
  33. )
  34. from .exceptions import (
  35. InvalidOutputFormatError,
  36. UnavailableFeatureError,
  37. OutOfRangeDateError as DateRangeError,
  38. )
  39. from kosmorro.i18n.utils import _
  40. def run():
  41. env_vars = environment.get_env_vars()
  42. output_formats = get_dumpers()
  43. args = get_args(list(output_formats.keys()))
  44. debug.show_debug_messages = args.show_debug_messages
  45. output_format = args.format
  46. set_colors_activated(args.colors)
  47. if args.special_action is not None:
  48. return 0 if args.special_action() else 1
  49. try:
  50. compute_date = parse_date(args.date)
  51. except ValueError as error:
  52. print_stderr(colored(error.args[0], color="red", attrs=["bold"]))
  53. return -1
  54. position = None
  55. if args.position not in [None, ""]:
  56. position = get_position(args.position)
  57. elif env_vars.position not in [None, ""]:
  58. position = get_position(env_vars.position)
  59. # if output format is not specified, try to use output file extension as output format
  60. if args.output is not None and output_format is None:
  61. file_extension = os.path.splitext(args.output)[-1][1:].lower()
  62. if file_extension:
  63. output_format = file_extension
  64. # default to .txt if output format was not given and output file did not have file extension
  65. if output_format is None:
  66. output_format = "txt"
  67. if output_format == "tex" and position is None:
  68. print_stderr(
  69. colored(
  70. _(
  71. "Output file will not contain the ephemerides, because you didn't provide the observation "
  72. "coordinates."
  73. ),
  74. "yellow",
  75. )
  76. )
  77. timezone = args.timezone
  78. if timezone is None and env_vars.timezone is not None:
  79. timezone = int(env_vars.timezone)
  80. elif timezone is None:
  81. timezone = 0
  82. try:
  83. use_colors = not environment.NO_COLOR and args.colors
  84. output = get_information(
  85. compute_date,
  86. position,
  87. timezone,
  88. output_format,
  89. use_colors,
  90. args.show_graph,
  91. )
  92. except InvalidOutputFormatError as error:
  93. print_stderr(colored(error.msg, "red"))
  94. debug.debug_print(error)
  95. return 3
  96. except UnavailableFeatureError as error:
  97. print_stderr(colored(error.msg, "red"))
  98. debug.debug_print(error)
  99. return 2
  100. except DateRangeError as error:
  101. print_stderr(colored(error.msg, "red"))
  102. debug.debug_print(error)
  103. return 1
  104. if args.output is not None:
  105. try:
  106. file_content = output.to_string()
  107. opening_mode = get_opening_mode(output_format)
  108. with open(args.output, opening_mode) as output_file:
  109. output_file.write(file_content)
  110. except UnavailableFeatureError as error:
  111. print_stderr(colored(error.msg, "red"))
  112. debug.debug_print(error)
  113. return 2
  114. except OSError as error:
  115. print_stderr(
  116. colored(
  117. _('The file could not be saved in "{path}": {error}').format(
  118. path=args.output, error=error.strerror
  119. ),
  120. "red",
  121. )
  122. )
  123. debug.debug_print(error)
  124. return 3
  125. elif not output.is_file_output_needed():
  126. print(output)
  127. else:
  128. print_stderr(
  129. colored(
  130. _("Please provide a file path to export in this format (--output)."),
  131. color="red",
  132. )
  133. )
  134. return 1
  135. return 0
  136. def get_information(
  137. compute_date: date,
  138. position: Position,
  139. timezone: int,
  140. output_format: str,
  141. colors: bool,
  142. show_graph: bool,
  143. ) -> dumper.Dumper:
  144. try:
  145. if position is not None:
  146. eph = get_ephemerides(
  147. for_date=compute_date, position=position, timezone=timezone
  148. )
  149. else:
  150. eph = []
  151. try:
  152. moon_phase = get_moon_phase(for_date=compute_date, timezone=timezone)
  153. except OutOfRangeDateError as error:
  154. moon_phase = None
  155. print_stderr(
  156. colored(
  157. _(
  158. "Moon phase can only be computed between {min_date} and {max_date}"
  159. ).format(
  160. min_date=format_date(error.min_date, "long"),
  161. max_date=format_date(error.max_date, "long"),
  162. ),
  163. "yellow",
  164. )
  165. )
  166. events_list = get_events(compute_date, timezone)
  167. return get_dumpers()[output_format](
  168. ephemerides=eph,
  169. moon_phase=moon_phase,
  170. events=events_list,
  171. date=compute_date,
  172. timezone=timezone,
  173. with_colors=colors,
  174. show_graph=show_graph,
  175. )
  176. except KeyError as error:
  177. raise InvalidOutputFormatError(output_format, list(get_dumpers().keys()))
  178. except OutOfRangeDateError as error:
  179. raise DateRangeError(error.min_date, error.max_date)
  180. def get_dumpers() -> {str: dumper.Dumper}:
  181. return {
  182. "txt": dumper.TextDumper,
  183. "json": dumper.JsonDumper,
  184. "tex": dumper.LatexDumper,
  185. }
  186. def get_opening_mode(format: str) -> str:
  187. return "w"
  188. def output_version() -> bool:
  189. python_version = "%d.%d.%d" % (
  190. sys.version_info[0],
  191. sys.version_info[1],
  192. sys.version_info[2],
  193. )
  194. print("Kosmorro %s" % KOSMORRO_VERSION)
  195. print(
  196. _(
  197. "Running on Python {python_version} "
  198. "with Kosmorrolib v{kosmorrolib_version}"
  199. ).format(python_version=python_version, kosmorrolib_version=KOSMORROLIB_VERSION)
  200. )
  201. return True
  202. def get_args(output_formats: [str]):
  203. today = date.today()
  204. parser = argparse.ArgumentParser(
  205. description=_(
  206. "Compute the ephemerides and the events for a given date and a given position on Earth."
  207. ),
  208. epilog=_(
  209. "By default, only the events will be computed for today.\n"
  210. "To compute also the ephemerides, latitude and longitude arguments are needed."
  211. ),
  212. )
  213. parser.add_argument(
  214. "--version",
  215. "-v",
  216. dest="special_action",
  217. action="store_const",
  218. const=output_version,
  219. default=None,
  220. help=_("Show the program version"),
  221. )
  222. parser.add_argument(
  223. "--format",
  224. "-f",
  225. type=str,
  226. default=None,
  227. choices=output_formats,
  228. help=_(
  229. "The format to output the information to. If not provided, the output format "
  230. "will be inferred from the file extension of the output file."
  231. ),
  232. )
  233. parser.add_argument(
  234. "--position",
  235. "-p",
  236. type=str,
  237. default=None,
  238. help=_(
  239. 'The observer\'s position on Earth, in the "{latitude},{longitude}" format. '
  240. "Can also be set in the KOSMORRO_POSITION environment variable."
  241. ),
  242. )
  243. parser.add_argument(
  244. "--date",
  245. "-d",
  246. type=str,
  247. default=today.strftime("%Y-%m-%d"),
  248. help=_(
  249. "The date for which the ephemerides must be calculated. Can be in the YYYY-MM-DD format "
  250. 'or an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers). '
  251. "Defaults to current date."
  252. ).format(default_date=today.strftime("%Y-%m-%d")),
  253. )
  254. parser.add_argument(
  255. "--timezone",
  256. "-t",
  257. type=int,
  258. default=None,
  259. help=_(
  260. "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
  261. "Can also be set in the KOSMORRO_TIMEZONE environment variable."
  262. ),
  263. )
  264. parser.add_argument(
  265. "--no-colors",
  266. dest="colors",
  267. action="store_false",
  268. help=_("Disable the colors in the console."),
  269. )
  270. parser.add_argument(
  271. "--output",
  272. "-o",
  273. type=str,
  274. default=None,
  275. help=_(
  276. "A file to export the output to. If not given, the standard output is used."
  277. ),
  278. )
  279. parser.add_argument(
  280. "--no-graph",
  281. dest="show_graph",
  282. action="store_false",
  283. help=_(
  284. "Do not generate a graph to represent the rise and set times in the LaTeX file."
  285. ),
  286. )
  287. parser.add_argument(
  288. "--debug",
  289. dest="show_debug_messages",
  290. action="store_true",
  291. help=_("Show debugging messages"),
  292. )
  293. return parser.parse_args()
  294. def main():
  295. sys.exit(run())