Files
circular-trello-report/main.py
2024-08-29 10:50:19 +02:00

231 lines
6.6 KiB
Python

from configparser import ConfigParser # For reading the config.ini file
from shutil import copy # For creating a new config.ini file
from requests import request # For using the Trello API
import smtplib, ssl, email # For sending emails
### Constants
TRELLO_API_CARDS_URL = "https://api.trello.com/1/boards/{id}/cards"
###
# TODO: Set up actual logging
def log(text: str):
print(text)
def read_config() -> dict:
"""
Reads the file config.ini. If the there is no file, it will create one. If
the file has empty fields, it will notify the user and end the program.
RETURNS
-------
- dict
The contents of the config.ini file.
"""
config = ConfigParser()
# Reads the file and checks if it exists
if config.read('config.ini') == []:
copy("config.ini.sample","config.ini")
log("Creating config.ini file")
exit()
# Creates the dict and removes the "DEFAULTS" section
config_dict = {
key: dict(value) for key,value in dict(config).items()
if key != "DEFAULT"
}
# Checks if every field is filled out
all_filled_out = all(
all(v2 != '' for _,v2 in v1.items())
for _,v1 in config_dict.items()
)
if not all_filled_out:
log("Empty fields in config.ini")
exit()
return config_dict
def get_trello_cards(config: dict) -> list[dict]:
"""
Uses the Trello API to get every card from the board, and extracts the important information.
PARAMETERS
----------
- config: dict
The config file data. Uses this for the board ID, and the API key and
token.
RETURNS
-------
- list[dict]
A list of each card, with the following information: card id, name of the card, when the last activity on the card was performed, the IDs of whatever members are assigned, and the labels the card has.
"""
url = TRELLO_API_CARDS_URL.format(id=config["Trello"]["boardid"])
query = {
'key': config["Trello"]["apikey"],
'token': config["Trello"]["apitoken"]
}
# Calling the API
response = request(
"GET",
url,
params=query
)
# Making a list of cards with relevant information
cards = [
{
"id": card["id"],
"name": card["name"],
"lastActivity": card["dateLastActivity"],
"memberIDs": card["idMembers"],
"labels": [label["name"] for label in card["labels"]]
}
for card in response.json()
]
return cards
def flag_out_of_date_cards(cards: list[dict]) -> list[dict]:
"""
Flags cards as out of date, if they exceed the deadline.
PARAMETERS
----------
- cards: list[dict]
The list of cards.
RETURNS
-------
- list[dict]
The same list of cards, but with an out_of_date boolean field.
"""
pass
def generate_reports(cards: list[dict]) -> tuple[str,list]:
"""
Generates the PDF reports for each user. Each report will be found at
./reports/TIMESTAMP/USERID.pdf
PARAMETERS
----------
- cards: list[dict]
The list of cards.
RETURNS
-------
- str
The timestamp used for the folder in which the reports are generated.
- list
The list of users reports were generated for.
"""
pass
def get_emails(config: dict, user_ids: list[str]) -> dict:
"""
Uses the Trello API to find the email addresses of the given users.
PARAMETERS
----------
- config: dict
The config file data. Uses this for the board ID, and the API key and
token.
- user_ids: list[str]
A list of user IDs.
RETURNS
-------
- dict
A dict where each user ID is the key and the value is their email.
"""
pass
def send_emails(config: dict, users: dict, timestamp: str) -> None:
"""
Sending out emails.
PARAMETERS
----------
- config: dict
The config file data. Uses this for the SMTP server, port, username,
and password.
- users: dict
Each user ID and the email connected to it.
- timestamp: str
The timestamp used to create the folder that the reports are in.
"""
# Create a secure SSL context
context = ssl.create_default_context()
# Logging in
server_url = config["SMTP"]["server"]
port = config["SMTP"]["port"]
username = config["SMTP"]["username"]
password = config["SMTP"]["password"]
with smtplib.SMTP_SSL(server_url, port, context=context) as server:
server.login(username, password)
# Looping through each user and sending them an email with the pdf
for user_id, receiver in users.items():
pdf_path = f"reports/{timestamp}/{user_id}.pdf"
subject = "Trello Report"
body = "Here is your Trello Report"
# Preparing the message
message = email.mime.multipart.MIMEMultipart()
message["From"] = username
message["To"] = receiver
message["Subject"] = subject
message.attach(email.mime.text.MIMEText(body, "plain"))
# Open PDF file in binary mode, and attach it to the email
with open(pdf_path, "rb") as file_pointer:
part = email.mime.base.MIMEBase("application", "octet-stream")
part.set_payload(file_pointer.read())
# Encode file in ASCII characters to send by email
email.encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
f"attachment; filename= TrelloReport-{user_id}-{timestamp}.pdf",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
server.sendmail(username, receiver, text)
def main():
# Reading the config file
config = read_config()
# Getting the cards from Trello
cards = get_trello_cards(config)
# Flagging the cards if they've exceeded their deadline
cards = flag_out_of_date_cards(cards)
# Generate the PDF reports
timestamp, user_ids = generate_reports(cards)
# Get the email address of each user
users = get_emails(config, user_ids)
# Send out the emails
send_emails(config, users, timestamp)
if __name__ == "__main__":
main()