diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/Emanuele.iml b/.idea/Emanuele.iml
new file mode 100644
index 0000000..74d515a
--- /dev/null
+++ b/.idea/Emanuele.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d56657a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c687523
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APIManager.py b/APIManager.py
new file mode 100644
index 0000000..d917606
--- /dev/null
+++ b/APIManager.py
@@ -0,0 +1,57 @@
+import requests # to get/download the data from the api
+import json # parse the api data
+
+"""
+Class APIManager
+
+Gestisce la comunicazione HTTP con le API che forniscono i dati per le newsletter
+"""
+
+class APIManager:
+
+ """
+
+ Metodo: getNews
+
+ Invia una richiesta GET tramite la libreria requests alle API trovate online
+
+ - Costruisce il dizionario con i parametri della richiesta (header + querystring)
+ - Invia la richiesta con metodo GET all'url delle API (trovato online su rapidapi.com)
+ - decodifica la risposta json in un Dizionario e restituisce i campi d'interesse (titolo, corpo, url) della news
+
+ """
+
+
+ def getNews(self, category):
+
+ url = "https://contextualwebsearch-websearch-v1.p.rapidapi.com/api/search/NewsSearchAPI" # url of the api
+
+ # I parametri che andiamo a fornire alle API per la nostra richiesta.
+ # Questi sono da trovare nella documentazione delle API
+
+ querystring = {
+ "q": f"{category}", # query to follow, topic to get the about
+ "pageNumber": "1", # no of pages cna be change
+ "pageSize": "1", # size of the page should limit to paragraphs
+ "autoCorrect": "true", # spelling checker
+ "fromPublishedDate": "null", # date init
+ "toPublishedDate": "null" # date end
+ }
+
+ headers = {
+ 'x-rapidapi-key': "20c7edee10msh9b13ee9a5b2a529p1d3da9jsnbed33a1d0b94", # sending header
+ 'x-rapidapi-host': "contextualwebsearch-websearch-v1.p.rapidapi.com" # hosting sender
+ }
+
+ # creating requests
+ response = requests.request("GET", url, headers=headers, params=querystring)
+
+ responseText = response.text # leggiamo la risposta in JSON che ci ha dato il server (si trova nel campo text)
+ data = json.loads(responseText) # fornattiamo il JSON in un dizionario Python
+
+ # Estrai i dati dal dizionario data e ritorna i singoli campi
+ title = data['value'][0]['title']
+ body = data['value'][0]['body']
+ url = data['value'][0]['url']
+
+ return title, body, url
\ No newline at end of file
diff --git a/Database.py b/Database.py
new file mode 100644
index 0000000..95ff8f7
--- /dev/null
+++ b/Database.py
@@ -0,0 +1,289 @@
+import pandas as pd
+import os
+
+class DatabaseManager:
+
+ """
+ Metodo: checkDatabaseExists
+
+ Controlla se nella cartella dello script esistono i file csv che contengono i dati del software relativi agli utenti e le iscrizioni.
+ Se questi non esistono:
+ - Crea una cartella ./data
+ - Crea due Dataframe da Pandas che conterranno i dati relativi a utenti e iscrizioni.
+ - Salva i dataframe in due file .csv distinti all'interno della cartella ./data
+ """
+
+
+ def checkDatabaseExists(self):
+ # check if data folder exists in root folder, either creates it
+ if not os.path.exists("./data"):
+ print("Creating data folder...")
+ os.mkdir("data")
+
+ # database files
+ files = ["users.csv", 'subscriptions.csv'] #Dichiara una lista di stringhe con i nomi dei fila che vuole utilizzare
+ for file in files:
+ # check if files exists, else create them
+ if not os.path.exists(f"./data/{file}"):
+ print(f"{file} Not Exists")
+ print(f"Creating {file}...")
+
+ if 'users' in file:
+ #Crea il database con pandas e lo salva nel file users.csv
+ # init the dataframe
+ df = pd.DataFrame({
+ "name": [],
+ "username": [],
+ "password": [],
+ })
+
+ # save the dataframe to users
+ df.to_csv("./data/users.csv", index=False)
+
+ elif 'subscriptions' in file:
+ # init the dataframe
+ df = pd.DataFrame({
+ "username": [],
+ "email": [],
+ "business": [],
+ "finance": [],
+ "computer": [],
+ "games": [],
+ "entertainment": [],
+ "music": [],
+ "currentAffairs": [],
+ "health": [],
+ "lifestyle": [],
+ "sports": [],
+ "culture": [],
+ "religion": [],
+ })
+ # save the dataframe
+ df.to_csv("./data/subscriptions.csv", index=False)
+
+
+ '''
+ Metodo: getUsersDF
+
+ - Chiama il metodo checkDatabaseExists
+ - Legge e restituisce il dataframe con i dati relativi agli utenti
+
+ '''
+ def getUsersDF(self):
+ self.checkDatabaseExists()
+ df = pd.read_csv("./data/users.csv")
+ return df
+
+
+
+ '''
+ Metodo: getSubscriptionsDF
+
+ - Chiama il metodo checkDatabaseExists
+ - Legge e restituisce il dataframe con i dati relativi alle iscrizioni
+
+ '''
+ def getSubscriptionsDF(self):
+ self.checkDatabaseExists()
+ df = pd.read_csv("./data/subscriptions.csv")
+ return df
+
+
+ '''
+ Metodo: pintSubscriptions
+
+ Stampa la tabella delle iscrizioni
+
+ - Chiama il metodo getSubscriptionsDF
+ - Stampa il risultato
+
+ '''
+
+ def printSubscriptions(self):
+ print(self.getSubscriptionsDF())
+
+ '''
+ Metodo: removeUser
+ Param: username
+
+ - Controlla se username è contenuto nella tabella users.csv
+ - Elimina il record relativo a username in entrambe le tabelle
+ - Salva le modifiche effettuate
+
+ '''
+
+
+ def removeUser(self, uname):
+ dfSub = self.getSubscriptionsDF()
+ dfUsers = self.getUsersDF()
+ # if user found
+ if dfSub['username'].str.contains(uname).any(): # if username exists in any record
+ print("Username found in database")
+
+ # drop the row from users.csv and subscriptions.csv
+
+ # Rimuove la riga dalla tabella dfSub che contiene 'uname' nel campo username. Con inplace = True modifico la tabella senza creare una copia
+ dfSub.drop(dfSub[dfSub['username'] == uname].index, inplace=True)
+ # Rimuove la riga dalla tabella dfUsers che contiene 'uname' nel campo username. Con inplace = True modifico la tabella senza creare una copia
+ dfUsers.drop(dfUsers[dfUsers['username'] == uname].index, inplace=True)
+
+ # save the new csv
+ dfSub.to_csv("./data/subscriptions.csv", index=False)
+ dfUsers.to_csv("./data/users.csv", index=False)
+
+ print(f"'{uname}' removed sucessfully")
+ else: # if user not found
+ print("username not found")
+
+ '''
+ Metodo: getSubscriptionsFor
+ Param: username
+
+ - Ritorna il record del Dataframe che contiene il nome utente indicato e quindi tutte le sue iscrizioni
+
+ '''
+
+ def getSubscriptionsFor(self,uname):
+ df = self.getSubscriptionsDF()
+ data = df[df['username'] == uname]
+ return data #Ritorna il record del Dataframe che contiene il nome utente indicato e quindi tutte le sue iscrizioni
+
+
+ '''
+ Metodo: getSubscriptionsCategories
+
+
+ - Restituisce le colonne della tabella delle iscrizioni a partire dalla terza
+
+ '''
+
+ def getSubscriptionsCategories(self):
+ return self.getSubscriptionsDF().columns[2:]
+
+ '''
+ Metodo: subscribeUserToCategory
+
+ - Inserisce il carattere 'y' nella colonna indicata dal parametro category
+ - Salva il dataframe nell'apposito file.
+
+
+ '''
+
+ def subscribeUserToCategory(self,uname,category):
+ df = self.getSubscriptionsDF() #ottiene il Df delle iscrizioni
+ #Recupera il recordo con i dati dell'utente
+ df.loc[df.username == uname, category] = 'y'# inserisce 'y' alla colonna della categoria indicata nel record dell'utente indicato
+ print(df)
+ self.__saveSubscriptionsDF(df) # Chiama la funzione per salvare il file (to_csv)
+ print(f"{uname} successfully subscribed to {category}")
+
+ '''
+ Metodo: unsubscribeUserToCategory
+
+ - Inserisce il carattere 'n' nella colonna indicata dal parametro category
+ - Salva il dataframe nell'apposito file.
+
+
+ '''
+
+ def unsubscribeUserToCategory(self, uname, category):
+ df = self.getSubscriptionsDF()
+ df.loc[df.username == uname, category] = 'n' # inserisce 'n' alla colonna della categoria indicata nel record dell'utente indicato
+ self.__saveSubscriptionsDF(df)
+ print("Unsubscribed successfully")
+
+ def __saveSubscriptionsDF(self,df):
+ df.to_csv("./data/subscriptions.csv", index=False)
+
+ def __saveUsersDF(self,df):
+ df.to_csv("./data/users.csv", index=False)
+
+ '''
+ Metodo: subscribeToAll
+
+ - Inserisce il carattere 'n' in tutte le colonne della riga che si riferisce all'utente indicato
+ - Salva il dataframe nell'apposito file.
+
+
+ '''
+
+ def unsubscribeToAll(self, uname):
+ df = self.getSubscriptionsDF() #Ottiene il dataframe delle iscrizioni
+ row = df.loc[df.username == uname] #Prende il riferimento alla riga contenente i dati dell'utente
+
+ for category in self.getSubscriptionsCategories(): #Per ogni colonna della tabella
+ row[category] = 'n' #Inserisce il carattere 'n' nelle colonna di quella specifica riga
+
+ self.__saveSubscriptionsDF(df) #Salva
+
+
+ '''
+ Metodo: userExists
+ Param: uname
+
+ - Controlla se il nome utente inserito è contenuto in almeno un record della tabella
+
+ '''
+
+ def userExists(self,uname):
+ df = self.getUsersDF()
+ return df['username'].str.contains(uname).any() #any() serve a capire se esiste almeno un elemento qualsiasi dove la condizione di contains() è verificata
+
+ '''
+ Metodo: uregUser
+ Param: uname , name, email, password
+
+ - Controlla se il nome utente inserito è contenuto in almeno un record della tabella
+ - Crea I dizionari relativi alla tabella utente e a quella iscrizioni
+ - Imposta i valori iniziali
+ - Inserisce in append i due record con i dati dell'utente e le sue iscrizioni
+ - Salva i due file csv
+
+ '''
+
+ def regUser(self, uname,name, email,password):
+
+ if not self.userExists(uname):
+
+ #Crea il dizionario che rappresenta il record con i dati dell'utente (andrà nella tabella DFUsers)
+ udata = {
+ "username": uname,
+ "name": name,
+ "password": password,
+ }
+
+ #Crea il dizionario che rappresenta il record con i dati delle iscrizioni (andrà nella tabella DFSubscriptions)
+ sdata = {
+ "username": uname,
+ "email": email,
+ "business": 'n',
+ "finance": 'n',
+ "computer": 'n',
+ "games": 'n',
+ "entertainment": 'n',
+ "music": 'n',
+ "currentAffairs": 'n',
+ "health": 'n',
+ "lifestyle": 'n',
+ "sports": 'n',
+ "culture": 'n',
+ "religion": 'n',
+ }
+
+ dfUsers = self.getUsersDF() # Prende un riferimento alla tabella DFUsers
+ dfSub = self.getSubscriptionsDF() # Prende un riferimento alla tabella DFSubscriptions
+
+ #Inserisce con il metodo append (all'ultimo elemento) il record con i dati dell'utente nella tabella DFUsers
+ dfUsers = dfUsers.append(udata, ignore_index=True)
+ self.__saveUsersDF(dfUsers) #salva
+
+ #Inserisce con il metodo append (all'ultimo elemento) il record con i dati delle iscrizioni nella tabella DFSub
+ dfSub = dfSub.append(sdata, ignore_index=True)
+ self.__saveSubscriptionsDF(dfSub) #salva
+
+ # output if everything completes
+ print("You are successfully registed but not Subscribed to any list. Please login to your account to select any subscription.")
+ else:
+ print("Username already exists")
+
+
diff --git a/EmailManager.py b/EmailManager.py
new file mode 100644
index 0000000..8a58e47
--- /dev/null
+++ b/EmailManager.py
@@ -0,0 +1,61 @@
+import smtplib # to connect to gmail api
+from email.message import EmailMessage # email formatting and reshaping
+from APIManager import APIManager
+from Database import DatabaseManager
+
+
+class EmailManager:
+
+ dm = DatabaseManager()
+ api = APIManager()
+
+ # ---------------------- sends the email to a user
+ def sendEmailtoUser(self,email, subject, title, body, url):
+ EMAIL_ADDR = 'ema.univ@gmail.com' # email from
+ EMAIL_PASS = 'Emaun1v2k21' # password
+
+ # enter the content, prettify
+ content = f'''
+ News Alert
+ -----------------------------------------------
+
+ Title: {title}
+
+ {body}
+
+ Reference url: {url}
+ '''
+
+ msg = EmailMessage()
+ msg['Subject'] = subject # adding title as email subject
+ msg['From'] = EMAIL_ADDR # from email
+ msg['To'] = email # senders email
+ msg.set_content(content) # enter the content
+
+ with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
+ smtp.login(EMAIL_ADDR, EMAIL_PASS) # login to API
+ smtp.send_message(msg) # send the msg
+ print(f"Email sent to {email} for {subject}") # print msg acknowledgement
+
+ # ---------------------- fectch emails and categories
+ def sendEmails(self):
+ print('Its Email time')
+
+ print('''
+ ----------------------------
+ Sending Emails
+ ----------------------------
+ ''')
+ # import pandas as pd
+ df = self.dm.getSubscriptionsDF() # get subscription csv
+
+ cats = ['business', 'finance', 'computer', 'games', 'entertainment', 'music', 'currentAffairs', 'health',
+ 'lifestyle', 'sports', 'culture', 'religion']
+ for cat in cats:
+ print(">>", cat)
+ title, body, url = self.api.getNews(cat) # get the news of ceertain category
+
+ for email in df[df[cat] == 'y']['email']: # if cat has 'y' variable
+ self.sendEmailtoUser(email, title, title, body, url) # send the email
+
+
diff --git a/UserInterface.py b/UserInterface.py
new file mode 100644
index 0000000..61555e4
--- /dev/null
+++ b/UserInterface.py
@@ -0,0 +1,219 @@
+from Database import DatabaseManager
+
+'''
+Classe utilizzata per simulare un interfaccia utente
+'''
+class UserInterface:
+
+ dm = DatabaseManager() #Istanza del database manager utilizzata per le operazioni sui dati
+
+ '''
+ Metodo: showMainMenu
+
+ Presenta al terminale un'interfaccia all'avvio del programma che permette di scegliere le azioni da eseguire
+
+ '''
+
+ def showMainMenu(self):
+ menu = '''
+ ------------------ MAIN MENU ----------------------
+ Press following numbers to select
+
+ 1- Admin (Manage Requests, Remove Users)
+ 2- Login
+ 3- Register
+ 4- Send Emails (Admin only)
+ 5- Exit
+ ----------------------------------------------------
+ '''
+
+ print(menu) # print it menu
+
+ '''
+ Metodo: showAdminMenu
+
+ Presenta al terminale un'interfaccia per il pannello di controllo dell'amministratore di sistema, una volta che
+ l'amministratore ha effettuato l'accesso
+
+ Permette di leggere le iscrizioni di tutti gli utenti ed eliminare gli utenti iscritti
+ '''
+
+ def showAdminMenu(self):
+
+ while True:
+ # print the menu
+ menu = '''
+ ------------------- ADMIN MENU ---------------------
+ Press following numbers to select
+
+ 1- See all users' subscriptions
+ 2- Remove User
+ 3- Exit
+ ----------------------------------------------------
+ '''
+ print(menu)
+
+ _sel = int(input("> ")) # selt the input from menu
+
+ # shows all the database
+ if _sel == 1:
+ self.dm.printSubscriptions()
+ # remove the specific user
+ elif _sel == 2:
+ uname = input("Enter username: ") # enter the username
+ self.dm.removeUser(uname)
+
+ elif _sel == 3:
+ return
+ else:
+ print("Wrong selection")
+
+ '''
+ Metodo: showUserMenu
+
+ Presenta al terminale un'interfaccia per il pannello di controllo dell'utente , una volta che ha effettuato l'accesso
+
+ Permette di iscriversi o disiscriversi ai diversi topic di newsletter
+ '''
+
+ def showUserMenu(self, uname):
+
+ categories = self.dm.getSubscriptionsCategories()
+ while True:
+ # prints the menu
+ menu = '''
+ -------------------- USER MENU --------------------
+ Press following numbers to select
+
+ 1- See all subscriptions
+ 2- Subscribe to news feed
+ 3- Unsubscribe to news feed
+ 4- Unsubscribe to all News Feeds
+ 5- Exit
+ ---------------------------------------------------
+
+ '''
+ print(menu)
+
+ _sel = int(input("> ")) # select the input
+
+ # see all subscribed lists
+ if _sel == 1:
+ userData = self.dm.getSubscriptionsFor(uname).to_dict('records')[0] # see all list by that user
+ for k, v in userData.items():
+ if v == 'y':
+ print(k, ":", "Subcribed") # print the keys and "subscribed" next to it
+ #df.to_csv("./data/subscriptions.csv", index=False) # save the change to file
+
+ # sub to new category
+ elif _sel == 2:
+
+ print("You can subscribe to any of these categories \n", categories) # get all cols and print
+ catsel = input("type category (full): ") # select for the category
+
+ if not catsel in categories: # if not in cols
+ print("invalid selection")
+ else: # else
+ self.dm.subscribeUserToCategory(uname,catsel)
+
+ # unsub to any cay
+ elif _sel == 3:
+ print("You can unsubscribe to any of these categories \n", categories) # show all cats
+ catsel = input("type category (full): ") # input the cat
+
+ if not catsel in categories: # if invalid selection
+ print("invalid selection")
+ else:
+ self.dm.unsubscribeUserToCategory(uname,catsel)
+
+
+ elif _sel == 4:
+ self.dm.unsubscribeToAll(uname)
+
+ elif _sel == 5:
+
+ return
+
+ else:
+ print("Wrong selection")
+
+ '''
+ Metodo: showRegMenu
+
+ Presenta al terminale un'interfaccia per le registrazione dell'utente
+
+ Controlla l'esistenza del nuovo username e registra i nuovi dati
+
+ '''
+
+ def showRegMenu(self):
+
+
+ menu = '''
+ -------------------- REGISTER MENU --------------------
+ Press Enter your following details to Register yourself
+ -------------------------------------------------------
+ '''
+
+ print(menu) # print the menu
+
+ # inputs user's informations
+ name = input("Name: ") #
+ uname = input("Username (unique): ")
+
+ if self.dm.userExists(uname): # check if same username exists or not
+ print("Username already exists, Please choose different") # if yes then raise error
+ return
+
+ password = input("password: ")
+ email = input("email: ")
+
+ self.dm.regUser(uname,name,email,password)
+
+ '''
+ Metodo: showLoginAdmin
+
+ Presenta al terminale un'interfaccia per il login dell'amministratore
+
+ Controlla la correttezza dei dati e restituisce True o False in base ad essa
+
+ '''
+
+ def showLoginAdmin(self):
+ _user = input("username: ")
+ _pass = input("Password: ")
+
+ if (_user == 'admin') and (_pass == 'admin'):
+ return True # true if user pass matchs
+ else:
+ return False
+
+
+ '''
+ Metodo: showLoginUser
+
+ Presenta al terminale un'interfaccia per il login dell'utente
+
+ Controlla la correttezza dei dati e restituisce True o False in base ad essa
+
+ '''
+
+ def showLoginUser(self):
+
+ df = self.dm.getUsersDF()
+
+ _user = input('username: ')
+ _pass = input('password: ')
+
+ if self.dm.userExists(_user): # Se il nome utente che hai inserito esiste
+ userData = df[df['username'] == _user] #Legge i dati relativi al nome utente che è stato inserito {"nome,""username","Password"}
+ if userData['password'].str.contains(_pass).any() : #Se la password che hai inserito è corretta
+ name = userData['name'] # get the name by username from csv file
+ print(f"welcome {name.to_string().split()[1]}") #Questa operazione sulla variabile name serve a prendere solo la seconda parola della stringa presente (nel caso abbia due nomi)
+ return True, _user # return true with name
+ else:
+ return False, None
+ else:
+ return False, None
+
+
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..9b0ee21
--- /dev/null
+++ b/main.py
@@ -0,0 +1,59 @@
+from Database import DatabaseManager
+from EmailManager import EmailManager
+from UserInterface import UserInterface
+import pandas as pd
+import sys
+
+
+def main():
+
+ dm = DatabaseManager() # Si occupa delle operazioni sul DB
+ email = EmailManager() # Si occupa di inviare le email
+ ui = UserInterface() # Mostra i menù e chiede gli input
+
+
+ dm.checkDatabaseExists() # checks for the database folder and database files exists or not. Creates the files of not exists
+ ui.showMainMenu() # shows the main menu
+
+ select = int(input('>> ')) # waits for the input for 5mnts.
+
+ if select == 1: # Admin (Manage Requests, Remove Users) Apre il pannello Admin
+
+ if ui.showLoginAdmin(): # if admin true
+ print("Welcome admin")
+ ui.showAdminMenu() # shows admin menu
+ else:
+ print("Wrong username or password please retry") # raise error
+
+
+ elif select == 2: # Effettua il login dell'utente
+ re = ui.showLoginUser() # check if user exists re = (Esito, nomeUtente)
+ if re[0]: # Se il login va a buon fine
+ ui.showUserMenu(re[1]) # user menu
+ else:
+ print("User not exists or wrong password") # raises the error
+
+ elif select == 3:
+ ui.showRegMenu() # registration menu and control panel
+
+ elif select == 4: # Invia le email
+ re = ui.showLoginAdmin() # login admin
+ if re: # Se il login
+ print("Welcome admin")
+ email.sendEmails() # start sending the emails
+ else:
+ print("Wrong Username or Password")
+
+ elif select == 5:
+ print("Bye...") # exits the system
+ sys.exit() # exits the system
+
+
+# Press the green button in the gutter to run the script.
+if __name__ == '__main__':
+ while True:
+ main()
+
+
+
+