Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

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