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]
|
[Trello]
|
||||||
TrelloAPIKey=
|
APIKey=
|
||||||
|
APIToken=
|
||||||
|
BoardID=
|
||||||
|
|
||||||
[SMTP]
|
[SMTP]
|
||||||
Server=
|
Server=
|
||||||
|
Port=
|
||||||
Username=
|
Username=
|
||||||
Password=
|
Password=
|
||||||
|
240
main.py
240
main.py
@ -1,27 +1,231 @@
|
|||||||
def main():
|
from configparser import ConfigParser # For reading the config.ini file
|
||||||
# Step 1:
|
from shutil import copy # For creating a new config.ini file
|
||||||
# Create an config file from the sample file if there is none,
|
from requests import request # For using the Trello API
|
||||||
# then return.
|
import smtplib, ssl, email # For sending emails
|
||||||
# Read config file if it already existed.
|
|
||||||
|
|
||||||
# Step 2:
|
### Constants
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Step 3:
|
TRELLO_API_CARDS_URL = "https://api.trello.com/1/boards/{id}/cards"
|
||||||
# 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.
|
|
||||||
|
|
||||||
# Step 4:
|
###
|
||||||
# Generate reports for each person, showing their own cards.
|
|
||||||
# Perhaps a "master report" with every card.
|
|
||||||
|
|
||||||
# Step 5:
|
# TODO: Set up actual logging
|
||||||
# Send out the reports via email.
|
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
|
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__":
|
if __name__ == "__main__":
|
||||||
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