def _convert_data(self, raw_data: RawImageData) -> ImageData: """ Cleans data from a picture that a user sends :param raw_data: object with raw info about a picture :return: object with formatted info about a picture """ date_time = (str(raw_data.date_time) if raw_data.date_time else None) # Merge a brand and model together camera = f'{raw_data.camera_brand} {raw_data.camera_model}' lens = f'{raw_data.lens_brand} {raw_data.lens_model}' # Get rid of repetitive words camera = (self._dedupe_string(camera) if camera != ' ' else None) lens = (self._dedupe_string(lens) if lens != ' ' else None) camera, lens = self._check_camera_tags(camera, lens) try: latitude, longitude = self._convert_coordinates(raw_data) except (InvalidCoordinates, NoCoordinates): address = country = latitude = longitude = None else: try: address, country = self._get_address(latitude, longitude) except Exception as e: log.warning(e) address = country = None return ImageData(self.user, date_time, camera, lens, address, country, latitude, longitude)
def execute_query(self, query: str, parameters: tuple = None, trials: int = 0): """ Executes a given query :param query: query to execute :param parameters: parameters for query :param trials: integer that denotes number of trials to execute a query in case of known errors :return: cursor object """ if not self.conn or not self.conn.open: self.connect() try: cursor = self.conn.cursor() cursor.execute(query, parameters) # try to reconnect if MySQL server has gone away except MySQLdb.OperationalError as e: # (2013, Lost connection to MySQL server during query) # (2006, Server has gone away) if e.args[0] in [2006, 2013]: log.info(e) self.connect() if trials <= 3: # trying to execute query one more time trials += 1 log.warning(e) log.info("Trying execute the query again...") return self.execute_query(query, parameters, trials) if trials > 3: log.error(e) log.warning("Ran out of limit of trials...") raise DatabaseConnectionError("Cannot connect to the " "database") else: log.error(e) raise except Exception as e: log.error(e) raise else: return cursor
def clean_cache(self, limit: int) -> None: """ Method that remove several User objects from cache - the least active users :param limit: number of the users that the method should remove from cache :return: None """ log.info('Figuring out the least active users...') # Select users that the least active recently user_ids = tuple(self.users.keys()) query = ('SELECT chat_id ' 'FROM photo_queries_table2 ' f'WHERE chat_id in {user_ids} ' 'GROUP BY chat_id ' 'ORDER BY MAX(time) ' f'LIMIT %s') parameters = limit, try: cursor = db.execute_query(query, parameters) except DatabaseConnectionError: log.error("Can't figure out the least active users...") return if not cursor.rowcount: log.warning("There are no users in the db") return # Make list out of tuple of tuples that is returned by MySQL least_active_users = [chat_id[0] for chat_id in cursor.fetchall()] log.info('Removing %d least active users from cache...', limit) num_deleted_entries = 0 for entry in least_active_users: log.debug('Deleting %s...', entry) deleted_entry = self.users.pop(entry, None) if deleted_entry: num_deleted_entries += 1 log.debug("%d users were removed from cache.", num_deleted_entries)
def start_bot(self) -> None: """ Method to get the bot started Wrapper around self._run just for the sake of making it more reliable and reconnect in case of errors. And to store time when bot started to work :return: None """ try: self.start_time = datetime.now() self._run() except requests.exceptions.ReadTimeout as e: log.error(e) self.stop_polling() log.warning('Pausing bot for 30 seconds...') time.sleep(30) log.warning('Try to start the bot again...') self._run()
def _check_camera_tags(*tags: str) -> List[str]: """ Converts camera and lens name to proper ones Function that convert stupid code name of a smartphone or a camera from EXIF to a meaningful one by looking a collation in a special MySQL table For example instead of just Nikon there can be NIKON CORPORATION in EXIF :param tags: a tuple with a name of a camera and lens from EXIF :return: list with one or two strings which are name of camera and/or lens. If there is not better name for the gadget in database, function just returns name how it is """ checked_tags = [] for tag in tags: if tag: # If there was this information inside EXIF of the photo tag = str(tag).strip() log.info('Looking up collation for %s', tag) query = ('SELECT right_tag ' 'FROM tag_table ' 'WHERE wrong_tag=%s') parameters = tag, cursor = db.execute_query(query, parameters) if not cursor: log.error("Can't check the tag because of the db error") log.warning("Tag will stay as is.") continue if cursor.rowcount: # Get appropriate tag from the table tag = cursor.fetchone()[0] log.info('Tag after looking up in tag_tables - %s.', tag) checked_tags.append(tag) return checked_tags
def get_most_popular_items(item_type: str, message: Message) -> str: """ Get the most common cameras/lenses/countries from database and make list of them :param item_type: string with column name to choose between cameras, lenses and countries :param message: telebot object with info about user and his message :return: string which is either list of most common cameras/lenses/countries or message which states that list is empty """ user = users.find_one(message) def tuple_to_ordered_str_list(list_of_gadgets: Tuple[Tuple[str]]) -> str: """ Converts Python list to ordered list as a string Example: 1. Canon 80D 2. iPhone 4S :param list_of_gadgets: tuple of tuples with string where every string is a name of a camera or lens or a country :return: ordered list as a string """ string_roaster = '' index = 1 for item in list_of_gadgets: if not item[0]: continue string_roaster += '{}. {}\n'.format(index, item[0]) index += 1 return string_roaster log.debug('Evaluating most popular things...') # This query returns item types in order where the first one item # has the highest number of occurrences # in a given column query = (f'SELECT {item_type} FROM photo_queries_table2 ' f'GROUP BY {item_type} ' f'ORDER BY count({item_type}) ' 'DESC') try: cursor = db.execute_query(query) except DatabaseConnectionError: log.error("Can't evaluate a list of the most popular items") return messages[user.language]['doesnt work'] # Almost impossible case but still if not cursor.rowcount: log.warning('There is nothing in the main database table') bot.send_message(chat_id=config.MY_TELEGRAM, text='There is nothing in the main database table') return messages[user.language]['no_top'] popular_items = cursor.fetchall() log.info('Finish evaluating the most popular items') return tuple_to_ordered_str_list(popular_items[:30])