chgksuite 0.25.1__py3-none-any.whl → 0.26.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.
chgksuite/parser.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import base64
4
4
  import codecs
5
5
  import datetime
6
+ import hashlib
6
7
  import itertools
7
8
  import json
8
9
  import os
@@ -13,12 +14,14 @@ import subprocess
13
14
  import sys
14
15
  import tempfile
15
16
  import urllib
17
+ import time
16
18
 
17
19
  import bs4
18
20
  import chardet
19
21
  import dashtable
20
22
  import mammoth
21
23
  import pypandoc
24
+ import requests
22
25
  import toml
23
26
  from bs4 import BeautifulSoup
24
27
  from parse import parse
@@ -26,11 +29,11 @@ from parse import parse
26
29
  import chgksuite.typotools as typotools
27
30
  from chgksuite.common import (
28
31
  QUESTION_LABELS,
29
- DefaultArgs,
30
32
  DefaultNamespace,
31
33
  DummyLogger,
32
34
  check_question,
33
35
  compose_4s,
36
+ get_chgksuite_dir,
34
37
  get_lastdir,
35
38
  init_logger,
36
39
  load_settings,
@@ -40,6 +43,7 @@ from chgksuite.common import (
40
43
  from chgksuite.composer import gui_compose
41
44
  from chgksuite.composer.composer_common import make_filename
42
45
  from chgksuite.parser_db import chgk_parse_db
46
+ from chgksuite.typotools import re_url
43
47
  from chgksuite.typotools import remove_excessive_whitespace as rew
44
48
 
45
49
  ENC = sys.stdout.encoding or "utf8"
@@ -91,10 +95,23 @@ class ChgkParser:
91
95
  BADNEXTFIELDS = set(["question", "answer"])
92
96
  RE_NUM = re.compile("^([0-9]+)\\.?$")
93
97
  RE_NUM_START = re.compile("^([0-9]+)\\.")
98
+ ZERO_PREFIXES = ("Нулевой вопрос", "Разминочный вопрос")
99
+ TOUR_NUMBERS_AS_WORDS = (
100
+ "Первый",
101
+ "Второй",
102
+ "Третий",
103
+ "Четвертый",
104
+ "Пятый",
105
+ "Шестой",
106
+ "Седьмой",
107
+ "Восьмой",
108
+ "Девятый",
109
+ "Десятый",
110
+ )
94
111
 
95
112
  def __init__(self, defaultauthor=None, args=None, logger=None):
96
113
  self.defaultauthor = defaultauthor
97
- args = args or DefaultArgs()
114
+ args = args or DefaultNamespace()
98
115
  self.regexes = load_regexes(args.regexes)
99
116
  self.logger = logger or init_logger("parser")
100
117
  self.args = args
@@ -108,6 +125,148 @@ class ChgkParser:
108
125
  if self.args.language == "en":
109
126
  self.args.typography_quotes = "off"
110
127
 
128
+ def _setup_image_cache(self):
129
+ """Setup image download cache directory and load existing cache"""
130
+ if not hasattr(self, "_image_cache"):
131
+ self.image_cache_dir = os.path.join(
132
+ get_chgksuite_dir(), "downloaded_images"
133
+ )
134
+ os.makedirs(self.image_cache_dir, exist_ok=True)
135
+
136
+ self.image_cache_file = os.path.join(
137
+ get_chgksuite_dir(), "image_download_cache.json"
138
+ )
139
+ if os.path.isfile(self.image_cache_file):
140
+ try:
141
+ with open(self.image_cache_file, encoding="utf8") as f:
142
+ self._image_cache = json.load(f)
143
+ except (json.JSONDecodeError, OSError):
144
+ self._image_cache = {}
145
+ else:
146
+ self._image_cache = {}
147
+
148
+ def _download_image(self, url):
149
+ """Download image from URL and return local filename"""
150
+ self._setup_image_cache()
151
+ url = url.replace("\\", "")
152
+
153
+ # Check cache first
154
+ url_hash = hashlib.sha256(url.encode("utf-8")).hexdigest()[:20]
155
+ if url_hash in self._image_cache:
156
+ cached_filename = self._image_cache[url_hash]
157
+ cached_path = os.path.join(self.image_cache_dir, cached_filename)
158
+ if os.path.isfile(cached_path):
159
+ return cached_path
160
+
161
+ # Determine file extension
162
+ parsed_url = urllib.parse.urlparse(url)
163
+ path_lower = parsed_url.path.lower()
164
+ ext = None
165
+ for image_ext in [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"]:
166
+ if path_lower.endswith(image_ext):
167
+ ext = image_ext
168
+ break
169
+
170
+ if not ext:
171
+ # Try to guess from URL structure
172
+ if any(
173
+ img_ext in path_lower
174
+ for img_ext in [
175
+ ".jpg",
176
+ ".jpeg",
177
+ ".png",
178
+ ".webp",
179
+ ".gif",
180
+ ".bmp",
181
+ ".svg",
182
+ ]
183
+ ):
184
+ for image_ext in [
185
+ ".jpg",
186
+ ".jpeg",
187
+ ".png",
188
+ ".webp",
189
+ ".gif",
190
+ ".bmp",
191
+ ".svg",
192
+ ]:
193
+ if image_ext in path_lower:
194
+ ext = image_ext
195
+ break
196
+ else:
197
+ ext = ".jpg" # Default extension
198
+
199
+ filename = url_hash + ext
200
+ filepath = os.path.join(self.image_cache_dir, filename)
201
+
202
+ try:
203
+ self.logger.info(f"Downloading image from {url}")
204
+ headers = {
205
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
206
+ "Accept": "image/png,image/jpeg,image/webp,image/gif,image/*,*/*;q=0.8",
207
+ "Accept-Language": "en-US,en;q=0.9",
208
+ "Accept-Encoding": "gzip, deflate, br",
209
+ "Connection": "keep-alive",
210
+ "Upgrade-Insecure-Requests": "1",
211
+ }
212
+ response = requests.get(url, timeout=30, stream=True, headers=headers)
213
+ response.raise_for_status()
214
+ time.sleep(0.5) # rate limiting
215
+
216
+ with open(filepath, "wb") as f:
217
+ for chunk in response.iter_content(chunk_size=8192):
218
+ f.write(chunk)
219
+
220
+ # Update cache
221
+ self._image_cache[url_hash] = filename
222
+ with open(self.image_cache_file, "w", encoding="utf8") as f:
223
+ json.dump(self._image_cache, f, indent=2, sort_keys=True)
224
+
225
+ return filepath
226
+
227
+ except Exception as e:
228
+ self.logger.warning(f"Failed to download image from {url}: {e}")
229
+ return None
230
+
231
+ def _process_images_in_text(self, text):
232
+ """Process text to find image URLs and replace them with local references"""
233
+ if not text or not getattr(self.args, "download_images", False):
234
+ return text
235
+
236
+ if isinstance(text, list):
237
+ return [self._process_images_in_text(item) for item in text]
238
+
239
+ if not isinstance(text, str):
240
+ return text
241
+
242
+ # Find all URLs in the text
243
+ for match in re_url.finditer(text):
244
+ url = match.group(0)
245
+ url_lower = url.lower()
246
+
247
+ # Check if it's a direct image URL
248
+ if any(
249
+ url_lower.endswith(ext)
250
+ for ext in [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"]
251
+ ):
252
+ local_filename = self._download_image(url)
253
+ if local_filename:
254
+ # Replace URL with chgksuite image syntax
255
+ img_reference = f"(img {local_filename})"
256
+ text = text.replace(url, img_reference)
257
+
258
+ return text
259
+
260
+ def _process_question_images(self, question):
261
+ """Process a question dict to download images from URLs"""
262
+ if not getattr(self.args, "download_images", False):
263
+ return
264
+
265
+ # Process all fields except 'source'
266
+ for field in question:
267
+ if field != "source":
268
+ question[field] = self._process_images_in_text(question[field])
269
+
111
270
  def merge_to_previous(self, index):
112
271
  target = index - 1
113
272
  if self.structure[target][1]:
@@ -200,7 +359,7 @@ class ChgkParser:
200
359
  @classmethod
201
360
  def _replace(cls, obj, val, new_val):
202
361
  if isinstance(obj, str):
203
- return obj.replace(val, new_val)
362
+ return obj.replace(val, new_val).strip()
204
363
  elif isinstance(obj, list):
205
364
  for i, el in enumerate(obj):
206
365
  obj[i] = cls._replace(el, val, new_val)
@@ -214,6 +373,11 @@ class ChgkParser:
214
373
  for el in val:
215
374
  self._get_strings(el, list_)
216
375
 
376
+ def _get_strings_joined(self, val):
377
+ strings = []
378
+ self._get_strings(val, strings)
379
+ return "\n".join(strings)
380
+
217
381
  def _try_extract_field(self, question, k):
218
382
  regex = self.regexes[k]
219
383
  keys = sorted(question.keys())
@@ -255,14 +419,18 @@ class ChgkParser:
255
419
  and not question["number"].strip()
256
420
  ):
257
421
  question.pop("number")
422
+ question_str = self._get_strings_joined(question["question"])
423
+ for prefix in self.ZERO_PREFIXES:
424
+ if question_str.startswith(prefix):
425
+ question["question"] = self._replace(question["question"], prefix, "")
426
+ question["number"] = 0
427
+ question_str = self._get_strings_joined(question["question"])
258
428
  for k in ("zachet", "nezachet", "source", "comment", "author"):
259
429
  if k not in question:
260
430
  self._try_extract_field(question, k)
261
- strings = []
262
- self._get_strings(question["question"], strings)
263
- strings = "\n".join(strings)
431
+ question_str = self._get_strings_joined(question["question"])
264
432
  handout = self.labels["question_labels"]["handout"]
265
- srch = re.search(f"{handout}:([ \n]+)\\[", strings, flags=re.DOTALL)
433
+ srch = re.search(f"{handout}:([ \n]+)\\[", question_str, flags=re.DOTALL)
266
434
  if srch:
267
435
  question["question"] = self._replace(
268
436
  question["question"],
@@ -502,17 +670,30 @@ class ChgkParser:
502
670
  if element[0] == "question":
503
671
  try:
504
672
  num = regexes["question"].search(element[1])
505
- if num:
506
- self.structure.insert(_id, ["number", num.group(1)])
673
+ if num and num.group("number"):
674
+ self.structure.insert(_id, ["number", num.group("number")])
507
675
  except Exception as e:
508
676
  sys.stderr.write(
509
677
  f"exception at line 445 of parser: {type(e)} {e}\n"
510
678
  )
511
- # TODO: переделать корявую обработку авторки на нормальную
512
- before_replacement = element[1]
513
- element[1] = regexes[element[0]].sub("", element[1], 1)
679
+ if (
680
+ not num
681
+ and ("нулевой вопрос" in element[1].lower())
682
+ or ("разминочный вопрос" in element[1].lower())
683
+ ):
684
+ self.structure.insert(_id, ["number", "0"])
685
+ if element[0] == "question":
686
+ lines = element[1].split(SEP)
687
+ for i, line in enumerate(lines):
688
+ if regexes["question"].search(line):
689
+ lines[i] = regexes["question"].sub("", line, 1)
690
+ element[1] = SEP.join([x.strip() for x in lines if x.strip()])
691
+ else:
692
+ before_replacement = element[1]
693
+ element[1] = regexes[element[0]].sub("", element[1], 1)
514
694
  if element[1].startswith(SEP):
515
695
  element[1] = element[1][len(SEP) :]
696
+ # TODO: переделать корявую обработку авторки на нормальную
516
697
  if element[0] == "author" and "авторка:" in before_replacement.lower():
517
698
  element[1] = "!!Авторка" + element[1]
518
699
 
@@ -529,7 +710,7 @@ class ChgkParser:
529
710
  try:
530
711
  num = regexes["question"].search(element[1])
531
712
  if num:
532
- self.structure.insert(_id, ["number", num.group(1)])
713
+ self.structure.insert(_id, ["number", num.group("number")])
533
714
  except Exception as e:
534
715
  sys.stderr.write(
535
716
  f"exception at line 470 of parser: {type(e)} {e}\n"
@@ -537,7 +718,6 @@ class ChgkParser:
537
718
  element[1] = regexes["question"].sub("", element[1])
538
719
 
539
720
  # detect inner lists
540
-
541
721
  mo = {
542
722
  m for m in re.finditer(r"(\s+|^)(\d+)[\.\)]\s*(?!\d)", element[1], re.U)
543
723
  }
@@ -608,11 +788,13 @@ class ChgkParser:
608
788
 
609
789
  for element in self.structure:
610
790
  if (
611
- element[0] in set(["number", "tour", "tourrev", "question", "meta", "editor"])
791
+ element[0]
792
+ in set(["number", "tour", "tourrev", "question", "meta", "editor"])
612
793
  and "question" in current_question
613
794
  ):
614
795
  if self.defaultauthor and "author" not in current_question:
615
796
  current_question["author"] = self.defaultauthor
797
+ self._process_question_images(current_question)
616
798
  check_question(current_question, logger=logger)
617
799
  final_structure.append(["Question", current_question])
618
800
  current_question = {}
@@ -646,6 +828,7 @@ class ChgkParser:
646
828
  if current_question != {}:
647
829
  if self.defaultauthor and "author" not in current_question:
648
830
  current_question["author"] = self.defaultauthor
831
+ self._process_question_images(current_question)
649
832
  check_question(current_question, logger=logger)
650
833
  final_structure.append(["Question", current_question])
651
834
 
@@ -682,9 +865,18 @@ class ChgkParser:
682
865
  except ValueError:
683
866
  pass
684
867
 
868
+ tour_cnt = 0
685
869
  for i, element in enumerate(final_structure):
686
870
  if element[0] == "Question":
687
871
  self.postprocess_question(element[1])
872
+ elif element[0] == "tour" and self.args.tour_numbers_as_words == "on":
873
+ element[1] = f"{self.TOUR_NUMBERS_AS_WORDS[tour_cnt]} тур"
874
+ tour_cnt += 1
875
+ elif element[0] not in ["Question", "source"] and getattr(
876
+ self.args, "download_images", False
877
+ ):
878
+ # Process images in metadata fields (excluding source)
879
+ element[1] = self._process_images_in_text(element[1])
688
880
 
689
881
  if debug:
690
882
  with codecs.open("debug_final.json", "w", "utf8") as f:
@@ -694,7 +886,8 @@ class ChgkParser:
694
886
 
695
887
  def chgk_parse(text, defaultauthor=None, args=None):
696
888
  parser = ChgkParser(defaultauthor=defaultauthor, args=args)
697
- return parser.parse(text)
889
+ parsed = parser.parse(text)
890
+ return parsed
698
891
 
699
892
 
700
893
  class UnknownEncodingException(Exception):
@@ -740,7 +933,7 @@ def ensure_line_breaks(tag):
740
933
 
741
934
  def chgk_parse_docx(docxfile, defaultauthor="", args=None, logger=None):
742
935
  logger = logger or DummyLogger()
743
- args = args or DefaultArgs()
936
+ args = args or DefaultNamespace()
744
937
  for_ol = {}
745
938
 
746
939
  def get_number(tag):
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?Т[Уу][Рр]\\s?№?\\s?([0-9IVXLCDM]*)([\\.:])?$",
3
3
  "tourrev": "^([0-9IVXLCDM]+)\\s[Тт][Уу][Рр]([\\.:])?$",
4
- "question": "П[Ыы][Тт][Аа][Нн][Ьь]?[Нн][Ее]\\s?[№N]?([0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
4
+ "question": "П[Ыы][Тт][Аа][Нн][Ьь]?[Нн][Ее]\\s?[№N]?(?P<number>[0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
5
5
  "handout": "Р[Аа][Зз][Дд][Аа][Тт]([Аа][Чч][Нн]|[Кк][Аа][Вв])[Ыы][\\s\\s][Мм][Аа][Тт][Ээ][Рр][Ыы][Яя][Лл][\\.:]",
6
6
  "answer": "А[Дд][Кк][Аа][Зз][Ыы]?\\s?[№N]?([0-9]+)?\\s?[\\.:]",
7
7
  "zachet": "З[Аа][Лл][Іі][Кк]\\s?[\\.:]",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?(B[Ll][Oo][Cc][Kk]|R[Oo][Uu][Nn][Dd])\\s?№?\\s?([0-9IVXLCDM]*)([\\.:])?$",
3
3
  "tourrev": "^([0-9IVXLCDM]+)\\s(B[Ll][Oo][Cc][Kk]|R[Oo][Uu][Nn][Dd])([\\.:])?$",
4
- "question": "Q[Uu][Ee][Ss][Tt][Ii][Oo][Nn]\\s?[№N]?([0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
4
+ "question": "Q[Uu][Ee][Ss][Tt][Ii][Oo][Nn]\\s?[№N]?(?P<number>[0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
5
5
  "answer": "A[Nn][Ss][Ww][Ee][Rr]\\s?[№N]?([0-9]+)?\\s?[\\.:]",
6
6
  "handout": "^H[Aa][Nn][Dd][Oo][Uu][Tt][\\.:]",
7
7
  "zachet": "A[Cc][Ee][Pp][Tt]\\s?[\\.:]",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?([0-9]+)\\s?[-–—]\\s?[Тт][Уу][Рр]\\s*$",
3
- "question": "([0-9]+)\\s?[-–—]\\s?\\sС[Ұұ][Рр][Аа][Ққ]([\\.:]|\n|\r\n|$)",
3
+ "question": "(?P<number>[0-9]+)\\s?[-–—]\\s?\\sС[Ұұ][Рр][Аа][Ққ]([\\.:]|\n|\r\n|$)",
4
4
  "handout": "^Ү[Лл][Ее][Сс][Тт][Іі][Рр][Уу][\\s\\s][Мм][Аа][Тт][Ее][Рр][Ии][Аа][Лл][Ыы][\\.:]",
5
5
  "answer": "Ж[Аа][Уу][Аа][Пп]\\s?[№N]?([0-9]+)?\\s?[\\.:]",
6
6
  "zachet": "Қ[Аа][Бб][Ыы][Лл][Дд][Аа][Нн][Аа][Тт][Ыы][Нн]\\s[Жж][Аа][Уу][Аа][Пп](\\([Тт][Аа][Рр]\\))?\\s?[\\.:]",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?Т[Уу][Рр]\\s?№?\\s?([0-9IVXLCDM]*)([\\.:])?$",
3
- "tourrev": "^([0-9IVXLCDM]+)\\s[Тт][Уу][Рр]([\\.:])?$",
4
- "question": "В[Оо][Пп][Рр][Оо][Сс]\\s?[№N]?([0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
3
+ "tourrev": "^([0-9IVXLCDM]+|[Пп][Ее][Рр][Вв][Ыы][Йй]|[Вв][Тт][Оо][Рр][Оо][Йй]|[Тт][Рр][Ее][Тт][Ии][Йй]|[Чч][Ее][Тт][Вв][Ее][Рр][Тт][Ыы][Йй]|[Пп][Яя][Тт][Ыы][Йй]|[Шш][Ее][Сс][Тт][Оо][Йй]|[Сс][Ее][Дд][Ьь][Мм][Оо][Йй]|[Вв][Оо][Сс][Ьь][Мм][Оо][Йй]|[Дд][Ее][Вв][Яя][Тт][Ыы][Йй]|[Дд][Ее][Сс][Яя][Тт][Ыы][Йй])\\s[Тт][Уу][Рр]([\\.:])?$",
4
+ "question": "^([Нн][Уу][Лл][Ее][Вв][Оо][Йй]|[Рр][Аа][Зз][Мм][Ии][Нн][Оо][Чч][Нн][Ыы][Йй])? ?[Вв][Оо][Пп][Рр][Оо][Сс]\\s?[№N]?(?P<number>[0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
5
5
  "handout": "^Р[Аа][Зз][Дд][Аа][Тт][Оо][Чч][Нн][Ыы][Йй]\\s+[Мм][Аа][Тт][Ее][Рр][Ии][Аа][Лл][\\.:]",
6
6
  "answer": "О[Тт][Вв][Ее][Тт][Ыы]?\\s?[№N]?([0-9]+)?\\s?[\\.:]",
7
7
  "zachet": "З[Аа][Чч][ЕеЁё][Тт]\\s?[\\.:]",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?R[Uu][Nn][Dd][Aa]\\s?№?\\s?([0-9IVXLCDM]*)([\\.:])?$",
3
3
  "tourrev": "^([0-9IVXLCDM]+)\\sR[Uu][Nn][Dd][Aa]([\\.:])?$",
4
- "question": "P[Ii][Tt][Aa][Nn][Jj][Ee]\\s?[№N]?([0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
4
+ "question": "P[Ii][Tt][Aa][Nn][Jj][Ee]\\s?[№N]?(?P<number>[0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
5
5
  "answer": "O[Dd][Gg][Oo][Vv][Oo][Rr]\\s?[№N]?([0-9]+)?\\s?[\\.:]",
6
6
  "handout": "^(M[Aa][Tt][Ee][Rr][Ii][Jj][Aa][Ll]|P[Oo][Dd][Ee][Ll][Jj][Ee][Nn][Oo])[\\.:]",
7
7
  "zachet": "P[Rr][Ii][Hh][Vv][Aa][Tt][Aa]\\s[Ss][Ee]\\s?[\\.:]",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?Т[Уу][Рр]\\s?№?\\s?([0-9IVXLCDM]*)([\\.:])?$",
3
3
  "tourrev": "^([0-9IVXLCDM]+)\\s[Тт][Уу][Рр]([\\.:])?$",
4
- "question": "З[Аа][Пп][Ии][Тт][Аа][Нн][Нн][Яя]\\s?[№N]?([0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
4
+ "question": "З[Аа][Пп][Ии][Тт][Аа][Нн][Нн][Яя]\\s?[№N]?(?P<number>[0-9\\s]*)\\s?([\\.:]|\n|\r\n|$)",
5
5
  "handout": "Р[Оо][Зз][Дд][Аа][Тт][Кк][Оо][Вв][Ии][Йй][\\s\\s][Мм][Аа][Тт][Ее][Рр][Іі][Аа][Лл][\\.:]",
6
6
  "answer": "В[Іі][Дд][Пп][Оо][Вв][Іі][Дд][Ьь]?\\s?[№N]?([0-9]+)?\\s?[\\.:]",
7
7
  "zachet": "З[Аа][Лл][Іі][Кк]\\s?[\\.:]",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "tour": "^(#\\s+)?([0-9]+)\\s?[-–—]\\s?[Тт][Уу][Рр]\\s*$",
3
- "question": "([0-9]+)\\s?[-–—]\\s?\\sС[Аа][Вв][Оо][Лл]([\\.:]|\n|\r\n|$)",
3
+ "question": "(?P<number>[0-9]+)\\s?[-–—]\\s?\\sС[Аа][Вв][Оо][Лл]([\\.:]|\n|\r\n|$)",
4
4
  "answer": "Ж[Аа][Вв][Оо][Бб]\\s?[№N]?([0-9]+)?\\s?[\\.:]",
5
5
  "handout": "Т[Аа][Рр][Ққ][Аа][Тт][Мм][Аа][\\s\\s][Мм][Аа][Тт][Ее][Рр][Ии][Аа][Лл][\\.:]",
6
6
  "zachet": "Қ[Аа][Бб][Уу][Лл]\\s?[\\.:]",
chgksuite/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.25.1"
1
+ __version__ = "0.26.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chgksuite
3
- Version: 0.25.1
3
+ Version: 0.26.0
4
4
  Summary: A package for chgk automation
5
5
  Home-page: https://gitlab.com/peczony/chgksuite
6
6
  Author: Alexander Pecheny
@@ -20,12 +20,14 @@ Requires-Dist: parse
20
20
  Requires-Dist: Pillow
21
21
  Requires-Dist: ply
22
22
  Requires-Dist: pypandoc
23
+ Requires-Dist: pypdf
23
24
  Requires-Dist: pyperclip
24
25
  Requires-Dist: python-docx
25
26
  Requires-Dist: python-pptx
26
- Requires-Dist: requests
27
27
  Requires-Dist: python-telegram-bot
28
+ Requires-Dist: requests
28
29
  Requires-Dist: toml
30
+ Requires-Dist: watchdog
29
31
  Dynamic: author
30
32
  Dynamic: author-email
31
33
  Dynamic: classifier
@@ -1,27 +1,34 @@
1
1
  chgksuite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  chgksuite/__main__.py,sha256=0-_jfloveTW3SZYW5XEagbyaHKGCiDhGNgcLxsT_dMs,140
3
- chgksuite/cli.py,sha256=8YoHWfc-wv6gdYMPH3X8HxV9SG70S7M6IQqavJaBXV4,31990
3
+ chgksuite/cli.py,sha256=iZGpx4-_vTxdKsKsRXCu7vjpiFhR_oyhLRGXyvGl7i8,40901
4
4
  chgksuite/common.py,sha256=VkOhoBA_P3qY5VgtvfrBjOsm5uVNL2s2Th2AhGB2pg8,11207
5
- chgksuite/parser.py,sha256=AVNeTUgv0aGHsxWunV_7bM4Ul2hbDslkzqg0dU49lic,37285
5
+ chgksuite/parser.py,sha256=n-NAYRdxHwgvc-4oSpoLGQW6CYniQKLo4iR6PGaSdPw,44962
6
6
  chgksuite/parser_db.py,sha256=W1--OcDnx18mehH1T2ISHu3Saeq-9mqHo-xJopNySXI,11135
7
7
  chgksuite/trello.py,sha256=BG1Qb_W7Uu4o3Mfc_tK71ElU8ysdSplGlj_sAKfvUn4,14730
8
8
  chgksuite/typotools.py,sha256=Jdk65Wn_bXqpQtOT7PkBZyD2ZG1MBeeZFPMzcHEPkf4,12771
9
- chgksuite/version.py,sha256=ACu2Z3Q3TFgYpAno_eu9ssJ1QULjNXvjGvyqDSHrQ_o,23
9
+ chgksuite/version.py,sha256=S1dNBNSmHJkAHkq1vL1Yg-dFpQk0fri_CstNlL1u5qw,23
10
10
  chgksuite/vulture_whitelist.py,sha256=P__p_X0zt10ivddIf81uyxsobV14vFg8uS2lt4foYpc,3582
11
11
  chgksuite/composer/__init__.py,sha256=MAOVZIYXmZmD6nNQSo9DueV6b5RgxF7_HGeLvsAhMJs,6490
12
12
  chgksuite/composer/chgksuite_parser.py,sha256=MFcLUWbccMqo3OYEuaAIA0loEvWM_PNS9vR7c1z_c60,8843
13
- chgksuite/composer/composer_common.py,sha256=S5ipehxep6LlGIZ9dcBnifbVaMYXijMhq6-pUHRISo8,15309
13
+ chgksuite/composer/composer_common.py,sha256=f48tUeZH5UIZxBaODcqhHGwmIdEGpMVP8lxMcl4rwTQ,15433
14
14
  chgksuite/composer/db.py,sha256=71cINE_V8s6YidvqpmBmmlWbcXraUEGZA1xpVFAUENw,8173
15
- chgksuite/composer/docx.py,sha256=5MASXACM-ztWrr3VdO8HZ-W-hWWQ5TY1jXMsCQIufGc,18346
15
+ chgksuite/composer/docx.py,sha256=kJMpWHFyxIZJSA9Ul_2xRsWZnuVkjfko5D08Xi45HiU,23059
16
16
  chgksuite/composer/latex.py,sha256=WtLdUICxeX4_5vHEJRF0YhFLpTsOUwBkQFunQS488FA,9248
17
17
  chgksuite/composer/lj.py,sha256=nty3Zs3N1H0gNK378U04aAHo71_5cABhCM1Mm9jiUEA,15213
18
18
  chgksuite/composer/openquiz.py,sha256=4adZewvRXpZhKrh9H4epKoMMDhmki9US55-Q0LcpZW0,7019
19
- chgksuite/composer/pptx.py,sha256=BriA-tJyfQwJJtxuq6ppUwdTHtJVlsh_x4Nfo00bBv4,23079
19
+ chgksuite/composer/pptx.py,sha256=KszvRAbSbKmbPS257YcYhE6wB1gSf7gIXRH8VwiSqyg,23775
20
20
  chgksuite/composer/reddit.py,sha256=-Eg4CqMHhyGGfCteVwdQdtE1pfUXQ42XcP5OYUrBXmo,3878
21
21
  chgksuite/composer/stats.py,sha256=GbraSrjaZ8Mc2URs5aGAsI4ekboAKzlJJOqsbe96ELA,3995
22
- chgksuite/composer/telegram.py,sha256=sVbJFdHTprlDEASvQlOdXgd7tV7xGUOzjCPk1lnBqko,46579
22
+ chgksuite/composer/telegram.py,sha256=ZiCBFXxKJJ5vD5ttkfvmURd4Q9Gif6eT8R93nz-LAq0,47146
23
23
  chgksuite/composer/telegram_bot.py,sha256=xT5D39m4zGmIbHV_ZfyQ9Rc8PAmG2V5FGUeDKpkgyTw,3767
24
24
  chgksuite/composer/telegram_parser.py,sha256=50WqOuvzzdMJJm5wsSLS49oURAQRYToPnbPJjQbMYC4,8096
25
+ chgksuite/handouter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ chgksuite/handouter/gen.py,sha256=Rnrcg1APBt35Joloce-q3gA4OJUXudH8snbGLSmVXJw,5045
27
+ chgksuite/handouter/installer.py,sha256=u4jQKeCn0VjOnaDFezx35g8oRjji5edvYGj5xSHCEW4,7574
28
+ chgksuite/handouter/pack.py,sha256=H-Ln1JqKK2u3jFI5wwsh7pQdJBpQJ-s8gV9iECQ3kgU,2504
29
+ chgksuite/handouter/runner.py,sha256=SHxGCAbQ9s6h23IaqNhJtWN0VMjM9hwy6oIgt0-WuD0,8380
30
+ chgksuite/handouter/tex_internals.py,sha256=GtcoGd1FD4Oi9nnMzb2KcdCNIHNQV1fcKmQyW9AXA1k,1394
31
+ chgksuite/handouter/utils.py,sha256=0RoECvHzfmwWnRL2jj4WKh22oTCPh2MXid_a9ShplDA,2243
25
32
  chgksuite/resources/cheader.tex,sha256=Jfe3LESk0VIV0HCObbajSQpEMljaIDAIEGSs6YY9rTk,3454
26
33
  chgksuite/resources/fix-unnumbered-sections.sty,sha256=FN6ZSWC6MvoRoThPm5AxCF98DdgcxbxyBYG6YImM05s,1409
27
34
  chgksuite/resources/labels_by.toml,sha256=kSbIYBnxneRJAcDmeoFGlPpjogWuoMO11302p0BxB-s,824
@@ -34,20 +41,19 @@ chgksuite/resources/labels_ua.toml,sha256=sUZdwwpkddb_56bmrZyO56cDrm68ft_JoMqR38
34
41
  chgksuite/resources/labels_uz.toml,sha256=uRnf6tjBf1rYpWjt675ecB2MPn_Ohz-Hj6XDYNPpe3o,609
35
42
  chgksuite/resources/labels_uz_cyr.toml,sha256=ztqHItwhJjfBTOIj_vA1hrneYo8s5gvZkkSr0k9S3rs,814
36
43
  chgksuite/resources/pptx_config.toml,sha256=pbdQpZTBZajfXxf9Ej6jFQv8luA9_h2Lsihx9hs2jkI,570
37
- chgksuite/resources/regexes_by.json,sha256=43K_jjNocNrx75OcSlVxg2zHNJKoHwzceFbtfZgM0Vo,1296
38
- chgksuite/resources/regexes_en.json,sha256=v1RPW8Oj0UiS_86ieYjpFOGm3qN5-OlM-qgAqa6oCy4,1003
39
- chgksuite/resources/regexes_kz_cyr.json,sha256=QW6MxkPjSRNU-KPAAqh4jwFZ--ckJo9EOn_p30gihiQ,1679
40
- chgksuite/resources/regexes_ru.json,sha256=_ZgSG31eV348eIYgmcEWvygKL3cWZeaauXsBWyrCWaM,1652
41
- chgksuite/resources/regexes_sr.json,sha256=SiDDtxWND41XOBvHM4MgSXcogh7bRTqsY3JbJPT2wF4,925
42
- chgksuite/resources/regexes_ua.json,sha256=IXtJtoUY15OoBu5Y5yW6adhJUhsDRkwfxWsPYIrbgPA,1330
43
- chgksuite/resources/regexes_uz_cyr.json,sha256=mIvWjyZZXQ3lr_ntuC7-ybknnYJxSSQEKxqnGluTvow,1106
44
+ chgksuite/resources/regexes_by.json,sha256=ps_dLYxgPnZRCUkLo_ti1tsZAWZNIf48UobRvQ88z6E,1306
45
+ chgksuite/resources/regexes_en.json,sha256=XdpKRlT2azLglFeXRKv3BdZKHDem2qXFbwRixiUVmbU,1013
46
+ chgksuite/resources/regexes_kz_cyr.json,sha256=wkkf_Gll07i9V1QLdexKG2DjxAhV9ZZOWMjvbX7Tjvg,1689
47
+ chgksuite/resources/regexes_ru.json,sha256=3OrNLq6LkIB2rVErPRYYHrljrKK-mOdna1zfXCaIUK0,2187
48
+ chgksuite/resources/regexes_sr.json,sha256=S8IRDUh3E4y3G8Cavb4F6gPDfHzOk2HH3RXy_D32RFw,935
49
+ chgksuite/resources/regexes_ua.json,sha256=vLUP9nRsVB7nPk4Ea3CpC7zcg57N62Uf4A24YJjRMFA,1340
50
+ chgksuite/resources/regexes_uz_cyr.json,sha256=GlGkA6ys2e1-9xPcgOL8Sy9ZuDZHQZXp_p_MvGJirMU,1116
44
51
  chgksuite/resources/template.docx,sha256=Do29TAsg3YbH0rRSaXhVzKEoh4pwXkklW_idWA34HVE,11189
45
52
  chgksuite/resources/template.pptx,sha256=hEFWqE-yYpwZ8ejrMCJIPEyoMT3eDqaqtiEeQ7I4fyk,29777
46
- chgksuite/resources/template_shorin.pptx,sha256=hEFWqE-yYpwZ8ejrMCJIPEyoMT3eDqaqtiEeQ7I4fyk,29777
47
53
  chgksuite/resources/trello.json,sha256=M5Q9JR-AAJF1u16YtNAxDX-7c7VoVTXuq4POTqYvq8o,555
48
- chgksuite-0.25.1.dist-info/licenses/LICENSE,sha256=_a1yfntuPmctLsuiE_08xMSORuCfGS8X5hQph2U_PUw,1081
49
- chgksuite-0.25.1.dist-info/METADATA,sha256=Y7IyImZaA6Vod1uZThExsHg8X5jvsS37FJ6AFD9_qx8,1294
50
- chgksuite-0.25.1.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
51
- chgksuite-0.25.1.dist-info/entry_points.txt,sha256=lqjX6ULQZGDt0rgouTXBuwEPiwKkDQkSiNsT877A_Jg,54
52
- chgksuite-0.25.1.dist-info/top_level.txt,sha256=cSWiRBOGZW9nIO6Rv1IrEfwPgV2ZWs87QV9wPXeBGqM,10
53
- chgksuite-0.25.1.dist-info/RECORD,,
54
+ chgksuite-0.26.0.dist-info/licenses/LICENSE,sha256=_a1yfntuPmctLsuiE_08xMSORuCfGS8X5hQph2U_PUw,1081
55
+ chgksuite-0.26.0.dist-info/METADATA,sha256=hi39ekThVjt85G9FFedbR1QdYk6pqEE0_oSbt-I-G5Y,1339
56
+ chgksuite-0.26.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ chgksuite-0.26.0.dist-info/entry_points.txt,sha256=lqjX6ULQZGDt0rgouTXBuwEPiwKkDQkSiNsT877A_Jg,54
58
+ chgksuite-0.26.0.dist-info/top_level.txt,sha256=cSWiRBOGZW9nIO6Rv1IrEfwPgV2ZWs87QV9wPXeBGqM,10
59
+ chgksuite-0.26.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
Binary file