Commit c830cc47 authored by Dominik Woiwode's avatar Dominik Woiwode
Browse files

Add Email Implementation

parent 4eaa2978
import datetime
import hashlib
import os
from enum import Enum
import smtplib
from dataclasses import dataclass
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from pathlib import Path
from typing import Optional, Dict, List
......@@ -12,17 +17,79 @@ ATTACHEMENT_SIGN = "+"
FACHGRUPPENVOLLVERSAMMLUNG_SIGN = ">"
TEMPLATE_FOLDER = "templates"
DATA_FOLDER = "data"
DEFAULT_GREMIUM = None
LDAP_USER = None
LDAP_PASSWORD = None
LDAP_MAIL = None
LDAP_NAME = None
class Template(Enum):
UNDEFINED = None
FINF = "pandoc.finf.tex"
FSR = "pandoc.fsr.tex"
TOPICS = "pandoc.topics.tex"
ATTACHEMENT = "pandoc.attachement.tex"
def fullPath(self):
return str(Path(os.path.join(TEMPLATE_FOLDER, self.value)).absolute())
@dataclass
class Gremium:
name: str
template: str
def fullTemplate(self):
return str(Path(os.path.join(TEMPLATE_FOLDER, self.template)).absolute())
GREMIUM_FR = Gremium("finf", "pandoc.finf.tex")
GREMIUM_FSR = Gremium("fsr", "pandoc.fsr.tex")
@dataclass
class MailParticipant:
mail: str
alias: str = None
def __post_init__(self):
self.alias = self.mail
def __str__(self):
return f"{self.alias} <{self.mail}>"
@staticmethod
def FR_INFO():
return MailParticipant("fr-info@finf.uni-hannover.de", "FR-Info")
@staticmethod
def FR():
return MailParticipant("fr@finf.uni-hannover.de", "Fachrat Informatik")
# Helper Functions
def sendFRMail(text: str, subject: str, sender: MailParticipant, receivers: List[MailParticipant],
attachement: "BasePad" = None):
mail = MIMEMultipart()
mail['Content-transfer-encoding'] = '8bit'
mail['From'] = sender.alias
mail['Subject'] = subject
receiveralias = ", ".join([r.alias for r in receivers])
mail['To'] = receiveralias
msg = MIMEText(text, "plain", "utf-8")
mail.attach(msg)
if attachement is not None:
payload = MIMEBase('application', 'octet-stream')
with open(attachement.compiledPath, "rb") as d:
payload.set_payload(d.read())
encoders.encode_base64(payload)
payload.add_header('Content-Disposition', f"attachment; filename={attachement.displayName}")
mail.attach(payload)
try:
server = smtplib.SMTP("mail.finf.uni-hannover.de", 587)
server.starttls()
server.login(sender.mail, LDAP_PASSWORD)
server.sendmail(sender.mail, [r.mail for r in receivers], mail.as_string())
server.quit()
print(f"Email \"{subject}\" an {receivers} erfolgreich versendet")
except smtplib.SMTPException:
print(f"Fehler beim Senden der Email \"{subject}\" von {sender} an {receivers}")
raise
# === Source Types ===
......@@ -59,48 +126,105 @@ class LocalCache(SourceTypes):
# === Pad Types ===
class BasePad:
def __init__(self, source: SourceTypes, *_, rawPath=None, **kwargs):
def __init__(self, source: SourceTypes, *_, displayName: str = None, rawPath=None, **kwargs):
self.source = source
self.metadata = kwargs
self.rawPath = rawPath or hashlib.md5(source.url.encode("utf-8"))
self.displayName = displayName or self.DEFAULT_DISPLAY_NAME()
self.compiledPath = None
def __repr__(self):
return f"{self.__class__.__name__}({self.source})"
def download(self, path, override=True):
def DEFAULT_DISPLAY_NAME(self):
return self.source.url
def DEFAULT_RAW_PATH(self):
return os.path.join(DATA_FOLDER, "raw", f"{self.displayName}.md")
def DEFAULT_COMPILED_PATH(self):
return os.path.join(DATA_FOLDER, "result", f"{self.displayName}.pdf")
def download(self, path=None, override=True):
if path is None:
path = self.DEFAULT_RAW_PATH()
content = self.source.retrieveContent()
if not override and os.path.exists(path):
raise FileExistsError(f"{path} already exists!")
with open(path, "w", encoding="utf-8") as d:
d.write(content)
self.rawPath = path
def compile(self, outFilePath, template: Template = None) -> int:
def compile(self, outFilePath: str = None, template: str = None) -> int:
if outFilePath is None:
outFilePath = self.DEFAULT_COMPILED_PATH()
outPath = Path(outFilePath)
os.makedirs(outPath.parent, exist_ok=True)
metadataStr = " " + " ".join([f'-M {key}="{val}"' for key, val in self.metadata.items()])
templateStr = ""
if template is not None and template != Template.UNDEFINED:
templateStr = f' --template="{template.fullPath()}" '
cmd = f'pandoc "{self.rawPath}"{metadataStr}{templateStr}-o "{str(outPath.absolute())}"'
return os.system(cmd)
if template is None and hasattr(self, "gremium"):
templateStr = f' --template="{getattr(self, "gremium").fullTemplate()}" '
elif template != "":
templateStr = f' --template="{template}" '
cmd = f'pandoc "{self.rawPath}" {metadataStr}{templateStr}-o "{str(outPath.absolute())}"'
ret = os.system(cmd)
if ret == 0:
self.compiledPath = outFilePath
return ret
class ProtocolPad(BasePad):
def __init__(self, source: SourceTypes, *_, date: datetime.datetime = None,
isFachgruppenvollversammlung: bool = False, isApproved: bool = False):
super().__init__(source)
isFachgruppenvollversammlung: bool = False, isApproved: bool = False, gremium: Gremium = None):
self.gremium = gremium or DEFAULT_GREMIUM
self.date = date
super().__init__(source)
self.isApproved = isApproved
self.isFachgruppenvollversammlung = isFachgruppenvollversammlung
def __repr__(self):
return super(ProtocolPad, self).__repr__()[:-1] + f", {self.date})"
def sendMailAsApproved(self) -> bool:
pass
def DEFAULT_DISPLAY_NAME(self):
return f"Protokoll {self.gremium.name} {self.date.strftime('%d.%m.%Y')}"
def sendMailAsUnapproved(self) -> bool:
pass
sender = MailParticipant(LDAP_MAIL, LDAP_NAME)
receiver = [
MailParticipant.FR()
]
text = (f"Hallo zusammen,\n\n"
f"anbei das vorläufige Protokoll der Fachrats-Sitzung vom {self.date.strftime('%d.%m.%Y')}.\n\n"
f"Änderungswünsche bitte direkt an mich, bzw. mir kurz Bescheid geben, wenn ihr was im Pad geändert habt.\n\n"
f"Viele Grüße\n"
f"{LDAP_NAME}\n")
subject = f"Vorläufiges Protokoll der FR-Sitzung vom {self.date.strftime('%d.%m.%Y')}"
try:
sendFRMail(text, subject, sender, receiver, attachement=self)
except Exception:
return False
return True
def sendMailAsApproved(self) -> bool:
if not self.isApproved:
print("Protocol is not approved yet!")
raise PermissionError("Protocol is not approved yet!")
sender = MailParticipant(LDAP_MAIL, LDAP_NAME)
receiver = [
MailParticipant.FR_INFO()
]
text = (f"Hallo zusammen,\n\n"
f"anbei das Protokoll der Fachrats-Sitzung vom {self.date.strftime('%d.%m.%Y')}.\n\n"
f"Alle Protokolle sind zu finden unter:\n"
f"https://cloud.finf.uni-hannover.de/index.php/s/Erw9Cbta0PruqZI\n\n"
f"Viele Grüße\n"
f"{LDAP_NAME}\n")
subject = f"Protokoll der FR-Sitzung vom {self.date.strftime('%d.%m.%Y')}"
try:
sendFRMail(text, subject, sender, receiver, attachement=self)
except Exception:
return False
return True
def uploadToCloud(self) -> bool:
pass
......@@ -214,9 +338,9 @@ if __name__ == '__main__':
raise AttributeError("Cannot have --finf AND --fsr")
if parsed.finf:
DEFAULT_TYPE = "finf"
DEFAULT_GREMIUM = GREMIUM_FR
if parsed.fsr:
DEFAULT_TYPE = "fsr"
DEFAULT_GREMIUM = GREMIUM_FSR
if parsed.override:
OVERRIDE_ACCEPTED = True
......
import tkinter as tk
from functools import wraps
import linkToPDF2 as pter
......@@ -38,33 +40,33 @@ class ProtokollGUI(tk.Tk):
def updatePadFrame(self):
folderName = list(self.folders.keys())[self.folderIndex]
self.folderLabel.configure(text=folderName)
self.currentPads = self.folders[folderName]
self.currentPads = [p for p in self.folders[folderName] if isinstance(p, pter.ProtocolPad)]
for i, pad in enumerate(self.currentPads):
assert isinstance(pad, pter.ProtocolPad) or isinstance(pad, pter.AttachementPad)
assert isinstance(pad, pter.ProtocolPad)
tk.Label(self.padFrame, text=pad.source.url,anchor=tk.W, bg="red").grid(row=i,column=0, sticky=tk.W) # URL
tk.Label(self.padFrame, text=pad.date,anchor=tk.W, bg="blue").grid(row=i,column=1) # Date
# Button Download
btnDownload = tk.Button(self.padFrame, text="Download")
btnDownload = tk.Button(self.padFrame, text="Download", command=pad.download)
btnDownload.grid(row=i, column=2)
# Button Compile
btnCompile = tk.Button(self.padFrame, text="Compile")
btnCompile = tk.Button(self.padFrame, text="Compile", command=pad.compile)
btnCompile.grid(row=i, column=3)
# Button Mail FR
btnMailFR = tk.Button(self.padFrame, text="Vorläufige FR-Mail")
btnMailFR = tk.Button(self.padFrame, text="Vorläufige FR-Mail", command=pad.sendMailAsUnapproved)
btnMailFR.grid(row=i, column=4)
# Button Mail FR-Info
btnMailFRInfo = tk.Button(self.padFrame, text="FR-Info Mail", state=tk.DISABLED)
btnMailFRInfo = tk.Button(self.padFrame, text="FR-Info Mail", state=tk.DISABLED, command=pad.sendMailAsApproved)
btnMailFRInfo.grid(row=i, column=5)
# Button Website
btnWebsite = tk.Button(self.padFrame, text="Approved Website", state=tk.DISABLED)
btnWebsite = tk.Button(self.padFrame, text="Approved Website", state=tk.DISABLED, command=pad.uploadToGrav)
btnWebsite.grid(row=i, column=6)
# Button Cloud
btnCloud = tk.Button(self.padFrame, text="Approved Cloud", state=tk.DISABLED)
btnCloud = tk.Button(self.padFrame, text="Approved Cloud", state=tk.DISABLED, command=pad.uploadToCloud)
btnCloud.grid(row=i, column=7)
......@@ -72,6 +74,7 @@ class ProtokollGUI(tk.Tk):
if __name__ == '__main__':
# gui = ProtokollGUI(pter.LocalCache("data/rootPad.md"))
gui = ProtokollGUI(pter.CodiMD("https://pad.finf.uni-hannover.de/protokolllinks_terces"))
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment