statistika

  1from datetime import datetime, timedelta
  2import hashlib
  3import os
  4import re
  5import sqlite3
  6import time
  7from threading import Thread
  8
  9from flask import Flask, g, jsonify, redirect, render_template, request, session
 10from flask_cors import CORS
 11import requests
 12
 13# import db_initialization
 14import db_management
 15import statistika_logger
 16
 17app = Flask(__name__)
 18app.secret_key = os.urandom(24)  # для работы session
 19
 20CORS(app)
 21
 22log = statistika_logger.get_logger(__name__)
 23
 24
 25def get_db_connection() -> sqlite3.Connection:
 26    """
 27    Функция, которая получает соединение с базой данных.
 28    Если соединение не существует, устанавливает новое соединение с использованием SQLite с 'data.db'.
 29    Возвращает существующее или новосозданное соединение с базой данных.
 30    """
 31    db_connection = getattr(g, '_database', None)
 32    if db_connection is None:
 33        # для тестирования
 34        # раскомментировать, если необходимо пересоздать БД
 35        # os.remove('../data.db')
 36        if not os.path.exists('../data.db'):
 37            db_connection = g._database = sqlite3.connect('../data.db')
 38            db_initialization.create_tables(db_connection)
 39            db_initialization.load_test_data(db_connection)
 40        else:
 41            db_connection = g._database = sqlite3.connect('../data.db')
 42    return db_connection
 43
 44
 45@app.teardown_appcontext
 46def close_db_connection(exception: Exception) -> None:
 47    """
 48    Функция завершения, которая закрывает соединение с базой данных.
 49    Параметры:
 50    - exception: Исключение, которое вызвало завершение, если таковое имеется.
 51    """
 52    db_connection = getattr(g, '_database', None)
 53    if db_connection is not None:
 54        db_connection.close()
 55
 56
 57def update_players_tournaments() -> None:
 58    """
 59    Постоянно обновляет турниры игроков в БД.
 60
 61    Извлекает список игроков из БД и для каждого игрока
 62    извлекает турниры из Интернета и обновляет базу данных, если
 63    турниры разные. Между каждой итерацией проходит 1 день.
 64    """
 65    # log.warning('Updating players tournaments...')
 66
 67    # Подключаемся к БД.
 68    con = sqlite3.connect('../data.db')
 69    cursor = con.cursor()
 70
 71    while True:
 72        # Получаем список игроков.
 73        query = "SELECT player_id FROM players"
 74        players_ids: list[str] = [row[0] for row in cursor.execute(query).fetchall()]
 75
 76        # Проход по всем игрокам.
 77        for player_id in players_ids:
 78            # Запрос на получение турниров игрока из БД.
 79            query = "SELECT tournaments FROM players WHERE player_id = ?"
 80            tournaments_in_db: str = cursor.execute(query, (player_id,)).fetchall()[0][0]
 81
 82            # Запрос на получение турниров игрока {player_id} из Интернета.
 83            # log.warning(f'Запрос на получение турниров игрока {player_id} из Интернета.')
 84            try:
 85                url = f'https://api.rating.chgk.net/players/{player_id}/tournaments'
 86                response = requests.get(url=url)
 87                if response.status_code != 200:
 88                    raise Exception(
 89                        f'Something wrong with request to API. Status code: {response.status_code}, from {url}')
 90
 91                tournaments_in_web = [i['idtournament'] for i in response.json()]
 92                tournaments_in_web = ','.join(map(str, tournaments_in_web))
 93
 94                # Коли записи разнятся, обновляем БД.
 95                # log.warning(f'Обновляем БД для игрока {player_id}.')
 96                if tournaments_in_web != tournaments_in_db:
 97                    query = "UPDATE players SET tournaments = ? WHERE player_id = ?"
 98                    cursor.execute(query, (tournaments_in_web, player_id))
 99                    con.commit()
100                    time.sleep(2)
101            # Если 404, то предполагаем неполадки сети, логируем, ждём 1 час и продолжаем.
102            except Exception as e:
103                # log.error(e)
104                time.sleep(60 * 60)  # 1-hour sleep after error
105                continue
106
107        # После прохода по всем игрокам ждём 1 день.
108        time.sleep(60 * 60 * 24)  # 1 day
109
110
111thread = Thread(target=update_players_tournaments)
112thread.start()
113
114
115@app.route('/')
116def index():
117    """
118    Обработчик маршрута для корневого URL-адреса ("/"). Очищает сеанс, извлекает данные из БД,
119    добавляет имена столбцов в начало данных, преобразует данные в формат JSON, сортирует их по полю "_sum_minus_2",
120    а затем отображает шаблон index.html с отсортированными данными и именами столбцов.
121    """
122    session.clear()
123
124    # Получаем данные из базы
125    db_connection = get_db_connection()
126    data, columns = db_management.get_maintable(db_connection)
127
128    # Добавление названий столбцов в начало
129    thead = ['Команда', 'Сумма', 'Сумма - 2'] + columns[3:]
130
131    # Преобразование данных в формат JSON
132    json_data = to_json(data, columns)
133
134    # Сортировка по сумме
135    data = sorted(json_data, key=lambda x: x['_sum_minus_2'], reverse=True)
136
137    return render_template('index.html', thead=thead, data=data)
138
139
140@app.route('/login', methods=['POST', 'GET'])
141def admin_login():
142    """
143    Обрабатывает логин в систему. Считывает с формы логин/пароль (index.html).
144
145    Проверяет в базе наличие хэша пароля. В случае успеха делает редирект на основную страницу.
146    Помечает успешный залогин в кукисе session[login] = login.
147    В противном случае - пишет Fail и отображает страницу ввода пароля снова.
148    """
149    db_connection = get_db_connection()
150    cursor = db_connection.cursor()
151
152    log.info('Logon.')
153
154    if request.method == 'POST':
155        login = request.form.get('login')
156        password = request.form.get('password')
157        query = 'SELECT salt FROM users WHERE login = :login'
158        login_exists = cursor.execute(query, {'login': login}).fetchall()
159
160        if login_exists:
161            log.info('There is a salt')
162            salt = login_exists[0][0]
163
164            password_hash = hashlib.scrypt(password=bytes(password, encoding='UTF-8'),
165                                           salt=bytes(salt, encoding='UTF-8'),
166                                           n=2 ** 14, r=8, p=1, dklen=64).hex()
167
168            query = """
169                    SELECT
170                        count(1) _count
171                    FROM
172                        users
173                    WHERE 
174                        login =:login
175                        AND 
176                        hash =:hash
177                """
178
179            data = {'login': login, 'hash': password_hash}
180
181            if cursor.execute(query, data).fetchall()[0][0]:
182                session['login'] = login
183                return redirect('/main_table')
184        else:
185            return render_template('login.html', message='Fail.')
186
187    return render_template('login.html')
188
189
190@app.route('/main_table')
191def main_table():
192    """
193    Функция, которая служит обработчиком маршрута для таблицы. Отображает шаблон 'main_table.html'.
194    """
195    log.info('Function main_table() was called...')
196
197    thread_status = '🟢' if thread.is_alive() else '🔴'
198
199    if session.get('login'):
200        if request.method == 'GET':
201            return render_template('main_table.html', thread_status=thread_status)
202        if request.method == 'POST':
203            log.debug(request.data)
204    else:
205        return redirect('/login')
206
207
208@app.route('/add_game', methods=['GET'])
209def add_game() -> str:
210    return render_template('add_game.html')
211
212
213@app.route('/add_player', methods=['GET', 'POST'])
214def add_player():
215    db_connection = get_db_connection()
216    # Получаем список команд
217    teams = [i[0] for i in db_management.get_teams(db_connection)]
218    return render_template('add_player.html', teams=teams)
219
220
221@app.route('/add_score', methods=['GET', 'POST'])
222def add_score():
223    db_connection = get_db_connection()
224    data = [i[0] for i in db_management.get_teams(db_connection)]
225
226    message = ''
227    if request.method == 'POST':
228        log.debug(request.form)
229        message = 'Данные внесли.'
230    return render_template('add_score.html', data=data, message=message)
231
232
233@app.route('/add_team', methods=['GET'])
234def add_team():
235    return render_template('add_team.html')
236
237
238@app.route('/get_columns', methods=['GET'])
239def get_columns() -> list[dict]:
240    """
241    Функция для извлечения названий столбцов из таблицы базы данных и преобразования их в определенный формат
242    для отображения.
243    """
244    # Подключение к базе данных SQLite и получение данных для главной таблицы
245    db_connection = get_db_connection()
246    data, columns = db_management.get_maintable(db_connection)
247
248    transformed_columns = []
249
250    # Цикл для преобразования каждого элемента массива строк в объект
251    for column in columns:
252        if column == 'team_name':
253            field_title = "Команда"
254        elif column == '_sum_':
255            field_title = 'Cумма'
256        elif column == '_sum_minus_2':
257            field_title = 'Cум(-2)'
258        else:
259            field_title = '.'.join((column.split('-')[2], column.split('-')[1]))
260
261        transformed_columns.append({
262            # Пример условной логики для определения значения editor
263            'editor': 'input',  # if column == 'team_name' else 'number'
264            'field': column,
265            'hozAlign': 'center' if column == 'summa_2' else 'left',
266            'sorter': 'number',
267            'title': field_title,
268            'validator': 'numeric',
269            # 'contextMenu': 'cellContextMenu'
270        })
271    return transformed_columns
272
273
274def to_json(data, columns):
275    """Вспомогательная функция для преобразования данных в формат JSON."""
276    log.info('Function to_json() was called...')
277    json_data = []
278    for row in data:
279        row_data = {}
280        for i in range(0, len(columns)):
281            row_data[columns[i]] = row[i]
282        json_data.append(row_data)
283
284    return json_data
285
286
287@app.route('/get_data', methods=['GET'])
288def get_data():
289    if request.referrer is None:
290        return 'Що таке?'
291
292    db_connection = get_db_connection()
293
294    who = request.referrer.split('/')[-1]
295    match who:
296        case 'main_table':
297            data, columns = db_management.get_maintable(db_connection)
298            return to_json(data, columns)
299        case 'add_player':
300            data = db_management.get_players(db_connection)
301            log.debug(data)
302            columns = ['fio', 'player_id', 'team_name']  # менять на человеческие 'ФИО', 'ИД игрока' в table_players.js
303            return to_json(data, columns)
304        case 'add_team':
305            data = db_management.get_teams(db_connection)
306            columns = ['Name']
307            return to_json(data, columns)
308
309
310@app.route('/check_packet', methods=['POST'])
311def check_packet():
312    log.debug(request.json)
313    packets = [i for i in request.json if i]
314    answer = []
315    for packet_id in packets:
316        r = requests.get(f'https://rating.maii.li/b/tournament/{packet_id}/')
317
318        pattern = r'/\/b\/player\/(\d+)/gm'
319        matches = re.findall(pattern, r.text)
320        print(matches)
321        print(len(matches))
322        answer.append(matches)
323
324    return jsonify(success=True, data=''.join(answer))
325
326
327@app.route('/test', methods=['POST', 'GET'])
328def test():
329    if request.method == 'POST':
330        log.debug(request.data.decode('utf-8'))
331        data = request.data.decode('utf-8')
332        log.info('OK')
333    # json_data = [
334    #     {'id': 1, 'name': "Tiger Nixon", 'position': "System Architect", 'office': "Edinburgh", 'extension': "5421",
335    #      'startDate': "2011/04/25", 'salary': "Tiger Nixon"}
336    # ]
337    # lll = {'data': [[1, 'test', 78],
338    #               [2, 'test2', 145],
339    #               [3, 'test3', 23],
340    #               [4, 'test4', 45]],
341    #      'columns': ['id', 'name', 'position']
342    #      }
343
344    return render_template('tablecelledit.html')
345
346
347@app.route('/update', methods=['POST', 'GET'])
348def update():
349    if request.method == 'POST':
350        db_connection = get_db_connection()
351        who = request.referrer.split('/')[-1]
352        match who:
353            case 'main_table':
354                log.debug(request.json)
355                date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
356                db_management.update_main_table(db_connection, request.json, date)
357                # case 'add_player':
358            #     return render_template('add_player.html')
359            # case 'add_team':
360            #     return render_template('add_team.html')
361    return {'success': True}
362
363
364# @app.route('/update_from_github', methods=['POST', 'GET'])
365# def update_from_github() -> jsonify:# todo
366#     """
367#     Функция для обновления данных из GitHub.
368#     Эта функция скачивает последние изменения в репозитории и обновляет проект.
369#     """
370#     # print(request.json)
371#     cmd = 'echo "kek" > lol.kek'
372#     os.system(cmd)
373#     # cmd = 'sudo touch lol.kek'
374#     cmd = 'sudo ./update.sh'
375#     os.system(cmd)
376#     print(cmd)
377#     # return jsonify(success=True, data=request.json)
378#     return str(request.json)
379
380
381@app.route('/update_table_players', methods=['POST'])
382def update_table_players():
383    """
384    Функция для обновления таблицы игроков данными, полученными через POST-запрос.
385    Эта функция добавляет нового игрока в таблицу players, используя его полное имя (fio) и идентификатор игрока.
386    В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение
387    об ошибке.
388    """
389    db_connection = get_db_connection()
390    cursor = db_connection.cursor()
391    try:
392        d = request.json
393        log.debug(d)
394        fio = d['playerFIO']
395        player_id = d['playerName']
396        team_name = d['teamName']
397        log.debug(fio, player_id, team_name)
398        # Получаем идентификатор команды
399        query = 'select id from teams where name = ?'
400        cursor.execute(query, (team_name,))
401        team_id = cursor.fetchone()[0]
402        log.debug(team_id)
403        # Записываем данные в таблицу players
404        query = 'insert into players (fio, player_id, team_id) values (?, ?, ?)'
405        cursor.execute(query, (fio, player_id, team_id))
406        db_connection.commit()
407        log.info(f'Записали игрока {fio} в таблицу players.')
408
409        response = {'success': True}
410        return jsonify(response)
411
412    except Exception as e:
413        log.error(f'Error: {e}')
414        return jsonify(success=False, error=str(e))
415
416
417@app.route('/update_table_teams', methods=['POST'])
418def update_table_teams():
419    """
420    Функция для обновления таблицы команд данными, полученными через POST-запрос.
421    Эта функция добавляет новую команду в таблицу teams.
422    В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение
423    об ошибке.
424    """
425    db_connection = get_db_connection()
426    cursor = db_connection.cursor()
427    try:
428        log.debug(request.json)
429        d = request.json
430        log.debug(type(d))
431        t_name = d['team_name']
432        query = 'insert into teams(name) values (?)'
433        cursor.execute(query, (t_name,))
434        db_connection.commit()
435
436        response = {'success': True}
437        return jsonify(response)
438
439    except Exception as e:
440        log.error(f'Error: {e}')
441        return jsonify(success=False, error=str(e))
442
443
444@app.errorhandler(404)
445def page_not_found(error):
446    """Отображает страницу ошибки в случае перехода на несуществующую страницу."""
447    return render_template('404.html', error=error), 404
448
449
450if __name__ == '__main__':
451    app.run(debug=True, host='0.0.0.0', port=5555)
app = <Flask 'statistika'>
log = <Logger statistika (DEBUG)>
def get_db_connection() -> sqlite3.Connection:
26def get_db_connection() -> sqlite3.Connection:
27    """
28    Функция, которая получает соединение с базой данных.
29    Если соединение не существует, устанавливает новое соединение с использованием SQLite с 'data.db'.
30    Возвращает существующее или новосозданное соединение с базой данных.
31    """
32    db_connection = getattr(g, '_database', None)
33    if db_connection is None:
34        # для тестирования
35        # раскомментировать, если необходимо пересоздать БД
36        # os.remove('../data.db')
37        if not os.path.exists('../data.db'):
38            db_connection = g._database = sqlite3.connect('../data.db')
39            db_initialization.create_tables(db_connection)
40            db_initialization.load_test_data(db_connection)
41        else:
42            db_connection = g._database = sqlite3.connect('../data.db')
43    return db_connection

Функция, которая получает соединение с базой данных. Если соединение не существует, устанавливает новое соединение с использованием SQLite с 'data.db'. Возвращает существующее или новосозданное соединение с базой данных.

@app.teardown_appcontext
def close_db_connection(exception: Exception) -> None:
46@app.teardown_appcontext
47def close_db_connection(exception: Exception) -> None:
48    """
49    Функция завершения, которая закрывает соединение с базой данных.
50    Параметры:
51    - exception: Исключение, которое вызвало завершение, если таковое имеется.
52    """
53    db_connection = getattr(g, '_database', None)
54    if db_connection is not None:
55        db_connection.close()

Функция завершения, которая закрывает соединение с базой данных. Параметры:

  • exception: Исключение, которое вызвало завершение, если таковое имеется.
def update_players_tournaments() -> None:
 58def update_players_tournaments() -> None:
 59    """
 60    Постоянно обновляет турниры игроков в БД.
 61
 62    Извлекает список игроков из БД и для каждого игрока
 63    извлекает турниры из Интернета и обновляет базу данных, если
 64    турниры разные. Между каждой итерацией проходит 1 день.
 65    """
 66    # log.warning('Updating players tournaments...')
 67
 68    # Подключаемся к БД.
 69    con = sqlite3.connect('../data.db')
 70    cursor = con.cursor()
 71
 72    while True:
 73        # Получаем список игроков.
 74        query = "SELECT player_id FROM players"
 75        players_ids: list[str] = [row[0] for row in cursor.execute(query).fetchall()]
 76
 77        # Проход по всем игрокам.
 78        for player_id in players_ids:
 79            # Запрос на получение турниров игрока из БД.
 80            query = "SELECT tournaments FROM players WHERE player_id = ?"
 81            tournaments_in_db: str = cursor.execute(query, (player_id,)).fetchall()[0][0]
 82
 83            # Запрос на получение турниров игрока {player_id} из Интернета.
 84            # log.warning(f'Запрос на получение турниров игрока {player_id} из Интернета.')
 85            try:
 86                url = f'https://api.rating.chgk.net/players/{player_id}/tournaments'
 87                response = requests.get(url=url)
 88                if response.status_code != 200:
 89                    raise Exception(
 90                        f'Something wrong with request to API. Status code: {response.status_code}, from {url}')
 91
 92                tournaments_in_web = [i['idtournament'] for i in response.json()]
 93                tournaments_in_web = ','.join(map(str, tournaments_in_web))
 94
 95                # Коли записи разнятся, обновляем БД.
 96                # log.warning(f'Обновляем БД для игрока {player_id}.')
 97                if tournaments_in_web != tournaments_in_db:
 98                    query = "UPDATE players SET tournaments = ? WHERE player_id = ?"
 99                    cursor.execute(query, (tournaments_in_web, player_id))
100                    con.commit()
101                    time.sleep(2)
102            # Если 404, то предполагаем неполадки сети, логируем, ждём 1 час и продолжаем.
103            except Exception as e:
104                # log.error(e)
105                time.sleep(60 * 60)  # 1-hour sleep after error
106                continue
107
108        # После прохода по всем игрокам ждём 1 день.
109        time.sleep(60 * 60 * 24)  # 1 day

Постоянно обновляет турниры игроков в БД.

Извлекает список игроков из БД и для каждого игрока извлекает турниры из Интернета и обновляет базу данных, если турниры разные. Между каждой итерацией проходит 1 день.

thread = <Thread(Thread-1 (update_players_tournaments), started 11576)>
@app.route('/')
def index():
116@app.route('/')
117def index():
118    """
119    Обработчик маршрута для корневого URL-адреса ("/"). Очищает сеанс, извлекает данные из БД,
120    добавляет имена столбцов в начало данных, преобразует данные в формат JSON, сортирует их по полю "_sum_minus_2",
121    а затем отображает шаблон index.html с отсортированными данными и именами столбцов.
122    """
123    session.clear()
124
125    # Получаем данные из базы
126    db_connection = get_db_connection()
127    data, columns = db_management.get_maintable(db_connection)
128
129    # Добавление названий столбцов в начало
130    thead = ['Команда', 'Сумма', 'Сумма - 2'] + columns[3:]
131
132    # Преобразование данных в формат JSON
133    json_data = to_json(data, columns)
134
135    # Сортировка по сумме
136    data = sorted(json_data, key=lambda x: x['_sum_minus_2'], reverse=True)
137
138    return render_template('index.html', thead=thead, data=data)

Обработчик маршрута для корневого URL-адреса ("/"). Очищает сеанс, извлекает данные из БД, добавляет имена столбцов в начало данных, преобразует данные в формат JSON, сортирует их по полю "_sum_minus_2", а затем отображает шаблон index.html с отсортированными данными и именами столбцов.

@app.route('/login', methods=['POST', 'GET'])
def admin_login():
141@app.route('/login', methods=['POST', 'GET'])
142def admin_login():
143    """
144    Обрабатывает логин в систему. Считывает с формы логин/пароль (index.html).
145
146    Проверяет в базе наличие хэша пароля. В случае успеха делает редирект на основную страницу.
147    Помечает успешный залогин в кукисе session[login] = login.
148    В противном случае - пишет Fail и отображает страницу ввода пароля снова.
149    """
150    db_connection = get_db_connection()
151    cursor = db_connection.cursor()
152
153    log.info('Logon.')
154
155    if request.method == 'POST':
156        login = request.form.get('login')
157        password = request.form.get('password')
158        query = 'SELECT salt FROM users WHERE login = :login'
159        login_exists = cursor.execute(query, {'login': login}).fetchall()
160
161        if login_exists:
162            log.info('There is a salt')
163            salt = login_exists[0][0]
164
165            password_hash = hashlib.scrypt(password=bytes(password, encoding='UTF-8'),
166                                           salt=bytes(salt, encoding='UTF-8'),
167                                           n=2 ** 14, r=8, p=1, dklen=64).hex()
168
169            query = """
170                    SELECT
171                        count(1) _count
172                    FROM
173                        users
174                    WHERE 
175                        login =:login
176                        AND 
177                        hash =:hash
178                """
179
180            data = {'login': login, 'hash': password_hash}
181
182            if cursor.execute(query, data).fetchall()[0][0]:
183                session['login'] = login
184                return redirect('/main_table')
185        else:
186            return render_template('login.html', message='Fail.')
187
188    return render_template('login.html')

Обрабатывает логин в систему. Считывает с формы логин/пароль (index.html).

Проверяет в базе наличие хэша пароля. В случае успеха делает редирект на основную страницу. Помечает успешный залогин в кукисе session[login] = login. В противном случае - пишет Fail и отображает страницу ввода пароля снова.

@app.route('/main_table')
def main_table():
191@app.route('/main_table')
192def main_table():
193    """
194    Функция, которая служит обработчиком маршрута для таблицы. Отображает шаблон 'main_table.html'.
195    """
196    log.info('Function main_table() was called...')
197
198    thread_status = '&#128994;' if thread.is_alive() else '&#128308;'
199
200    if session.get('login'):
201        if request.method == 'GET':
202            return render_template('main_table.html', thread_status=thread_status)
203        if request.method == 'POST':
204            log.debug(request.data)
205    else:
206        return redirect('/login')

Функция, которая служит обработчиком маршрута для таблицы. Отображает шаблон 'main_table.html'.

@app.route('/add_game', methods=['GET'])
def add_game() -> str:
209@app.route('/add_game', methods=['GET'])
210def add_game() -> str:
211    return render_template('add_game.html')
@app.route('/add_player', methods=['GET', 'POST'])
def add_player():
214@app.route('/add_player', methods=['GET', 'POST'])
215def add_player():
216    db_connection = get_db_connection()
217    # Получаем список команд
218    teams = [i[0] for i in db_management.get_teams(db_connection)]
219    return render_template('add_player.html', teams=teams)
@app.route('/add_score', methods=['GET', 'POST'])
def add_score():
222@app.route('/add_score', methods=['GET', 'POST'])
223def add_score():
224    db_connection = get_db_connection()
225    data = [i[0] for i in db_management.get_teams(db_connection)]
226
227    message = ''
228    if request.method == 'POST':
229        log.debug(request.form)
230        message = 'Данные внесли.'
231    return render_template('add_score.html', data=data, message=message)
@app.route('/add_team', methods=['GET'])
def add_team():
234@app.route('/add_team', methods=['GET'])
235def add_team():
236    return render_template('add_team.html')
@app.route('/get_columns', methods=['GET'])
def get_columns() -> list[dict]:
239@app.route('/get_columns', methods=['GET'])
240def get_columns() -> list[dict]:
241    """
242    Функция для извлечения названий столбцов из таблицы базы данных и преобразования их в определенный формат
243    для отображения.
244    """
245    # Подключение к базе данных SQLite и получение данных для главной таблицы
246    db_connection = get_db_connection()
247    data, columns = db_management.get_maintable(db_connection)
248
249    transformed_columns = []
250
251    # Цикл для преобразования каждого элемента массива строк в объект
252    for column in columns:
253        if column == 'team_name':
254            field_title = "Команда"
255        elif column == '_sum_':
256            field_title = 'Cумма'
257        elif column == '_sum_minus_2':
258            field_title = 'Cум(-2)'
259        else:
260            field_title = '.'.join((column.split('-')[2], column.split('-')[1]))
261
262        transformed_columns.append({
263            # Пример условной логики для определения значения editor
264            'editor': 'input',  # if column == 'team_name' else 'number'
265            'field': column,
266            'hozAlign': 'center' if column == 'summa_2' else 'left',
267            'sorter': 'number',
268            'title': field_title,
269            'validator': 'numeric',
270            # 'contextMenu': 'cellContextMenu'
271        })
272    return transformed_columns

Функция для извлечения названий столбцов из таблицы базы данных и преобразования их в определенный формат для отображения.

def to_json(data, columns):
275def to_json(data, columns):
276    """Вспомогательная функция для преобразования данных в формат JSON."""
277    log.info('Function to_json() was called...')
278    json_data = []
279    for row in data:
280        row_data = {}
281        for i in range(0, len(columns)):
282            row_data[columns[i]] = row[i]
283        json_data.append(row_data)
284
285    return json_data

Вспомогательная функция для преобразования данных в формат JSON.

@app.route('/get_data', methods=['GET'])
def get_data():
288@app.route('/get_data', methods=['GET'])
289def get_data():
290    if request.referrer is None:
291        return 'Що таке?'
292
293    db_connection = get_db_connection()
294
295    who = request.referrer.split('/')[-1]
296    match who:
297        case 'main_table':
298            data, columns = db_management.get_maintable(db_connection)
299            return to_json(data, columns)
300        case 'add_player':
301            data = db_management.get_players(db_connection)
302            log.debug(data)
303            columns = ['fio', 'player_id', 'team_name']  # менять на человеческие 'ФИО', 'ИД игрока' в table_players.js
304            return to_json(data, columns)
305        case 'add_team':
306            data = db_management.get_teams(db_connection)
307            columns = ['Name']
308            return to_json(data, columns)
@app.route('/check_packet', methods=['POST'])
def check_packet():
311@app.route('/check_packet', methods=['POST'])
312def check_packet():
313    log.debug(request.json)
314    packets = [i for i in request.json if i]
315    answer = []
316    for packet_id in packets:
317        r = requests.get(f'https://rating.maii.li/b/tournament/{packet_id}/')
318
319        pattern = r'/\/b\/player\/(\d+)/gm'
320        matches = re.findall(pattern, r.text)
321        print(matches)
322        print(len(matches))
323        answer.append(matches)
324
325    return jsonify(success=True, data=''.join(answer))
@app.route('/test', methods=['POST', 'GET'])
def test():
328@app.route('/test', methods=['POST', 'GET'])
329def test():
330    if request.method == 'POST':
331        log.debug(request.data.decode('utf-8'))
332        data = request.data.decode('utf-8')
333        log.info('OK')
334    # json_data = [
335    #     {'id': 1, 'name': "Tiger Nixon", 'position': "System Architect", 'office': "Edinburgh", 'extension': "5421",
336    #      'startDate': "2011/04/25", 'salary': "Tiger Nixon"}
337    # ]
338    # lll = {'data': [[1, 'test', 78],
339    #               [2, 'test2', 145],
340    #               [3, 'test3', 23],
341    #               [4, 'test4', 45]],
342    #      'columns': ['id', 'name', 'position']
343    #      }
344
345    return render_template('tablecelledit.html')
@app.route('/update', methods=['POST', 'GET'])
def update():
348@app.route('/update', methods=['POST', 'GET'])
349def update():
350    if request.method == 'POST':
351        db_connection = get_db_connection()
352        who = request.referrer.split('/')[-1]
353        match who:
354            case 'main_table':
355                log.debug(request.json)
356                date = (datetime.today() - timedelta(days=1)).strftime('%Y-%m-%d')
357                db_management.update_main_table(db_connection, request.json, date)
358                # case 'add_player':
359            #     return render_template('add_player.html')
360            # case 'add_team':
361            #     return render_template('add_team.html')
362    return {'success': True}
@app.route('/update_table_players', methods=['POST'])
def update_table_players():
382@app.route('/update_table_players', methods=['POST'])
383def update_table_players():
384    """
385    Функция для обновления таблицы игроков данными, полученными через POST-запрос.
386    Эта функция добавляет нового игрока в таблицу players, используя его полное имя (fio) и идентификатор игрока.
387    В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение
388    об ошибке.
389    """
390    db_connection = get_db_connection()
391    cursor = db_connection.cursor()
392    try:
393        d = request.json
394        log.debug(d)
395        fio = d['playerFIO']
396        player_id = d['playerName']
397        team_name = d['teamName']
398        log.debug(fio, player_id, team_name)
399        # Получаем идентификатор команды
400        query = 'select id from teams where name = ?'
401        cursor.execute(query, (team_name,))
402        team_id = cursor.fetchone()[0]
403        log.debug(team_id)
404        # Записываем данные в таблицу players
405        query = 'insert into players (fio, player_id, team_id) values (?, ?, ?)'
406        cursor.execute(query, (fio, player_id, team_id))
407        db_connection.commit()
408        log.info(f'Записали игрока {fio} в таблицу players.')
409
410        response = {'success': True}
411        return jsonify(response)
412
413    except Exception as e:
414        log.error(f'Error: {e}')
415        return jsonify(success=False, error=str(e))

Функция для обновления таблицы игроков данными, полученными через POST-запрос. Эта функция добавляет нового игрока в таблицу players, используя его полное имя (fio) и идентификатор игрока. В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение об ошибке.

@app.route('/update_table_teams', methods=['POST'])
def update_table_teams():
418@app.route('/update_table_teams', methods=['POST'])
419def update_table_teams():
420    """
421    Функция для обновления таблицы команд данными, полученными через POST-запрос.
422    Эта функция добавляет новую команду в таблицу teams.
423    В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение
424    об ошибке.
425    """
426    db_connection = get_db_connection()
427    cursor = db_connection.cursor()
428    try:
429        log.debug(request.json)
430        d = request.json
431        log.debug(type(d))
432        t_name = d['team_name']
433        query = 'insert into teams(name) values (?)'
434        cursor.execute(query, (t_name,))
435        db_connection.commit()
436
437        response = {'success': True}
438        return jsonify(response)
439
440    except Exception as e:
441        log.error(f'Error: {e}')
442        return jsonify(success=False, error=str(e))

Функция для обновления таблицы команд данными, полученными через POST-запрос. Эта функция добавляет новую команду в таблицу teams. В случае успешного выполнения возвращает JSON-ответ с сообщением об успехе, в противном случае возвращает сообщение об ошибке.

@app.errorhandler(404)
def page_not_found(error):
445@app.errorhandler(404)
446def page_not_found(error):
447    """Отображает страницу ошибки в случае перехода на несуществующую страницу."""
448    return render_template('404.html', error=error), 404

Отображает страницу ошибки в случае перехода на несуществующую страницу.