Skip to content

Commit

Permalink
Merge pull request #17 from kuczynskimaciej1/Sending
Browse files Browse the repository at this point in the history
Sending
  • Loading branch information
ikarmus2001 authored Jun 3, 2024
2 parents 8a7c30a + 9a20cbd commit b6208e6
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 138 deletions.
29 changes: 29 additions & 0 deletions DataSources/dataSourceAbs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from abc import abstractmethod


class IDataSource():
current_instance = None

@classmethod
def __subclasshook__(cls, subclass: type) -> bool:
return (hasattr(subclass, 'GetData') and
callable(subclass.GetData) and
hasattr(subclass, 'checkIntegrity') and
callable(subclass.checkIntegrity) and
hasattr(subclass, 'LoadSavedState') and
callable(subclass.LoadSavedState)
)

@abstractmethod
def GetData(self):
raise RuntimeError

@abstractmethod
def checkIntegrity(self):
raise RuntimeError

@abstractmethod
def LoadSavedState(self):
"""Collect all data saved in data source and instantiate adjacent model objects
"""
raise RuntimeError
83 changes: 37 additions & 46 deletions DataSources/dataSources.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations
from abc import ABCMeta, abstractmethod
from collections.abc import Iterable
from enum import Enum
from .dataSourceAbs import IDataSource
from pandas import read_csv, read_excel, DataFrame
from additionalTableSetup import GroupContacts
from models import DataImport, IModel, Contact
from group_controller import GroupController
from models import DataImport, Group, IModel, Contact, Template
import sqlalchemy as alchem
import sqlalchemy.orm as orm
from sqlalchemy.exc import IntegrityError
Expand All @@ -13,33 +14,6 @@
class SupportedDbEngines(Enum):
SQLite=1

class IDataSource():
current_instance = None

@classmethod
def __subclasshook__(cls, subclass: type) -> bool:
return (hasattr(subclass, 'GetData') and
callable(subclass.GetData) and
hasattr(subclass, 'checkIntegrity') and
callable(subclass.checkIntegrity) and
hasattr(subclass, 'LoadSavedState') and
callable(subclass.LoadSavedState)
)

@abstractmethod
def GetData(self):
raise RuntimeError

@abstractmethod
def checkIntegrity(self):
raise RuntimeError

@abstractmethod
def LoadSavedState(self):
"""Collect all data saved in data source and instantiate adjacent model objects
"""
raise RuntimeError

class DatabaseHandler(IDataSource):
def __init__(self, connectionString: str, tableCreators: Iterable[IModel],
engine: SupportedDbEngines = SupportedDbEngines.SQLite) -> None:
Expand Down Expand Up @@ -118,6 +92,20 @@ def LoadSavedState(self) -> None:
print(e)
continue
IModel.run_loading = False
self.runAdditionalBindings()


def runAdditionalBindings(self):
for g in Group.all_instances:
g.contacts = GroupController.get_contacts(g)

for t in Template.all_instances:
if t.dataimport_id != None:
for di in DataImport.all_instances:
if t.dataimport_id == di.id:
t.dataimport = di
break # TODO w razie dodanie większej ilości di - templatek


def Update(self, obj: IModel):
Session = orm.sessionmaker(bind=self.dbEngineInstance)
Expand Down Expand Up @@ -164,7 +152,6 @@ def GetData(self) -> DataFrame:
print("Błąd podczas wczytywania pliku XLSX:", e)
return None


class CSVHandler(IDataSource):
def __init__(self, path: str) -> None:
self.file_path = path
Expand All @@ -187,9 +174,10 @@ class GapFillSource():
all_instances: list[GapFillSource] = []

def __init__(self, source: IDataSource | IModel = Contact) -> None:
if isinstance(source, IDataSource):
self.iData: IDataSource = source
elif isinstance(source, DataImport) or isinstance(source, list):
# if isinstance(source, IDataSource):
# self.iData: IDataSource = source
# el
if isinstance(source, DataImport) or isinstance(source, list):
self.model_source: IModel = source
elif issubclass(source, IModel):
self.model_source: IModel = source
Expand All @@ -200,25 +188,28 @@ def __init__(self, source: IDataSource | IModel = Contact) -> None:
self.get_possible_values()

def get_possible_values(self):
if hasattr(self, "iData"):
idata_type = type(self.iData)
match(idata_type):
case type(DatabaseHandler):
# openTablePicker()
pass
case type(XLSXHandler):
# openSheetPicker()
pass
case type(CSVHandler):
# openCsvPicker()
pass
elif hasattr(self, "model_source"):
# if hasattr(self, "iData"):
# idata_type = type(self.iData)
# match(idata_type):
# case type(DatabaseHandler):
# # openTablePicker()
# pass
# case type(XLSXHandler):
# # openSheetPicker()
# pass
# case type(CSVHandler):
# # openCsvPicker()
# pass
# el
if hasattr(self, "model_source"):
if self.model_source == Contact:
self.possible_values = { name: attr for name, attr in Contact.__dict__.items() if isinstance(attr, hybrid_property) and attr != "all_instances" }
elif isinstance(self.model_source, DataImport):
self.possible_values = self.model_source.getColumnPreview()
else:
raise AttributeError(f"{type(self.model_source)} isn't supported")
else:
raise AttributeError(f"Incorrectly created GapFillSource, expected 'model_source'={self.model_source} to be present.")

@staticmethod
def getPreviewText(searched: str) -> str | None:
Expand Down
15 changes: 2 additions & 13 deletions Interface/AddContactWindow.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
from collections.abc import Callable, Iterable
from enum import Enum
from sqlalchemy.exc import IntegrityError
from types import TracebackType
from traceback import print_tb
from typing import Literal, Any, NoReturn
from tkinter import Event, Menu, simpledialog, ttk, Listbox, Tk, Text, Button, Frame, Label, Entry, Scrollbar, Toplevel, Misc, messagebox, Menubutton, Canvas,Checkbutton,BooleanVar, VERTICAL, RAISED
from tkinter.ttk import Combobox
from tkinter.constants import NORMAL, DISABLED, BOTH, RIDGE, END, LEFT, RIGHT, TOP, X, Y, INSERT, SEL, WORD
from group_controller import GroupController
from models import Contact, IModel, Template, Group
from tkhtmlview import HTMLLabel, HTMLText
from DataSources.dataSources import GapFillSource
from tkinter import Button, Label, Entry, Toplevel, messagebox
from models import Contact


class AddContactWindow(Toplevel):
Expand Down
14 changes: 2 additions & 12 deletions Interface/AppUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,10 @@ def __exit_clicked(self) -> NoReturn | None:
exit()

def update_templates(self):
# if isinstance(content, Template):
# if content not in self.szablony:
# self.szablony.append(content)
# else:
# [self.szablony.append(i)
# for i in content if i not in self.szablony]
self.szablony = Template.all_instances
self.__update_listbox(self.template_listbox, self.szablony)

def update_groups(self):
# if isinstance(g, Group):
# if g not in self.grupy:
# self.grupy.append(g)
# else:
# [self.grupy.append(i) for i in g if i not in self.grupy]
self.grupy = Group.all_instances
self.__update_listbox(self.grupy_listbox, self.grupy)

Expand Down Expand Up @@ -124,7 +113,8 @@ def __send_clicked(self) -> None:

u = User.GetCurrentUser()
self.sender.Send(self.selected_mailing_group, self.selected_template_group, u)
#send_email()
messagebox.showinfo("Zakończono wysyłanie", "Ukończono wysyłanie maili")


def __template_selection_changed(self, _event):
selected = self.template_listbox.curselection()
Expand Down
14 changes: 3 additions & 11 deletions Interface/ContactList.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
from collections.abc import Callable, Iterable
from enum import Enum
from sqlalchemy.exc import IntegrityError
from types import TracebackType
from traceback import print_tb
from typing import Literal, Any, NoReturn
from tkinter import Event, Menu, simpledialog, ttk, Listbox, Tk, Text, Button, Frame, Label, Entry, Scrollbar, Toplevel, Misc, messagebox, Menubutton, Canvas,Checkbutton,BooleanVar, VERTICAL, RAISED
from tkinter.ttk import Combobox
from tkinter.constants import NORMAL, DISABLED, BOTH, RIDGE, END, LEFT, RIGHT, TOP, X, Y, INSERT, SEL, WORD
from tkinter import Button, Frame, Label, Entry, Scrollbar, Toplevel, Canvas, Checkbutton, BooleanVar, VERTICAL
from tkinter.constants import BOTH, LEFT, RIGHT, X, Y
from group_controller import GroupController
from models import Contact, IModel, Template, Group
from tkhtmlview import HTMLLabel, HTMLText
from DataSources.dataSources import GapFillSource
from models import Contact, Group
from .AddContactWindow import AddContactWindow

class ContactList(Toplevel):
Expand Down
15 changes: 2 additions & 13 deletions Interface/Settings.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
from collections.abc import Callable, Iterable
from enum import Enum
from sqlalchemy.exc import IntegrityError
from types import TracebackType
from traceback import print_tb
from typing import Literal, Any, NoReturn
from tkinter import Event, Menu, simpledialog, ttk, Listbox, Tk, Text, Button, Frame, Label, Entry, Scrollbar, Toplevel, Misc, messagebox, Menubutton, Canvas,Checkbutton,BooleanVar, VERTICAL, RAISED
from tkinter import simpledialog, Tk, Button, Label, Entry, Toplevel, messagebox
from tkinter.ttk import Combobox
from tkinter.constants import NORMAL, DISABLED, BOTH, RIDGE, END, LEFT, RIGHT, TOP, X, Y, INSERT, SEL, WORD
from group_controller import GroupController
from models import Contact, IModel, Template, Group, User
from tkhtmlview import HTMLLabel, HTMLText
from DataSources.dataSources import GapFillSource
#from main import ui
#import MessagingService.smtp_data
from models import User


class Settings(Toplevel):
Expand Down
3 changes: 2 additions & 1 deletion Interface/TemplateEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ def update_preview():

def __save_template_clicked(self, template_name: str, template_content: str) -> None:
if template_name != "" and template_content != "":
self.currentTemplate = Template(_name=template_name, _content=template_content)
self.currentTemplate.name = template_name
self.currentTemplate.content = template_content
self.parent.update_templates()
self.destroy()

Expand Down
13 changes: 11 additions & 2 deletions MessagingService/senders.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from abc import ABCMeta, abstractmethod
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from smtplib import *
from models import Group, Template, User, Message

Expand Down Expand Up @@ -30,10 +32,17 @@ def Send(self, g: Group, t: Template, u: User) -> None:
server.login(u._email, u.password)
print("logged in successfully")
emailsSent = 0
print(f"Sending {t.name} to {len(g.contacts)} contacts")
for contact in g.contacts:
print(f"Sending do {contact.email}")
message = Message(t, contact)
#server.sendmail(u._email, contact.email, message.getParsedBody())
server.sendmail(u._email, contact.email, message.prepareMail())
mimemsg = MIMEMultipart("alternative")
mimemsg["From"] = u._email
mimemsg["To"] = contact.email
mimemsg["Subject"] = "MailBuddy mailing" # TODO Temat maila w TemplateEditor, aby dało się go parametryzować
xd = MIMEText(message.getParsedBody(), "html")
mimemsg.attach(xd)
server.send_message(mimemsg)
emailsSent += 1
server.quit()
print(f"Sent {emailsSent}")
Expand Down
6 changes: 3 additions & 3 deletions group_controller.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from additionalTableSetup import GroupContacts
from models import Group, Contact
from DataSources.dataSources import DatabaseHandler
from DataSources.dataSourceAbs import IDataSource

class GroupController:
dbh: DatabaseHandler = None
dbh: IDataSource = None
@classmethod
def setDbHandler(cls, handler: DatabaseHandler) -> None:
def setDbHandler(cls, handler: IDataSource) -> None:
cls.dbh = handler

@classmethod
Expand Down
Loading

0 comments on commit b6208e6

Please sign in to comment.