Beispiel #1
0
    def __init__(self, configfile: str):

        # Log messages to the server to get insights about user activities beside the print statements
        logging.basicConfig(
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            level=logging.INFO)

        # Log data to file FoodBot.log
        self.logger = logging.getLogger(__name__)
        fh = logging.FileHandler('CrispyBeefAlert.log')
        fh.setLevel(logging.DEBUG)
        self.logger.addHandler(fh)

        # read in config json
        with open(configfile) as f:
            self.config_json = json.load(f)

        # read in bot message
        with open(self.config_json["message_json"]) as f:
            self.message_json = json.load(f)
        # Create DataBase
        db_file = self.config_json["db_path"]
        """
        
        try:
            self.conn = sqlite3.connect(db_file, check_same_thread=False)
        except RuntimeError as e:
            print(e)
        """
        self.db = DataBaseHandler(db_file)
Beispiel #2
0
    def __init__(self):
        with open('config.json') as f:
            self.config_json = json.load(f)
        self.token = self.config_json["token"]

        self.db_file = self.config_json["db_path"]
        self.db = DataBaseHandler(self.db_file)

        # scrape menues
        today = datetime.today().date()
        if today.weekday() == 5:  # if saturnday
            today = today + timedelta(days=2)
        elif today.weekday() == 6:  # if saturnday
            today = today + timedelta(days=1)

        # read in bot message
        with open(self.config_json["message_json"]) as f:
            self.message_json = json.load(f)

        # get all supportet universities
        self.universities = self.db.get_universities()

        # scrape instances:
        self.scrapers = {"UZH_Mensa": UZHMenu(), "ETH_Mensa": ETHMenu()}

        self.non_weekend_menu = ["UZH_Mensa"]

        self.bot = telegram.Bot(token=self.config_json["token"])
Beispiel #3
0
    def __init__(self, configfile: str, list_of_mensa_json: str):
        with open(configfile) as f:
            self.config_json = json.load(f)

        # Create DataBase
        db_file = self.config_json["db_path"]
        self.db = DataBaseHandler(db_file)
        try:
            self.conn = sqlite3.connect(db_file)
        except RuntimeError as e:
            print(e)

        c = self.conn.cursor()

        # Create DataBase
        self.db.createDataBase()


        for path in list_of_mensa_json:
            uni = os.path.splitext(os.path.basename(path))[0]  # get the filename as the university name
            with open(path) as f:
                mensa_dict = json.load(f)
            for mensa_key in mensa_dict:
                print(mensa_key, mensa_dict[mensa_key]["id"], uni)
                try:
                    c.execute("""INSERT INTO available_mensa (mensa, online, university) VALUES (?, ?, ?);""",
                              (mensa_key, mensa_dict[mensa_key]["id"], uni))
                except:
                    print("already saved")
                for mensa_alias in mensa_dict[mensa_key]["alias"]:
                    try:
                        c.execute("""INSERT INTO mensa_alias (mensa, alias) VALUES (?, ?);""", (mensa_key, mensa_alias))
                    except:
                        print("already saved")
        self.conn.commit()

        # load previous data
        try:
            AlertBot = MenuAlertBot("./config.json")
            with open(self.config_json["json_path"]) as f:
                self.json_db = json.load(f)
            key_list = [*self.json_db]
            print("key_list",key_list)
            key_list.remove("lastUpdate")
            for mensa_dict_key in key_list:
                value_dict = self.json_db[mensa_dict_key]
                menus = [*value_dict]
                mensa_name = self.db.get_mensa_by_online_id(mensa_dict_key)
                for menu in menus:
                    for user_id in value_dict[menu]:
                        if not self.db.is_user_saved(user_id):
                            self.db.write_user(user_id)
                        AlertBot.save_alert(user_id, mensa_name, menu)
                        #self.db.write_alert(user_id, mensa_name, menu)
                        #if not self.db.is_user_saved(user_id):
                            #self.db.write_user(user_id)
        except e:
            print(e)
            print("no previous data loaded.")
Beispiel #4
0
def upload_delays_worker(data_dict, *args, **kwargs):
    """ Upload final delays for every train every day.
    active_trains = {train_number : (record, missing_counter)}"""
    if data_dict is None:
        logger.error('Empty dictionary received!')
        return
    active_trains = kwargs.get('active_trains')
    database = final_delay_worker_conf['database']
    user = final_delay_worker_conf['user']
    table = final_delay_worker_conf['table']

    data_dict_train_numbers = tuple(tr['@TrainNumber']
                                    for tr in data_dict['train_data'])
    for data in data_dict['train_data']:
        active_train_record = [  # Add train to active_trains
            (data_dict['creation_time'], data_dict['day'],
             data.get('@Relation'), data['@TrainNumber'], data['@Delay'],
             data['@Lat'], data['@Lon']), 0
        ]  # Reset missing_counter for every train present
        active_trains[data['@TrainNumber']] = active_train_record

    trains_arrived = []
    active_trains_to_del = []
    for key, value in active_trains.items():
        if key not in data_dict_train_numbers:
            active_trains[key][1] = value[1] + 1  # increment missing_counter
        if active_trains[key][1] >= final_delay_worker_conf[
                'missing_threshold']:
            trains_arrived.append(value[0])
            active_trains_to_del.append(key)

    for k in active_trains_to_del:
        active_trains.pop(k, None)

    logger.info('{} trains arrived.'.format(len(trains_arrived)))
    if trains_arrived == []:
        return  # nothing to upload

    try:
        db_handler = DataBaseHandler(database=database, user=user)
    except Exception as e:
        logger.error(
            'Could not initialize a DataBaseHandler instance with the given parameters:db:{}, user:{}'
            .format(database, user))
        logger.error(str(e))
        return trains_arrived

    db_handler.ping(reconnect=True)  # reconnects if needed
    insert = """INSERT INTO {} (creation_time, day, relation, train_number, delay, coord_lat, coord_lon)
            VALUES (%s, %s, %s, %s, %s, %s, %s);""".format(table)
    logger.info('Connected to database.')
    try:
        db_handler.upload_records_commit(insert, trains_arrived)
    finally:
        if db_handler is not None:
            db_handler.close()
            logger.info('Database connection closed.')
        return trains_arrived
Beispiel #5
0
def upload_all_worker(data_dict, *args, **kwargs):
    """ Worker thread utilized by the VonatDataGetter classself.
    Uploads all data in the trains """
    database = upload_all_worker_conf['database']
    user = upload_all_worker_conf['user']
    table = upload_all_worker_conf['table']
    if data_dict is None:
        logger.error('Empty dictionary received!')
        return
    try:
        db_handler = DataBaseHandler(database=database, user=user)
    except Exception as e:
        logger.error(
            'Could not initialize a DataBaseHandler instance with the given parameters:db:{}, user:{}'
            .format(database, user))
        logger.error(str(e))
        return

    db_handler.ping(reconnect=True)  # reconnects if needed
    insert = """INSERT INTO {} (creation_time, day, relation, train_number, line, delay, elvira_id, coord_lat, coord_lon, company)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);""".format(table)

    logger.info('Connected to database.')
    record_list = []
    for data in data_dict['train_data']:
        try:
            # create record
            rec = (
                data_dict['creation_time'],
                data_dict['day'],
                data.get('@Relation'),  # @Relation can be missing
                data['@TrainNumber'],
                data['@Line'],
                data['@Delay'],
                data['@ElviraID'],
                data['@Lat'],
                data['@Lon'],
                data['@Menetvonal'])
            # insert record
            logger.debug('Inserting record to list: {}'.format(data))
            record_list.append(rec)
        except KeyError as e:
            logger.info('Key {} missing'.format(e))
    try:
        db_handler.upload_records_commit(insert, record_list)
    finally:
        db_handler.close()
        logger.info('Database connection closed.')
Beispiel #6
0
class DfHandler():
    """docstring for DfHandler"""
    TABLE = 'table'
    LHS = 'lhs'
    RHS = 'rhs'

    def __init__(self, dataBase):
        """
		Parametre: une dataBase
		"""
        self.dbh = DataBaseHandler(dataBase)
        self.dataBaseName = dataBase

    def __isDep(self, table, lhs, rhs):
        """
		Parametres: lhs et rhs str nettoyés !!
		Return True si la table existe, lhs et rhs sont des attributs de la table
		"""

        listeTable = self.getTableName()

        if table not in listeTable:
            return False
        listeAttributs = self.dbh.getTableAttribute(
            table)  # liste des attributs de la table

        #verifie que les noms dans lhs sont des attributs de la table
        sLhs = lhs.split()

        for item in sLhs:
            if item not in listeAttributs:
                return False

        #verifie que rhs ne contient qu'un element
        if rhs.count(' ') != 0:
            return False

        #verifie que rhs est un attribut de la table
        if rhs not in listeAttributs:
            return False
        return True

    def __depExist(self, table, lhs, rhs):
        """
		Parametres: une certaine df
		Return True si la df existe, False sinon
		"""

        r = self.dbh.getOneDep(table, lhs, rhs)
        return r != None and len(r) == 3

    def removeDep(self, table, lhs, rhs):
        """
		Parametres: une certaine df
		Return True si la df a bien ete supprimee, False sinon
		"""
        #verifie que la df a bien ete supprimee
        if self.__depExist(table, lhs, rhs):
            self.dbh.removeDep(table, lhs, rhs)
            return True
        else:
            return False

    def getAllDep(self):
        """
		Return toutes les dependances de FuncDep
		"""
        return self.dbh.getAllDep()

    def insertDep(self, table, lhs, rhs):
        """
		Parametres: une certaine df
		Return True si la df a pu etre ajoutee, False sinon
		"""
        if self.__isDep(table, lhs,
                        rhs) and not self.__depExist(table, lhs, rhs):
            r = self.dbh.insertDep(table, lhs, rhs)
            if r != None:
                return True
            else:
                return False

        else:
            return False

    def editDep(self, table, lhs, rhs, newData, whatModif):
        """
		Parametre : une certaine df, la nouvelle donnee, ce qui doit etre modifie
		Return True si la df a pu etre modifiee, False sinon
		"""

        #on verifie que la df existe
        if not self.__depExist(table, lhs,
                               rhs):  #on verifie que la df existe deja
            return False

        #pour la modif de la table
        if whatModif == DfHandler.TABLE:
            if not self.__isDep(newData, lhs, rhs) or self.__depExist(
                    newData, lhs, rhs):
                return False
            else:
                self.dbh.editTableDep(table, lhs, rhs, newData)
                return True

        #pour la modif du Rhs
        elif whatModif == DfHandler.RHS:
            if not self.__isDep(table, lhs, newData) or self.__depExist(
                    table, lhs,
                    newData):  #on verifie que la nouvelle df est bien une df
                return False
            else:  #si c'est pas une df
                self.dbh.editRhsDep(table, lhs, rhs, newData)
                return True

        #pour modif Lhs
        elif whatModif == DfHandler.LHS:
            if not self.__isDep(table, newData, rhs) or self.__depExist(
                    table, newData, rhs):
                return False
            else:
                self.dbh.ediLhsDep(table, lhs, rhs, newData)
                return True

        #Modif inconnu
        else:
            return False

    def isBcnf(self, table):
        """
		je selectionne tous les lhs je les split en un tableau 
		il faut qu'une df lhs --> a avec a notIn lhs
		Return True si c'est en BCNF, False sinon
		"""
        allLhs = self.dbh.getAllLhs(table)
        allAttributs = self.dbh.getTableAttribute(table)
        for lhs in allLhs:
            lhsTab = lhs.split()
            for attribute in allAttributs:
                if attribute not in lhsTab:
                    if not self.isLogicConsequence(table, lhs, attribute,
                                                   False):
                        return False
        return True

    def getAllTableInFuncDep(self):
        """
		Return tous les noms de tables presentes dans la table FuncDep
		"""
        return self.dbh.getAllTableInFuncDep()

    def getTableName(self):
        """
		Return tous les noms de table present dans la base de donnee
		"""

        return self.dbh.getTableName()

    def getDepByRelation(self, table):
        """
		Parametre: une table
		Return toutes les dependances concernant cette table
		"""

        return self.dbh.getDepByRelation(relation)

    def is3nf(self, table):
        """
		Parametre: une table
		Return True si c'est en 3NF, False sinon
		"""

        if self.prem3NF(table) or self.lhs3NF(table):
            return True
        else:
            return False

    def prem3NF(self, table):
        """
		Parametre: une table
		Return True si tous les attributs sont dans au moins une cle, False sinon
		"""
        attribute = self.dbh.getTableAttribute(table)
        cles = self.getCle(table)
        for att in attribute:
            isInCle = False
            for cle in cles:
                if att in cle:
                    isInCle = True
            if not isInCle:
                return False
        return True

    def lhs3NF(self, table):
        """
		Parametre: une table
		Return True si tous les Lhs sont des cles, False sinon
		"""

        tabLhs = self.dbh.getAllLhs(table)
        tabCle = self.getCle(table)

        for i in range(0, len(tabLhs)):
            lhs = tabLhs[i]

            if lhs.split() not in tabCle:
                return False
        return True

    def __getAttributeNeverInRhs(self, table):
        """
		Parametre: une table
		Return tous les attributs de cette table n'etant pas dans les Rhs des df associees a la table
		"""
        attribute = self.dbh.getTableAttribute(table)
        allRhs = self.dbh.getAllRhs(table)
        never = []
        for item in attribute:
            if item not in allRhs:
                never.append(item)
        return never

    def __canContinue(self, ligne, nbrAttribute):
        """
		Parametre: ligne -> ligne courante de la cle, nbrAttribute -> nombre total d'attribut
		Return True si on peut continuer, False sinon
		"""

        if len(ligne) == 0:
            return True
        for cle in ligne:

            if len(cle) >= nbrAttribute:
                return False

        return True

    def __exept(self, A, B):
        """
		Parametre: 2 arguments
		Return A\B
		"""
        retour = []
        for item in A:
            if (item not in B):
                retour.append(item)
        return retour

    def isAKey(self, futureKey, attribute, table):
        """
		Parametre: une possible cle, un attribut, une table
		Return True si c'est une cle, False sinon
		"""

        att = copy.deepcopy(attribute)
        possibleKey = copy.deepcopy(futureKey)
        result = self.__doFermeture(self.dbh.getDepByRelation(table),
                                    possibleKey)
        result.sort()
        att.sort()

        return result == att

    def sansBacN(self, s):
        """
		Parametre : un nom
		Return le nom sans \n
		"""
        s2 = copy.deepcopy(s)
        if '\n' in s2:
            s2.remove('\n')
        return s2

    def canAddToCle(self, cles, item):
        """
		Parametre : une cle, un tableau
		Return True si la cle peut etre ajoutee, False sinon
		"""

        for cle in cles:
            if self.__isIn(cle, item):
                return False
        return True

    def __recurseCle(self, attribute, cles, supercle, table):
        """
		Parametre: un attribut, une cle, une supercle et une table
		Return les cles et supercles
		"""
        if (not self.__canContinue(cles, len(attribute))) or (
                not self.__canContinue(supercle, len(attribute))):
            return cles, supercle

        if len(cles) == 0 and len(supercle) == 0:
            newLine = self.__getAttributeNeverInRhs(table)
            newLine.sort()
            if len(newLine) == 0:
                for i in range(0, len(attribute)):
                    it = [attribute[i]]
                    it.sort()
                    if self.isAKey(it, attribute, table) and self.canAddToCle(
                            cles, [it]):
                        if it not in supercle:
                            cles.append(it)
                    if newLine not in supercle:
                        supercle.append(it)
            else:
                if self.isAKey(newLine, attribute, table) and self.canAddToCle(
                        cles, [newLine]) and newLine not in cles:
                    cles.append(newLine)
                if newLine not in supercle:
                    supercle.append(newLine)
            return self.__recurseCle(attribute, cles, supercle, table)

        else:
            newSuperCle = []
            for item in supercle:
                rajout = self.__exept(attribute, item)
                for itemToAdd in rajout:
                    new = [itemToAdd]
                    new.extend(item)
                    new.sort()
                    if self.isAKey(new, attribute, table) and self.canAddToCle(
                            cles, new) and new not in cles:
                        cles.append(new)
                    if new not in newSuperCle:
                        newSuperCle.append(new)
                if self.isAKey(item, attribute,
                               table) and item not in newSuperCle:
                    newSuperCle.append(item)
            return self.__recurseCle(attribute, cles, newSuperCle, table)

    def getCle(self, table):
        """
		Parametre : une table
		Return les cles de la table
		"""
        cles, supercle = self.__recurseCle(self.dbh.getTableAttribute(table),
                                           [], [], table)
        return cles

    def getSuperCle(self, table):
        """
		Parametre: une table
		Return les supercles
		"""

        cles, supercle = self.__recurseCle(self.dbh.getTableAttribute(table),
                                           [], [], table)
        return supercle

    def getCouvertureMinimale(self, table):
        """
		Parametre: une table
		Return la couverture minimale ('ensemble irreductible de df')
		"""
        deps = self.dbh.getDepByRelation(table)
        newDeps = []

        #pour chaque df fermeture( toute les df, et une partie du lhs de la df)
        #si femeture == lhs (selectionnee) on peut la reduire a ça
        #si non on essaye une autre partie du lhs
        #si tout essayé on ne peut pas reduire

        for df in deps:
            lhs = df[1].split()
            if len(lhs) == 1:
                newDeps.append(df)
            else:
                canReplace = None
                for att in lhs:
                    fermeture = self.__doFermeture(deps, lhs)

                    if fermeture == [att]:
                        canReplace = att
                        break
                if canReplace != None:
                    newDeps.append([df[0], canReplace, df[2]])
                else:
                    newDeps.append(df)

        deps = newDeps
        newDeps = []
        for dep in deps:
            if not self.isLogicConsequence(
                    dep[0], dep[1], dep[2],
                    True):  # si elle n'est pas redondante
                newDeps.append(dep)  #on l'ajoute a la couverture minimale
        return newDeps

    def createNewDataBase(self, newDataBaseName, data):
        """
		Parametre : un nom de DataBase, des donnees
		Cree une nouvelle base de donnee avec les tables issues de la decomposition en 3NF
		"""

        if self.dataBaseName == newDataBaseName:
            newDataBaseName += '2'
        shutil.copyfile(self.dataBaseName, newDataBaseName)

        dbhIn = DataBaseHandler(newDataBaseName)
        dbhIn.dropTable('FuncDep')
        dbhIn.closeDataBase()
        dbhIn = DataBaseHandler(newDataBaseName)
        rep = {}
        tables = self.getTableName()
        for item in tables:
            attributesList = self.dbh.getTableAttribute(item)
            for att in attributesList:
                if att not in rep:
                    rep.update({att: item})

        for table in data:
            oldTableName = []
            attributes = table[1]
            for at in attributes:
                oldTableName.append(rep[at])
            newTableName = table[0]
            if newTableName in tables:
                newTableName += '2'

            dbhIn.createTable(
                newTableName, attributes,
                oldTableName)  #cree la table et y insere les donnees
            dep = table[2]
            if len(dep) == 3:
                dbhIn.insertDep(dep[0], dep[1], dep[2])
        dbhIn.removeOldTable(oldTableName)

    def getDecomposition3nf(self, table):
        """
		Parametre: une table
		Return la decomposition en 3NF de la table
		"""
        irreductible = self.getCouvertureMinimale(table)
        newDataBase = []
        keys = self.getCle(table)
        tableid = 1
        while len(irreductible) > 0:
            table = [
                str(tableid)
            ]  # newDataBase[['numTable', [attributs], ['numTable', lhs, rhs]],[]]
            dep = irreductible.pop(0)
            lhs = dep[1].split()
            rhs = dep[2].split()

            attribute = []
            attribute.extend(lhs)
            attribute.extend(rhs)
            table.append(attribute)

            df = [str(tableid), dep[1], dep[2]]
            table.append(df)

            newDataBase.append(table)
            tableid += 1
        doBreak = False
        for cle in keys:
            for table in newDataBase:
                if not self.__isIn(cle, table[1]):
                    newDataBase.append([str(tableid), cle, []])
                    doBreak = True
                    break
            if doBreak:
                break
        return newDataBase

    def cleanDep(self, table):
        """
		Parametre : une table
		Return les df de cette table sans le parametre table dedans
		"""

        for df in table:
            df.pop(0)
        return table

    def closeDataBase(self):
        """
		ferme la base de donnee
		"""
        self.dbh.closeDataBase()

    def satisfaitPasDF(self, table, lhs, rhs):
        """
		Parametre: une certaine df
		Return les lignes qui ne satisfont pas la df (table, lhs, rhs)
		"""

        if self.__depExist(table, lhs, rhs):
            return self.dbh.DFisOk(table, lhs, rhs)
        else:
            return None

    def getInutileDF(self, table=None, lhs=None, rhs=None):
        """
		Parametre: une certaine df
		Return les df qui sont inutiles (celles dont des arguments n'existent plus, les conséq logiques et celle qi ne respectent pas la table)
		"""
        #si table et lhs et rhs valent None, regarde pour une df
        notDf = []
        isConsequenceLogic = []
        pasRespectee = [
        ]  #[[table, lhs, rhs, [ligne qui ne respenctent pas la df]], [], [], ..., []]
        if table == None and lhs == None and rhs == None:
            allDf = self.dbh.getAllDep()
            for dep in allDf:
                if not self.__isDep(dep[0], dep[1], dep[2]):
                    notDf.append(dep)
                elif self.isLogicConsequence(dep[0], dep[1], dep[2], True):
                    logic.append(dep)
                elif len(self.satisfaitPasDF(dep[0], dep[1], dep[2])) != 0:
                    retour = self.satisfaitPasDF(dep[0], dep[1], dep[2])
                    tab = [dep, retour]
                    pasRespectee.append(tab)
        elif table != None and lhs != None and rhs != None:
            if not self.__depExist(table, lhs, rhs):
                notDf.append([table, lhs, rhs])
            elif self.isLogicConsequence(table, lhs, rhs, True):
                isConsequenceLogic.append([table, lhs, rhs])
            elif len(self.satisfaitPasDF(table, lhs, rhs)) != 0:
                pasRespectee.append(
                    [table, lhs, rhs,
                     self.satisfaitPasDF(table, lhs, rhs)])

        return notDf, isConsequenceLogic, pasRespectee

    def isLogicConsequence(self, table, lhs, rhs, remove):
        """
		Parametre: une certaine df et un booleen
		retourn True si elle est une conséq logique, False sinon
		"""
        if self.__depExist(table, lhs, rhs):
            ens = self.dbh.getDepByRelation(table)
            if remove:
                ens.remove([table, lhs, rhs])
            result = self.__doFermeture(ens, lhs.split())

            return rhs in result
        else:
            return None

    def __doFermeture(self, dFs, x):
        """
		Parametre : une df et un attribut
		Return la fermeture de l'ensemble d attribut par rapport a l'ensemble de df
		"""

        df = copy.deepcopy(dFs)
        newDep = copy.deepcopy(x)
        oldDep = None

        while oldDep != newDep:
            oldDep = copy.deepcopy(newDep)
            for item in df:
                w = item[1]
                z = item[2]

                if self.__isIn(w.split(), newDep):
                    if z not in newDep:
                        newDep.append(z)
        return newDep

    def __isIn(self, small, big):
        """
		Parametre: 1 element inclus dans un autre
		Return True if small is in big else return False
		"""
        for sItem in small:
            sItemIsInBig = False
            for bItem in big:
                if sItem == bItem:
                    sItemIsInBig = True
            if sItemIsInBig == False:
                return False
        return True
Beispiel #7
0
    def createNewDataBase(self, newDataBaseName, data):
        """
		Parametre : un nom de DataBase, des donnees
		Cree une nouvelle base de donnee avec les tables issues de la decomposition en 3NF
		"""

        if self.dataBaseName == newDataBaseName:
            newDataBaseName += '2'
        shutil.copyfile(self.dataBaseName, newDataBaseName)

        dbhIn = DataBaseHandler(newDataBaseName)
        dbhIn.dropTable('FuncDep')
        dbhIn.closeDataBase()
        dbhIn = DataBaseHandler(newDataBaseName)
        rep = {}
        tables = self.getTableName()
        for item in tables:
            attributesList = self.dbh.getTableAttribute(item)
            for att in attributesList:
                if att not in rep:
                    rep.update({att: item})

        for table in data:
            oldTableName = []
            attributes = table[1]
            for at in attributes:
                oldTableName.append(rep[at])
            newTableName = table[0]
            if newTableName in tables:
                newTableName += '2'

            dbhIn.createTable(
                newTableName, attributes,
                oldTableName)  #cree la table et y insere les donnees
            dep = table[2]
            if len(dep) == 3:
                dbhIn.insertDep(dep[0], dep[1], dep[2])
        dbhIn.removeOldTable(oldTableName)
Beispiel #8
0
    def __init__(self, dataBase):
        """
		Parametre: une dataBase
		"""
        self.dbh = DataBaseHandler(dataBase)
        self.dataBaseName = dataBase
Beispiel #9
0
class MenuAlertBot:
    def __init__(self, configfile: str):

        # Log messages to the server to get insights about user activities beside the print statements
        logging.basicConfig(
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            level=logging.INFO)

        # Log data to file FoodBot.log
        self.logger = logging.getLogger(__name__)
        fh = logging.FileHandler('CrispyBeefAlert.log')
        fh.setLevel(logging.DEBUG)
        self.logger.addHandler(fh)

        # read in config json
        with open(configfile) as f:
            self.config_json = json.load(f)

        # read in bot message
        with open(self.config_json["message_json"]) as f:
            self.message_json = json.load(f)
        # Create DataBase
        db_file = self.config_json["db_path"]
        """
        
        try:
            self.conn = sqlite3.connect(db_file, check_same_thread=False)
        except RuntimeError as e:
            print(e)
        """
        self.db = DataBaseHandler(db_file)

    def __uniquify_menu(self, user_menu: str) -> str:
        max_score = 0
        found_menu = None
        for db_menu in self.db.selectMenus():
            print("db_menu", db_menu)
            score = fuzz.token_set_ratio(db_menu, user_menu)
            print("score", score)
            if score > 50 and score > max_score:
                max_score = score
                found_menu = db_menu
        return found_menu

    def __search_mensa_by_alias(self, user_mensa):
        found_mensi = []
        for db_mensa, db_alias in self.db.selectMensaAlias():
            if fuzz.token_set_ratio(
                    db_alias, user_mensa) > 50 and db_mensa not in found_mensi:
                found_mensi.append(db_mensa)
        return found_mensi

    def save_alert(self, chat_id: int, user_mensa: str, user_menu: str) -> str:
        """saves the requested menu/mensa alert after checking that the alert was not already saved
        and checks for matching menus that are already saved to prevent searching for same menus with slightly different
        names.

        :param chat_id:
        :type chat_id: Integer
        :param user_mensa:
        :type user_mensa: String
        :param user_menu:
        :type user_menu: String

        :return: Success/ Error Message
        :rtype: String
        """
        user_language = self.db.get_user_language(chat_id)

        found_mensi = self.__search_mensa_by_alias(user_mensa)
        if len(found_mensi) > 0:
            found_menu = self.__uniquify_menu(user_menu)
            print("founde menu", found_menu)
            for found_mensa in found_mensi:
                if not found_menu:
                    print("add new menu to database")
                    # add new menu to database
                    self.db.writeMenu(str(user_menu))
                    # save alert under user given name
                    self.db.write_alert(chat_id, found_mensa, user_menu)
                    return '\n'.join(
                        self.message_json["save_alert"]["new_menu_saved"]
                        [user_language]) % (user_menu, found_mensa)
                else:
                    if self.db.isAlertSaved(chat_id, found_mensa, found_menu):
                        # same menu already saved for this user
                        return '\n'.join(self.message_json["save_alert"]
                                         ["menu_already_saved"][user_language])
                    else:
                        # save alert under user already know name
                        self.db.write_alert(chat_id, found_mensa, found_menu)
                        return '\n'.join(
                            self.message_json["save_alert"]["similar_Menu"]
                            [user_language]) % (found_menu, found_mensa)
        else:
            # no matching mensa found
            return '\n'.join(self.message_json["save_alert"]["no_mensa_found"]
                             [user_language])

    def delete_alert(self, chat_id: int, user_mensa: str,
                     user_menu: str) -> str:
        """delete the requested menu/mensa alert after checking that the alert was already saved.

        :param chat_id:
        :type chat_id: Integer
        :param user_mensa:
        :type user_mensa: String
        :param user_menu:
        :type user_menu: String

        :return: Success/ Error Message
        :rtype: String
        """
        user_language = self.db.get_user_language(chat_id)

        found_mensi = self.__search_mensa_by_alias(user_mensa)
        if len(found_mensi) > 0:
            found_menu = self.__uniquify_menu(user_menu)
            if not found_menu:
                return '\n'.join(self.message_json["delete_alert"]
                                 ["no_menu_found"][user_language])
            else:
                for found_mensa in found_mensi:
                    if self.db.isAlertSaved(chat_id, found_mensa, found_menu):
                        # menu alert for user was saved -> delete
                        self.db.delete_alert(chat_id, found_mensa, found_menu)
                        return '\n'.join(
                            self.message_json["delete_alert"]["deleted_alert"]
                            [user_language]) % (found_menu, found_mensa)

                    else:
                        return '\n'.join(self.message_json["delete_alert"]
                                         ["no_saved_alert"][user_language])

        else:
            return '\n'.join(self.message_json["delete_alert"]
                             ["no_mensa_found"][user_language])

    def get_mensa(self, chat_id):
        user_language = self.db.get_user_language(chat_id)
        sting_list = self.message_json["mensa_list"][user_language]
        uni_list = self.db.get_universities()
        for uni in uni_list:
            sting_list = sting_list + [uni + "\n"]
            sting_list = sting_list + self.db.get_mensa_by_uni(uni) + ["\n"]
        return '\n'.join(sting_list)

    def get_alert_overview(self, chat_id: int):
        user_language = self.db.get_user_language(chat_id)
        admin_id = self.config_json["admin_chat_id"]
        if (chat_id == admin_id):
            str = ""
            for entry in self.db.countAlerts():
                print(entry)

                mensa, menu, sub_count = entry[0], entry[1], entry[2]
                print(mensa, menu, sub_count)
                str += '\n'.join(self.message_json["overview"]
                                 [user_language]) % (menu, mensa, sub_count)
            return str
        else:
            return '\n'.join(self.message_json["admin_only"][user_language])

    def save_message(self, chat_id, message):
        self.db.write_message(chat_id, message)
        # forward to admin
        admin_id = self.config_json["admin_chat_id"]
        admin_language = self.db.get_user_language(admin_id)
        self.sendMessage(
            '\n'.join(self.message_json["user_sent_message"][admin_language]) %
            (chat_id, message), admin_id)

    def change_language(self, chat_id, message):
        user_language = self.db.get_user_language(chat_id)
        if message in self.message_json["available_languages"]:
            if self.db.get_user_language(chat_id) == message:
                return '\n'.join(self.message_json["change_language"]
                                 ["already_language"][user_language]) % message
            else:
                self.db.change_user_language(chat_id, message)
                user_language = self.db.get_user_language(chat_id)
                return '\n'.join(self.message_json["change_language"]
                                 ["changed_language"][user_language]) % message
        else:
            return '\n'.join([
                self.message_json["change_language"]["no_valid_language"]
                [user_language], self.message_json["available_languages"]
            ])

    def start_bot(self) -> None:
        """ Booting up the Bot which listens for user interaction

        :return: None
        """
        updater = Updater(self.config_json["token"])
        dispatcher = updater.dispatcher

        # Tell dispatcher which functions are to execute according to which user commands
        dispatcher.add_handler(CommandHandler("start", self.start))
        dispatcher.add_handler(CommandHandler("help", self.help))
        dispatcher.add_handler(CommandHandler("hilfe", self.help))
        dispatcher.add_handler(CommandHandler("save", self.save))
        dispatcher.add_handler(CommandHandler("delete", self.delete))
        dispatcher.add_handler(CommandHandler("mensa", self.mensa))
        dispatcher.add_handler(CommandHandler("overview", self.overview))
        dispatcher.add_handler(CommandHandler("language", self.language))

        dispatcher.add_handler(MessageHandler(Filters.command, self.unknown))

        # Handler that listens to user messages as text and reacts to it
        dispatcher.add_handler(MessageHandler(Filters.text, self.listen))

        # Error handling that calls the error function to log the error messages
        dispatcher.add_error_handler(self.error)

        # Start bot
        updater.start_polling()

        # Keep the bot alive even though nobody is requesting anything
        print("Bot has booted up")
        updater.idle()

    # function that is executed when the user types /start
    def start(self, bot: telegram.bot.Bot, update) -> None:
        """ When user starts interaction with the Bot.
        Welcoming user and tell them how to use the Bot.
        Also save the user to the list of users in the Database
        And save "start" into the list of messages.


        :param bot:
        :param update:
        :return:
        """
        # save user to list of users:
        if not self.db.is_user_saved(int(update.message.chat_id)):
            self.db.write_user(update.message.chat_id)

        # save message, note it as "start"
        self.save_message(update.message.chat_id, "start")

        # Sending some welcome messages and instructions about the bot usage
        msg = self.message_json["welcome"][self.db.get_user_language(
            update.message.chat_id)]
        bot.send_message(chat_id=update.message.chat_id,
                         text='\n'.join(msg),
                         parse_mode=telegram.ParseMode.MARKDOWN)

    # function that is executed when the user types /help
    def help(self, bot, update) -> None:
        # send back  help instructions
        bot.send_message(
            chat_id=update.message.chat_id,
            text='Um ein Menu zu speichern \nschreibe /save <Mensa>, <Menu> \n'
            'Beispiel: /save Clausiusbar, Crispy Beef \n\n'
            'Wenn du für ein Gericht keinen Alarm mehr bekommen möchtest, \nschreibe /delete <Mensa>, <Menu> \n'
            'Beispiel: /delete Clausiusbar, Crispy Beef',
            parse_mode=telegram.ParseMode.MARKDOWN)

    def save(self, bot: telegram.bot.Bot, update: telegram.Update) -> None:
        """Save a menu-mensa pair for which the communicating user wants to be notified.

        :param bot:
        :type bot: telegram.bot.Bot
        :param update:
        :type update: telegram.Update
        :return: None
        """

        if not self.db.is_user_saved(update.message.chat_id):
            msg = self.message_json["user_not_found"][
                self.db.get_user_language(update.message.chat_id)]
        else:
            # save message, note it as "start"
            self.save_message(update.message.chat_id, update.message.text)

            response = update.message.text
            msg = self.save_alert(update.message.chat_id,
                                  response.split(',')[0],
                                  response.split(',')[1])
        bot.send_message(chat_id=update.message.chat_id,
                         text=msg,
                         parse_mode=telegram.ParseMode.MARKDOWN)

    def delete(self, bot: telegram.bot.Bot, update: telegram.Update) -> None:
        """
        Delete entry from subscription JSON
        :param bot: Bot
        :param update: Bot-Update (Message)
        :return: Nothing
        """
        # save message, note it as "start"
        self.save_message(update.message.chat_id, update.message.text)

        if not self.db.is_user_saved(update.message.chat_id):
            msg = self.message_json["user_not_found"][
                self.db.get_user_language(update.message.chat_id)]
        else:
            response = update.message.text
            msg = self.delete_alert(update.message.chat_id,
                                    response.split(',')[0],
                                    response.split(',')[1])
        bot.send_message(chat_id=update.message.chat_id,
                         text=msg,
                         parse_mode=telegram.ParseMode.MARKDOWN)

    def mensa(self, bot: telegram.bot.Bot, update: telegram.Update) -> None:
        """ Get list of all supported Mensa

        :param bot: Bot
        :param update: Bot-Update (Message)
        :return: Nothing
        """
        # save message
        self.save_message(update.message.chat_id, update.message.text)

        if not self.db.is_user_saved(update.message.chat_id):
            msg = self.message_json["user_not_found"][
                self.db.get_user_language(update.message.chat_id)]
        else:
            msg = self.get_mensa(update.message.chat_id)
        bot.send_message(chat_id=update.message.chat_id,
                         text=msg,
                         parse_mode=telegram.ParseMode.MARKDOWN)

    def language(self, bot: telegram.bot.Bot, update: telegram.Update) -> None:
        """ Change language

        :param bot: Bot
        :param update: Bot-Update (Message)
        :return: Nothing
        """
        # save message
        self.save_message(update.message.chat_id, update.message.text)

        if not self.db.is_user_saved(update.message.chat_id):
            msg = self.message_json["user_not_found"][
                self.db.get_user_language(update.message.chat_id)]
        else:
            msg = self.change_language(update.message.chat_id,
                                       update.message.text)
        bot.send_message(chat_id=update.message.chat_id,
                         text=msg,
                         parse_mode=telegram.ParseMode.MARKDOWN)

    def overview(self, bot: telegram.bot.Bot, update: telegram.Update) -> None:
        # save message
        self.save_message(update.message.chat_id, update.message.text)

        msg = self.get_alert_overview(update.message.chat_id)
        bot.send_message(chat_id=update.message.chat_id,
                         text=msg,
                         parse_mode=telegram.ParseMode.MARKDOWN)

    # function reads the users messages
    def listen(self, bot, update):
        bot.send_message(
            chat_id=update.message.chat_id,
            text=
            'I have no news for you. I will message you as soon as I know more',
            parse_mode=telegram.ParseMode.MARKDOWN)

    # function that is being executed when an error occurs
    def error(self, bot, update, error):
        # Logg all errors as warnings as well as the current update to analyse it later on
        self.logger.warning('Update "%s" caused error "%s"', update, error)

    # Reacting to unknown commands via printing help
    def unknown(self, bot, update):
        self.help(bot, update)

        # def sendMessage(self, msg, chat_id):
        """
        Send a mensage to a telegram user specified on chatId
        chat_id must be a number!
        """
        # bot = telegram.Bot(token=self.TOKEN)
        # bot.sendMessage(chat_id=chat_id, text=msg, parse_mode=telegram.ParseMode.MARKDOWN)

    def sendMessage(self, msg, chat_id):
        """
        Send a mensage to a telegram user specified on chatId
        chat_id must be a number!
        """
        bot = telegram.Bot(token=self.config_json["token"])
        bot.sendMessage(chat_id=chat_id,
                        text=msg,
                        parse_mode=telegram.ParseMode.MARKDOWN)
Beispiel #10
0
class MenuAlert:
    def __init__(self):
        with open('config.json') as f:
            self.config_json = json.load(f)
        self.token = self.config_json["token"]

        self.db_file = self.config_json["db_path"]
        self.db = DataBaseHandler(self.db_file)

        # scrape menues
        today = datetime.today().date()
        if today.weekday() == 5:  # if saturnday
            today = today + timedelta(days=2)
        elif today.weekday() == 6:  # if saturnday
            today = today + timedelta(days=1)

        # read in bot message
        with open(self.config_json["message_json"]) as f:
            self.message_json = json.load(f)

        # get all supportet universities
        self.universities = self.db.get_universities()

        # scrape instances:
        self.scrapers = {"UZH_Mensa": UZHMenu(), "ETH_Mensa": ETHMenu()}

        self.non_weekend_menu = ["UZH_Mensa"]

        self.bot = telegram.Bot(token=self.config_json["token"])

    def compare_and_alert(self,
                          alert_menu,
                          menu,
                          mensa_name,
                          day,
                          generic=False):
        menu_normalized = re.sub('\W+', ' ', menu)
        max_token_set_ratio = 0
        for i in range(len(alert_menu)):
            alert_menu_space = alert_menu[:i] + ' ' + alert_menu[i:]
            # compare string of alert-menu and and scraped manu
            token_set_ratio = fuzz.token_set_ratio(menu_normalized,
                                                   alert_menu_space)
            if token_set_ratio > max_token_set_ratio:
                max_token_set_ratio = token_set_ratio
        print("Compare", alert_menu, menu, max_token_set_ratio)
        # is the correlation is over 96% then raise an alert
        if max_token_set_ratio > 95:
            # get all users which have an alert for this menu.
            alert_user_list = self.db.get_useralert_by_mensa_and_menu(
                mensa_name, alert_menu)
            for alert_user in alert_user_list:
                if generic:
                    print("Generic", day)
                    self.send_generic_alert_to_user(menu, mensa_name,
                                                    alert_user, day)
                else:
                    self.send_alert_to_user(menu, mensa_name, alert_user, day)

    def send_alert_to_user(self, menu, mensa_name, alert_user, day):
        day_rel = self.getDayRelation(day)
        lang = self.db.get_user_language(alert_user)
        msg = '\n'.join(
            self.message_json["alert"][day_rel][lang]) % (menu, mensa_name)
        self.bot.sendMessage(chat_id=alert_user,
                             text=msg,
                             parse_mode=telegram.ParseMode.MARKDOWN)

    def send_generic_alert_to_user(self, menu, mensa_name, alert_user, day):
        lang = self.db.get_user_language(alert_user)
        msg = '\n'.join(self.message_json["alert"]["generic_day"][lang]) % (
            self.message_json["days"][str(day)][lang], menu, mensa_name)
        self.bot.sendMessage(chat_id=alert_user,
                             text=msg,
                             parse_mode=telegram.ParseMode.MARKDOWN)

    def send_message_to_admin(self, mensa, uni):
        admin_user = self.config_json["admin_chat_id"]
        lang = self.db.get_user_language(admin_user)
        msg = '\n'.join(
            self.message_json["failed_scrping"][lang]) % (mensa, uni)
        self.bot.sendMessage(chat_id=admin_user,
                             text=msg,
                             parse_mode=telegram.ParseMode.MARKDOWN)

    def getDayRelation(self, menu_weekday):
        """ calculate the relation between day of the menu and today, is it today, os ot tomorrow,...?

        :param menu_weekday: datetime representation of the weekday of the manu

        :return: Relation (Today, tomorrow, ....)
        :rtype: String
        """
        today_weekday = datetime.today().weekday()
        if menu_weekday == today_weekday:
            relations = "Today"
        elif menu_weekday - today_weekday > 0 and menu_weekday - today_weekday == 1:
            relations = "Tomorrow"
        elif menu_weekday - today_weekday < 0 and (menu_weekday +
                                                   7) - today_weekday == 1:
            relations = "Tomorrow"
        elif menu_weekday - today_weekday > 0 and menu_weekday - today_weekday == 2:
            relations = "Day after Tomorrow"
        elif menu_weekday - today_weekday < 0 and (menu_weekday +
                                                   7) - today_weekday == 2:
            relations = "Day after Tomorrow"
        else:
            relations = menu_weekday
        return relations

    def get_menu_for_days(self,
                          days_to_alert,
                          menus_week,
                          mensa_name,
                          generic=False):
        for day in days_to_alert:
            menus = menus_week[day]
            for menu in menus:
                all_alert_menus = self.db.selectMenus()
                for alert_menu in all_alert_menus:
                    self.compare_and_alert(alert_menu, menu, mensa_name, day,
                                           generic)

    def get_dates_to_scrape(self):
        """ calculate which days have to be screped today. On weekdays always scrape today and the next two days,
        For weekends scrape the whole upcoming week (Mo-Fr)

        :return: dates for the days to scrape
        """
        today = datetime.today()
        if today.weekday() == 5:
            # if today is saturday
            return today + timedelta(days=2), [0, 1, 2, 3, 4]
        elif today.weekday() == 6:
            # if today is sunday
            return today + timedelta(days=1), [0, 1, 2, 3, 4]
        else:
            return today, list(range(today.weekday(), today.weekday() + 3))

    def main(self):
        # for the set institutes
        for uni in self.universities:
            # get the according scraper
            uni_scraper = self.scrapers[uni]
            # get all the mensi from the Database
            mensi = self.db.get_mensainfo_by_uni(uni)
            print("mensi", mensi)
            # for all mensi get their name and id for, iterate over all the mensi of that institution
            for mensa_name, mensa_id in mensi:
                # get the dates which should be scraped for (dependent on today)
                date_to_scrape, days_to_alert = self.get_dates_to_scrape()
                # scrape the menus for the mensa and the scrape-day (always get the whole week of menus
                status, menus_week = uni_scraper.scrape(
                    date_to_scrape.date(), mensa_id)
                print("status", status)
                print("menus_week", menus_week)
                print("days_to_alert", days_to_alert)
                print("date_to_scrape", date_to_scrape)

                # end loop is scraping was not successful
                if status != "success":
                    self.send_message_to_admin(mensa_name, uni)
                    continue
                if len(days_to_alert) == 3:
                    # is_weekday, so alert with day relation
                    self.get_menu_for_days(days_to_alert,
                                           menus_week,
                                           mensa_name,
                                           generic=False)
                else:
                    # is weekend, so don't scrape for institutes that do not support next weeks menu on weekends
                    if uni in self.non_weekend_menu:
                        continue
                    # on weekend alert with weekday string (not relation)
                    print("days_to_alert", days_to_alert)
                    self.get_menu_for_days(days_to_alert,
                                           menus_week,
                                           mensa_name,
                                           generic=True)