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.

179 lines
6.7KB

  1. #!/usr/bin/env python3
  2. import csv
  3. import argparse
  4. import configparser
  5. from datetime import datetime, timedelta
  6. from O365 import (Account, Connection, FileSystemTokenBackend,
  7. MSOffice365Protocol)
  8. parser = argparse.ArgumentParser(prog='seatingplan', description="Script to generate a seating plan via Office365 Calendars")
  9. parser.add_argument("-i", "--client-id", type=str, dest='client_id', default="",
  10. help='Client ID for registered azure application')
  11. parser.add_argument("-s", "--client-secret", type=str, dest='client_secret', default="",
  12. help='Client secret for registered azure application')
  13. parser.add_argument("-t", "--tenant-id", type=str, dest='tenant_id', default="",
  14. help='Tenant ID for registered azure application')
  15. parser.add_argument("-c", "--config", type=str, dest="config_path", default="",
  16. help="Path to a config file to read settings from.")
  17. # TODO: DATETIME FORMATTING, MAKE THIS AVALIABLE FOR THE OUTPUT PATH SO THAT YOU CAN DO THE COOL SCRIPT THINGS
  18. parser.add_argument("-o", "--output", type=str, dest="output_path", default="Seating Plan.csv",
  19. help="Path of the outputted csv file.")
  20. # TODO: Setup providing config file via commandline args
  21. def read_config_file(config_path):
  22. """
  23. Reads config file and sets up variables foo
  24. :return: credentials: ('client_id', 'client_secret') and tenant: str
  25. """
  26. config = configparser.ConfigParser()
  27. config.read(config_path)
  28. credentials = (config["client"]["id"], config["client"]["secret"])
  29. tenant = config["client"]["tenant"]
  30. return credentials, tenant
  31. def parse_args():
  32. """
  33. Parses arguments from the commandline.
  34. :return: args, a name space with variables you can find out about with the -h flag
  35. """
  36. # TODO: Maybe the prints before exit could be a lil better at explaining the error.
  37. args = parser.parse_args()
  38. using_args = False
  39. using_conf = False
  40. if args.client_id and args.tenant_id and args.client_secret:
  41. using_args = True
  42. if args.config_path:
  43. using_conf = True
  44. if using_args and using_conf:
  45. # Using both types of config input. Confusing so dump it
  46. print("Cannot use both command-line arguments and config file. Please only use one.")
  47. exit(1)
  48. elif using_conf:
  49. # Read the file provided and return the required config
  50. credentials, tenant = read_config_file(args.config_path)
  51. args.client_id = credentials[0]
  52. args.client_secret = credentials[1]
  53. args.tenant_id = tenant
  54. return args
  55. elif using_args:
  56. # Just grab the config from the command line args
  57. return args
  58. else:
  59. # If the code has gotten here, then a config can't be parsed so we must close the program
  60. print("Cannot login. No config/partial was provided.")
  61. exit(1)
  62. def create_session(credentials, tenant_id):
  63. """
  64. Create a session with the API and save the token for later use.
  65. :param credentials: tuple of (client_id, client_secret)
  66. :param tentant_id: str of tenant_id
  67. :return: Account class and email: str
  68. """
  69. my_protocol = MSOffice365Protocol(api_version='v2.0')
  70. token_backend = FileSystemTokenBackend(token_filename='access_token')
  71. return Account(
  72. credentials,
  73. protocol=my_protocol,
  74. tenant_id=tenant_id,
  75. token_backend=token_backend
  76. )
  77. def authenticate_session(session: Account):
  78. """
  79. Authenticates account session object with oauth. Uses the default auth flow that comes with the library
  80. It could be merged with oauth wrapper but this works too.
  81. :param session: Account object
  82. :return:
  83. """
  84. try:
  85. session.con.oauth_request("https://outlook.office.com/api/v2.0/me/calendar", "get")
  86. except RuntimeError: # Not authenticated. Need to ask user for url
  87. session.authenticate(scopes=['basic', 'address_book', 'users', 'calendar_shared'])
  88. session.con.oauth_request("https://outlook.office.com/api/v2.0/me/calendar", "get")
  89. def oauth_request(connection: Connection, url: str):
  90. """
  91. Wrapper for Connection.oauth_request to provide some error handling
  92. :param connection:
  93. :param url:
  94. :return:
  95. """
  96. request = connection.oauth_request(url, "get")
  97. if request.status_code != 200:
  98. print(f"Request failed: GET request to {url} failed with {request.status_code} error")
  99. else:
  100. return request
  101. def get_week_datetime():
  102. """
  103. Gets the current week's Monday and Friday to be used to filter a calendar.
  104. If this script is ran during the work week (Monday-Friday), it will be the current week. If it is ran on the weekend, it will generate for next week.
  105. :return: Monday and Friday: Datetime object
  106. """
  107. today = datetime.now()
  108. weekday = today.weekday()
  109. # If this script is run during the week
  110. if weekday <= 4: # 0 = Monday, 6 = Sunday
  111. monday = today - timedelta(days=weekday) # Monday = 0-0, Friday = 4-4
  112. friday = today + timedelta(days=4 - weekday) # Fri to Fri 4 + (4 - 4), Tues to Fri = 2 + (4 - 2)
  113. return monday, friday
  114. else:
  115. monday = today - timedelta(days=weekday) + timedelta(days=7) # Monday = 0-0, Friday = 4-4
  116. friday = today + timedelta(days=4 - weekday) + timedelta(
  117. days=7) # Fri to Fri 4 + (4 - 4), Tues to Fri = 2 + (4 - 2)
  118. return monday, friday
  119. def create_csv(output_path):
  120. with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
  121. fieldnames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
  122. writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
  123. pass
  124. # TODO: https://docs.python.org/3.7/library/csv.html#csv.DictWriter Need to finish this when we actually get some data to input.
  125. def main():
  126. """
  127. Main function that is ran on start up. Script is ran from here.
  128. """
  129. args = parse_args()
  130. session = create_session((args.client_id, args.client_secret), args.tenant_id)
  131. authenticate_session(session)
  132. r = session.con.oauth_request("https://outlook.office.com/api/v2.0/users/{email}/calendar/events", "get")
  133. # request = account.con.oauth_request("https://outlook.office.com/api/v2.0/users/EMAIL/calendar/events?$filter=Start/DateTime ge '2019-09-04T08:00' AND End/Datetime le '2019-09-05T08:00'&$top=50", "get")
  134. print(r.text)
  135. # setup csv
  136. # API request events from out of office email
  137. # loop over events
  138. # find out who the event is talking about
  139. # insert into csv
  140. # output csv
  141. monday, friday = get_week_datetime()
  142. print(f"Monday is {monday}, Friday is {friday}")
  143. create_csv(args.output_path)
  144. if __name__ == "__main__":
  145. # This is for running the file in testing, rather than installing via pip
  146. main()