Compare commits
6 Commits
b9be1e829f
...
main
Author | SHA1 | Date | |
---|---|---|---|
5e6e40f576 | |||
7edba24b51 | |||
37702b7380 | |||
c42cf9cb1e | |||
2fd0163a34 | |||
9b5bed321c |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.ini
|
@ -1,7 +1,10 @@
|
||||
[Trello]
|
||||
TrelloAPIKey=
|
||||
APIKey=
|
||||
APIToken=
|
||||
BoardID=
|
||||
|
||||
[SMTP]
|
||||
Server=
|
||||
Port=
|
||||
Username=
|
||||
Password=
|
||||
|
240
main.py
240
main.py
@ -1,27 +1,231 @@
|
||||
def main():
|
||||
# Step 1:
|
||||
# Create an config file from the sample file if there is none,
|
||||
# then return.
|
||||
# Read config file if it already existed.
|
||||
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
|
||||
|
||||
# Step 2:
|
||||
# Get data from Trello API
|
||||
# - Time each card has been in the step it's in
|
||||
# - Escalation level
|
||||
# - Who is assigned (as well as their email)
|
||||
### Constants
|
||||
|
||||
# Step 3:
|
||||
# For each card, compare to escalation rules to see if it's at the step
|
||||
# and escalation level it should be at. If not, flag it.
|
||||
TRELLO_API_CARDS_URL = "https://api.trello.com/1/boards/{id}/cards"
|
||||
|
||||
# Step 4:
|
||||
# Generate reports for each person, showing their own cards.
|
||||
# Perhaps a "master report" with every card.
|
||||
###
|
||||
|
||||
# Step 5:
|
||||
# Send out the reports via email.
|
||||
# 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()
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
requests==2.32.3
|
Reference in New Issue
Block a user