def delete_value(self): """ Удаляет выбранную запись :return: """ try: values = self.get_choose_value() pk = values['pk'] LOGGER.warning( 'Запрос на удаление элемента в таблице GetRequestsApi') delete_request_db = DeleteRequestsToDB() delete_request_db.delete_record_in_bd( tb_name=delete_request_db.get_requests, where=f'pk={pk}') delete_request_db.delete_record_in_bd( tb_name=delete_request_db.additional_get_requests, where=f'pk_attachment={pk}') except IndexError as error: if str(error) == 'не выбран элемент': return for row in self.tree_view.get_children(): self.tree_view.delete(row) del self.values gc.collect() self.__get_records__() self.completion_tree_view()
def get_race_info_before(self, html) -> dict: data_list = [] soup = BS(html, "html.parser") main_window = soup.find("div", attrs={"id": "PassBody"}) if not main_window: LOGGER.warning( "Could not find PassBody because of the race did not start yet" ) return [] main_table = main_window.find("table", attrs={"id": "race-pass-body"}) tbodies = main_table.find_all("tbody", class_="rp-table-row") local_race_data = {} runners = len(tbodies) local_race_data["runners"] = runners local_race_data.update(self.date_and_time) row = list(local_race_data.values()) data_list.append(row[1:] + row[:1]) return data_list
def get_race_datetime(self, html, race_type): if not html: LOGGER.error("Could not scrape %s because of broken HTML" % str(html)) return [] soup = BS(html, "html.parser") race_header = soup.find("section", attrs={"id": "rp-header"}) header_rows = race_header.find_all("tr") if len(header_rows) < 2: LOGGER.warning("Could not fetch DATE and TIME from %s" % self.url) return [] race_datetime = header_rows[0].h2.text.split() if len(race_datetime) != 5: LOGGER.warning("Could not fetch DATE and TIME from %s" % self.url) return [] race_time = race_datetime[0] race_date_str = " ".join(race_datetime[1:]) try: race_date = dt.datetime.strftime( dt.datetime.strptime(race_date_str, "%a %d %B %Y"), "%-d/%-m/%y") except ValueError: race_date = dt.datetime.strftime( dt.datetime.strptime(race_date_str, "%A %d %B %Y"), "%-d/%-m/%y") distance_of_the_race = header_rows[1].find_all("td")[-1].text self.date_and_time["date"] = race_date self.date_and_time["time"] = race_time date_and_time_section = soup.find("span", class_='rp-title-course-name') if date_and_time_section: self.date_and_time["track"] = date_and_time_section.text.title( ).strip() else: title_text = soup.find('h1', class_='rp-title').text self.date_and_time["track"] = ' '.join( title_text.split()[:-1]).title().strip() self.date_and_time["distance"] = distance_of_the_race.split( ":")[-1].replace('(Inner)', '').replace(' (Old)', '').replace(' (XC)', '').replace(' (New)', '').strip() self.date_and_time["race_type"] = race_type return self.date_and_time
def cancel(self): """ Обработка нажатия отмены введения токена :return: """ self.token = DEFAULT_VALUE_FOR_BD messagebox.showwarning('Отмена авторизации', WARNING_MSG['VK_API']['cancel_get_token']) LOGGER.warning('Во время выполнения метода get_token, он был отменён') self.token_window.destroy()
def delete_all_records(self, name_table: str) -> None: """ Функция удаления всех записей в таблице :param name_table: имя очищаемой таблицы :return: None """ LOGGER.warning(f'Начинаю удаление таблицы {name_table}') self.remote_control_bd.execute(f'DROP TABLE IF EXISTS {name_table}') self.connect_bd.commit() MainDB() LOGGER.info('Успешно удалена')
def insert_many_values_into_get_requests(self, type_request: str, count: int, response: List[dict], time: float, last_parse: int) -> None: """ Функция вставки данных в таблицу GET запросов к Vk в том случае. Создана потому что Windows плохо работает если нужно большое количество данных вставить в БД :param type_request: тип парсинга :param count: количество людей :param response: результат выполнения парсинга :param time: время парсинга :param last_parse: возможен ли дальнейший парсинг по данным :return: """ if count <= COUNT_MANY_INSERT: # Если норм количество людей в записи peoples = json.dumps(response, ensure_ascii=False) self.insert_in_table( tb_name=self.get_requests, data=[type_request, count, peoples, time, last_parse]) else: # Если слишком много людей в записи self.insert_in_table( # Вставка заглушки в основную тб tb_name=self.get_requests, data=[type_request, count, REQUIRES_DATA, time, last_parse]) get_request_db = GetRequestsToDB() pk = get_request_db.get_records(tb_name=self.get_requests, select=['pk'], one_record=True, order='pk DESC') get_request_db.connect_bd.close() attachment_pk = int(pk['pk']) slice_from = 0 slice_to = COUNT_MANY_INSERT + 1 while True: peoples = response[slice_from:slice_to] peoples = json.dumps(peoples, ensure_ascii=False)[1:-1] self.insert_in_table(tb_name=self.additional_get_requests, data=[attachment_pk, peoples]) if slice_to == count + 1: break slice_from = slice_to if slice_to + COUNT_MANY_INSERT + 1 > count + 1: slice_to = count + 1 else: slice_to += COUNT_MANY_INSERT + 1 LOGGER.warning('Очистка данных') del type_request, count, response, time, last_parse, peoples gc.collect()
def create_db(self, tb_name: str, request: str, data: List[Union[str, int]] = None) -> None: """ Функция создания таблиц и заполнения их дефолтными значениями :param tb_name: имя таблицы :param request: запрос к sqlite на создание таблицы :param data: список со значениями для заполнения default: None :return: """ LOGGER.warning(f'Начинаю создание таблицы {tb_name}') self.remote_control_bd.execute(request.format(tb_name=tb_name)) self.connect_bd.commit() LOGGER.warning('Удачно создана') if data is None: return LOGGER.warning(f'Начинаю default заполнение {tb_name}') data = ', '.join(data) self.remote_control_bd.execute( f'INSERT INTO {tb_name} VALUES ({data})') self.connect_bd.commit() LOGGER.warning('Удачно заполнена')
def delete_record_in_bd(self, tb_name: str, where: str) -> None: """ Функция удаления записи из таблицы :param tb_name: имя таблицы из которой удаляется запись :param where: параметры по которым искать нужную запись :return: """ LOGGER.warning( f'Начинаю удаление записи where={where}, таблица GetRequestsApi' ) self.remote_control_bd.execute(f'DELETE FROM {tb_name} WHERE {where}') self.connect_bd.commit() LOGGER.info('Успешно удалена')
def update_lineup(df, starter_dict): pbp_dict = df.to_dict(orient='records') for i in range(len(pbp_dict)): if i == 0: for t, s in starter_dict.iteritems(): pbp_dict[i][t] = s.copy() pbp_dict[i]['SECS'] = 0 continue for t in starter_dict.keys(): pbp_dict[i][t] = pbp_dict[i - 1][t].copy() if pbp_dict[i - 1]['AC'] == 'SUBST': t = pbp_dict[i - 1]['T1'] if pbp_dict[i - 1]['SU'] == '+': pbp_dict[i][t].add(pbp_dict[i - 1]['C1']) elif pbp_dict[i - 1]['SU'] == '-': if pbp_dict[i - 1]['C1'] in pbp_dict[i][t]: pbp_dict[i][t].remove(pbp_dict[i - 1]['C1']) else: LOGGER.warning(str(pbp_dict[i - 1])) elif 'C2' in pbp_dict[i - 1]: LOGGER.warning('%d-%d %s' % (pbp_dict[i - 1]['SA'], pbp_dict[i - 1]['SB'], pbp_dict[i - 1]['Time'])) LOGGER.warning(pbp_dict[i][t]) pbp_dict[i][t].add(pbp_dict[i - 1]['C1']) LOGGER.warning(pbp_dict[i][t]) pbp_dict[i][t].remove(pbp_dict[i - 1]['C2']) LOGGER.warning(pbp_dict[i][t]) if pbp_dict[i - 1]['AC'] == 'ENDP': pbp_dict[i]['SECS'] = 0 else: pbp_dict[i]['SECS'] = time_diff(pbp_dict[i - 1]['Time'], pbp_dict[i]['Time']) pbp_df = pd.DataFrame(pbp_dict) def to_sorted_tuple(x): result = list(x) result.sort() return tuple(result) for t in starter_dict.keys(): df[t] = pbp_df[t].apply(lambda x: to_sorted_tuple(x)) df['SECS'] = pbp_df['SECS']
def clear_values(self): """ Очищает все записи в BD :return: """ ask = askyesno('Очистка записей', 'Вы уверены, что хотите удалить все записи?') if ask is True: LOGGER.warning('Запрос на удаление таблицы GetRequestsApi') delete_request_db = DeleteRequestsToDB() delete_request_db.delete_all_records( delete_request_db.get_requests) delete_request_db.delete_all_records( delete_request_db.additional_get_requests) self.window.destroy()
def __scheduler__() -> None: """ Функция отвечает за сброс мусора и ведение логгера с информацией об этом :return: """ scheduler_logger = LOGGER('scheduler', 'main') size_last, peak = tracemalloc.get_traced_memory() size_last = size_last // 1024 scheduler_logger.warning('Запускаю очситку мусора') gc.collect() size_now, size_peak = tracemalloc.get_traced_memory() size_now = size_now // 1024 size_peak = size_peak // 1024 scheduler_logger.warning( f'Использовалось: {size_last}Mib, Теперь: {size_now}Mib, ' f'В пике: {size_peak}Mib')
def params_to_query(params: list, params_allowed: set = PARAMS_ALLOWED) -> str: if len(params) > MAX_PARAMS_ALLOWED: LOGGER.warning( f'Too many params. Only {MAX_PARAMS_ALLOWED} are allowed') raise Exception query = '' for param_key, param_value in params: if not {param_key}.issubset(params_allowed): continue if param_key == 'distrito': query = f'"distrito" = \'{param_value}\'' elif param_key == 'regiao5': query = f'"regiao5" = \'{param_value}\'' elif param_key == 'nome_feira': query = f'"nome_feira" = \'{param_value}\'' elif param_key == 'bairro': query = f'"bairro" = \'{param_value}\'' return query
def get_token(self) -> Union[str, None]: """ Функция получения токнеа пользователя :return: """ showinfo('Получение токена!', INFO_MSG['VK_API']['get_token']) web_open_new_tab(HTTP_GET_TOKEN) token = self.__additional_windows().get_token() token = self.preparation_final_token(token) if token == DEFAULT_VALUE_FOR_BD: LOGGER.warning( 'При выполнении функции get_token был получен невалидный токен' ) return None params = {'v': VERSION_API, 'access_token': token} try: request = requests.get( HTTP_FOR_REQUESTS.format(method='users.get'), params=params).json() except ConnectionError: showerror( 'Нет подключения', 'Не возиожно авторизоваться, нетп подключения к интернету') return None if request.get('error'): showerror( 'Авторизация не удалась', 'Неверный токен авторизации, произошла ошибка, ' 'повторите попытку') return None update_requests_db = UpdateRequestsToDB() update_requests_db.update_table(tb_name=update_requests_db.userdata, update_row={'access_token': token}) del request return token
def insert_in_table(self, tb_name: str, data: List[Union[str, int]]) -> None: """ Функция вставляющая запись в таблицу :param tb_name: имя таблицы :param data: список со значениями :return: """ LOGGER.warning(f'Начинаю добавлять даные в таблицу {tb_name}') columns = self.columns[tb_name] try: del (columns[columns.index('pk')]) except ValueError: pass question_marks = ', '.join(['?'] * len(columns)) columns = ', '.join(columns) request = f''' INSERT INTO {tb_name} ({columns}) VALUES ({question_marks}) ''' self.remote_control_bd.execute(request, data) self.connect_bd.commit() LOGGER.warning(f'Добавлены данные в таблицу {tb_name}') LOGGER.warning('Очистка данных') del tb_name, data, question_marks, request, columns gc.collect()
def update_table(self, tb_name: str, update_row: Dict[str, Union[str, int]], where: str = None) -> None: """ функция выполняющая обноление данных таблицы :param tb_name: имя таблицы :param update_row: словарь {колонка: значение} :param where: параметры выборки запии default: не используется :return: """ update_row_values = self.__get_update_row__(tb_name, update_row, where=where) request = 'UPDATE {tb_name} SET {set}' if where is not None: request += f' WHERE {where}' SET = [] for key, value in update_row_values.items(): if type(value) == int: SET += [f'{key}={value}'] else: SET += [f'{key}="{value}"'] SET = ', '.join(SET) request = request.format(tb_name=tb_name, set=SET) LOGGER.info(f'Обновляю данные {request}') self.remote_control_bd.execute(request) self.connect_bd.commit() LOGGER.warning(f'Обновлены данные таблицы {tb_name}') LOGGER.warning('Очистка данных') del tb_name, update_row, where, request, gc.collect()
def get_race_info_after(self, html) -> dict: soup = BS(html, "html.parser") main_window = soup.find("div", attrs={"id": "ReportBody"}) if not main_window: LOGGER.warning( "Could not find ReportBody on %s because of the race did not start yet" % self.url) return [] main_table = main_window.find("table", class_="rp-table rp-results") tbodies = main_table.find_all("tbody", class_="rp-table-row") for tbody in tbodies: local_race_data = {} trs = tbody.find_all("tr") upper_tds = trs[0].find_all('td') lower_tds = trs[1].find_all('td') horse_number = trs[0].find_all("td")[0].find_all("span")[0].text # Some runners have no number they ended a race local_race_data["horse_number"] = horse_number position_spans = trs[0].find_all("td")[0].find_all("span") local_race_data["position"] = '' if len(position_spans) > 1: local_race_data["position"] = position_spans[1].text[1:-1] horse_name = upper_tds[4].text.split('.')[-1].strip() local_race_data["horse_name"] = horse_name jokey = upper_tds[10].text.strip() local_race_data["jokey"] = jokey trainer = lower_tds[3].text.strip() local_race_data["trainer"] = trainer horse_age = int(upper_tds[11].text.strip()) local_race_data["horse_age"] = horse_age horse_weight = upper_tds[12].text.strip() local_race_data["horse_weight"] = horse_weight bsp = upper_tds[15].text.strip() local_race_data["bsp"] = float(bsp) if len(bsp) > 0 else 0 bpsp = lower_tds[-1].text.strip()[1:-1] local_race_data["bpsp"] = float(bpsp) if len(bpsp) > 0 else 0 try: high, low = trs[0].find_all("td")[16].text.strip().split('/') except IndexError: LOGGER.critical("Cookies have been expired for %s" % self.url) print( '\nATTENTION: Your Cookies expired!\nPlease UPDATE cookies!\n' ) exit() local_race_data["high"] = float(high) if high.isdigit() else 0 local_race_data["low"] = float(low) if low.isdigit() else 0 B2L = local_race_data["bsp"] - local_race_data["low"] local_race_data["B2L"] = B2L L2B = local_race_data["high"] - local_race_data["bsp"] local_race_data["L2B"] = L2B runners = len(tbodies) local_race_data["runners"] = runners local_race_data.update(self.date_and_time) row = list(local_race_data.values()) self.race_data.append(row[14:] + row[:14]) return local_race_data
def get_records(self, tb_name: str, select: List[str] = None, one_record: bool = False, where: str = None, order: str = None, limit: Union[str, int] = None, dictionary: bool = True) -> Union[List[dict], dict]: """ Функция получения записей из базы данных :param tb_name: название таблицы :param select: имена колонн, которые нужно взять default: все :param one_record: булево одну ли брать запись default: False :param where: параметр выборки default: не используется :param order: параметр сортировки default: не используется :param limit: параметр ограничения кол-ва строк default: не используется :param dictionary: параметр определения вида выходных даных, если False, то вернётся читый результат SQL, если True, то словарь {столбец: значение} default True :return: """ LOGGER.warning(f'Начинаю брать данные из {tb_name}') response = [] select = self.__get_select__(tb_name, select) LOGGER.info('Собираю запрос') request = f'SELECT {", ".join(select)} FROM {tb_name}' if where is not None: request += f' WHERE {where}' if order is not None: request += f' ORDER BY {order}' if limit is not None: request += f' LIMIT {limit}' LOGGER.info(f'Получаю данные {request}') self.remote_control_bd.execute(request) if one_record is True: records = [self.remote_control_bd.fetchone()] else: record = [list(self.remote_control_bd.fetchone())] __record__ = self.remote_control_bd.fetchall() if len(__record__) > 0: records = record + __record__ else: records = record LOGGER.info('Обрабатываю') if dictionary is False: response = records else: for i in range(len(records)): record = records[i] if len(record) == 0 or len(record) < len(select): continue response.append({}) for k in range(len(select)): name = select[k] response[i][name] = record[k] del record LOGGER.warning(f'Успешно получены данные из {tb_name}') LOGGER.warning('Очистка данных') del tb_name, select, one_record, where, limit, dictionary, request, \ records gc.collect() return response[0] if len(response) == 1 else response
class ConfigureVkApi: """ Класс отвечающий за нстройку инструментов для запросов к API Vk """ def __init__(self, ignore_existing_token: bool = False): self.logger = LOGGER('config_vk_api', 'vk_api') get_requests_db = GetRequestsToDB() user_data_table_value = get_requests_db.get_records( tb_name=get_requests_db.userdata, one_record=True, select=['access_token']) token = user_data_table_value['access_token'] self.__additional_windows = AdditionalWindows if ignore_existing_token is False: if (token is None) or (token == DEFAULT_VALUE_FOR_BD): token = self.get_token() else: token = self.get_token() if (token is not None) or (token != DEFAULT_VALUE_FOR_BD): is_donat = self.check_is_donat(token) if is_donat is False: token = None self.token = token if self.token is not None: vk_session = vk_api.VkApi(token=self.token) self.vk_tool = vk_api.tools.VkTools(vk_session) if ignore_existing_token is True: showinfo('Авторизовались', 'Вы удачно авторизовались!') self.logger.info('Получен vk_tool и сам токен') else: self.logger.error('vk_tool не удалось получить') self.vk_tool = None del get_requests_db, user_data_table_value def get_token(self) -> Union[str, None]: """ Функция получения токнеа пользователя :return: """ showinfo('Получение токена!', INFO_MSG['VK_API']['get_token']) web_open_new_tab(HTTP_GET_TOKEN) token = self.__additional_windows().get_token() token = self.preparation_final_token(token) if token == DEFAULT_VALUE_FOR_BD: LOGGER.warning( 'При выполнении функции get_token был получен невалидный токен' ) return None params = {'v': VERSION_API, 'access_token': token} try: request = requests.get( HTTP_FOR_REQUESTS.format(method='users.get'), params=params).json() except ConnectionError: showerror( 'Нет подключения', 'Не возиожно авторизоваться, нетп подключения к интернету') return None if request.get('error'): showerror( 'Авторизация не удалась', 'Неверный токен авторизации, произошла ошибка, ' 'повторите попытку') return None update_requests_db = UpdateRequestsToDB() update_requests_db.update_table(tb_name=update_requests_db.userdata, update_row={'access_token': token}) del request return token @staticmethod def check_is_donat(token: str) -> bool: """ Функция проверки оплаты подписки на программу пользователем :param token: токен пользователя :return: """ params = { 'v': VERSION_API, 'access_token': token, 'owner_id': ID_GROUP_VK } try: request = requests.get( HTTP_FOR_REQUESTS.format(method='donut.isDon'), params=params).json() except ConnectionError: showerror( 'Нет подключения', 'Невозможно авторизоваться, нет подключения к интернету') return False if request.get('error'): showerror('Ошибка', f'Произошла непредвиденная ошибка {request["error"]}') response = request.get('response') if int(response) == 1: return True else: get_requests_db = GetRequestsToDB() __start = GetRequestsToDB().get_records( select=['start_free_version'], one_record=True, tb_name=get_requests_db.settings)['start_free_version'] if __start is None: warning = WARNING_MSG['VK_API']['is_not_donat_free'] showwarning('Пробная версия!', warning.format(min=TIME_FREE_VERSION // 60)) start_free_version = time_now() update_request_db = UpdateRequestsToDB() update_request_db.update_table( tb_name=update_request_db.settings, update_row={'start_free_version': int(start_free_version)}) return True else: time_use_free_version = ceil(time_now()) - int(__start) if time_use_free_version >= TIME_FREE_VERSION: warning = WARNING_MSG['VK_API']['is_not_donat'] showwarning('Пробная версия!', warning) return False else: time_left = TIME_FREE_VERSION - time_use_free_version warning = WARNING_MSG['VK_API']['is_not_donat_free'] showwarning('Пробная версия!', warning.format(min=time_left // 60)) return True def preparation_final_token(self, token: str) -> str: """ Функция обработки ссылки и получения из неё токена :param token: ссылка с токеном :return: """ token = token.split('access_token=') if len(token) == 2: token = token[1].split('&')[0] return token showwarning('Не смог распознать токен', WARNING_MSG['VK_API']['non_inspected_token']) self.logger.warning( 'При выполнении preparation_final_token, не смог распознать токен') return DEFAULT_VALUE_FOR_BD
class BrainForApp: """ Класс отвечающий за настройку и запуск приложения """ def __init__(self, window_preview): """ Создаёт превью и проверяет нужные настройки для программы, а также запускает её :param window_preview: объект окна превью """ self.logger = LOGGER('main', 'main') png_preview_open, png_preview = self.preview_image_open() self.preview_image_set(png_preview_open, png_preview, window_preview) window_preview.update() tracemalloc.start() time.sleep(2) MainDB() get_requests_db = GetRequestsToDB() settings = get_requests_db.get_records( tb_name=get_requests_db.settings, one_record=True, select=['first_start', 'auto_update']) first_start = settings['first_start'] auto_update = settings['auto_update'] if first_start == 1: self.logger.info('Первый запуск') window_preview.destroy() done = AdditionalWindows().person_and_agreement_data() if done is True: update_requests_db = UpdateRequestsToDB() update_requests_db.update_table( tb_name=update_requests_db.settings, update_row={'first_start': 0}) self.logger.warning('Очистка от лишних файлов в директории') list_path = os.listdir(path) if REPO_BRANCH_UPDATER in list_path: rmtree(REPO_BRANCH_UPDATER, ignore_errors=True, onerror=None) if REPO_BRANCH_VERSION in list_path: rmtree(REPO_BRANCH_VERSION, ignore_errors=True, onerror=None) if REPO_BRANCH_MASTER in list_path: rmtree(REPO_BRANCH_MASTER, ignore_errors=True, onerror=None) try: self.logger.warning('Закрытие окна первью') window_preview.destroy() except TclError: pass del settings, first_start, list_path, window_preview gc.collect() self.logger.info('Создание задачи scheduler') scheduler = BackgroundScheduler() scheduler.start() scheduler.add_job(__scheduler__, 'interval', minutes=1) self.logger.info('Запуск приложения') from windows import App App(auto_update, OS) self.logger.info('Закрытие приложения') def preview_image_open(self): """ Возвращает первью картинку """ while True: try: png_preview_open = Image.open( os.path.join(path_to_dir_ico, 'preview.png')) png_preview = ImageTk.PhotoImage(png_preview_open) return png_preview_open, png_preview except FileNotFoundError as err: self.logger.error(str(err)) @staticmethod def preview_image_set(png_preview_open, png_preview, window_preview): """ Устанавливает размеры окна, ставит его по середине, устанавливает картинку как фон """ x_img, y_img = png_preview_open.size x = (window_preview.winfo_screenwidth() - x_img) // 2 y = (window_preview.winfo_screenheight() - y_img) // 2 window_preview.geometry("%ix%i+%i+%i" % (x_img, y_img, x, y)) Label(window_preview, image=png_preview).pack(side='top')