cv-study-utils 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cv_study_utils-0.1.0/PKG-INFO +74 -0
- cv_study_utils-0.1.0/README.md +58 -0
- cv_study_utils-0.1.0/cv_study_utils/__init__.py +25 -0
- cv_study_utils-0.1.0/cv_study_utils/api.py +271 -0
- cv_study_utils-0.1.0/cv_study_utils/cli.py +53 -0
- cv_study_utils-0.1.0/cv_study_utils/practice_solutions.py +1091 -0
- cv_study_utils-0.1.0/cv_study_utils/sources.py +36 -0
- cv_study_utils-0.1.0/cv_study_utils/theory_answers.py +290 -0
- cv_study_utils-0.1.0/cv_study_utils/theory_extras.py +287 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/PKG-INFO +74 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/SOURCES.txt +15 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/dependency_links.txt +1 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/entry_points.txt +2 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/requires.txt +3 -0
- cv_study_utils-0.1.0/cv_study_utils.egg-info/top_level.txt +1 -0
- cv_study_utils-0.1.0/pyproject.toml +30 -0
- cv_study_utils-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cv-study-utils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Notebook-friendly study utilities for computer vision theory and practice snippets.
|
|
5
|
+
Author: CV Exam Helper
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: computer-vision,exam,jupyter,colab
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Intended Audience :: Education
|
|
11
|
+
Classifier: Topic :: Education
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Provides-Extra: clipboard
|
|
15
|
+
Requires-Dist: pyperclip>=1.8.2; extra == "clipboard"
|
|
16
|
+
|
|
17
|
+
# CV Study Utils
|
|
18
|
+
|
|
19
|
+
Небольшая библиотека для подготовки к экзамену по машинному зрению. Она выводит ответы на теоретические вопросы и готовые кодовые шаблоны для практических заданий прямо в Jupyter/Colab.
|
|
20
|
+
|
|
21
|
+
## Установка
|
|
22
|
+
|
|
23
|
+
Из этой папки:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
После загрузки проекта на GitHub в Colab/Jupyter можно будет ставить так:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
!pip install git+https://github.com/<your-user>/<your-repo>.git
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
После публикации на PyPI:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
!pip install cv-study-utils
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Использование
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from cv_study_utils import theory, practice, find
|
|
45
|
+
|
|
46
|
+
theory(5) # ответ на теоретический вопрос 5
|
|
47
|
+
practice(3) # код для практического задания 3
|
|
48
|
+
find("Canny") # поиск по теории и практике
|
|
49
|
+
practice("границ") # найти практику по фрагменту текста
|
|
50
|
+
|
|
51
|
+
theory(12, copy=True) # скопировать ответ, если доступен clipboard
|
|
52
|
+
practice(15, copy=True) # в ноутбуке появится кнопка Copy, если pyperclip недоступен
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Командная строка:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cvstudy theory 5
|
|
59
|
+
cvstudy practice 3 --copy
|
|
60
|
+
cvstudy find "Vision Transformer"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Что внутри
|
|
64
|
+
|
|
65
|
+
- `cv_study_utils/theory_answers.py` - 48 кратких ответов на теоретические вопросы.
|
|
66
|
+
- `cv_study_utils/theory_extras.py` - формулы, схемы архитектур и внешние источники для теории.
|
|
67
|
+
- `cv_study_utils/practice_solutions.py` - 15 готовых кодовых шаблонов для практических заданий.
|
|
68
|
+
- `content/theory_answers.md` и `content/practice_solutions.py` - человекочитаемые экспортные файлы, которые можно открыть отдельно.
|
|
69
|
+
- `Ответы_теория_МЗ2026.docx` и `Ответы_практика_МЗ2026.docx` - Word-версии материалов.
|
|
70
|
+
- `dist/` - готовые wheel/sdist артефакты для загрузки на PyPI.
|
|
71
|
+
|
|
72
|
+
## Источники
|
|
73
|
+
|
|
74
|
+
Основой являются файлы из папки экзамена и лекции `lecture_2` - `Lecture_8`. Для актуализации API и формулировок использованы официальные документации OpenCV, Pillow, Matplotlib, scikit-image, scikit-learn, PyTorch/torchvision, TensorFlow/Keras, Ultralytics YOLO, а также оригинальные статьи R-CNN/Fast R-CNN/Faster R-CNN, YOLO, SSD, U-Net, Mask R-CNN, GAN, DDPM, Transformer, ViT и image captioning.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CV Study Utils
|
|
2
|
+
|
|
3
|
+
Небольшая библиотека для подготовки к экзамену по машинному зрению. Она выводит ответы на теоретические вопросы и готовые кодовые шаблоны для практических заданий прямо в Jupyter/Colab.
|
|
4
|
+
|
|
5
|
+
## Установка
|
|
6
|
+
|
|
7
|
+
Из этой папки:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
После загрузки проекта на GitHub в Colab/Jupyter можно будет ставить так:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
!pip install git+https://github.com/<your-user>/<your-repo>.git
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
После публикации на PyPI:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
!pip install cv-study-utils
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Использование
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from cv_study_utils import theory, practice, find
|
|
29
|
+
|
|
30
|
+
theory(5) # ответ на теоретический вопрос 5
|
|
31
|
+
practice(3) # код для практического задания 3
|
|
32
|
+
find("Canny") # поиск по теории и практике
|
|
33
|
+
practice("границ") # найти практику по фрагменту текста
|
|
34
|
+
|
|
35
|
+
theory(12, copy=True) # скопировать ответ, если доступен clipboard
|
|
36
|
+
practice(15, copy=True) # в ноутбуке появится кнопка Copy, если pyperclip недоступен
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Командная строка:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cvstudy theory 5
|
|
43
|
+
cvstudy practice 3 --copy
|
|
44
|
+
cvstudy find "Vision Transformer"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Что внутри
|
|
48
|
+
|
|
49
|
+
- `cv_study_utils/theory_answers.py` - 48 кратких ответов на теоретические вопросы.
|
|
50
|
+
- `cv_study_utils/theory_extras.py` - формулы, схемы архитектур и внешние источники для теории.
|
|
51
|
+
- `cv_study_utils/practice_solutions.py` - 15 готовых кодовых шаблонов для практических заданий.
|
|
52
|
+
- `content/theory_answers.md` и `content/practice_solutions.py` - человекочитаемые экспортные файлы, которые можно открыть отдельно.
|
|
53
|
+
- `Ответы_теория_МЗ2026.docx` и `Ответы_практика_МЗ2026.docx` - Word-версии материалов.
|
|
54
|
+
- `dist/` - готовые wheel/sdist артефакты для загрузки на PyPI.
|
|
55
|
+
|
|
56
|
+
## Источники
|
|
57
|
+
|
|
58
|
+
Основой являются файлы из папки экзамена и лекции `lecture_2` - `Lecture_8`. Для актуализации API и формулировок использованы официальные документации OpenCV, Pillow, Matplotlib, scikit-image, scikit-learn, PyTorch/torchvision, TensorFlow/Keras, Ultralytics YOLO, а также оригинальные статьи R-CNN/Fast R-CNN/Faster R-CNN, YOLO, SSD, U-Net, Mask R-CNN, GAN, DDPM, Transformer, ViT и image captioning.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Convenient helpers for the computer vision exam materials."""
|
|
2
|
+
|
|
3
|
+
from .api import (
|
|
4
|
+
all_practice,
|
|
5
|
+
all_theory,
|
|
6
|
+
copy_practice,
|
|
7
|
+
copy_theory,
|
|
8
|
+
find,
|
|
9
|
+
list_practice,
|
|
10
|
+
list_theory,
|
|
11
|
+
practice,
|
|
12
|
+
theory,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"all_practice",
|
|
17
|
+
"all_theory",
|
|
18
|
+
"copy_practice",
|
|
19
|
+
"copy_theory",
|
|
20
|
+
"find",
|
|
21
|
+
"list_practice",
|
|
22
|
+
"list_theory",
|
|
23
|
+
"practice",
|
|
24
|
+
"theory",
|
|
25
|
+
]
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import difflib
|
|
4
|
+
import html
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import textwrap
|
|
8
|
+
from typing import Any, Iterable
|
|
9
|
+
|
|
10
|
+
from .practice_solutions import PRACTICE
|
|
11
|
+
from .theory_answers import THEORY
|
|
12
|
+
from .theory_extras import THEORY_EXTRAS
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _normalize(text: str) -> str:
|
|
16
|
+
return re.sub(r"\s+", " ", text.casefold()).strip()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _score(query: str, item_text: str) -> float:
|
|
20
|
+
q = _normalize(query)
|
|
21
|
+
hay = _normalize(item_text)
|
|
22
|
+
if not q:
|
|
23
|
+
return 0.0
|
|
24
|
+
if q in hay:
|
|
25
|
+
return 1.0
|
|
26
|
+
words = [w for w in re.split(r"\W+", q) if len(w) > 2]
|
|
27
|
+
if words:
|
|
28
|
+
overlap = sum(1 for w in words if w in hay) / len(words)
|
|
29
|
+
else:
|
|
30
|
+
overlap = 0.0
|
|
31
|
+
ratio = difflib.SequenceMatcher(None, q, hay[: max(200, len(q) * 4)]).ratio()
|
|
32
|
+
return max(overlap, ratio)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _item_text(item: dict[str, Any], kind: str) -> str:
|
|
36
|
+
if kind == "theory":
|
|
37
|
+
extra = THEORY_EXTRAS.get(item["id"], {})
|
|
38
|
+
formulae = " ".join(extra.get("formulae", []))
|
|
39
|
+
structure = " ".join(extra.get("structure", []))
|
|
40
|
+
return f"{item['question']} {item['answer']} {formulae} {structure}"
|
|
41
|
+
return f"{item['task']} {item['description']} {item['code']}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _title_text(item: dict[str, Any]) -> str:
|
|
45
|
+
return item.get("question") or item.get("task") or ""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _rank_score(query: str, item: dict[str, Any], kind: str) -> float:
|
|
49
|
+
base = _score(query, _item_text(item, kind))
|
|
50
|
+
q = _normalize(query)
|
|
51
|
+
title = _normalize(_title_text(item))
|
|
52
|
+
if q and q in title:
|
|
53
|
+
base += 0.75
|
|
54
|
+
return base
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_by_number(items: list[dict[str, Any]], number: int) -> dict[str, Any]:
|
|
58
|
+
for item in items:
|
|
59
|
+
if item["id"] == number:
|
|
60
|
+
return item
|
|
61
|
+
raise ValueError(f"Нет элемента с номером {number}.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _best_match(items: list[dict[str, Any]], query: str, kind: str) -> dict[str, Any]:
|
|
65
|
+
ranked = sorted(
|
|
66
|
+
((_rank_score(query, item, kind), item) for item in items),
|
|
67
|
+
key=lambda pair: pair[0],
|
|
68
|
+
reverse=True,
|
|
69
|
+
)
|
|
70
|
+
if not ranked or ranked[0][0] < 0.18:
|
|
71
|
+
raise ValueError(f"Ничего не найдено по запросу: {query!r}.")
|
|
72
|
+
return ranked[0][1]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _display_markdown(markdown: str) -> None:
|
|
76
|
+
try:
|
|
77
|
+
from IPython.display import Markdown, display
|
|
78
|
+
|
|
79
|
+
display(Markdown(markdown))
|
|
80
|
+
except Exception:
|
|
81
|
+
print(markdown)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _display_code(code: str) -> None:
|
|
85
|
+
try:
|
|
86
|
+
from IPython.display import Markdown, display
|
|
87
|
+
|
|
88
|
+
display(Markdown("```python\n" + code.strip() + "\n```"))
|
|
89
|
+
except Exception:
|
|
90
|
+
print(code)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def copy_text(text: str) -> str:
|
|
94
|
+
"""Copy text if possible; in notebooks, fall back to a Copy button."""
|
|
95
|
+
try:
|
|
96
|
+
import pyperclip
|
|
97
|
+
|
|
98
|
+
pyperclip.copy(text)
|
|
99
|
+
return "clipboard"
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
from IPython.display import HTML, display
|
|
105
|
+
|
|
106
|
+
payload = json.dumps(text)
|
|
107
|
+
display(
|
|
108
|
+
HTML(
|
|
109
|
+
"""
|
|
110
|
+
<button style="padding:6px 10px;border:1px solid #888;border-radius:6px"
|
|
111
|
+
onclick='navigator.clipboard.writeText(%s); this.textContent="Copied";'>
|
|
112
|
+
Copy answer
|
|
113
|
+
</button>
|
|
114
|
+
"""
|
|
115
|
+
% payload
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
return "button"
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
"Не удалось скопировать текст. Установите pyperclip или используйте Jupyter/Colab."
|
|
122
|
+
) from exc
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _format_theory_item(item: dict[str, Any]) -> str:
|
|
126
|
+
extra = THEORY_EXTRAS.get(item["id"], {})
|
|
127
|
+
parts = [f"### {item['id']}. {item['question']}", "", item["answer"]]
|
|
128
|
+
if extra.get("formulae"):
|
|
129
|
+
parts.extend(["", "**Формулы:**"])
|
|
130
|
+
parts.extend(f"- {line}" for line in extra["formulae"])
|
|
131
|
+
if extra.get("structure"):
|
|
132
|
+
parts.extend(["", "**Структура / схема:**"])
|
|
133
|
+
parts.extend(f"- {line}" for line in extra["structure"])
|
|
134
|
+
sources = ", ".join([*item.get("sources", []), *extra.get("sources", [])])
|
|
135
|
+
source_line = f"\n\nИсточники: {sources}" if sources else ""
|
|
136
|
+
return "\n".join(parts) + source_line
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _format_practice_item(item: dict[str, Any], include_task: bool = True) -> str:
|
|
140
|
+
header = ""
|
|
141
|
+
if include_task:
|
|
142
|
+
header = (
|
|
143
|
+
f"# Практика {item['id']}. {item['task']}\n"
|
|
144
|
+
f"# {item['description']}\n\n"
|
|
145
|
+
)
|
|
146
|
+
return header + item["code"].strip() + "\n"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def list_theory(display: bool = True) -> str:
|
|
150
|
+
text = "\n".join(f"{item['id']}. {item['question']}" for item in THEORY)
|
|
151
|
+
if display:
|
|
152
|
+
_display_markdown(text)
|
|
153
|
+
return text
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def list_practice(display: bool = True) -> str:
|
|
157
|
+
text = "\n".join(f"{item['id']}. {item['task']}" for item in PRACTICE)
|
|
158
|
+
if display:
|
|
159
|
+
_display_markdown(text)
|
|
160
|
+
return text
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def theory(
|
|
164
|
+
number: int | str | None = None,
|
|
165
|
+
*,
|
|
166
|
+
query: str | None = None,
|
|
167
|
+
copy: bool = False,
|
|
168
|
+
display: bool = True,
|
|
169
|
+
) -> str:
|
|
170
|
+
"""Show a theory answer by number or approximate query."""
|
|
171
|
+
if isinstance(number, str) and query is None:
|
|
172
|
+
query = number
|
|
173
|
+
number = None
|
|
174
|
+
if number is None and query is None:
|
|
175
|
+
return list_theory(display=display)
|
|
176
|
+
item = _best_match(THEORY, query, "theory") if query else _get_by_number(THEORY, int(number))
|
|
177
|
+
text = _format_theory_item(item)
|
|
178
|
+
if copy:
|
|
179
|
+
copy_text(text)
|
|
180
|
+
if display:
|
|
181
|
+
_display_markdown(text)
|
|
182
|
+
return text
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def practice(
|
|
186
|
+
number: int | str | None = None,
|
|
187
|
+
*,
|
|
188
|
+
query: str | None = None,
|
|
189
|
+
copy: bool = False,
|
|
190
|
+
display: bool = True,
|
|
191
|
+
include_task: bool = True,
|
|
192
|
+
) -> str:
|
|
193
|
+
"""Show a practical code template by number or approximate query."""
|
|
194
|
+
if isinstance(number, str) and query is None:
|
|
195
|
+
query = number
|
|
196
|
+
number = None
|
|
197
|
+
if number is None and query is None:
|
|
198
|
+
return list_practice(display=display)
|
|
199
|
+
item = _best_match(PRACTICE, query, "practice") if query else _get_by_number(PRACTICE, int(number))
|
|
200
|
+
code = _format_practice_item(item, include_task=include_task)
|
|
201
|
+
if copy:
|
|
202
|
+
copy_text(code)
|
|
203
|
+
if display:
|
|
204
|
+
_display_code(code)
|
|
205
|
+
return code
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def find(query: str, *, kind: str = "all", limit: int = 8, display: bool = True) -> list[dict[str, Any]]:
|
|
209
|
+
"""Search theory and/or practice by a text fragment."""
|
|
210
|
+
pools: list[tuple[str, list[dict[str, Any]]]] = []
|
|
211
|
+
if kind in {"all", "theory"}:
|
|
212
|
+
pools.append(("theory", THEORY))
|
|
213
|
+
if kind in {"all", "practice"}:
|
|
214
|
+
pools.append(("practice", PRACTICE))
|
|
215
|
+
if not pools:
|
|
216
|
+
raise ValueError("kind должен быть 'all', 'theory' или 'practice'.")
|
|
217
|
+
|
|
218
|
+
found: list[dict[str, Any]] = []
|
|
219
|
+
for pool_kind, items in pools:
|
|
220
|
+
for item in items:
|
|
221
|
+
score = _rank_score(query, item, pool_kind)
|
|
222
|
+
if score >= 0.18:
|
|
223
|
+
found.append(
|
|
224
|
+
{
|
|
225
|
+
"kind": pool_kind,
|
|
226
|
+
"id": item["id"],
|
|
227
|
+
"title": _title_text(item),
|
|
228
|
+
"score": round(score, 3),
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
found.sort(key=lambda row: row["score"], reverse=True)
|
|
232
|
+
found = found[:limit]
|
|
233
|
+
if display:
|
|
234
|
+
if found:
|
|
235
|
+
lines = [
|
|
236
|
+
f"- `{row['kind']}({row['id']})` - {html.escape(row['title'])} "
|
|
237
|
+
f"(score {row['score']})"
|
|
238
|
+
for row in found
|
|
239
|
+
]
|
|
240
|
+
_display_markdown("\n".join(lines))
|
|
241
|
+
else:
|
|
242
|
+
_display_markdown("Ничего не найдено.")
|
|
243
|
+
return found
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def all_theory(*, copy: bool = False, display: bool = True) -> str:
|
|
247
|
+
text = "\n\n".join(_format_theory_item(item) for item in THEORY)
|
|
248
|
+
if copy:
|
|
249
|
+
copy_text(text)
|
|
250
|
+
if display:
|
|
251
|
+
_display_markdown(text)
|
|
252
|
+
return text
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def all_practice(*, copy: bool = False, display: bool = True) -> str:
|
|
256
|
+
text = "\n\n".join(
|
|
257
|
+
"```python\n" + _format_practice_item(item).strip() + "\n```" for item in PRACTICE
|
|
258
|
+
)
|
|
259
|
+
if copy:
|
|
260
|
+
copy_text(text)
|
|
261
|
+
if display:
|
|
262
|
+
_display_markdown(text)
|
|
263
|
+
return text
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def copy_theory(number: int) -> str:
|
|
267
|
+
return theory(number, copy=True, display=False)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def copy_practice(number: int) -> str:
|
|
271
|
+
return practice(number, copy=True, display=False)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from .api import all_practice, all_theory, find, list_practice, list_theory, practice, theory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
parser = argparse.ArgumentParser(prog="cvstudy")
|
|
10
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
11
|
+
|
|
12
|
+
p_theory = sub.add_parser("theory", help="Show a theory answer")
|
|
13
|
+
p_theory.add_argument("number", nargs="?")
|
|
14
|
+
p_theory.add_argument("--query", "-q")
|
|
15
|
+
p_theory.add_argument("--copy", action="store_true")
|
|
16
|
+
p_theory.add_argument("--all", action="store_true")
|
|
17
|
+
|
|
18
|
+
p_practice = sub.add_parser("practice", help="Show a practical code template")
|
|
19
|
+
p_practice.add_argument("number", nargs="?")
|
|
20
|
+
p_practice.add_argument("--query", "-q")
|
|
21
|
+
p_practice.add_argument("--copy", action="store_true")
|
|
22
|
+
p_practice.add_argument("--all", action="store_true")
|
|
23
|
+
|
|
24
|
+
p_find = sub.add_parser("find", help="Search theory and practice")
|
|
25
|
+
p_find.add_argument("query")
|
|
26
|
+
p_find.add_argument("--kind", choices=["all", "theory", "practice"], default="all")
|
|
27
|
+
p_find.add_argument("--limit", type=int, default=8)
|
|
28
|
+
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
if args.command == "theory":
|
|
31
|
+
if args.all:
|
|
32
|
+
print(all_theory(copy=args.copy, display=False))
|
|
33
|
+
elif args.number is None and args.query is None:
|
|
34
|
+
print(list_theory(display=False))
|
|
35
|
+
else:
|
|
36
|
+
number = int(args.number) if args.number and args.number.isdigit() else args.number
|
|
37
|
+
print(theory(number, query=args.query, copy=args.copy, display=False))
|
|
38
|
+
elif args.command == "practice":
|
|
39
|
+
if args.all:
|
|
40
|
+
print(all_practice(copy=args.copy, display=False))
|
|
41
|
+
elif args.number is None and args.query is None:
|
|
42
|
+
print(list_practice(display=False))
|
|
43
|
+
else:
|
|
44
|
+
number = int(args.number) if args.number and args.number.isdigit() else args.number
|
|
45
|
+
print(practice(number, query=args.query, copy=args.copy, display=False))
|
|
46
|
+
elif args.command == "find":
|
|
47
|
+
rows = find(args.query, kind=args.kind, limit=args.limit, display=False)
|
|
48
|
+
for row in rows:
|
|
49
|
+
print(f"{row['kind']}({row['id']}): {row['title']} [score={row['score']}]")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|