matthew-ide 1.2.0__py3-none-any.whl

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.
Files changed (47) hide show
  1. matthew_ide/.DS_Store +0 -0
  2. matthew_ide/__init__.py +15 -0
  3. matthew_ide/app/.DS_Store +0 -0
  4. matthew_ide/app/dev_runner.py +3 -0
  5. matthew_ide/app/matthew_app.py +114 -0
  6. matthew_ide/app/runner.py +10 -0
  7. matthew_ide/assets/demo.yaml +421 -0
  8. matthew_ide/features/.DS_Store +0 -0
  9. matthew_ide/features/app_folder/__init__.py +44 -0
  10. matthew_ide/features/events/__init__.py +5 -0
  11. matthew_ide/features/events/hook.py +51 -0
  12. matthew_ide/features/events/sender/__init__.py +0 -0
  13. matthew_ide/features/events/sender/api_sender.py +62 -0
  14. matthew_ide/features/events/sender/event_sender.py +32 -0
  15. matthew_ide/features/events/sender/no_sender.py +11 -0
  16. matthew_ide/features/run_code/__init__.py +80 -0
  17. matthew_ide/features/run_code/_runner.py +160 -0
  18. matthew_ide/features/update/__init__.py +56 -0
  19. matthew_ide/py.typed +0 -0
  20. matthew_ide/screens/.DS_Store +0 -0
  21. matthew_ide/screens/code_simulator/__init__.py +7 -0
  22. matthew_ide/screens/code_simulator/components/code_editor.py +93 -0
  23. matthew_ide/screens/code_simulator/components/current_task_info.py +33 -0
  24. matthew_ide/screens/code_simulator/components/explorer.py +137 -0
  25. matthew_ide/screens/code_simulator/components/test_results/__init__.py +5 -0
  26. matthew_ide/screens/code_simulator/components/test_results/result_item.py +59 -0
  27. matthew_ide/screens/code_simulator/components/test_results/test_results.py +54 -0
  28. matthew_ide/screens/code_simulator/editor.tcss +19 -0
  29. matthew_ide/screens/code_simulator/model.py +87 -0
  30. matthew_ide/screens/code_simulator/screen.py +172 -0
  31. matthew_ide/screens/message_box/mb_screen.py +38 -0
  32. matthew_ide/screens/message_box/mb_screen.tcss +49 -0
  33. matthew_ide/screens/save_question/__init__.py +39 -0
  34. matthew_ide/screens/save_question/styles.tcss +49 -0
  35. matthew_ide/screens/welcome/__init__.py +107 -0
  36. matthew_ide/screens/welcome/styles.tcss +42 -0
  37. matthew_ide/shared/.DS_Store +0 -0
  38. matthew_ide/shared/__init__.py +19 -0
  39. matthew_ide/shared/io/__init__.py +0 -0
  40. matthew_ide/shared/io/load.py +133 -0
  41. matthew_ide/shared/io/save_quiz.py +72 -0
  42. matthew_ide/shared/state/__init__.py +11 -0
  43. matthew_ide/shared/state/data_state.py +151 -0
  44. matthew_ide-1.2.0.dist-info/METADATA +40 -0
  45. matthew_ide-1.2.0.dist-info/RECORD +47 -0
  46. matthew_ide-1.2.0.dist-info/WHEEL +4 -0
  47. matthew_ide-1.2.0.dist-info/entry_points.txt +3 -0
matthew_ide/.DS_Store ADDED
Binary file
@@ -0,0 +1,15 @@
1
+ from importlib.metadata import version
2
+ from typing import Literal
3
+
4
+ __version__ = version(__name__)
5
+
6
+
7
+ APP_PACKAGE_NAME = "analyst-klondike"
8
+ APP_FOLDER_NAME = "AnalystKlondike"
9
+
10
+
11
+ WELLCOME_SCREEN_APP_NAME = "Клондайк аналитика"
12
+ WELCOME_SCREEN_APP_TITLE = "Klondike"
13
+ WELCOME_SCREEN_APP_SUBTITLE = "code editor"
14
+
15
+ EVENT_DESTINATION: Literal["local_db", "remote_db", "no_events"] = "local_db"
Binary file
@@ -0,0 +1,3 @@
1
+ from matthew_ide.app.matthew_app import MatthewApp
2
+
3
+ app = MatthewApp()
@@ -0,0 +1,114 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+ from typing import Callable
3
+
4
+
5
+ import pyperclip # type: ignore
6
+ from textual import on, work
7
+ from textual.reactive import reactive
8
+ from textual.app import App
9
+
10
+ from matthew_ide import APP_PACKAGE_NAME
11
+ from matthew_ide.features.app_folder import init_app_working_folder
12
+ from matthew_ide.features.events import send_event
13
+ from matthew_ide.features.update import is_outdated
14
+ from matthew_ide.screens.code_simulator import CodeSimulatorScreen
15
+ from matthew_ide.screens.message_box.mb_screen import MessageBoxScreen
16
+ from matthew_ide.screens.save_question import SaveQuestion, SaveQuestionModalResult
17
+
18
+
19
+ class MatthewApp(App[int]):
20
+
21
+ MODES = { # type: ignore
22
+ "code_simulator": CodeSimulatorScreen,
23
+ }
24
+
25
+ demo_file_name = reactive("demo.yaml")
26
+ save_func: reactive[Callable[[], None] | None] = reactive(None)
27
+
28
+ @work
29
+ async def on_mount(self) -> None:
30
+ self.theme = "textual-dark"
31
+ self.title = self.app_title
32
+ self.sub_title = "Интерактивный тренажер Python на вашем компьютере"
33
+
34
+ self._try_init_app()
35
+ self.send_event("app_opened")
36
+ await self._display_message_if_app_outdated()
37
+
38
+ self.switch_mode("code_simulator")
39
+
40
+ def send_event(
41
+ self,
42
+ event_name: str,
43
+ event_json: dict[str, str | int | bool] | None = None) -> None:
44
+ send_event(self, event_name, event_location="/", event_json=event_json)
45
+
46
+ async def _display_message_if_app_outdated(self) -> None:
47
+ update_check = is_outdated()
48
+
49
+ if update_check is None:
50
+ await self.push_screen_wait(MessageBoxScreen(
51
+ message="Ошибка при загрузке последней версии приложения"
52
+ ))
53
+ self.send_event("fail_to_get_latest_version")
54
+ self.exit()
55
+ if update_check and update_check.is_outdated:
56
+ await self.push_screen_wait(MessageBoxScreen(
57
+ message=f"Доступна новая версия {update_check.latest_version} " +
58
+ f"(сейчас установлена {update_check.current_version}). \n"
59
+ "Обновите приложение. \n" +
60
+ "Для этого закройте приложение и запустите команду: \n" +
61
+ "[@click=app.copy_update_str_to_clipboard]uv tool upgrade analyst-klondike[/]"
62
+ ))
63
+ self.send_event("app_is_outdated")
64
+ self.exit()
65
+
66
+ @on(CodeSimulatorScreen.NotifyToChangeAppTitle)
67
+ def on_task_selected(self, event: CodeSimulatorScreen.NotifyToChangeAppTitle) -> None:
68
+ self.sub_title = event.task_title
69
+
70
+ @property
71
+ def app_title(self) -> str:
72
+ try:
73
+ return f"Клондайк аналитика {version(APP_PACKAGE_NAME)}"
74
+ except PackageNotFoundError:
75
+ return "Клондайк аналитика"
76
+
77
+ # Copy to clipboard update string
78
+ def action_copy_update_str_to_clipboard(self) -> None:
79
+ try:
80
+ pyperclip.copy('uv tool upgrade analyst-klondike')
81
+ self.notify(
82
+ message="Скопировано uv tool upgrade analyst-klondike",
83
+ title="Клондайк аналитика",
84
+ severity="information"
85
+ )
86
+ self.send_event("update_command_is_copied")
87
+ except Exception: # pylint: disable=W0718
88
+ self.notify(
89
+ message="Ошибка при копировании uv tool upgrade analyst-klondike в буфер обмена",
90
+ title="Клондайк аналитика",
91
+ severity="error"
92
+ )
93
+
94
+ async def action_quit(self) -> None:
95
+ def _callback(res: SaveQuestionModalResult | None) -> None:
96
+ if res == "save":
97
+ if self.save_func:
98
+ self.save_func()
99
+ if res != "cancel":
100
+ self.send_event("app_normally_closed")
101
+ self.exit(0)
102
+
103
+ self.push_screen(SaveQuestion(), _callback)
104
+
105
+ def _try_init_app(self) -> None:
106
+ try:
107
+ init_app_working_folder()
108
+ self.send_event("app_folder_created")
109
+ except Exception: # pylint: disable=broad-exception-caught
110
+ self.send_event("fail_to_create_app_folder")
111
+ self.notify(
112
+ "Не удалось создать папку с данными приложения",
113
+ severity="error"
114
+ )
@@ -0,0 +1,10 @@
1
+ from matthew_ide.app.matthew_app import MatthewApp
2
+
3
+
4
+ def run_matthew():
5
+ app = MatthewApp()
6
+ app.run()
7
+
8
+
9
+ if __name__ == "__main__":
10
+ run_matthew()
@@ -0,0 +1,421 @@
1
+ quiz_info:
2
+ min_supported_app_version: 1.0.17
3
+ user_info:
4
+ email: analyst_klondike
5
+ quizes:
6
+ python_basics:
7
+ title: Основы Python
8
+ questions:
9
+ Изменить порядок слов в предложении:
10
+ text: "Дано предложение, без запятых, только английские символы, используются\
11
+ \ только строчные буквы. \n\nНаписать функцию, меняющую порядок слов в предложении\
12
+ \ на обратный. \n\nНапример 'the sky is blue' -> 'blue is sky the'"
13
+ code_template: "def solution(s: str) -> str:\n pass"
14
+ code: "def solution(s: str) -> str:\n words = s.split()\n rev = reversed(words)\n\
15
+ \ return \" \".join(rev) "
16
+ test_cases:
17
+ - s: hello
18
+ expected: hello
19
+ - s: hello world
20
+ expected: world hello
21
+ - s: all work and no play makes jack a dull boy
22
+ expected: boy dull a jack makes play no and work all
23
+ is_passed: passed
24
+ params:
25
+ s: str
26
+ return: str
27
+ Сумма всех четных чисел до N:
28
+ text: Напишите функцию, которая вычисляет сумму всех четных чисел в промежутке
29
+ от 1 до N, включая N.
30
+ code_template: "def solution(n: int):\n return 0"
31
+ code: "def solution(n: int):\n return 0"
32
+ test_cases:
33
+ - n: 1
34
+ expected: 0
35
+ - n: 10
36
+ expected: 30
37
+ - n: 19
38
+ expected: 90
39
+ is_passed: failed
40
+ params:
41
+ n: int
42
+ return: int
43
+ Разворот числа:
44
+ text: 'Напишите программу, которая развернет число: 12345 -> 54321. Для отрицательных
45
+ чисел вернуть тоже самое число (без разворотов). Считаем, что функция принимает
46
+ только натуральные числа.'
47
+ code_template: "def solution(n: int):\n return n"
48
+ code: "def solution(n: int):\n print(\"Hello, world\")\n return n"
49
+ test_cases:
50
+ - n: 0
51
+ expected: 0
52
+ - n: 123
53
+ expected: 321
54
+ - n: 123456
55
+ expected: 654321
56
+ is_passed: failed
57
+ params:
58
+ n: int
59
+ return: int
60
+ more_difficult_tasks:
61
+ title: Сложные задачи по Python
62
+ questions:
63
+ Самый длинный префикс:
64
+ text: "Дан список слов. \nНаписать функцию, которая вернет самый длинный префикс.\n\
65
+ Префикс - набор букв, с которых начинается слово.\nНапример:\n - solution(['flower',\
66
+ \ 'flow', 'flight']) -> 'fl'"
67
+ code_template: "def solution(words: list[str]) -> str:\n return ''"
68
+ code: "def solution(words: list[str]) -> str:\n return ''"
69
+ test_cases:
70
+ - words:
71
+ - flower
72
+ - flow
73
+ - flight
74
+ expected: fl
75
+ - words:
76
+ - abc
77
+ - xyz
78
+ - hgf
79
+ expected: ''
80
+ - words:
81
+ - europe
82
+ - europe
83
+ - europe
84
+ expected: europe
85
+ is_passed: not_runned
86
+ params:
87
+ words: list
88
+ return: str
89
+ Сумма чисел:
90
+ text: 'Дан массив чисел и число target. Написать функцию, которая вернет список,
91
+ состоящий из двух индексов элементов, которые в сумме дают число target.
92
+ Допустим, что в массиве существует только одна пара таких элементов.
93
+
94
+ Например:
95
+
96
+ - solution([2,7,11,15], 9) -> [0,1], т.к. 2 + 7 = 9, числа находятся на
97
+ позициях 0 и 1 соответственно.'
98
+ code_template: "def solution(nums: list, target: int) -> list[int]:\n return\
99
+ \ []"
100
+ code: "def solution(nums: list, target: int) -> list[int]:\n return []"
101
+ test_cases:
102
+ - nums:
103
+ - 2
104
+ - 7
105
+ - 11
106
+ - 15
107
+ target: 9
108
+ expected:
109
+ - 0
110
+ - 1
111
+ - nums:
112
+ - 2
113
+ - 7
114
+ - 11
115
+ - 15
116
+ target: 18
117
+ expected:
118
+ - 1
119
+ - 2
120
+ is_passed: not_runned
121
+ params:
122
+ nums: list
123
+ target: int
124
+ return: list
125
+ Группировка анаграмм:
126
+ text: 'Написать функцию, которая группирует слова-анаграммы вместе.
127
+
128
+ Например для списка слов [''eat'',''tea'',''the'',''teh'',''hte'',''abc'',''edf'']
129
+ функция должна вернуть
130
+
131
+ [[''eat'',''tea''], [''the'',''teh'',''hte''], ''abc'',''edf''].
132
+
133
+ Два слова являются анаграммами если они состоят из одних и тех же букв,
134
+ но в разном порядке.'
135
+ code_template: "def solution(words: list):\n return []"
136
+ code: "def solution(words: list):\n return []"
137
+ test_cases:
138
+ - words:
139
+ - eat
140
+ - tea
141
+ - the
142
+ - teh
143
+ - hte
144
+ - abc
145
+ - edf
146
+ expected:
147
+ - - eat
148
+ - tea
149
+ - - the
150
+ - teh
151
+ - hte
152
+ - abc
153
+ - edf
154
+ is_passed: not_runned
155
+ params:
156
+ words: list
157
+ return: list
158
+ list_string_tasks:
159
+ title: Списки и строки
160
+ questions:
161
+ Поиск подстрок:
162
+ text: 'Написать функцию, которая принимает строку subs и строку long_string.
163
+
164
+ Функция вернет список индексов всех вхождений подстроки subs в строке long_string.
165
+
166
+ Номера позиций начинаются с нуля.
167
+
168
+ Например:
169
+
170
+ - подстрока "hello" встречается в строке "hello, world, all people hello"
171
+ два раза: на позиции 0 (начало строки) и 25.
172
+
173
+ - подстрока "xyz" встречается в строке "abc def xyz ghr" один раз на позиции
174
+ 8.
175
+
176
+ Таким образом:
177
+
178
+ - solution("hello, world, all people hello") -> [0, 25]
179
+
180
+ - solution("xyz", "abc def xyz ghr") -> [8]
181
+
182
+ - solution("", "my string") -> [] (для пустой строки вернет пустой список)
183
+
184
+ - solution("hello", "ok, great!") -> [] (нет вхождений)'
185
+ code_template: "def solution(subs: str, long_string: str):\n return []"
186
+ code: "def solution(subs: str, long_string: str):\n return []"
187
+ test_cases:
188
+ - subs: abc
189
+ long_string: _abc_abc
190
+ expected:
191
+ - 1
192
+ - 5
193
+ - subs: hello
194
+ long_string: hello world hello
195
+ expected:
196
+ - 0
197
+ - 12
198
+ - subs: xyz
199
+ long_string: abc def xyz ghr
200
+ expected:
201
+ - 8
202
+ is_passed: not_runned
203
+ params:
204
+ subs: str
205
+ long_string: str
206
+ return: list
207
+ Нерадивый ученик:
208
+ text: 'Ученик написал на доске предложение:
209
+
210
+ ''привет, мир. какая хорошая погода. мне все нравится.''
211
+
212
+ Но он забыл, что предложения начинаются с большой буквы.
213
+
214
+ Напишите функцию, которая принимает строку с таким предложением и возвращает
215
+ новую строку, где все предложения начинаются с большой буквы.
216
+
217
+ Считаем, что предложение может заканчиваться только точкой.'
218
+ code_template: "def solution(sentence: str):\n return ''"
219
+ code: "def solution(sentence: str):\n return ''"
220
+ test_cases:
221
+ - sentence: ''
222
+ expected: ''
223
+ - sentence: привет.
224
+ expected: Привет.
225
+ - sentence: анализ данных - лучшая профессия.
226
+ expected: Анализ данных - лучшая профессия.
227
+ - sentence: привет, мир. какая хорошая погода. мне все нравится.
228
+ expected: Привет, мир. Какая хорошая погода. Мне все нравится.
229
+ is_passed: not_runned
230
+ params:
231
+ sentence: str
232
+ return: str
233
+ Аббревиатура:
234
+ text: 'Напишите функцию, которая принимает название учреждения и возвращает
235
+ аббревиатуру.
236
+
237
+ Например: - Министерство Иностранных Дел -> МИД - Российская Федерация ->
238
+ РФ
239
+
240
+ Нельзя привязываться к числу слов, их может быть бесконечное число. Буквы
241
+ в entity могут быть как большими, так и маленькими. Если в entity одно слово,
242
+ то вернуть его первую букву, например: ''учреждение'' -> ''У'''
243
+ code_template: "def solution(entity: str):\n return ''"
244
+ code: "def solution(entity: str):\n return ''"
245
+ test_cases:
246
+ - entity: ''
247
+ expected: ''
248
+ - entity: учреждение
249
+ expected: У
250
+ - entity: Российская Федерация
251
+ expected: РФ
252
+ - entity: министерство иностранных дел
253
+ expected: МИД
254
+ - entity: союз советских социалистических республик
255
+ expected: СССР
256
+ is_passed: not_runned
257
+ params:
258
+ entity: str
259
+ return: str
260
+ Локальные максимумы:
261
+ text: 'Напишите функцию, которая принимает список и возвращает список локальных
262
+ максимумов. Крайние элементы списка максимумами не являются.
263
+
264
+ Локальный максимум - это элемент, который:
265
+
266
+ - больше предыдущего
267
+
268
+ - больше следующего
269
+
270
+ Например:
271
+
272
+ - [1,2,5,3,4,7,8,3,2,0] -> [5, 8]
273
+
274
+ - [3,2,1] -> [] пустой список, не лок. максимумов.'
275
+ code_template: "def solution(lst: list):\n return []"
276
+ code: "def solution(lst: list):\n return []"
277
+ test_cases:
278
+ - lst:
279
+ - 8
280
+ - 7
281
+ - 6
282
+ - 5
283
+ - 4
284
+ expected: []
285
+ - lst:
286
+ - 8
287
+ - 10
288
+ - 6
289
+ - 5
290
+ - 4
291
+ expected:
292
+ - 10
293
+ - lst:
294
+ - 1
295
+ - 4
296
+ - 2
297
+ - 5
298
+ - 10
299
+ - 3
300
+ - 2
301
+ expected:
302
+ - 4
303
+ - 10
304
+ - lst:
305
+ - 1
306
+ - 2
307
+ - 5
308
+ - 3
309
+ - 4
310
+ - 7
311
+ - 8
312
+ - 3
313
+ - 2
314
+ - 0
315
+ expected:
316
+ - 5
317
+ - 8
318
+ is_passed: not_runned
319
+ params:
320
+ lst: list
321
+ return: list
322
+ Сумма положительных:
323
+ text: 'Написать функцию, которая принимает список чисел и вернет сумму только
324
+ положительных элементов.
325
+
326
+ Для пустого списка вернуть 0. Аналогично, вернуть 0 если нет ни одного положительного
327
+ элемента.
328
+
329
+ Например:
330
+
331
+ - solution([]) -> 0
332
+
333
+ - solution([1, 2, -3]) -> 3
334
+
335
+ - solution([3, 4, -2, 7, -10, 12]) -> 26'
336
+ code_template: "def solution(lst: list):\n return 0"
337
+ code: "def solution(lst: list):\n return 0"
338
+ test_cases:
339
+ - lst: []
340
+ expected: 0
341
+ - lst:
342
+ - 14
343
+ expected: 14
344
+ - lst:
345
+ - 2
346
+ - 5
347
+ - 8
348
+ expected: 15
349
+ - lst:
350
+ - 3
351
+ - -2
352
+ - -2
353
+ - -15
354
+ - -10
355
+ - 12
356
+ expected: 15
357
+ - lst:
358
+ - 1
359
+ - 2
360
+ - -3
361
+ expected: 3
362
+ - lst:
363
+ - 3
364
+ - 4
365
+ - -2
366
+ - 7
367
+ - -10
368
+ - 12
369
+ expected: 26
370
+ - lst:
371
+ - -3
372
+ - -4
373
+ - -2
374
+ - -7
375
+ - -10
376
+ - -12
377
+ expected: 0
378
+ is_passed: not_runned
379
+ params:
380
+ lst: list
381
+ return: int
382
+ Простое число:
383
+ text: 'Напишите функцию, которая возращает true, если число является простым.
384
+
385
+ Число является простым, если нацело делится только на 1 и само себя.
386
+
387
+ Например:
388
+
389
+ - 7 - простое, т.к. делится нацело только на себя и 1.
390
+
391
+ - 8, к примеру, простым не является, потому что без остатка делится еще
392
+ и на 2, и 4.
393
+
394
+ Подсказка: пройдетесь по всем числам, меньше заданного и проверьте делимость'
395
+ code_template: "def solution(n: int):\n return True"
396
+ code: "def solution(n: int):\n return True"
397
+ test_cases:
398
+ - n: 1
399
+ expected: true
400
+ - n: 2
401
+ expected: true
402
+ - n: 3
403
+ expected: true
404
+ - n: 4
405
+ expected: false
406
+ - n: 5
407
+ expected: true
408
+ - n: 6
409
+ expected: false
410
+ - n: 8
411
+ expected: false
412
+ - n: 11
413
+ expected: true
414
+ - n: 13
415
+ expected: true
416
+ - n: 14
417
+ expected: false
418
+ is_passed: not_runned
419
+ params:
420
+ n: int
421
+ return: bool
Binary file
@@ -0,0 +1,44 @@
1
+ import uuid
2
+ from pathlib import Path
3
+ from platformdirs import user_data_dir
4
+
5
+ from matthew_ide import APP_FOLDER_NAME
6
+
7
+
8
+ def init_app_working_folder() -> None:
9
+ _create_app_folder()
10
+ write_user_id()
11
+
12
+
13
+ class NoUserIdException(Exception):
14
+ def __init__(self, user_id_file_path: str) -> None:
15
+ super().__init__(*user_id_file_path)
16
+ self.user_id_file_path = user_id_file_path
17
+
18
+
19
+ def read_user_id() -> str:
20
+ app_folder = _get_app_folder()
21
+ user_id_file = app_folder / "user_id.txt"
22
+ if not user_id_file.exists():
23
+ raise FileExistsError()
24
+ user_id = user_id_file.read_text()
25
+ if user_id == "":
26
+ raise NoUserIdException(str(user_id_file))
27
+ return user_id
28
+
29
+
30
+ def write_user_id() -> None:
31
+ app_folder = _get_app_folder()
32
+ user_id_file = app_folder / "user_id.txt"
33
+ user_id = str(uuid.uuid4())
34
+ user_id_file.write_text(user_id)
35
+
36
+
37
+ def _get_app_folder() -> Path:
38
+ return Path(user_data_dir()) / APP_FOLDER_NAME
39
+
40
+
41
+ def _create_app_folder() -> None:
42
+ app_folder = _get_app_folder()
43
+ if not app_folder.exists():
44
+ app_folder.mkdir()
@@ -0,0 +1,5 @@
1
+ from .hook import send_event
2
+
3
+ __all__ = [
4
+ "send_event"
5
+ ]
@@ -0,0 +1,51 @@
1
+ from typing import Any, Literal
2
+
3
+ from textual import log
4
+ from textual.app import App
5
+
6
+ from matthew_ide import EVENT_DESTINATION
7
+
8
+ from .sender.event_sender import BaseEventSender
9
+ from .sender.api_sender import ApiEventSender
10
+ from .sender.no_sender import NoEventSender
11
+
12
+
13
+ def _create_sender(
14
+ sender_type: Literal["local_db", "remote_db", "no_events"]
15
+ ) -> BaseEventSender:
16
+ if sender_type == "local_db":
17
+ return ApiEventSender(
18
+ api_url="https://localhost:7150/api/NeonApi/PostEvent")
19
+ if sender_type == "remote_db":
20
+ return ApiEventSender(
21
+ api_url="https://www.xn----7sbbaat6aaehdfdhwgj7f.xn--p1ai/api/NeonApi/PostEvent")
22
+ if sender_type == "no_events":
23
+ return NoEventSender()
24
+ raise ValueError(f"Event sender <{sender_type}> supported")
25
+
26
+
27
+ def _wrapper():
28
+
29
+ _sender = _create_sender(EVENT_DESTINATION)
30
+
31
+ def _send_event(
32
+ app: App[Any],
33
+ event_name: str,
34
+ event_location: str,
35
+ event_json: dict[str, str | int | bool] | None = None) -> None:
36
+
37
+ async def _send_async(event_name: str,
38
+ event_location: str) -> None:
39
+ try:
40
+ await _sender.send_event(event_name=event_name,
41
+ event_location=event_location,
42
+ event_json=event_json)
43
+ except Exception as ex: # pylint: disable=broad-exception-caught
44
+ log(f"Ошибка отправки события {event_name}/{event_location}: {str(ex)}")
45
+
46
+ app.run_worker(_send_async(event_name, event_location), thread=True)
47
+
48
+ return _send_event
49
+
50
+
51
+ send_event = _wrapper()
File without changes