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.

194 lines
6.7KB

  1. #!/usr/bin/env python3
  2. # Copyright (C) 2019 Campaign Against Arms Trade
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. """Entry point for Supper Script"""
  16. import argparse
  17. import csv
  18. import logging
  19. from os.path import abspath
  20. from datetime import datetime
  21. from pathlib import Path
  22. import yaml
  23. from . import LOG, HANDLER, dates
  24. from .api import Account
  25. parser = argparse.ArgumentParser(
  26. prog='supper',
  27. description="Script to generate a seating plan via Office365 Calendars"
  28. )
  29. parser.add_argument(
  30. "-c", "--config",
  31. type=str,
  32. dest="config_path",
  33. default="{}/.config/supper.yaml".format(Path.home()),
  34. help="Path to a config file to read settings from. Default: ~/.config/supper.yaml"
  35. )
  36. parser.add_argument(
  37. "-o", "--output",
  38. type=str,
  39. dest="output_path",
  40. default="Seating Plan.csv",
  41. help="Path to save the output csv file to"
  42. )
  43. parser.add_argument("-d", "--debug", action="store_true", help="Enable debug output")
  44. parser.add_argument("-w", "--weeks", type=int, default=0, dest="weeks_extra", help="Number of weeks to add extra to the current week")
  45. def read_config_file(config_path: str):
  46. """
  47. Reads config file and sets up variables foo
  48. :return: config as a dict
  49. """
  50. with open(config_path, "r") as file:
  51. config = yaml.load(file, Loader=yaml.FullLoader)
  52. return config
  53. def format_output_path(output_path: str, date: datetime):
  54. """
  55. Checks the string for datetime formatting and formats it if possible.
  56. :param output_path: str of the output path
  57. :return: str of the new output path
  58. """
  59. try:
  60. new_path = output_path.format(date)
  61. if new_path.split(".")[-1] != "csv":
  62. new_path += ".csv"
  63. LOG.info("Output path does NOT have '.csv' file extension. Adding '.csv' to end of output_path.")
  64. LOG.debug("Formatted output file successfully as '%s'", new_path)
  65. return new_path
  66. except (SyntaxError, KeyError):
  67. # This is raised when formatting is incorrectly used in naming the output
  68. LOG.error("Invalid formatting pattern given for output_path. Cannot name output_path. Exiting...")
  69. exit(1)
  70. def parse_args():
  71. """
  72. Parses arguments from the commandline.
  73. :return: config yaml file as a dict
  74. """
  75. args = parser.parse_args()
  76. if args.debug:
  77. LOG.setLevel(logging.DEBUG)
  78. HANDLER.setLevel(logging.DEBUG)
  79. else:
  80. LOG.setLevel(logging.WARNING)
  81. HANDLER.setLevel(logging.WARNING)
  82. if args.config_path:
  83. # Read the file provided and return the required config
  84. try:
  85. config = read_config_file(args.config_path)
  86. config["weeks_extra"] = args.weeks_extra
  87. config["config_path"] = args.config_path
  88. config["output_path"] = args.output_path # Needs formatting
  89. # make all names lowercase and sort alphabetically
  90. config["users"] = sorted([x.lower() for x in config["users"]])
  91. LOG.debug("Loaded config successfully from '%s'", args.config_path)
  92. return config
  93. except FileNotFoundError:
  94. # Cannot open file
  95. LOG.error("Cannot find config file provided (%s). Maybe you mistyped it? Exiting...", args.config_path)
  96. exit(1)
  97. except (yaml.parser.ParserError, TypeError):
  98. # Cannot parse opened file
  99. # TypeError is raised if the metadata of the file is correct but content doesn't parse
  100. LOG.error("Cannot parse config file. Make sure the provided config is a YAML file and that is is formatted correctly. Exiting...")
  101. exit(1)
  102. else:
  103. # No provided -c argument
  104. LOG.error("Cannot login. No config file was provided. Exiting...")
  105. exit(1)
  106. def create_ooo_csv(ooo: list, users: list, output_path: str):
  107. """
  108. Creates a csv of who is in the office and on what day.
  109. :param ooo: a list of lists representing each day of a 5 day week. Each day's list has users who are not in that day
  110. :param users: a list of names of people in the office
  111. :param output_path: a str representing the output path of the csv file
  112. """
  113. with open(output_path, 'w', newline='', encoding='utf-8') as file:
  114. fieldnames = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
  115. writer = csv.DictWriter(file, fieldnames=fieldnames)
  116. writer.writeheader()
  117. for user in users:
  118. row = {}
  119. for i, day in enumerate(fieldnames):
  120. # for each day, check if the user is in that day, if not do not write their name into that day.
  121. if user not in ooo[i]:
  122. row[day] = user
  123. else:
  124. row[day] = ""
  125. writer.writerow(row)
  126. LOG.debug("Created csv file.")
  127. def main():
  128. """
  129. Main function that is ran on start up. Script is ran from here.
  130. """
  131. config = parse_args()
  132. client_id = config["client_id"]
  133. client_secret = config["client_secret"]
  134. tenant_id = config["tenant_id"]
  135. output_path = config["output_path"]
  136. users = config["users"]
  137. email = config["ooo_email"]
  138. session = Account.create_session((client_id, client_secret), tenant_id)
  139. auth = False
  140. while not auth:
  141. auth = session.authenticate_session()
  142. LOG.debug("Session created and authenticated. %s", session)
  143. filenames = []
  144. for i in range(config["weeks_extra"] + 1):
  145. ooo = session.get_ooo_list(email, i)
  146. monday, friday = dates.get_week_datetime(i)
  147. output_filename = format_output_path(output_path, monday)
  148. # Check if filename is the same
  149. if output_filename in filenames:
  150. # Add number to end of filename to avoid overwrites
  151. path, ext = output_filename.split(".")
  152. output_filename = ".".join([path + f"_{i}", ext])
  153. filenames.append(output_filename)
  154. create_ooo_csv(ooo, users, output_filename)
  155. print(
  156. "Created CSV seating plan for week {:%a %d/%m/%Y} to {:%a %d/%m/%Y} at {}".format(
  157. monday, friday, abspath(output_filename)
  158. )
  159. )
  160. if __name__ == "__main__":
  161. # This is for running the file in testing, rather than installing via pip
  162. main()