0
0
mirror of https://gitflic.ru/project/maks1ms/ocab.git synced 2025-11-28 10:21:55 +03:00

5 Commits

35 changed files with 2846 additions and 199 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ venv
__pycache__
OCAB.db
config.yaml
dist

View File

@@ -1,60 +1,44 @@
# OpenChatAiBot V2
# OCAB - Open Chat Ai Bot
## Что такое OCAB?
OCAB - это бот для Telegram, который призван помочь во взаимодействии с чатом.
Бот поддерживает интеграцию модулей для расширения функционала.
Фактически бот является платформой для запуска созданных для него модулей.
Модули могут взаимодействовать друг с другом или быть полностью независимыми.
OCAB - это платформа для разработки модульных Telegram-ботов, которая призвана упростить взаимодействие с чатами.
OCAB предоставляет возможность расширять функциональность бота с помощью интеграции различных модулей.
Код платформы и набор стандартных модулей находятся в этом монорепозитории.
## Что такое модуль?
## Структура монорепозитория
Модуль - это директория, которая содержит в себе код модуля и его конфигурацию.
Монорепозиторий OCAB включает в себя:
* **Ядро OCAB (src/ocab_core):** Содержит основные компоненты платформы, такие как система управления модулями,
логирование и утилиты.
* **Модули OCAB (src/ocab_modules):** Содержит стандартные и дополнительные модули, которые расширяют
функциональность ботов OCAB.
* **Пример бота (src/gnomik):** Пример реализации бота на платформе OCAB.
## Модули
Модули OCAB - это независимые компоненты, которые добавляют функциональность к боту.
### Структура модуля
*Будет дополнено после закрытия [issue #17](https://gitflic.ru/project/armatik/ocab/issue/17).*
Структура модуля представлена [здесь](docs/MODULES-SPEC.md).
## Стандартные модули
### Стандартные модули
В стандартный состав бота входят следующие модули:
Стандартные модули предоставляют базовые функции для работы бота:
* [admin](src/ocab_modules/ocab_modules/standard/admin/README.md) - модуль для модерирования чата.
* [roles](src/ocab_modules/ocab_modules/standard/roles/README.md) - модуль ролей пользователей.
* [config](src/ocab_modules/ocab_modules/standard/config/README.md) - модуль управления конфигурацией бота.
* [database](src/ocab_modules/ocab_modules/standard/database/README.md) - модуль для работы с базой данных.
* [fsm_database_storage](src/ocab_modules/ocab_modules/standard/fsm_database_storage/README.md) - модуль для хранения состояний FSM в базе данных.
* [filters](src/ocab_modules/ocab_modules/standard/filters/README.md) - модуль, предоставляющий фильтры для aiogram.
* [message_processing](src/ocab_modules/ocab_modules/standard/message_processing/README.md) - модуль обработки входящих сообщений.
* [miniapp](src/ocab_modules/ocab_modules/standard/miniapp/README.md) - модуль для реализации веб-интерфейса бота.
* [command_helper](src/ocab_modules/ocab_modules/standard/command_helper/README.md) - модуль для упрощения регистрации команд бота.
* [info](src/ocab_modules/ocab_modules/standard/info/README.md) - модуль предоставления информации о пользователях и чатах.
* `admin` - модуль для модерирования чата. Позволяет удалять сообщения, банить пользователей и т.д.
* `reputation` - модуль репутации пользователей. Позволяет оценивать ответы пользователей и накапливать репутацию.
* `welcome` - модуль приветствия новых пользователей. Позволяет приветствовать новых пользователей в чате, а также
проверять пользователя капчей для предотвращения спама.
* `roles` - модуль ролей. Позволяет назначать пользователям роли и ограничивать доступ к командам бота по ролям.
Является важной частью системы прав доступа и модуля `admin`.
### Дополнительные официальные модули
## Дополнительные официальные модули
* `yandexgpt` - модуль для генерации ответов на основе нейросети GPT-3.5. Позволяет боту отвечать на сообщения
пользователей, используя нейросеть. Ключевой особенностью является построение линии контекста для нейросети,
которая позволяет боту отвечать на вопросы, используя контекст предыдущих сообщений. Для этого используется
модуль база данных хранящий историю сообщений.
<!--
* `bugzilla` - модуль для интеграции с BugZilla. Позволяет получать уведомления о новых багах в BugZilla, отслеживать их
статус, формировать стандартизированные сообщения для корректного описания багов. В будущем планируется интеграция с
API BugZilla для возможности создания багов из чата.
* `alt_packages` - модуль для интеграции с AltLinux Packages. Позволяет получать уведомления о новых пакетах в репозитории
AltLinux, поиска пакетов по названию, получения истории изменений, команды для установки пакета из репозитория и
прочей информации о пакете.
* `notes` - модуль заметок. Позволяет сохранять заметки для пользователей и чатов. Заметки являются ссылками на
сообщения в чате.
-->
Список модулей будет пополняться. Идеи для модулей можно оставлять в [issues](https://gitflic.ru/project/armatik/ocab/issue/create).
## Установка бота
### Docker
### Вручную
## Технологический стек
* Python 3.11.6 или выше - основной язык программирования.
* SQLite 3 - база данных для хранения информации о чате и пользователях.
* [Poetry](https://gitflic.ru/project/armatik/ocab/blob?file=how-to%20install%20deps.md&branch=OCAB-V2) - менеджер зависимостей.
* aiogram 3 - библиотека для работы с Telegram API.
* peewee - ORM для работы с базой данных.
Дополнительные официальные модули разработаны командой OCAB и предоставляют расширенные возможности для бота:
* [yandexgpt](src/ocab_modules/ocab_modules/external/yandexgpt/README.md) - модуль для интеграции с нейросетью YandexGPT.
* [create_report_apps](src/ocab_modules/ocab_modules/external/create_report_apps/README.md) - модуль для создания отчетов об ошибках.

39
docs/DEV.md Normal file
View File

@@ -0,0 +1,39 @@
## Настройка рабочего окружения
Данная инструкция поможет вам настроить рабочее окружение для разработки OCAB.
### Предварительные требования
* **Python 3.12:** OCAB требует Python 3.12.
* **VSCode:** Рекомендуется использовать VSCode для разработки.
* **Git:** У вас должен быть установлен Git для клонирования репозитория.
### Шаги
1. **Клонируйте репозиторий:**
```bash
git clone https://gitflic.ru/project/armatik/ocab.git
```
2. **Откройте проект в VSCode:**
* Откройте папку `ocab` в VSCode.
* VSCode автоматически предложит открыть проект как workspace, используя файл `ocab.code-workspace`.
Нажмите "Открыть Workspace", чтобы принять предложение.
3. **Настройте Poetry:**
* Установите Poetry, следуя инструкциям на официальном сайте: [https://python-poetry.org/docs/](https://python-poetry.org/docs/).
* **Для каждого пакета:**
* Перейдите в папку пакета (например, `src/ocab_core`).
* Выполните команду `poetry install`, чтобы установить зависимости пакета.
* Poetry создаст виртуальное окружение внутри папки пакета (`.venv`).
4. **Активируйте виртуальное окружение:**
* Выполните команду `poetry shell` в папке пакета, чтобы активировать виртуальное окружение.
Теперь ваше рабочее окружение настроено, и вы можете начать.
### Дополнительная информация
* Каждый пакет в монорепозитории имеет свой собственный файл `pyproject.toml`, где указаны его зависимости.
* Poetry автоматически управляет виртуальными окружениями для каждого пакета.
* Вы можете использовать команду `poetry add <package_name>` для добавления новых зависимостей.

View File

@@ -9,7 +9,7 @@
## Метаинформация о модуле (info.json)
Этот файл содержит метаинформацию о модуле в формате JSON. Пример структуры info.json приведён ниже:
Этот файл содержит метаинформацию о модуле в формате JSON. Пример структуры info.json приведен ниже:
```json
{
@@ -20,8 +20,26 @@
"version": "1.0.0",
"privileged": false,
"dependencies": {
"required": {
"standard.roles": "^1.0.0",
"standard.database": "^1.0.0"
"standard.database": {
"version": "^1.0.0",
"uses": [
"db_api"
]
}
},
"optional": {
"external.yandexgpt": "*"
}
},
"pythonDependencies": {
"required": {
"some_package": "^1.2.3"
},
"optional": {
"another_package": "*"
}
}
}
```
@@ -30,31 +48,60 @@
- `name`: Название модуля.
- `description`: Описание функциональности модуля.
- `author`: Автор модуля.
- `version`: Версия модуля.
- `version`: Версия модуля в формате [SemVer](https://semver.org/).
- `privileged`: Булево значение, указывающее, является ли модуль привилегированным.
- `dependencies`: Объект, описывающий зависимости модуля от других модулей с указанием версии.
- `dependencies`: Объект, описывающий зависимости модуля от других **OCAB** модулей.
- `required`: Обязательные зависимости. Ключ - идентификатор модуля, значение - версия или объект `DependencyInfo`.
- `optional`: Необязательные зависимости. Ключ - идентификатор модуля, значение - версия или объект `DependencyInfo`.
- `pythonDependencies`: Объект, описывающий зависимости модуля от внешних Python пакетов.
- `required`: Обязательные зависимости. Ключ - название пакета, значение - версия.
- `optional`: Необязательные зависимости. Ключ - название пакета, значение - версия.
### DependencyInfo
Объект `DependencyInfo` позволяет указать не только версию зависимости, но и список разрешенных к использованию
атрибутов модуля (`uses`). Если `uses` не указан, то доступ к модулю целиком запрещен.
```json
{
"version": "^1.0.0",
"uses": [
"db_api"
]
}
```
- `version`: Версия модуля.
- `uses`: Список разрешенных атрибутов модуля.
## Режимы выполнения модулей
Непривилегированный режим (`privileged: false`):
- Модуль выполняется в доверенной среде на основе RestrictedPython (это накладывает ряд ограничений);
- Может использовать только определенный набор разрешенных пакетов
**Непривилегированный режим** (`privileged: false`):
- Модуль выполняется в доверенной среде на основе RestrictedPython (это накладывает ряд ограничений).
- Может импортировать только явно разрешенные модули, указанные в `pythonDependencies`,
а также несколько стандартных модулей, необходимых для работы.
- Имеет доступ к пакету `ocab_core.modules_system.public_api` для взаимодействия с ботом.
Привилегированный режим (`privileged: true`):
**Привилегированный режим** (`privileged: true`):
- Модуль выполняется без ограничений.
- Имеет полный доступ ко всем пакетам.
- Имеет полный доступ ко всем пакетам, доступным в окружении.
- Должен использоваться с осторожностью и только для модулей, требующих расширенных прав.
## Жизненный цикл модуля
1. Загрузка метаданных из `info.json`
2. Проверка зависимостей
3. Загрузка кода модуля из `__init__.py`
4. Вызов функции `module_init` (если она есть)
1. Загрузка метаданных из `info.json`.
2. Проверка зависимостей:
- Проверяется наличие всех обязательных зависимостей.
- Проверяется совместимость версий зависимостей.
- Проверяется наличие Python зависимостей.
3. Загрузка кода модуля из `__init__.py`.
4. Вызов функции `module_init` (если она есть).
5. После загрузки всех модулей вызывается функция `module_late_init` (если она есть).
## Взаимодействие между модулями
Модули могут взаимодействовать друг с другом через [API](../src/ocab_core/modules_system/public_api/__init__.py), предоставляемое системой управления модулями.
Модули могут взаимодействовать друг с другом через [API](../src/ocab_core/ocab_core/modules_system/public_api/__init__.py),
предоставляемое системой управления модулями.
Например, есть функция `get_module`. Она позволяет получить модуль или предоставляемые им объекты по его идентификатору.
Например, есть функция `get_module`. Она позволяет получить модуль или предоставляемые им объекты по его
идентификатору.

View File

@@ -1,47 +0,0 @@
## Poetry
### Установка с официального сайта
```shell
curl -sSL https://install.python-poetry.org | python3 -
```
### Установка с PyPi
```shell
python3 -m pip install poetry
```
Доп информация:https://www.8host.com/blog/ustanovka-menedzhera-zavisimostej-poetry/
## Зависимости
### Добавление зависимости
```shell
poetry add NAME
```
`NAME` - название зависимости.
### Установка зависимостей
```shell
poetry install
```
### Обновление зависимостей
```shell
poetry update
```
## Виртуальное окружение
### Создание/активация
```shell
poetry shell
```
### Настройка
Хранить окружение внутри проекта
```shell
poetry config virtualenvs.in-project true
```

25
ocab.code-workspace Normal file
View File

@@ -0,0 +1,25 @@
{
"folders": [
{
"name": "OCAB Monorepo Root",
"path": ".",
},
{
"name": "OCAB Modules",
"path": "src/ocab_modules"
},
{
"name": "OCAB Core",
"path": "src/ocab_core"
},
{
"name": "Gnomik",
"path": "src/gnomik"
}
],
"extensions": {
"recommendations": [
"ms-python.python"
]
},
}

2
poetry.toml Normal file
View File

@@ -0,0 +1,2 @@
[virtualenvs]
in-project = true

23
src/gnomik/Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM python:3.12-slim as builder
RUN pip install poetry
RUN mkdir -p /app
COPY . /app
# Фикс
RUN sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/gnomik/pyproject.toml && \
sed -i '/ocab-modules = {/{s/, develop = true//}' /app/src/gnomik/pyproject.toml && \
sed -i '/ocab-core = {/{s/, develop = true//}' /app/src/ocab_modules/pyproject.toml
WORKDIR /app/src/gnomik
RUN poetry lock && poetry install
FROM python:3.12-slim as base
COPY --from=builder /app/src/gnomik /app
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "gnomik"]

View File

@@ -0,0 +1,14 @@
**/Dockerfile
**/*.dockerignore
**/docker-compose.yml
**/.git
**/.gitignore
**/.venv
**/.mypy_cache
**/__pycache__/
src/gnomik/config.yaml
src/gnomik/database/*

View File

@@ -1 +1,55 @@
# Гномик
# Gnomик
![Логотип](./docs/gnomik.jpg)
Чат-бот помощник в [ALT Gnome Chat](https://t.me/alt_gnome_chat).
ALT Regular Gnome Community - открытое сообщество пользователей операционной системы ALT Regular Gnome.
- [Канал](https://t.me/alt_gnome)
- [Wiki](https://alt-gnome.wiki)
## Описание
Gnomик - это чат-бот, разработанный на платформе Open Chat AI Bot (OCAB) для Telegram. Он предоставляет различные функции и возможности, помогающие пользователям операционной системы ALT Regular Gnome.
## Функционал
<!--
TODO: описать функционал
-->
## Запуск
### Docker
1. Соберите Docker-образ:
```bash
docker build -t gnomik .
```
2. Запустите контейнер:
```bash
docker run -p 9000:9000 -v ./config.yaml:/app/config.yaml -v ./database:/app/database gnomik
```
Замените `./config.yaml` и `./database` на пути к вашим локальным файлам конфигурации и паки для базы данных.
### Вручную
1. Активируйте виртуальное окружение Gnomика:
```bash
poetry shell
```
2. Запустите бота:
```bash
python -m gnomik
```
## Конфигурация
Конфигурация бота находится в файле `config.yaml`.
## Модули
Список загружаемых модулей указан в файле `__main__.py`.

View File

@@ -0,0 +1,18 @@
core:
mode: WEBHOOK
token: xxx
webhook:
public_url: xxx
filters:
approved_chat_id: -4128011756 | -4128011756
default_chat_tag: '@alt_gnome_chat'
miniapp:
public_url: xxx
yandexgpt:
catalogid: xxx
inword: помогите | не работает
prompt: Ты чат-бот ...
startword: Бот| Бот, | бот | бот,
token: xxx
token_for_answer: 2000
token_for_request: 8000

View File

@@ -0,0 +1,12 @@
version: '3'
services:
app:
build:
context: ../..
dockerfile: src/gnomik/Dockerfile
ports:
- 9000:9000
volumes:
- ./config.yaml:/app/config.yaml
- ./database:/app/database

BIN
src/gnomik/docs/gnomik.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -1 +1,23 @@
# OCAB Core
Это ядро OCAB, содержащее базовые компоненты:
- Система управления модулями.
- Логирование.
- Утилиты.
## Система управления модулями
Система управления модулями отвечает за:
- Загрузку модулей.
- Проверку зависимостей.
- Предоставление API для взаимодействия между модулями.
## Логирование
Модуль логирования предоставляет функции для записи логов в консоль.
## Утилиты
Модуль утилит содержит вспомогательные функции, например, для форматирования текста.

View File

@@ -78,7 +78,7 @@ class OCAB:
singleton = Singleton()
app = FastAPI()
config = get_module("standard.config", "config")
app.mount("/webapp", singleton.storage["webapp"])
app.mount(config.get("miniapp::prefix"), singleton.storage["webapp"])
await register_bot_webhook(app, singleton.bot, singleton.dp)
await singleton.bot.set_webhook(config.get("core::webhook::public_url"))
hyperConfig = HyperConfig()

View File

@@ -165,7 +165,7 @@ files = [
name = "anyio"
version = "4.4.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
@@ -200,6 +200,28 @@ tests = ["attrs[tests-no-zope]", "zope-interface"]
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]]
name = "blinker"
version = "1.8.2"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.8"
files = [
{file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"},
{file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"},
]
[[package]]
name = "cachelib"
version = "0.9.0"
description = "A collection of cache libraries in the same API interface."
optional = false
python-versions = ">=3.7"
files = [
{file = "cachelib-0.9.0-py3-none-any.whl", hash = "sha256:811ceeb1209d2fe51cd2b62810bd1eccf70feba5c52641532498be5c675493b3"},
{file = "cachelib-0.9.0.tar.gz", hash = "sha256:38222cc7c1b79a23606de5c2607f4925779e37cdcea1c2ad21b8bae94b5425a5"},
]
[[package]]
name = "certifi"
version = "2024.7.4"
@@ -211,6 +233,105 @@ files = [
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "click"
version = "8.1.7"
@@ -236,6 +357,128 @@ files = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "dash"
version = "2.17.1"
description = "A Python framework for building reactive web-apps. Developed by Plotly."
optional = false
python-versions = ">=3.8"
files = [
{file = "dash-2.17.1-py3-none-any.whl", hash = "sha256:3eefc9ac67003f93a06bc3e500cae0a6787c48e6c81f6f61514239ae2da414e4"},
{file = "dash-2.17.1.tar.gz", hash = "sha256:ee2d9c319de5dcc1314085710b72cd5fa63ff994d913bf72979b7130daeea28e"},
]
[package.dependencies]
dash-core-components = "2.0.0"
dash-html-components = "2.0.0"
dash-table = "5.0.0"
Flask = ">=1.0.4,<3.1"
importlib-metadata = "*"
nest-asyncio = "*"
plotly = ">=5.0.0"
requests = "*"
retrying = "*"
setuptools = "*"
typing-extensions = ">=4.1.1"
Werkzeug = "<3.1"
[package.extras]
celery = ["celery[redis] (>=5.1.2)", "redis (>=3.5.3)"]
ci = ["black (==22.3.0)", "dash-dangerously-set-inner-html", "dash-flow-example (==0.0.5)", "flake8 (==7.0.0)", "flaky (==3.8.1)", "flask-talisman (==1.0.0)", "jupyterlab (<4.0.0)", "mimesis (<=11.1.0)", "mock (==4.0.3)", "numpy (<=1.26.3)", "openpyxl", "orjson (==3.10.3)", "pandas (>=1.4.0)", "pyarrow", "pylint (==3.0.3)", "pytest-mock", "pytest-rerunfailures", "pytest-sugar (==0.9.6)", "pyzmq (==25.1.2)", "xlrd (>=2.0.1)"]
compress = ["flask-compress"]
dev = ["PyYAML (>=5.4.1)", "coloredlogs (>=15.0.1)", "fire (>=0.4.0)"]
diskcache = ["diskcache (>=5.2.1)", "multiprocess (>=0.70.12)", "psutil (>=5.8.0)"]
testing = ["beautifulsoup4 (>=4.8.2)", "cryptography", "dash-testing-stub (>=0.0.2)", "lxml (>=4.6.2)", "multiprocess (>=0.70.12)", "percy (>=2.0.2)", "psutil (>=5.8.0)", "pytest (>=6.0.2)", "requests[security] (>=2.21.0)", "selenium (>=3.141.0,<=4.2.0)", "waitress (>=1.4.4)"]
[[package]]
name = "dash-bootstrap-components"
version = "1.6.0"
description = "Bootstrap themed components for use in Plotly Dash"
optional = false
python-versions = "<4,>=3.8"
files = [
{file = "dash_bootstrap_components-1.6.0-py3-none-any.whl", hash = "sha256:97f0f47b38363f18863e1b247462229266ce12e1e171cfb34d3c9898e6e5cd1e"},
{file = "dash_bootstrap_components-1.6.0.tar.gz", hash = "sha256:960a1ec9397574792f49a8241024fa3cecde0f5930c971a3fc81f016cbeb1095"},
]
[package.dependencies]
dash = ">=2.0.0"
[package.extras]
pandas = ["numpy", "pandas"]
[[package]]
name = "dash-core-components"
version = "2.0.0"
description = "Core component suite for Dash"
optional = false
python-versions = "*"
files = [
{file = "dash_core_components-2.0.0-py3-none-any.whl", hash = "sha256:52b8e8cce13b18d0802ee3acbc5e888cb1248a04968f962d63d070400af2e346"},
{file = "dash_core_components-2.0.0.tar.gz", hash = "sha256:c6733874af975e552f95a1398a16c2ee7df14ce43fa60bb3718a3c6e0b63ffee"},
]
[[package]]
name = "dash-extensions"
version = "1.0.18"
description = "Extensions for Plotly Dash."
optional = false
python-versions = "<4,>=3.9"
files = [
{file = "dash_extensions-1.0.18-py3-none-any.whl", hash = "sha256:17f4469670bd70ce12fac1a05baaae119fb65eee7b012af47aff7377d0399eeb"},
{file = "dash_extensions-1.0.18.tar.gz", hash = "sha256:a6b6c0952b3af7ae84c418fea4b43cbd0bd4e82f20d91f1573380b8a3d90df0a"},
]
[package.dependencies]
dash = ">=2.17.0"
dataclass-wizard = ">=0.22.2,<0.23.0"
Flask-Caching = ">=2.1.0,<3.0.0"
jsbeautifier = ">=1.14.3,<2.0.0"
more-itertools = ">=10.2.0,<11.0.0"
pydantic = ">=2.7.1,<3.0.0"
ruff = ">=0.4.5,<0.5.0"
[package.extras]
mantine = ["dash-mantine-components (>=0.14.3,<0.15.0)"]
[[package]]
name = "dash-html-components"
version = "2.0.0"
description = "Vanilla HTML components for Dash"
optional = false
python-versions = "*"
files = [
{file = "dash_html_components-2.0.0-py3-none-any.whl", hash = "sha256:b42cc903713c9706af03b3f2548bda4be7307a7cf89b7d6eae3da872717d1b63"},
{file = "dash_html_components-2.0.0.tar.gz", hash = "sha256:8703a601080f02619a6390998e0b3da4a5daabe97a1fd7a9cebc09d015f26e50"},
]
[[package]]
name = "dash-table"
version = "5.0.0"
description = "Dash table"
optional = false
python-versions = "*"
files = [
{file = "dash_table-5.0.0-py3-none-any.whl", hash = "sha256:19036fa352bb1c11baf38068ec62d172f0515f73ca3276c79dee49b95ddc16c9"},
{file = "dash_table-5.0.0.tar.gz", hash = "sha256:18624d693d4c8ef2ddec99a6f167593437a7ea0bf153aa20f318c170c5bc7308"},
]
[[package]]
name = "dataclass-wizard"
version = "0.22.3"
description = "Marshal dataclasses to/from JSON. Use field properties with initial values. Construct a dataclass schema with JSON input."
optional = false
python-versions = "*"
files = [
{file = "dataclass-wizard-0.22.3.tar.gz", hash = "sha256:4c46591782265058f1148cfd1f54a3a91221e63986fdd04c9d59f4ced61f4424"},
{file = "dataclass_wizard-0.22.3-py2.py3-none-any.whl", hash = "sha256:63751203e54b9b9349212cc185331da73c1adc99c51312575eb73bb5c00c1962"},
]
[package.extras]
dev = ["Sphinx (==5.3.0)", "bump2version (==1.0.1)", "coverage (>=6.2)", "dataclass-factory (==2.12)", "dataclasses-json (==0.5.6)", "flake8 (>=3)", "jsons (==1.6.1)", "pip (>=21.3.1)", "pytest (==7.0.1)", "pytest-cov (==3.0.0)", "pytest-mock (>=3.6.1)", "pytimeparse (==1.1.8)", "sphinx-issues (==3.0.1)", "sphinx-issues (==4.0.0)", "tox (==3.24.5)", "twine (==3.8.0)", "watchdog[watchmedo] (==2.1.6)", "wheel (==0.37.1)", "wheel (==0.42.0)"]
timedelta = ["pytimeparse (>=1.1.7)"]
yaml = ["PyYAML (>=5.3)"]
[[package]]
name = "dataclasses-json"
version = "0.6.7"
@@ -255,7 +498,7 @@ typing-inspect = ">=0.4.0,<1"
name = "dnspython"
version = "2.6.1"
description = "DNS toolkit"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"},
@@ -271,11 +514,21 @@ idna = ["idna (>=3.6)"]
trio = ["trio (>=0.23)"]
wmi = ["wmi (>=1.5.1)"]
[[package]]
name = "editorconfig"
version = "0.12.4"
description = "EditorConfig File Locator and Interpreter for Python"
optional = false
python-versions = "*"
files = [
{file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"},
]
[[package]]
name = "email-validator"
version = "2.2.0"
description = "A robust email address syntax and deliverability validation library."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"},
@@ -290,7 +543,7 @@ idna = ">=2.0.0"
name = "fastapi"
version = "0.111.1"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "fastapi-0.111.1-py3-none-any.whl", hash = "sha256:4f51cfa25d72f9fbc3280832e84b32494cf186f50158d364a8765aabf22587bf"},
@@ -315,7 +568,7 @@ all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)"
name = "fastapi-cli"
version = "0.0.4"
description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"},
@@ -328,6 +581,43 @@ typer = ">=0.12.3"
[package.extras]
standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"]
[[package]]
name = "flask"
version = "3.0.3"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"},
{file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"},
]
[package.dependencies]
blinker = ">=1.6.2"
click = ">=8.1.3"
itsdangerous = ">=2.1.2"
Jinja2 = ">=3.1.2"
Werkzeug = ">=3.0.0"
[package.extras]
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
[[package]]
name = "flask-caching"
version = "2.3.0"
description = "Adds caching support to Flask applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "Flask_Caching-2.3.0-py3-none-any.whl", hash = "sha256:51771c75682e5abc1483b78b96d9131d7941dc669b073852edfa319dd4e29b6e"},
{file = "flask_caching-2.3.0.tar.gz", hash = "sha256:d7e4ca64a33b49feb339fcdd17e6ba25f5e01168cf885e53790e885f83a4d2cf"},
]
[package.dependencies]
cachelib = ">=0.9.0,<0.10.0"
Flask = "*"
[[package]]
name = "frozenlist"
version = "1.4.1"
@@ -418,7 +708,7 @@ files = [
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
optional = true
python-versions = ">=3.7"
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
@@ -429,7 +719,7 @@ files = [
name = "h2"
version = "4.1.0"
description = "HTTP/2 State-Machine based protocol implementation"
optional = false
optional = true
python-versions = ">=3.6.1"
files = [
{file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"},
@@ -444,7 +734,7 @@ hyperframe = ">=6.0,<7"
name = "hpack"
version = "4.0.0"
description = "Pure-Python HPACK header compression"
optional = false
optional = true
python-versions = ">=3.6.1"
files = [
{file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
@@ -455,7 +745,7 @@ files = [
name = "httpcore"
version = "1.0.5"
description = "A minimal low-level HTTP client."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
@@ -476,7 +766,7 @@ trio = ["trio (>=0.22.0,<0.26.0)"]
name = "httptools"
version = "0.6.1"
description = "A collection of framework independent HTTP protocol utils."
optional = false
optional = true
python-versions = ">=3.8.0"
files = [
{file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"},
@@ -524,7 +814,7 @@ test = ["Cython (>=0.29.24,<0.30.0)"]
name = "httpx"
version = "0.27.0"
description = "The next generation HTTP client."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
@@ -548,7 +838,7 @@ socks = ["socksio (==1.*)"]
name = "hypercorn"
version = "0.17.3"
description = "A ASGI Server based on Hyper libraries and inspired by Gunicorn"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "hypercorn-0.17.3-py3-none-any.whl", hash = "sha256:059215dec34537f9d40a69258d323f56344805efb462959e727152b0aa504547"},
@@ -571,7 +861,7 @@ uvloop = ["uvloop (>=0.18)"]
name = "hyperframe"
version = "6.0.1"
description = "HTTP/2 framing layer for Python"
optional = false
optional = true
python-versions = ">=3.6.1"
files = [
{file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
@@ -589,6 +879,36 @@ files = [
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "importlib-metadata"
version = "8.2.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"},
{file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "itsdangerous"
version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.8"
files = [
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
]
[[package]]
name = "jinja2"
version = "3.1.4"
@@ -606,6 +926,20 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "jsbeautifier"
version = "1.15.1"
description = "JavaScript unobfuscator and beautifier."
optional = false
python-versions = "*"
files = [
{file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"},
]
[package.dependencies]
editorconfig = ">=0.12.2"
six = ">=1.13.0"
[[package]]
name = "magic-filter"
version = "1.0.12"
@@ -624,7 +958,7 @@ dev = ["black (>=22.8.0,<22.9.0)", "flake8 (>=5.0.4,<5.1.0)", "isort (>=5.11.5,<
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
@@ -736,13 +1070,24 @@ tests = ["pytest", "pytz", "simplejson"]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
optional = true
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "more-itertools"
version = "10.3.0"
description = "More routines for operating on iterables, beyond itertools"
optional = false
python-versions = ">=3.8"
files = [
{file = "more-itertools-10.3.0.tar.gz", hash = "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463"},
{file = "more_itertools-10.3.0-py3-none-any.whl", hash = "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320"},
]
[[package]]
name = "multidict"
version = "6.0.5"
@@ -853,6 +1198,17 @@ files = [
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[[package]]
name = "nest-asyncio"
version = "1.6.0"
description = "Patch asyncio to allow nested event loops"
optional = false
python-versions = ">=3.5"
files = [
{file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"},
{file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
]
[[package]]
name = "ocab-modules"
version = "0.1.0"
@@ -862,6 +1218,14 @@ python-versions = "~3.12"
files = []
develop = true
[package.dependencies]
dash = "^2.17.1"
dash-bootstrap-components = "^1.6.0"
dash-extensions = "^1.0.18"
ocab-core = {path = "../ocab_core", develop = true}
peewee = "^3.17.6"
pyyaml = "^6.0.1"
[package.source]
type = "directory"
url = "../ocab_modules"
@@ -877,11 +1241,36 @@ files = [
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "peewee"
version = "3.17.6"
description = "a little orm"
optional = false
python-versions = "*"
files = [
{file = "peewee-3.17.6.tar.gz", hash = "sha256:cea5592c6f4da1592b7cff8eaf655be6648a1f5857469e30037bf920c03fb8fb"},
]
[[package]]
name = "plotly"
version = "5.23.0"
description = "An open-source, interactive data visualization library for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "plotly-5.23.0-py3-none-any.whl", hash = "sha256:76cbe78f75eddc10c56f5a4ee3e7ccaade7c0a57465546f02098c0caed6c2d1a"},
{file = "plotly-5.23.0.tar.gz", hash = "sha256:89e57d003a116303a34de6700862391367dd564222ab71f8531df70279fc0193"},
]
[package.dependencies]
packaging = "*"
tenacity = ">=6.2.0"
[[package]]
name = "priority"
version = "2.0.0"
description = "A pure-Python implementation of the HTTP/2 priority tree"
optional = false
optional = true
python-versions = ">=3.6.1"
files = [
{file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"},
@@ -1012,7 +1401,7 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
name = "pygments"
version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
@@ -1026,7 +1415,7 @@ windows-terminal = ["colorama (>=0.4.6)"]
name = "python-dotenv"
version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
@@ -1040,7 +1429,7 @@ cli = ["click (>=5.0)"]
name = "python-multipart"
version = "0.0.9"
description = "A streaming multipart parser for Python"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
@@ -1110,6 +1499,27 @@ files = [
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "restrictedpython"
version = "7.1"
@@ -1125,11 +1535,25 @@ files = [
docs = ["Sphinx", "sphinx-rtd-theme"]
test = ["pytest", "pytest-mock"]
[[package]]
name = "retrying"
version = "1.3.4"
description = "Retrying"
optional = false
python-versions = "*"
files = [
{file = "retrying-1.3.4-py3-none-any.whl", hash = "sha256:8cc4d43cb8e1125e0ff3344e9de678fefd85db3b750b81b2240dc0183af37b35"},
{file = "retrying-1.3.4.tar.gz", hash = "sha256:345da8c5765bd982b1d1915deb9102fd3d1f7ad16bd84a9700b85f64d24e8f3e"},
]
[package.dependencies]
six = ">=1.7.0"
[[package]]
name = "rich"
version = "13.7.1"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
optional = true
python-versions = ">=3.7.0"
files = [
{file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
@@ -1143,6 +1567,32 @@ pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "ruff"
version = "0.4.10"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"},
{file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"},
{file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"},
{file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"},
{file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"},
{file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"},
{file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"},
{file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"},
{file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"},
{file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"},
{file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"},
]
[[package]]
name = "semver"
version = "3.0.2"
@@ -1174,18 +1624,29 @@ test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata
name = "shellingham"
version = "1.5.4"
description = "Tool to Detect Surrounding Shell"
optional = false
optional = true
python-versions = ">=3.7"
files = [
{file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "sniffio"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
optional = true
python-versions = ">=3.7"
files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
@@ -1196,7 +1657,7 @@ files = [
name = "starlette"
version = "0.37.2"
description = "The little ASGI library that shines."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"},
@@ -1209,11 +1670,26 @@ anyio = ">=3.4.0,<5"
[package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
[[package]]
name = "tenacity"
version = "9.0.0"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.8"
files = [
{file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"},
{file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"},
]
[package.extras]
doc = ["reno", "sphinx"]
test = ["pytest", "tornado (>=4.5)", "typeguard"]
[[package]]
name = "typer"
version = "0.12.3"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
optional = false
optional = true
python-versions = ">=3.7"
files = [
{file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"},
@@ -1252,11 +1728,28 @@ files = [
mypy-extensions = ">=0.3.0"
typing-extensions = ">=3.7.4"
[[package]]
name = "urllib3"
version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uvicorn"
version = "0.30.3"
description = "The lightning-fast ASGI server."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"},
@@ -1281,7 +1774,7 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
name = "uvloop"
version = "0.19.0"
description = "Fast implementation of asyncio event loop on top of libuv"
optional = false
optional = true
python-versions = ">=3.8.0"
files = [
{file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"},
@@ -1325,7 +1818,7 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)"
name = "watchfiles"
version = "0.22.0"
description = "Simple, modern and high performance file watching and code reload in python."
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"},
@@ -1412,7 +1905,7 @@ anyio = ">=3.0.0"
name = "websockets"
version = "12.0"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
optional = true
python-versions = ">=3.8"
files = [
{file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"},
@@ -1489,11 +1982,28 @@ files = [
{file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
]
[[package]]
name = "werkzeug"
version = "3.0.3"
description = "The comprehensive WSGI web application library."
optional = false
python-versions = ">=3.8"
files = [
{file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"},
{file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"},
]
[package.dependencies]
MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog (>=2.3)"]
[[package]]
name = "wsproto"
version = "1.2.0"
description = "WebSockets state-machine based protocol implementation"
optional = false
optional = true
python-versions = ">=3.7.0"
files = [
{file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
@@ -1606,7 +2116,25 @@ files = [
idna = ">=2.0"
multidict = ">=4.0"
[[package]]
name = "zipp"
version = "3.19.2"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
{file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
]
[package.extras]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[extras]
webhook = ["fastapi", "hypercorn"]
[metadata]
lock-version = "2.0"
python-versions = "~3.12"
content-hash = "d0a976df8c14926cacbc93eda55b49eee73eeaa4fc66a9a1a6fd86f3d8f0db64"
content-hash = "374abe60f0df5be4f88b1b0aeefe73b4205ff2a08625359e8191dae5795d5c8f"

View File

@@ -1 +1,12 @@
# OCAB Modules
OCAB Modules содержит набор модулей для платформы Open Chat AI Bot (OCAB).
## Описание
OCAB - это платформа для создания чат-ботов Telegram. Модули - это расширения, которые добавляют функциональность ботам OCAB.
## Типы модулей
* **Стандартные модули (standard.*):** Предоставляют основные функции, такие как управление пользователями, ролями и настройками.
* **Дополнительные официальные модули (external.*):** Разработаны командой OCAB и предоставляют расширенные возможности, такие как интеграция с нейросетями, внешними сервисами и API.

View File

@@ -0,0 +1,19 @@
# Модуль Create Report Apps
Модуль `create_report_apps` предназначен для помощи пользователям в создании отчетов об ошибках в приложениях.
## Функциональность
- Задает пользователю ряд вопросов, необходимых для составления отчета.
- Собирает информацию о системе пользователя.
- Формирует отчет в текстовом формате.
## Команды
- `/create_report_apps` - запустить процесс создания отчета.
## Использование
1. Отправьте команду `/create_report_apps` боту в личных сообщениях или в групповом чате.
2. Ответьте на вопросы бота.
3. Бот сформирует отчет и отправит его вам.

View File

@@ -0,0 +1,22 @@
# Модуль YandexGPT
Модуль `yandexgpt` интегрирует в бота OCAB нейросеть YandexGPT.
## Функциональность
- Позволяет боту отвечать на сообщения пользователей, используя YandexGPT.
- Строит линию контекста для нейросети, используя историю сообщений.
## Конфигурация
- `yandexgpt::token` - API-ключ для доступа к YandexGPT.
- `yandexgpt::catalogid` - идентификатор каталога YandexGPT.
- `yandexgpt::prompt` - системная подсказка для YandexGPT.
- `yandexgpt::startword` - слова, с которых должно начинаться сообщение, чтобы бот ответил.
- `yandexgpt::inword` - слова, которые должны быть в сообщении, чтобы бот ответил.
## Использование
1. Настройте конфигурационные параметры модуля.
2. Отправьте боту сообщение, которое соответствует условиям, указанным в параметрах `startword` и `inword`.
3. Бот ответит на сообщение, используя YandexGPT.

View File

@@ -0,0 +1,20 @@
# Модуль Admin
Модуль `admin` предоставляет администраторам и модераторам чата инструменты для управления:
## Функциональность
- Удаление сообщений.
- Получение ID чата.
## Команды
- `/rm` - удалить сообщение, на которое отвечает команда.
- `/chatID` - получить ID текущего чата.
## Использование
1. Ответьте на сообщение, которое нужно удалить.
2. Отправьте команду `/rm`.
Чтобы получить ID чата, отправьте команду `/chatID`.

View File

@@ -0,0 +1,24 @@
# Модуль Command Helper
Модуль `command_helper` упрощает регистрацию команд бота и управление ими.
## Функциональность
- Регистрация команд бота.
- Установка команд для пользователей в зависимости от их роли.
## Использование
1. Импортируйте функцию `register_command`.
2. Вызовите функцию `register_command`, передав ей название команды, ее описание и роль пользователя,
которому доступна эта команда.
## Пример
```python
from ocab_core.modules_system.public_api import get_module
register_command = get_module("standard.command_helper", "register_command")
register_command("my_command", "Описание моей команды", role="ADMIN")
```

View File

@@ -0,0 +1,28 @@
# Модуль Config
Модуль `config` управляет конфигурацией бота.
## Функциональность
- Загрузка конфигурации из файла `config.yaml`.
- Сохранение конфигурации в файл.
- Регистрация параметров конфигурации.
- Получение значений параметров.
## Использование
1. Импортируйте объект `config`.
2. Вызовите метод `register`, чтобы зарегистрировать параметр конфигурации.
3. Вызовите метод `get`, чтобы получить значение параметра.
## Пример
```python
from ocab_core.modules_system.public_api import get_module
config = get_module("standard.config", "config")
config.register("my_parameter", "string", default_value="default")
value = config.get("my_parameter")
```

View File

@@ -36,8 +36,19 @@ class ConfigManager:
def save(self, file_path: str = ""):
if not file_path:
file_path = self.config_path
def nested_dict(flat_dict):
result = {}
for key, value in flat_dict.items():
keys = key.split("::")
d = result
for k in keys[:-1]:
d = d.setdefault(k, {})
d[keys[-1]] = value
return result
with open(file_path, "w", encoding="utf-8") as file:
yaml.dump(self._config, file, allow_unicode=True)
yaml.dump(nested_dict(self._config), file, allow_unicode=True)
def _check_rights(self, key, module_id, access_type="get"):
return
@@ -58,6 +69,16 @@ class ConfigManager:
module_id = caller_globals.get("__ocab_module_id__")
return module_id
def mass_set(self, updates: Dict[str, Any]):
module_id = self._get_module_id()
for key, value in updates.items():
self._check_rights(key, module_id, "set")
if key in self._metadata:
# TODO: add checks to validate the type and value based on metadata
self._config[key] = value
else:
raise KeyError(f"Key {key} is not registered.")
def register(
self,
key: str,

View File

@@ -15,6 +15,7 @@ def register_settings_page():
path="/settings",
blueprint=get_miniapp_blueprint(config, prefix),
prefix=prefix,
role="ADMIN",
)
pass

View File

@@ -1,13 +1,22 @@
import asyncio
from typing import TYPE_CHECKING
from .config_manager import ConfigManager
try:
import dash_bootstrap_components as dbc
import flask
from dash_extensions.enrich import ALL, Input, Output, State, dcc, html
DASH_AVAILABLE = True
except ImportError:
DASH_AVAILABLE = False
from ocab_core.modules_system.public_api import get_module
if TYPE_CHECKING:
from ocab_modules.standard.roles import Roles as IRoles
def create_control(key: str, config: ConfigManager):
value = config.get(key)
@@ -126,8 +135,11 @@ def create_settings_components(tree, level=0):
def get_miniapp_blueprint(config: ConfigManager, prefix: str):
Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
roles = Roles()
import datetime
import locale
from dash_extensions.enrich import DashBlueprint
@@ -172,6 +184,28 @@ def get_miniapp_blueprint(config: ConfigManager, prefix: str):
)
def save_settings(n_clicks, values, keys):
if n_clicks > 0:
user = getattr(flask.g, "user", None)
if user is None:
return (
dbc.Alert(
"Вы не авторизованы!",
color="danger",
duration=10000,
),
"-",
)
if not asyncio.run(roles.check_admin_permission(user["id"])):
return (
dbc.Alert(
"Вы не администратор!",
color="danger",
duration=10000,
),
"-",
)
# TODO: добавить валидацию значений
updated_settings = {}
@@ -179,9 +213,18 @@ def get_miniapp_blueprint(config: ConfigManager, prefix: str):
key: str = id_dict["key"]
if prefix:
key = key.removeprefix(f"{prefix}-")
meta = config.get_meta(key)
if meta["type"] == "password":
if value: # Only update if a new value is provided
updated_settings[key] = value
else:
updated_settings[key] = value
locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
config.mass_set(updated_settings)
config.save()
now = datetime.datetime.now()
date_str = now.strftime("%H:%M:%S")

View File

@@ -0,0 +1,29 @@
# Модуль Filters
Модуль `filters` предоставляет фильтры для aiogram, которые используются для ограничения доступа к командам
и обработчикам событий.
## Фильтры
- `ChatModerOrAdminFilter` - пропускает сообщения только от модераторов и администраторов чата.
- `ChatNotInApproveFilter` - пропускает сообщения только из чатов, не входящих в список разрешенных.
## Использование
Фильтры можно использовать в декораторах `@router.message` и `@router.callback_query`.
## Пример
```python
from aiogram import Router
from ocab_core.modules_system.public_api import get_module
ChatModerOrAdminFilter = get_module("standard.filters", "ChatModerOrAdminFilter")
router = Router()
@router.message(ChatModerOrAdminFilter())
async def admin_command(message: Message):
# Обработка команды, доступной только администраторам и модераторам.
pass
```

View File

@@ -0,0 +1,13 @@
# Модуль FSM Database Storage
Модуль `fsm_database_storage` реализует хранение состояний FSM (Finite State Machine) в базе данных.
## Функциональность
- Сохранение состояния FSM в базу данных.
- Получение состояния FSM из базы данных.
- Обновление данных состояния FSM.
## Использование
Модуль автоматически регистрирует хранилище состояний FSM при инициализации.

View File

@@ -0,0 +1,15 @@
# Модуль Info
Модуль `info` предоставляет информацию о пользователях и чатах.
## Команды
- `/info` - получить информацию о пользователе.
- `/chatinfo` - получить информацию о чате.
## Использование
Чтобы получить информацию о пользователе, отправьте команду `/info`,
ответив на сообщение пользователя или указав его тег.
Чтобы получить информацию о чате, отправьте команду `/chatinfo`.

View File

@@ -0,0 +1,14 @@
# Модуль Message Processing
Модуль `message_processing` обрабатывает все входящие сообщения.
## Функциональность
- Проверка чата и пользователя на наличие в базе данных.
- Обновление информации о чате и пользователе.
- Добавление статистики сообщений.
- Передача сообщения модулю `yandexgpt`, если оно соответствует условиям.
## Использование
Модуль автоматически обрабатывает все входящие сообщения.

View File

@@ -0,0 +1,23 @@
# Модуль Miniapp
Модуль `miniapp` реализует веб-интерфейс для бота, доступный через Telegram Mini Apps.
## Функциональность
- Регистрация страниц веб-интерфейса.
- Авторизация пользователей через Telegram.
## Использование
1. Импортируйте функцию `register_page`.
2. Вызовите функцию `register_page`, передав ей название страницы, ее путь, blueprint Dash и префикс.
## Пример
```python
from ocab_core.modules_system.public_api import get_module
register_page = get_module("standard.miniapp", "register_page")
register_page("Моя страница", "/my_page", my_blueprint, prefix="my_page")
```

View File

@@ -1,13 +1,104 @@
import flask
from aiogram.utils.web_app import safe_parse_webapp_init_data
from dash import Dash
from dash_extensions.enrich import Input
from dash_extensions.enrich import Input, Output
from flask import request
# TODO: добавить прокидывание BASE_PATH, т.к. это параметр из настроек
WEBAPP_LOADER_TEMPLATE = """
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OCAB</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<script>
window.addEventListener('message', function(event) {
if (event.origin !== window.location.origin) return;
if (event.data.type === 'iframe-url-changed') {
history.pushState(
null,
'',
window.BASE_PATH + event.data.pathname.substring(
window.INTERNAL_PATH.length
)
);
}
});
window.addEventListener('popstate', function(event) {
var iframe = document.getElementById('app-frame');
var iframeWindow = iframe.contentWindow;
iframeWindow.history.back();
});
</script>
<style>
#app-frame {
display:none;
width:100%;
height:100vh;
border:none;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="loading">Loading...</div>
<iframe id="app-frame"></iframe>
<script>
document.addEventListener('DOMContentLoaded', function() {
const tg = window.Telegram.WebApp;
document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
// if (!tg.initData) return;
const iframe = document.getElementById('app-frame');
// Константы для путей
const BASE_PATH = '/webapp';
const INTERNAL_PATH = '/webapp/_internal';
window.BASE_PATH = BASE_PATH;
window.INTERNAL_PATH = INTERNAL_PATH
// Текущий путь страницы
const currentPath = window.location.pathname;
// Формируем новый путь для iframe
let iframeSrc = INTERNAL_PATH;
// Если текущий путь начинается с BASE_PATH, убираем BASE_PATH из текущего пути
if (currentPath.startsWith(BASE_PATH)
&& currentPath.length > BASE_PATH.length) {
iframeSrc += currentPath.substring(BASE_PATH.length);
} else if (currentPath !== '/') {
iframeSrc += currentPath;
}
iframe.src = iframeSrc;
iframe.onload = function() {
document.getElementById('loading').style.display = 'none';
iframe.style.display = 'block';
};
});
</script>
</body>
</html>
"""
def get_auth_server(bot_token: str):
server = flask.Flask(__name__)
@server.route("/<path:rest>")
@server.route("/")
def webapp_loader(rest=None):
return flask.Response(WEBAPP_LOADER_TEMPLATE, mimetype="text/html")
@server.before_request
def add_auth_data():
init_data = request.cookies.get("tg_init_data")
@@ -21,13 +112,35 @@ def get_auth_server(bot_token: str):
return server
def setup_auth_clientcallback(app: Dash):
def setup_auth_clientcallbacks(app: Dash):
app.clientside_callback(
"""
function(n_inervals) {
const tg = window.Telegram.WebApp;
document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
function(n_intervals) {
if (window.webAppData) {
return window.webAppData;
}
function receiveMessage(event) {
if (event.data.type === 'webAppData') {
window.webAppData = event.data.webApp;
window.removeEventListener('message', receiveMessage);
}
}
window.addEventListener('message', receiveMessage, false);
return window.dash_clientside.no_update;
}
""",
Input("init-telegram-interval", "n_intervals"),
Output("hidden-div", "children"),
Input("interval-component", "n_intervals"),
)
app.clientside_callback(
"""
function(pathname) {
window.parent.postMessage({ type: 'iframe-url-changed', pathname }, '*');
}
""",
Input("url", "pathname"),
)

View File

@@ -1,26 +1,30 @@
import asyncio
from collections import OrderedDict
from typing import TYPE_CHECKING
import dash
import dash_bootstrap_components as dbc
import flask
from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
from dash_extensions.pages import setup_page_components
from ocab_core.modules_system.public_api import get_module
from ocab_core.modules_system.public_api import get_module, log
from .dash_telegram_auth import get_auth_server, setup_auth_clientcallback
from .dash_telegram_auth import get_auth_server, setup_auth_clientcallbacks
if TYPE_CHECKING:
from ocab_modules.standard.config import IConfig
from ocab_modules.standard.roles import Roles as IRoles
pages = OrderedDict()
def register_page(name, path, blueprint, prefix=""):
def register_page(name, path, blueprint, prefix="", role="USER"):
pages[path] = {
"name": name,
"blueprint": blueprint,
"prefix": prefix,
"role": role,
}
@@ -33,9 +37,14 @@ def register_home_page():
register_home_page()
config: "IConfig" = get_module("standard.config", "config")
Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
log(requests_pathname_prefix)
real_prefix = f"{requests_pathname_prefix}_internal/"
server = get_auth_server(config.get("core::token"))
app = DashProxy(
@@ -47,10 +56,13 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
dbc.icons.BOOTSTRAP,
],
external_scripts=[
#
"https://telegram.org/js/telegram-web-app.js"
], # Add Telegram Mini Apps script to <head>
],
server=server,
requests_pathname_prefix=requests_pathname_prefix,
requests_pathname_prefix=real_prefix,
routes_pathname_prefix="/_internal/",
# requests_pathname_prefix=requests_pathname_prefix,
meta_tags=[
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
],
@@ -63,30 +75,8 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
# Register pages
for path, page in pages.items():
# dash.register_page(page["name"], path=path, layout=page["layout"])
page["blueprint"].register(app, path, prefix=page["prefix"])
# Create sidebar
sidebar = dbc.Offcanvas(
id="offcanvas",
title="Меню",
is_open=False,
children=[
dbc.Nav(
[
dbc.NavLink(
page["name"],
href=f"{requests_pathname_prefix}{path.lstrip('/')}",
id={"type": "nav-link", "index": path},
)
for path, page in pages.items()
],
vertical=True,
pills=True,
),
],
)
# Create navbar
navbar = dbc.Navbar(
dbc.Container(
@@ -104,8 +94,47 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
dark=True,
)
# Define app layout
app.layout = html.Div(
roles = Roles()
def create_layout():
user = getattr(flask.g, "user", None)
if not user:
return html.Div()
user_id = user["id"]
user_permission = asyncio.run(roles.get_user_permission(user_id)) or "USER"
available_pages = {
path: page
for path, page in pages.items()
if (isinstance(page["role"], list) and user_permission in page["role"])
or page["role"] == user_permission
or page["role"] == "USER"
}
# Create sidebar
sidebar = dbc.Offcanvas(
id="offcanvas",
title="Меню",
is_open=False,
children=[
dbc.Nav(
[
dbc.NavLink(
page["name"],
href=f"{real_prefix}/{path.lstrip('/')}",
id={"type": "nav-link", "index": path},
)
for path, page in available_pages.items()
],
vertical=True,
pills=True,
),
],
)
layout = html.Div(
[
dcc.Location(id="url", refresh=False),
dcc.Interval(
@@ -121,7 +150,11 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
]
)
setup_auth_clientcallback(app)
return layout
app.layout = create_layout
setup_auth_clientcallbacks(app)
# Открытие на кнопку меню
app.clientside_callback(

View File

@@ -0,0 +1,36 @@
# Модуль Roles
Модуль `roles` управляет ролями пользователей.
## Роли
- `USER` - обычный пользователь.
- `MODERATOR` - модератор.
- `ADMIN` - администратор.
- `BOT` - бот.
## Функциональность
- Проверка роли пользователя.
- Получение имени роли по ID.
- Получение ID роли по имени.
## Использование
1. Импортируйте класс `Roles`.
2. Создайте экземпляр класса.
3. Вызовите методы класса для проверки роли пользователя или получения имени роли.
## Пример
```python
from ocab_core.modules_system.public_api import get_module
Roles = get_module("standard.roles", "Roles")
roles = Roles()
is_admin = await roles.check_admin_permission(user_id)
role_name = await roles.get_role_name(role_id)
```

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,8 @@ dash = "^2.17.1"
dash-extensions = "^1.0.18"
dash-bootstrap-components = "^1.6.0"
[tool.poetry-monorepo.deps]
enabled = true
[build-system]
requires = ["poetry-core"]