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.
 
 
 
 

308 lines
8.8 KiB

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