mirror of
https://gitflic.ru/project/maks1ms/ocab.git
synced 2025-03-13 22:03:50 +03:00
завершен standard.create_report_apps
This commit is contained in:
parent
c53f6025ae
commit
51f5290017
@ -7,3 +7,4 @@ from .public_api import (
|
|||||||
register_router,
|
register_router,
|
||||||
set_my_commands,
|
set_my_commands,
|
||||||
)
|
)
|
||||||
|
from .utils import Utils
|
||||||
|
12
src/ocab_core/modules_system/public_api/utils.py
Normal file
12
src/ocab_core/modules_system/public_api/utils.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
CLEAN_HTML = re.compile("<.*?>")
|
||||||
|
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
@staticmethod
|
||||||
|
def code_format(code: str, lang: str):
|
||||||
|
if lang:
|
||||||
|
return f'<pre><code class="language-{lang}">{code}</code></pre>'
|
||||||
|
else:
|
||||||
|
return f"<pre>{code}</pre>"
|
@ -15,7 +15,7 @@ from RestrictedPython.Guards import (
|
|||||||
safer_getattr,
|
safer_getattr,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ocab_core.logger import log_new
|
from ocab_core.logger import log
|
||||||
from ocab_core.modules_system.safe.zope_guards import extra_safe_builtins
|
from ocab_core.modules_system.safe.zope_guards import extra_safe_builtins
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ ALLOWED_IMPORTS = [
|
|||||||
|
|
||||||
def safes_getattr(object, name, default=None, getattr=safer_getattr):
|
def safes_getattr(object, name, default=None, getattr=safer_getattr):
|
||||||
if isinstance(object, Bot) and name == "token":
|
if isinstance(object, Bot) and name == "token":
|
||||||
log_new("Bot.token is not allowed")
|
log("Bot.token is not allowed")
|
||||||
raise Exception("Bot.token is not allowed")
|
raise Exception("Bot.token is not allowed")
|
||||||
|
|
||||||
return getattr(object, name, default)
|
return getattr(object, name, default)
|
||||||
|
@ -109,3 +109,117 @@ def guarded_all(seq):
|
|||||||
|
|
||||||
|
|
||||||
extra_safe_builtins["all"] = guarded_all
|
extra_safe_builtins["all"] = guarded_all
|
||||||
|
|
||||||
|
valid_inplace_types = (list, set)
|
||||||
|
|
||||||
|
inplace_slots = {
|
||||||
|
"+=": "__iadd__",
|
||||||
|
"-=": "__isub__",
|
||||||
|
"*=": "__imul__",
|
||||||
|
"/=": (1 / 2 == 0) and "__idiv__" or "__itruediv__",
|
||||||
|
"//=": "__ifloordiv__",
|
||||||
|
"%=": "__imod__",
|
||||||
|
"**=": "__ipow__",
|
||||||
|
"<<=": "__ilshift__",
|
||||||
|
">>=": "__irshift__",
|
||||||
|
"&=": "__iand__",
|
||||||
|
"^=": "__ixor__",
|
||||||
|
"|=": "__ior__",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def __iadd__(x, y):
|
||||||
|
x += y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __isub__(x, y):
|
||||||
|
x -= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __imul__(x, y):
|
||||||
|
x *= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __idiv__(x, y):
|
||||||
|
x /= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __ifloordiv__(x, y):
|
||||||
|
x //= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __imod__(x, y):
|
||||||
|
x %= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __ipow__(x, y):
|
||||||
|
x **= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __ilshift__(x, y):
|
||||||
|
x <<= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __irshift__(x, y):
|
||||||
|
x >>= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __iand__(x, y):
|
||||||
|
x &= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __ixor__(x, y):
|
||||||
|
x ^= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def __ior__(x, y):
|
||||||
|
x |= y
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
inplace_ops = {
|
||||||
|
"+=": __iadd__,
|
||||||
|
"-=": __isub__,
|
||||||
|
"*=": __imul__,
|
||||||
|
"/=": __idiv__,
|
||||||
|
"//=": __ifloordiv__,
|
||||||
|
"%=": __imod__,
|
||||||
|
"**=": __ipow__,
|
||||||
|
"<<=": __ilshift__,
|
||||||
|
">>=": __irshift__,
|
||||||
|
"&=": __iand__,
|
||||||
|
"^=": __ixor__,
|
||||||
|
"|=": __ior__,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def protected_inplacevar(op, var, expr):
|
||||||
|
"""Do an inplace operation
|
||||||
|
|
||||||
|
If the var has an inplace slot, then disallow the operation
|
||||||
|
unless the var an instance of ``valid_inplace_types``.
|
||||||
|
"""
|
||||||
|
if hasattr(var, inplace_slots[op]) and not isinstance(var, valid_inplace_types):
|
||||||
|
try:
|
||||||
|
cls = var.__class__
|
||||||
|
except AttributeError:
|
||||||
|
cls = type(var)
|
||||||
|
raise TypeError(
|
||||||
|
"Augmented assignment to %s objects is not allowed"
|
||||||
|
" in untrusted code" % cls.__name__
|
||||||
|
)
|
||||||
|
return inplace_ops[op](var, expr)
|
||||||
|
|
||||||
|
|
||||||
|
extra_safe_builtins["_inplacevar_"] = protected_inplacevar
|
||||||
|
@ -1,64 +1,136 @@
|
|||||||
from aiogram import Bot, Router
|
from aiogram import Bot, Router
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
from aiogram.fsm.state import State, StatesGroup
|
||||||
from aiogram.types import BufferedInputFile, Message
|
from aiogram.types import (
|
||||||
|
BufferedInputFile,
|
||||||
|
KeyboardButton,
|
||||||
|
Message,
|
||||||
|
ReplyKeyboardMarkup,
|
||||||
|
ReplyKeyboardRemove,
|
||||||
|
)
|
||||||
|
|
||||||
from ocab_core.modules_system.public_api import get_fsm_context
|
from ocab_core.modules_system.public_api import Utils, get_fsm_context
|
||||||
|
|
||||||
|
from .report import Report
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
class ReportState(StatesGroup):
|
class ReportState(StatesGroup):
|
||||||
input_kernel_info = State()
|
input_system_info = State()
|
||||||
input_app_name = State()
|
input_app_name = State()
|
||||||
input_problem_step_by_step = State()
|
input_problem_step_by_step = State()
|
||||||
|
input_actual_result = State()
|
||||||
|
input_expected_result = State()
|
||||||
|
input_additional_info = State()
|
||||||
|
|
||||||
|
|
||||||
|
system_info_code = """echo "SESSION_TYPE: ${XDG_SESSION_TYPE:-Unknown}"
|
||||||
|
[ -f /etc/os-release ] && grep "^PRETTY_NAME=" /etc/os-release | cut -d= -f2 \
|
||||||
|
| tr -d '"' | xargs echo "OS: "
|
||||||
|
echo "Kernel: $(uname -r)"
|
||||||
|
echo "DE: ${XDG_CURRENT_DESKTOP:-Unknown}"
|
||||||
|
grep "^model name" /proc/cpuinfo | head -n1 | cut -d: -f2 \
|
||||||
|
| xargs echo "CPU: "
|
||||||
|
lspci | grep "VGA compatible controller" | cut -d: -f3 \
|
||||||
|
| xargs -I{} echo "GPU: {}"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
system_info_message = """Укажите параметры свой системы.
|
||||||
|
Собрать информацию о системе можно с помощью данного скрипта:
|
||||||
|
""" + Utils.code_format(
|
||||||
|
system_info_code,
|
||||||
|
"shell",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def start_report(chat_id: int, bot: Bot):
|
async def start_report(chat_id: int, bot: Bot):
|
||||||
await bot.send_message(
|
await bot.send_message(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
text="Какая версия ядра у тебя на "
|
text=system_info_message,
|
||||||
"текущий момент? Можно узнать "
|
parse_mode=ParseMode.HTML,
|
||||||
"командой `uname -rm`",
|
reply_markup=ReplyKeyboardRemove(),
|
||||||
parse_mode="Markdown",
|
|
||||||
)
|
)
|
||||||
state = await get_fsm_context(chat_id, chat_id)
|
state = await get_fsm_context(chat_id, chat_id)
|
||||||
|
|
||||||
await state.set_state(ReportState.input_kernel_info)
|
await state.set_state(ReportState.input_system_info)
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_kernel_info)
|
app_info_message = """Укажите название и версию приложения.
|
||||||
async def kernel_version_entered(message: Message, state: FSMContext):
|
Узнать можно с помощью данной команды:""" + Utils.code_format(
|
||||||
await state.update_data(kernel=message.text)
|
"rpm -qa | grep -i НАЗВАНИЕ_ПРИЛОЖЕНИЯ", "shell"
|
||||||
await message.answer(text="В каком приложении возникла проблема?")
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(ReportState.input_system_info)
|
||||||
|
async def system_entered(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(system=message.text)
|
||||||
|
await message.answer(
|
||||||
|
text=app_info_message,
|
||||||
|
parse_mode=ParseMode.HTML,
|
||||||
|
)
|
||||||
await state.set_state(ReportState.input_app_name)
|
await state.set_state(ReportState.input_app_name)
|
||||||
|
|
||||||
|
|
||||||
|
step_by_step_message = (
|
||||||
|
"""Опиши проблему пошагово, что ты делал, что происходило, что не так."""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_app_name)
|
@router.message(ReportState.input_app_name)
|
||||||
async def app_name_entered(message: Message, state: FSMContext):
|
async def app_name_entered(message: Message, state: FSMContext):
|
||||||
await state.update_data(app_name=message.text)
|
await state.update_data(app=message.text)
|
||||||
await message.answer(
|
await message.answer(text=step_by_step_message)
|
||||||
text="Опиши проблему пошагово, что ты делал, что происходило, что не так"
|
|
||||||
)
|
|
||||||
await state.set_state(ReportState.input_problem_step_by_step)
|
await state.set_state(ReportState.input_problem_step_by_step)
|
||||||
|
|
||||||
|
|
||||||
@router.message(ReportState.input_problem_step_by_step)
|
@router.message(ReportState.input_problem_step_by_step)
|
||||||
async def problem_step_by_step_entered(message: Message, state: FSMContext):
|
async def problem_step_by_step_entered(message: Message, state: FSMContext):
|
||||||
await state.update_data(problem_step_by_step=message.text)
|
await state.update_data(problem_step_by_step=message.text)
|
||||||
await message.answer(text="Вот твой отчет сообщением, а также файлом:")
|
await message.answer(text="Опиши, что произошло (фактический результат).")
|
||||||
|
await state.set_state(ReportState.input_actual_result)
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(ReportState.input_actual_result)
|
||||||
|
async def actual_result_entered(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(actual=message.text)
|
||||||
|
await message.answer(text="Опиши ожидаемый результат.")
|
||||||
|
await state.set_state(ReportState.input_expected_result)
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(ReportState.input_expected_result)
|
||||||
|
async def expected_result_entered(message: Message, state: FSMContext):
|
||||||
|
await state.update_data(expected=message.text)
|
||||||
|
await message.answer(
|
||||||
|
text="Если есть дополнительная информация, то напиши ее.",
|
||||||
|
reply_markup=ReplyKeyboardMarkup(
|
||||||
|
resize_keyboard=True,
|
||||||
|
keyboard=[
|
||||||
|
[KeyboardButton(text="Дополнительной информации нет")],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
await state.set_state(ReportState.input_additional_info)
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(ReportState.input_additional_info)
|
||||||
|
async def additional_info_entered(message: Message, state: FSMContext):
|
||||||
|
if message.text == "Дополнительной информации нет":
|
||||||
|
additional_info = ""
|
||||||
|
else:
|
||||||
|
additional_info = message.text
|
||||||
|
await state.update_data(additional=additional_info)
|
||||||
|
await message.answer(
|
||||||
|
text="Вот твой отчет сообщением, а также файлом:",
|
||||||
|
reply_markup=ReplyKeyboardRemove(),
|
||||||
|
)
|
||||||
data = await state.get_data()
|
data = await state.get_data()
|
||||||
|
|
||||||
report = f"""Стенд с ошибкой:
|
report = Report(data)
|
||||||
# uname -rm
|
file_report = report.export(html=False).encode()
|
||||||
{data['kernel']}
|
|
||||||
|
|
||||||
Шаги, приводящие к ошибке:
|
await message.answer(text=report.export(html=True), parse_mode=ParseMode.HTML)
|
||||||
|
await message.answer_document(document=BufferedInputFile(file_report, "report.txt"))
|
||||||
{data['problem_step_by_step']}
|
|
||||||
"""
|
|
||||||
await message.answer(text=report)
|
|
||||||
await message.answer_document(
|
|
||||||
document=BufferedInputFile(report.encode(), "report.txt")
|
|
||||||
)
|
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
53
src/ocab_modules/standard/create_report_apps/report.py
Normal file
53
src/ocab_modules/standard/create_report_apps/report.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import aiogram
|
||||||
|
|
||||||
|
|
||||||
|
class ReportFormatter:
|
||||||
|
def __init__(self, html=True):
|
||||||
|
self.html = html
|
||||||
|
|
||||||
|
def bold(self, string):
|
||||||
|
if self.html:
|
||||||
|
return f"<b>{self.text(string)}</b>"
|
||||||
|
return self.text(string)
|
||||||
|
|
||||||
|
def text(self, string):
|
||||||
|
if self.html:
|
||||||
|
return aiogram.html.quote(string)
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
class Report:
|
||||||
|
def __init__(self, data: dict):
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def export(self, html=True):
|
||||||
|
data = self.data
|
||||||
|
f = ReportFormatter(html)
|
||||||
|
|
||||||
|
report = f"""{f.bold("Стенд с ошибкой:")}
|
||||||
|
|
||||||
|
{f.text(data['system'])}
|
||||||
|
|
||||||
|
{f.bold("Версия программы:")}
|
||||||
|
|
||||||
|
{f.text(data['app'])}
|
||||||
|
|
||||||
|
{f.bold("Шаги, приводящие к ошибке:")}
|
||||||
|
|
||||||
|
{f.text(data['problem_step_by_step'])}
|
||||||
|
|
||||||
|
{f.bold("Результат:")}
|
||||||
|
|
||||||
|
{f.text(data['actual'])}
|
||||||
|
|
||||||
|
{f.bold("Ожидаемый результат:")}
|
||||||
|
|
||||||
|
{f.text(data['expected'])}
|
||||||
|
"""
|
||||||
|
if data["additional"] != "":
|
||||||
|
report += f"""{f.bold("Дополнительно:")}
|
||||||
|
|
||||||
|
{f.text(data['additional'])}
|
||||||
|
"""
|
||||||
|
|
||||||
|
return report
|
Loading…
Reference in New Issue
Block a user