initual commit
Browse files- app.py +83 -0
- requirements.txt +32 -0
app.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import language_tool_python
|
2 |
+
import streamlit as st
|
3 |
+
from pathlib import Path
|
4 |
+
import html
|
5 |
+
|
6 |
+
|
7 |
+
@st.cache_resource
|
8 |
+
def load_assets():
|
9 |
+
tool = language_tool_python.LanguageTool('ru-RU',
|
10 |
+
language_tool_download_version="6.1")
|
11 |
+
return tool
|
12 |
+
|
13 |
+
|
14 |
+
def check_text(text, tool):
|
15 |
+
matches = tool.check(text)
|
16 |
+
errors = []
|
17 |
+
for match in matches:
|
18 |
+
error_info = {
|
19 |
+
'start': match.offset,
|
20 |
+
'end': match.offset + match.errorLength,
|
21 |
+
'message': match.message,
|
22 |
+
'context': match.context
|
23 |
+
}
|
24 |
+
errors.append(error_info)
|
25 |
+
return errors
|
26 |
+
|
27 |
+
|
28 |
+
def main():
|
29 |
+
st.title('Проверка орфографии')
|
30 |
+
text = st.text_area("Введите текст для проверки:", height=200)
|
31 |
+
|
32 |
+
|
33 |
+
if st.button('Проверить текст'):
|
34 |
+
if not text.strip():
|
35 |
+
st.warning("Введите текст для проверки")
|
36 |
+
else:
|
37 |
+
errors = check_text(text)
|
38 |
+
|
39 |
+
if not errors:
|
40 |
+
st.success("Ошибок не найдено! 👍")
|
41 |
+
else:
|
42 |
+
sorted_errors = sorted(errors, key=lambda x: x['start'])
|
43 |
+
|
44 |
+
highlighted = []
|
45 |
+
last_pos = 0
|
46 |
+
|
47 |
+
for error in sorted_errors:
|
48 |
+
highlighted.append(html.escape(text[last_pos:error['start']]))
|
49 |
+
|
50 |
+
highlighted.append(
|
51 |
+
f'<span style="background-color: #ffe066; border-bottom: 2px dotted #ff9900; '
|
52 |
+
f'position: relative;" title="{html.escape(error["message"])}">'
|
53 |
+
f'{html.escape(text[error["start"]:error["end"]])}'
|
54 |
+
f'</span>'
|
55 |
+
)
|
56 |
+
|
57 |
+
last_pos = error['end']
|
58 |
+
|
59 |
+
highlighted.append(html.escape(text[last_pos:]))
|
60 |
+
|
61 |
+
html_content = f"""
|
62 |
+
<div style="
|
63 |
+
background: white;
|
64 |
+
padding: 20px;
|
65 |
+
border-radius: 8px;
|
66 |
+
border: 1px solid #ddd;
|
67 |
+
white-space: pre-wrap;
|
68 |
+
font-family: monospace;
|
69 |
+
">
|
70 |
+
{''.join(highlighted)}
|
71 |
+
</div>
|
72 |
+
"""
|
73 |
+
|
74 |
+
st.markdown("### Результат проверки:")
|
75 |
+
st.markdown(html_content, unsafe_allow_html=True)
|
76 |
+
|
77 |
+
st.markdown("### Найденные ошибки:")
|
78 |
+
for i, error in enumerate(errors, 1):
|
79 |
+
st.markdown(f"{i}. **Позиция {error['start']}-{error['end']}**: {error['message']}")
|
80 |
+
|
81 |
+
|
82 |
+
if __name__ == "__main__":
|
83 |
+
main()
|
requirements.txt
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiohappyeyeballs==2.6.1
|
2 |
+
aiohttp==3.11.16
|
3 |
+
aiosignal==1.3.2
|
4 |
+
annotated-types==0.7.0
|
5 |
+
anyio==4.9.0
|
6 |
+
attrs==25.3.0
|
7 |
+
certifi==2025.1.31
|
8 |
+
charset-normalizer==3.4.1
|
9 |
+
distro==1.9.0
|
10 |
+
frozenlist==1.6.0
|
11 |
+
h11==0.14.0
|
12 |
+
httpcore==1.0.8
|
13 |
+
httpx==0.28.1
|
14 |
+
idna==3.10
|
15 |
+
jiter==0.9.0
|
16 |
+
language_tool_python==2.9.3
|
17 |
+
multidict==6.4.3
|
18 |
+
openai==0.28.0
|
19 |
+
pip==25.0.1
|
20 |
+
propcache==0.3.1
|
21 |
+
psutil==7.0.0
|
22 |
+
pydantic==2.11.3
|
23 |
+
pydantic_core==2.33.1
|
24 |
+
requests==2.32.3
|
25 |
+
setuptools==78.1.0
|
26 |
+
sniffio==1.3.1
|
27 |
+
toml==0.10.2
|
28 |
+
tqdm==4.67.1
|
29 |
+
typing_extensions==4.13.2
|
30 |
+
typing-inspection==0.4.0
|
31 |
+
urllib3==2.4.0
|
32 |
+
yarl==1.20.0
|