Документация ChemistryPro

Документация проекта ChemistryPro

Введение

ChemistryPro — это веб-приложение, разработанное для изучения Химии школьного уровня 8-11 классов. Оно предлагает пользователям различные функции для взаимодействия с данными о химических элементах.

Немного об архитектуре этого сайта: Сайт базирован на языке программирования Python. Python обладает большим количеством библиотек, что является преимуществом этого языка программирования. Flask - это основная библиотека на которой базируется этот сайт.

pip install flask

Фреймворк Flask является универсальным решением для многих сайтов, а также он легок в изучении. Html-файлы проекта хранятся в папке templates, а изображения в папке static. Структура проекта:


ChemistryPro/
├── static/                # Статические файлы, такие как CSS и изображения
│   ├── /upload                         # Аватары
│   └── fon.jpg...                      # Остальные фото
│── instance/              # База данных
│   └── products.db                     # Файл базы данных
├── templates/             # Шаблоны HTML
│   ├── about.html                      # Обо мне
│   ├── base.html                       # Базовый шаблон
│   ├── all_profiles.html               # Список всех пользователей сайта
│   ├── bug.html                        # В случае ошибок
│   ├── chat.html                       # Чат
│   ├── complete_reaction.html          # Дописывание реакций
│   ├── documentation.html              # Документация
│   ├── edit_profile.html               # Редактирование профиля
│   ├── electronic_configuration.html   # Электронная конфигурация
│   ├── get_reaction_chain.html         # Цепочки превращений
│   ├── index.html                      # Уравнивание реакций
│   ├── instruction.html                # Инструкция
│   ├── login.html                      # Логин
│   ├── main.html                       # Главная страница
│   ├── minigame.html                   # МиниИгра
│   ├── molyarnaya_massa.html           # Молярные массы
│   ├── orghim.html                     # Структура орг. в-в
│   ├── otherprofile.html               # Профиля пользователей (для пользователя, который просматривает другие профили)
│   ├── profile.html                    # Профили
│   ├── register.html                   # Регистрация
│   ├── tablica.html                    # Таблица Менделеева
│   ├── tablica_kislotnosti.html        # Таблица кислотностей
│   ├── tablica_rastvorimosti.html      # Таблица растворимостей
│   └── winning.html                    # Победа в МиниИгре
├── server.py              # Главный файл приложения
├── mod.py                 # Здесь хранятся все модели для базы данных пользователей
└── requirements.txt       # Список зависимостей(библиотек)
            

Исходный код программы открытый, программу можно найти по ссылке:

https://github.com/FanisGaripov/ChemistryPro

Функции

1. Дописывание химических реакций

Это функция позволяет, при наличии известных реагентов реакции, получить продукт реакции. Работает при помощи отправки запроса на сервер chemequations.com . Используется библиотека requests(python).

pip install requests

def get_chemical_equation_solution(reaction):
    '''метод обработчик дописывания хим.реакций. коротко о нем: принимает из основной функции реакцию, вставляет
    ее в ссылку и возвращает ответ, который парсит(выкидывает все лишнее) только до нужных строчек'''
    if request.method == 'POST':
        reaction = request.form.get("chemical_formula", False)
    # Кодируем реакцию для URL
        encoded_reaction = quote(reaction)

    # Формируем URL с учетом химической реакции
        url = f"https://chemequations.com/ru/?s={encoded_reaction}"

    # Отправляем GET-запрос
        response = requests.get(url)

    # Проверка успешности запроса
        if response.status_code == 200:
            # Парсим HTML-ответ
            soup = BeautifulSoup(response.text, 'html.parser')

            # Находим элемент с классом "equation main-equation well"
            result = soup.find('h1', class_='equation main-equation well')

            if result:
                return result.get_text(strip=True)
                # Возвращаем текст ответа
            else:
                return 'Решение не найдено.'
        else:
            return f"Ошибка при запросе: {response.status_code}"


@app.route('/complete_reaction', methods=['GET', 'POST'])
def complete_reaction_page():
    # страница, отвечающая за вывод завершенных реакций предыдущим методом
    react1 = ''
    user = flask_login.current_user
    reaction = ''
    if request.method == 'POST':
        reaction = request.form.get("chemical_formula", False)
        react1 = get_chemical_equation_solution(reaction)
        if '(g)' in react1:
            react1 = react1.replace('(g)', '')
        if '(s)' in react1:
            react1 = react1.replace('(s)', '')
        if '(aq)' in react1:
            react1 = react1.replace('(aq)', '')
        if '(l)' in react1:
            react1 = react1.replace('(l)', '')

    return render_template('complete_reaction.html', get_chemical_equation_solution=get_chemical_equation_solution, react1=react1, user=user, reaction=reaction)

2. Уравнивание химических реакций

Эта функция позволяет уравнять любую химическую реакцию. Используется библиотека chempy(python).

pip install chempy
Работает при помощи встроенного метода библиотеки chempy, а именно balance_stoichiometry.

def uravnivanie(formula):
    # баланс уравнений
    reactants_input, products_input = formula.split('=')
    reactants = {x.split()[0].strip(): int(x.split()[1]) if len(x.split()) > 1 else 1 for x in
                 reactants_input.split('+')}
    products = {x.split()[0].strip(): int(x.split()[1]) if len(x.split()) > 1 else 1 for x in products_input.split('+')}

    balanced_reaction = balance_stoichiometry(reactants, products)

    reactants_str = ' + '.join([f"{v}{k}" if v != 1 else f"{k}" for k, v in balanced_reaction[0].items()])
    products_str = ' + '.join([f"{v}{k}" if v != 1 else f"{k}" for k, v in balanced_reaction[1].items()])

    otvet = f"{reactants_str} = {products_str}"

    return otvet


@app.route('/uravnivanie', methods=['GET', 'POST'])
def osnova():
    # функция которая возвращает уравнивание хим.реакций( index.html )
    user = flask_login.current_user
    resultat2 = ''
    if request.method == 'POST':
        chemical_formula = request.form['chemical_formula']
        try:
            resultat2 = f'{chemical_formula}: {uravnivanie(chemical_formula)}'
            print(resultat2)
        except:
            redirect('/')
    return render_template('index.html', resultat2=resultat2, user=user)

3. Электронная конфигурация

Эта функция позволяет узнать электронную конфигурацию любого вещества без заглядывания в таблицу Менделеева. Не использует других библиотек, кроме flask(python, основная библиотека, на которой держится весь проект). Состоит из трех функций:

def electronic_configuration(element):
    elements_data = {
        'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10,
        'Na': 11, 'Mg': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, 'K': 19,
        'Ca': 20, 'Sc': 21, 'Ti': 22, 'V': 23, 'Cr': 24, 'Mn': 25, 'Fe': 26, 'Co': 27, 'Ni': 28,
        'Cu': 29, 'Zn': 30, 'Ga': 31, 'Ge': 32, 'As': 33, 'Se': 34, 'Br': 35, 'Kr': 36, 'Rb': 37,
        'Sr': 38, 'Y': 39, 'Zr': 40, 'Nb': 41, 'Mo': 42, 'Tc': 43, 'Ru': 44, 'Rh': 45, 'Pd': 46,
        'Ag': 47, 'Cd': 48, 'In': 49, 'Sn': 50, 'Sb': 51, 'Te': 52, 'I': 53, 'Xe': 54, 'Cs': 55,
        'Ba': 56, 'La': 57, 'Ce': 58, 'Pr': 59, 'Nd': 60, 'Pm': 61, 'Sm': 62, 'Eu': 63, 'Gd': 64,
        'Tb': 65, 'Dy': 66, 'Ho': 67, 'Er': 68, 'Tm': 69, 'Yb': 70, 'Lu': 71, 'Hf': 72, 'Ta': 73,
        'W': 74, 'Re': 75, 'Os': 76, 'Ir': 77, 'Pt': 78, 'Au': 79, 'Hg': 80, 'Tl': 81, 'Pb': 82,
        'Bi': 83, 'Po': 84, 'At': 85, 'Rn': 86, 'Fr': 87, 'Ra': 88, 'Ac': 89, 'Th': 90, 'Pa': 91,
        'U': 92, 'Np': 93, 'Pu': 94, 'Am': 95, 'Cm': 96, 'Bk': 97, 'Cf': 98, 'Es': 99, 'Fm': 100,
        'Md': 101, 'No': 102, 'Lr': 103, 'Rf': 104, 'Db': 105, 'Sg': 106, 'Bh': 107, 'Hs': 108,
        'Mt': 109, 'Ds': 110, 'Rg': 111, 'Cn': 112, 'Nh': 113, 'Fl': 114, 'Mc': 115, 'Lv': 116,
        'Ts': 117, 'Og': 118
    }

    atomic_number = elements_data.get(element)
    atomic_number1 = elements_data.get(element)
    atom = atomic_number
    if element == '':
        return "Введите элемент", ""
    if atomic_number is None:
        return "Элемент не найден", ""

    configurations = []
    configurations1 = []
    subshells = ['1s', '2s', '2p', '3s', '3p', '4s', '3d', '4p', '5s', '4d', '5p', '6s', '4f', '5d', '6p', '7s', '5f',
                 '6d', '7p']
    electrons = [2, 2, 6, 2, 6, 2, 10, 6, 2, 10, 6, 2, 14, 10, 6, 2, 14, 10, 6]

    subshells1 = ['1s', '2s', '2p', '3s', '3p', '3d', '4s', '4p', '4d', '4f', '5s', '5p', '5d', '5f', '6s', '6p', '6d',
                  '7s', '7p']
    electrons1 = [2, 2, 6, 2, 6, 10, 2, 6, 10, 14, 2, 6, 10, 14, 2, 6, 10, 2, 6]

    for i in range(len(subshells)):
        if atomic_number > 0:
            if atomic_number >= electrons[i]:
                configurations.append(f"{subshells[i]}^{electrons[i]}")
                atomic_number -= electrons[i]
            else:
                configurations.append(f"{subshells[i]}^{atomic_number}")
                break

    for i in range(len(subshells1)):
        if atomic_number1 > 0:
            if atomic_number1 >= electrons1[i]:
                configurations1.append(f"{subshells1[i]}^{electrons1[i]}")
                atomic_number1 -= electrons1[i]
            else:
                configurations1.append(f"{subshells1[i]}^{atomic_number1}")
                break

    configuration_string = ' '.join(configurations)

    configuration_string2 = ' '.join(configurations1)

    # Графическое представление(текстовое, используются [↑] и [↓], открывающая и закрывающая скобка - это одна клетка)
    graphic_representation = generate_graphical_representation(configurations)

    return configuration_string, configuration_string2, graphic_representation, atom


def generate_graphical_representation(configurations):
    # графическое представление электронной конфигурации
    representation = []
    grouped_representation = {}

    for config in configurations:
        subshell, count = config.split('^')
        count = int(count)

        if subshell[0] not in grouped_representation:
            grouped_representation[subshell[0]] = []

        cells = []

        if subshell.endswith('s'):
            for _ in range(1):
                if count > 0:
                    cells.append('[↑]')
                    count -= 1
                if count > 0:
                    cells[0] += '[↓]'
                    count -= 1
                else:
                    cells.append('[ ]')  # Пустая ячейка

        elif subshell.endswith('p'):
            # 3 p-орбитали
            for i in range(3):
                if count > 0:
                    cells.append('[↑]')
                    count -= 1
                else:
                    cells.append('[ ]')  # Пустая ячейка

            for i in range(3):
                if count > 0:
                    cells[i] += '[↓]'
                    count -= 1

        elif subshell.endswith('d'):
            # 5 d-орбиталей
            for i in range(5):
                if count > 0:
                    cells.append('[↑]')
                    count -= 1
                else:
                    cells.append('[ ]')  # Пустая ячейка

            for i in range(5):
                if count > 0:
                    cells[i] += '[↓]'
                    count -= 1

        elif subshell.endswith('f'):
            # 7 f-орбиталей
            for i in range(7):
                if count > 0:
                    cells.append('[↑]')
                    count -= 1
                else:
                    cells.append('[ ]')  # Пустая ячейка

            for i in range(7):
                if count > 0:
                    cells[i] += '[↓]'
                    count -= 1

        grouped_representation[subshell[0]].append(f"{subshell}: " + ' '.join(cells))

    # Сборка финального представления
    for level in sorted(grouped_representation.keys()):
        representation.extend(grouped_representation[level])

    return "\n".join(representation)


@app.route('/electronic_configuration', methods=['GET', 'POST'])
def electronic_configuration_page():
    # функция, которая отображает страницу электронной конфигурации, предыдущая функция отвечает за обработку ответа
    element = ''
    user = flask_login.current_user
    configuration = ''
    configuration1 = ''
    graphic_representation = ''
    atomic = ''
    if request.method == 'POST':
        element = request.form.get("element", False)
        try:
            configuration, configuration1, graphic_representation, atomic = electronic_configuration(element)
        except Exception as e:
            configuration1, configuration, graphic_representation, atomic = '', '', '', ''
    return render_template('electronic_configuration.html', configuration=configuration, configuration1=configuration1, graphic_representation=graphic_representation, atomic=atomic, user=user, element=element)
            

4. Молярная масса веществ

Функция позволяет узнать молярную реакцию любого вещества или реакции. Данные о массе элемента берутся из таблицы Менделеева, далее эта масса умножается на коэффициент элемента. Можно получить всю массу целиком, а также массу каждого элемента по отдельности.

def molecular_mass(formula):
    # Словарь с атомными массами элементов
    atomic_masses = {
        'H': 1.008,
        'He': 4.0026,
        'Li': 6.94,
        'Be': 9.0122,
        'B': 10.81,
        'C': 12.011,
        'N': 14.007,
        'O': 15.999,
        'F': 18.998,
        'Ne': 20.180,
        'Na': 22.99,
        'Mg': 24.305,
        'Al': 26.982,
        'Si': 28.085,
        'P': 30.974,
        'S': 32.06,
        'Cl': 35.45,
        'Ar': 39.948,
        'K': 39.098,
        'Ca': 40.078,
        'Sc': 44.956,
        'Ti': 47.867,
        'V': 50.941,
        'Cr': 51.996,
        'Mn': 54.938,
        'Fe': 55.845,
        'Co': 58.933,
        'Ni': 58.693,
        'Cu': 63.546,
        'Zn': 65.38,
        'Ga': 69.723,
        'Ge': 72.630,
        'As': 74.922,
        'Se': 78.971,
        'Br': 79.904,
        'Kr': 83.798,
        'Rb': 85.468,
        'Sr': 87.62,
        'Y': 88.906,
        'Zr': 91.224,
        'Nb': 92.906,
        'Mo': 95.95,
        'Tc': 98,
        'Ru': 101.07,
        'Rh': 102.905,
        'Pd': 106.42,
        'Ag': 107.868,
        'Cd': 112.414,
        'In': 114.818,
        'Sn': 118.710,
        'Sb': 121.760,
        'Te': 127.60,
        'I': 126.904,
        'Xe': 131.293,
        'Cs': 132.905,
        'Ba': 137.327,
        'La': 138.905,
        'Ce': 140.116,
        'Pr': 140.907,
        'Nd': 144.242,
        'Pm': 145,
        'Sm': 150.36,
        'Eu': 151.964,
        'Gd': 157.25,
        'Tb': 158.925,
        'Dy': 162.500,
        'Ho': 164.930,
        'Er': 167.259,
        'Tm': 168.934,
        'Yb': 173.04,
        'Lu': 174.966,
        'Hf': 178.49,
        'Ta': 180.947,
        'W': 183.84,
        'Re': 186.207,
        'Os': 190.23,
        'Ir': 192.217,
        'Pt': 195.084,
        'Au': 196.967,
        'Hg': 200.592,
        'Tl': 204.38,
        'Pb': 207.2,
        'Bi': 208.980,
        'Po': 209,
        'At': 210,
        'Rn': 222,
        'Fr': 223,
        'Ra': 226,
        'Ac': 227,
        'Th': 232.038,
        'Pa': 231.035,
        'U': 238.028,
        'Np': 237,
        'Pu': 244,
        'Am': 243,
        'Cm': 247,
        'Bk': 247,
        'Cf': 251,
        'Es': 252,
        'Fm': 257,
        'Md': 258,
        'No': 259,
        'Lr': 262,
        'Rf': 267,
        'Db': 270,
        'Sg': 271,
        'Bh': 270,
        'Hs': 277,
        'Mt': 276,
        'Ds': 281,
        'Rg': 282,
        'Cn': 285,
        'Nh': 286,
        'Fl': 289,
        'Mc': 290,
        'Lv': 293,
        'Ts': 294,
        'Og': 294,
    }

    def parse_formula(formula):
        stack = []
        current = {}
        i = 0
        while i < len(formula):
            if formula[i] == '(' or formula[i] == '[':
                stack.append(current)
                current = {}
                i += 1
            elif formula[i] == ')' or formula[i] == ']':
                i += 1
                num = ''
                while i < len(formula) and formula[i].isdigit():
                    num += formula[i]
                    i += 1
                multiplier = int(num) if num else 1
                for element, count in current.items():
                    current[element] = count * multiplier
                if stack:
                    parent = stack.pop()
                    for element, count in current.items():
                        if element in parent:
                            parent[element] += count
                        else:
                            parent[element] = count
                    current = parent
            else:
                match = re.match(r'([A-Z][a-z]?)(\d*)', formula[i:])
                if match:
                    element, count = match.groups()
                    count = int(count) if count else 1
                    if element in current:
                        current[element] += count
                    else:
                        current[element] = count
                    i += len(match.group(0))
                else:
                    i += 1
        return current

    element_counts = parse_formula(formula)
    mass = 0.0
    element_details = []
    for element, count in element_counts.items():
        element_mass = atomic_masses[element]
        total_mass = element_mass * count
        element_details.append((element, atomic_masses[element], count, total_mass))
        mass += total_mass

    return round(mass, 2), element_details

@app.route('/molyarnaya_massa', methods=['GET', 'POST'])
def molyar_massa():
    # метод для вычисления молярной массы и отображения ее на сайте
    user = flask_login.current_user
    global resultat, dlyproverki, c
    resultat = ''
    otdelno = []
    formatspisok = ''
    dlyproverki = 0
    if request.method == 'POST':
        chemical_formula = request.form['element']
        try:
            dlyproverki, element_details = molecular_mass(chemical_formula)
            resultat = f"Молярная масса {chemical_formula}: {dlyproverki} г/моль"
            for element, mass, count, total_mass in element_details:
                otdelno.append(f"{count} x {element} ({round(mass, 2)} г/моль): {round(total_mass, 2)} г/моль, что составляет {round((round(total_mass, 2) / dlyproverki) * 100, 2)}%")
        except Exception as e:
            otdelno.append(f"{e}: такого вещества или соединения не существует")
    return render_template('molyarnaya_massa.html', resultat=resultat, dlyproverki=dlyproverki, user=user, otdelno=otdelno)
            

5. Цепочки превращений

Функция позволяет узнать целую цепочку превращений вещества, а именно какие реакции должны произойти, чтобы получить то или иное вещество. Работает при помощи отправки запроса на сервер chemer.ru . Для реализации потребуется библиотека requests(python). Мы отправляем запрос на сервер, а тот должен нам вернуть html всех реакций. Крайне важен ввод веществ в поле. Т.к данные из поля вводятся в url сайта, то важно чтобы эти данные были отформатированы. Главное требование при вводе: все реакции вводить через знак "=". Например: Al=Al2O3=AlCl3=Al(OH)3=Na3AlO3=Al(NO3)3.

def get_reaction_chain(reaction):
    # цепочка превращений
    if request.method == 'POST':
        reaction = request.form.get("chemical_formula", False)
        url = f"https://chemer.ru/services/reactions/chains/{reaction}"
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
        }
        session = requests.Session()
        session.headers.update(headers)
        response = session.get(url)
        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            content_sections = soup.find_all('section', class_='content')  # Ищем все секции с классом 'content'

            results = []  # Список для хранения первых ответов из каждой секции
            if content_sections:
                for content_section in content_sections:
                    reactions = content_section.find_all('p', class_='resizable-block')  # Ищем все 'p' внутри каждой секции
                    if reactions:
                        first_reaction = reactions[0].get_text().strip()  # Берем первый ответ из секции
                        results.append(first_reaction)  # Добавляем его в результаты
                    else:
                        # Если нет реакций с классом 'resizable-block', ищем все 'p' и берем первый из них
                        all_paragraphs = content_section.find_all('p')
                        if all_paragraphs:
                            first_paragraph = all_paragraphs[0].get_text().strip()  # Берем текст первого 'p'
                            results.append(first_paragraph)
                        else:
                            results.append("Нет данных")  # Если нет ни одного 'p', добавляем сообщение

            return results  # Возвращаем список первых ответов из всех секций


@app.route('/get_reaction_chain', methods=['GET', 'POST'])
def get_reaction_chain_page():
    # страница, которая выводит цепочку превращений, т.е прошлую функцию
    user = flask_login.current_user
    react2 = ''
    reaction = ''
    if request.method == 'POST':
        reaction = request.form.get("chemical_formula", False)
        react2 = get_reaction_chain(reaction)

    return render_template('get_reaction_chain.html', get_reaction_chain=get_reaction_chain, user=user, reaction=reaction, react2=react2)
            

6. Структура органических реакций

Функция позволяет найти органическое вещество, а именно его графическое представление. Например: при вводе 2,2-диметилбутан, должны вывестись картинки в формате .svg. Первой такой картинкой будет само вещество, а остальные - все изомеры вещества(или те, что представляет ресурс, на который мы отправляем запрос). Все это реализовано при помощи библиотеки requests(python), т.е мы также отправляем запрос на сайт и он возвращает нам ответ. Несмотря на такую, казалось бы простую идею, код этой части был самым трудным в написании(были убраны <> из всех svg, т.к сайт их не отображает):


def get_substance_html(substance_name):
    # получение имени орг вещества из таблицы на сайте при помощи парсинга этой страницы
    url = "https://chemer.ru/services/organic/structural"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
    }
    session = requests.Session()
    session.headers.update(headers)
    response = session.get(url)
    global klass
    klass = ''
    namez = []

    if response.status_code == 200:
        soup = BeautifulSoup(response.text, 'html.parser')
        table = soup.find('table')
        rows = table.find_all('tr')

        for row in rows:
            cols = row.find_all('td')
            if cols:
                name = cols[0].text.strip()
                klass = cols[1].text.strip()
                link = cols[0].find('a')['href']
                if substance_name.lower() in name.lower() and substance_name.lower() == name.lower():
                    substance_url = f"https://chemer.ru/services/organic/{link}"
                    substance_response = session.get(substance_url)
                    return substance_response.text, None
                elif substance_name.lower() in name.lower() and name.lower()[2:] != substance_name.lower():
                    namez.append(name)
                elif substance_name.lower() in name.lower() and name.lower()[:2] == 'н-' and name.lower()[2:] == substance_name.lower():
                    substance_url = f"https://chemer.ru/services/organic/{link}"
                    substance_response = session.get(substance_url)
                    return substance_response.text, None
    return None, namez


def extract_svg_and_symbols(html_code):
    # занимается сохранением свг-картинок на сервер
    soup = BeautifulSoup(html_code, 'html.parser')
    svg_elements = soup.find_all('svg')
    symbols = soup.find_all('symbol')
    names = ''

    if not svg_elements:
        return None, None, None

    first_svg_content = str(svg_elements[0])
    if 'width' not in first_svg_content or 'height' not in first_svg_content:
        first_svg_content = first_svg_content.replace('svg', 'svg width="200" height="200"')

    isomer_svgs = []
    spacing = 220  # Расстояние между изомерами
    max_per_row = 20  # Максимум изомеров в строке

    # Извлечение секции с id='tab1'
    tab1_section = soup.find('section', id='tab1')

    if tab1_section:
        # Получаем все SVG элементы внутри секции
        svg_elements2 = tab1_section.find_all('svg')
        names = tab1_section.find_all('a')

        for index, svg in enumerate(svg_elements2):
            row = index // max_per_row  # Определяем номер строки
            col = index % max_per_row  # Определяем номер колонки
            x = col - 1  # Устанавливаем x координату
            y = row  # Устанавливаем y координату для новой строки
            svg_str = str(svg).replace('svg', f'svg x="{x}" y="{y}"')  # Устанавливаем координаты
            isomer_svgs.append(svg_str)

    isomer_svgs_content = ''.join(isomer_svgs)
    symbol_content = ''.join(str(symbol) for symbol in symbols)

    return first_svg_content, isomer_svgs_content, symbol_content, names


@app.route('/orghim', methods=['GET', 'POST'])
def orghim():
    # основная страница для работы с методами(предыдущими двумя). Все вместе это ф-ция орг.химии
    # ее суть в выведении названий веществ и картинок изомеров и самого вещества
    # изомеров только 10, т.к 70 изомеров декана, например, может сильно нагрузить устройство пользователя и сервер,
    # который может обрабатывать несколько таких запросов, что приведет к замедлению его работы(он и так бесплатный)
    global klass
    combined = ''
    isomer_files = []
    variants = ''
    user = flask_login.current_user
    substance_name = ''
    if request.method == 'POST':
        substance_name = request.form['substance_name']
        html_code, variants = get_substance_html(substance_name)

        if html_code:
            first_svg, isomers_svg, symbols_svg, names = extract_svg_and_symbols(html_code)

            # Сохраняем первую SVG-картинку и символы в файл
            if first_svg:
                with open('static/output.svg', 'w', encoding='utf-8') as f:
                    f.write(
                        f"svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>{symbols_svg}{first_svg}/svg")

            # Сохраняем изомеры в отдельные файлы
            if isomers_svg.strip():
                isomer_files = []
                nazvaniya = []
                for index, svg in enumerate(isomers_svg.split('/svg')):
                    if index <= 9:
                        if svg.strip():
                            file_name = f'static/isomer_{index}.svg'
                            with open(file_name, 'w', encoding='utf-8') as f:
                                f.write(f"svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>Х{symbols_svg}{svg}/svg/svg")
                            filename_without_static = f'isomer_{index}.svg'
                            isomer_files.append(filename_without_static)

                for i, nazv in enumerate(names):
                    if i <= 9:
                        soup = BeautifulSoup(str(nazv), 'html.parser')
                        nazv = soup.a.text
                        nazvaniya.append(nazv.capitalize())
                combined = list(zip(nazvaniya, isomer_files))

            return render_template('orghim.html', svg_file='output.svg', isomer_files=isomer_files, substance_name=substance_name, user=user, klass=klass, combined=combined)

    return render_template('orghim.html', svg_file=None, isomer_files=None, nazvaniya=None, user=user, variants=variants, substance_name=substance_name)
            

Таблицы

1.Таблица Менделеева

Страница возвращает таблицу Менделеева. Как она работает? Из интернет-ресурсов была скачана таблица ПСХЭ, которая отображается на странице. Также пролистнув страницу ниже можно увидеть подробности про каждый элемент.

2.Таблица растворимости

Страница возвращает таблицу растворимостей.

3.Таблица кислот и кислотных остатков

Страница возвращает кислоты и их остатки, также есть таблица сил кислот: от самой слабой, до самой сильной.

Код для всех этих страниц схож, покажу только один:


@app.route('/tablica', methods=['GET', 'POST'])
def tablica():
    # таблица менделеева
    user = flask_login.current_user
    return render_template('tablica.html', user=user)
            

Также, конечно, нужно создать html файл со страницей, прежде чем вписывать это себе в код.

Общий чат

Общий чат предназначен для общения пользователей сайта. Здесь Вы можете искать новых друзей, найти решение своей проблемы, а также просто поделиться своими находками. Обязательное условие пользования чатом: авторизация на сайте.

Мини-игра

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

Как играть:

  1. Введите название элемента на русском языке.
  2. Если ответ верный, будет показан следующий элемент.
  3. Игра продолжается до достижения 10 правильных ответов.
  4. Если игрок набирает больше 50% правильных ответов, то он выигрывает. Иначе-проигрыш.

Коды ошибок и их значения

Статус 0: Ошибок нет. Пользователь не видит этот код.

Статус 1: Такого пользователя не существует. Появляется на странице с авторизацией и обозначает то, что указанный в поле Логин имеет ошибку или такого пользователя нет в базе данных сайта. Необходимо проверить ошибку в написании логина или зарегистрироваться на сайте.

Статус 2: Неправильный пароль. Появляется также на странице с Логином. Причина в ошибке в пароле. Однако это означает, что такой аккаунт существует на сайте.

Статус 3: Попытка регистрации нового пользователя с похожим именем уже существующего аккаунта. Нужно изменить Логин(достаточно изменения на 1 символ).

Статус 4: Попытка регистрации с уже использованной электронной почтой. Такая почта есть в базе данных сайта. Нужно использовать другую.

Статус 5: Несовпадение пароля в полях "Пароль" и "Подтверждение пароля". Нужно просто перепроверить пароли и ввести заново.

Статус 6: Такой страницы не существует. Проверьте введенную ссылку.

Статус 7: Пользователя с таким логином не существует. Ошибка создана для страницы с удалением пользователей.

Статус 8: Ошибка при удалении профиля, возможно ошибка с базой данных, либо с удалением файлов, связанных с профилем.

Статус 9: Ошибка при сбросе статистики миниигры. Возможно ошибка с базой данных(вас нет в списке игроков, зайдите в миниигру для исправления) или ваша статистика уже была сброшена.

Контакты

Если у вас есть вопросы или предложения, пожалуйста, свяжитесь с нами по адресу: garipovfanis1@yandex.ru. Дополнительные контакты автора указаны снизу.