You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
10 KiB

8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
5 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
7 months ago
8 months ago
8 months ago
5 months ago
8 months ago
8 months ago
5 months ago
8 months ago
8 months ago
8 months ago
5 months ago
8 months ago
5 months ago
  1. #!/usr/bin/python3
  2. import pyamf
  3. import shutil
  4. import json
  5. import os
  6. import argparse
  7. import unicodedata
  8. import random
  9. import re
  10. import sys
  11. import readline
  12. DATA_DIR = os.environ.get('XDG_DATA_HOME', os.environ['HOME'] + '/.local/share') + '/klette'
  13. VOKABELN_FILE = DATA_DIR + '/vokabeln.json'
  14. STATUS_FILE = DATA_DIR + '/status.json'
  15. AUDIO_BASE = DATA_DIR + '/audio/'
  16. class bcolors:
  17. HEADER = '\033[95m'
  18. OKBLUE = '\033[94m'
  19. OKCYAN = '\033[96m'
  20. OKGREEN = '\033[92m'
  21. WARNING = '\033[93m'
  22. FAIL = '\033[91m'
  23. ENDC = '\033[0m'
  24. BOLD = '\033[1m'
  25. UNDERLINE = '\033[4m'
  26. LINK_START = '\033]8;;'
  27. LINK_MIDDLE = '\033\\'
  28. LINK_END = '\033]8;;\033\\'
  29. def import_vokabeln(file_name, audio_base):
  30. os.makedirs(AUDIO_BASE, exist_ok=True)
  31. palabras = []
  32. with open(file_name, 'rb') as reader:
  33. obj = pyamf.decode(reader.read())
  34. for unidad_data in obj.readElement()['lektionList']:
  35. unidad = unidad_data['name']
  36. for paso_data in unidad_data['lektionsTeilList']:
  37. paso = paso_data['name']
  38. for palabra in paso_data['entryList']:
  39. audio = palabra['vorderSeite']['swTonAbgleichwort']
  40. palabras.append({
  41. 'id': palabra['matId'],
  42. 'de': palabra['rueckSeite']['text'],
  43. 'es': palabra['vorderSeite']['text'],
  44. 'audio': audio,
  45. 'unidad': unidad,
  46. 'paso': paso
  47. })
  48. shutil.copy2(audio_base + audio + '.bin',
  49. AUDIO_BASE + audio + '.aac')
  50. with open(VOKABELN_FILE, 'x') as writer:
  51. writer.write(json.dumps(palabras))
  52. def import_status(file_name):
  53. os.makedirs(DATA_DIR, exist_ok=True)
  54. statuses = {}
  55. with open(file_name, 'rb') as reader:
  56. obj = pyamf.decode(reader.read()).readElement()[0]
  57. for idx in obj:
  58. item = obj[idx]
  59. statuses[item['matId']] = item['statusWortschatz']
  60. with open(STATUS_FILE, 'x') as writer:
  61. writer.write(json.dumps(statuses))
  62. def vergleiche(respuesta, expecta):
  63. if respuesta == "": return 4
  64. if respuesta[0] == '!' and expecta[0] == '¡':
  65. respuesta = '¡' + respuesta[1:]
  66. if respuesta[0] == '?' and expecta[0] == '¿':
  67. respuesta = '¿' + respuesta[1:]
  68. respuesta = re.sub('(~n|n~)', 'ñ', respuesta)
  69. expecta = re.sub(' \((irr.|pl.)\)', '', expecta)
  70. respuesta = unicodedata.normalize('NFD', respuesta)
  71. expecta = unicodedata.normalize('NFD', expecta)
  72. respuesta = re.sub(' ?([!?¿¡….]) ?', r'\1', respuesta)
  73. expecta = re.sub(' ?([!?¿¡….]) ?', r'\1', expecta)
  74. respuesta = re.sub(r'\.\.\.', '', respuesta)
  75. expecta = re.sub(r'\.\.\.', '', expecta)
  76. if respuesta == expecta: return 0
  77. respuesta = re.sub('[!?¿¡…. ]', '', respuesta)
  78. expecta = re.sub('[!?¿¡…. ]', '', expecta)
  79. if respuesta == expecta: return 1
  80. respuesta = respuesta.encode('ASCII', 'ignore').decode('ASCII')
  81. expecta = expecta.encode('ASCII', 'ignore').decode('ASCII')
  82. if respuesta == expecta: return 2
  83. respuesta = respuesta.casefold()
  84. expecta = expecta.casefold()
  85. if respuesta == expecta: return 3
  86. return 4
  87. assert vergleiche("e", "e") == 0
  88. assert vergleiche("é", "e") == 2
  89. assert vergleiche("Bien", "bien") == 3
  90. assert vergleiche("quétal", "Qué tal") == 3
  91. assert vergleiche("Buenas noches", "¡Buenas noches!") == 1
  92. assert vergleiche("ser", "ser (irr.)") == 0
  93. assert vergleiche("hacer algo", "hacer (irr.) algo") == 0
  94. assert vergleiche("padres", "padres (pl.)") == 0
  95. assert vergleiche("¿De dónde …?", "¿De dónde… ?") == 0
  96. assert vergleiche("el nombre", "el apellido") == 4
  97. assert vergleiche("Tengo … a~nos.", "Tengo... años.") == 0
  98. assert vergleiche("¿Cuántos a~nos tienes?", "¿Cuántos años tienes?") == 0
  99. class Resultado:
  100. CORRECTO = 0
  101. BIEN = 1
  102. NO_BIEN = 2
  103. MAL = 3
  104. ADIOS = 4
  105. def einzelne_abfrage(palabra, status):
  106. if status == None:
  107. status_text = f"{bcolors.OKCYAN}neu{bcolors.ENDC}"
  108. status = 0
  109. else:
  110. status_text = f"({status})"
  111. try:
  112. respuesta = input(f"{status_text} {bcolors.BOLD}{palabra['de']}{bcolors.ENDC} {bcolors.OKCYAN}")
  113. except EOFError:
  114. print()
  115. return Resultado.ADIOS
  116. finally:
  117. print(bcolors.ENDC, end="")
  118. print('\033[{}C\033[1A'.format(4 + len(palabra['de']) + 1 + len(respuesta)), end="")
  119. resultado = vergleiche(respuesta, palabra['es'])
  120. if resultado == 0:
  121. bien = Resultado.CORRECTO
  122. else:
  123. match status:
  124. case 0: bien = Resultado.BIEN if resultado < 4 else Resultado.NO_BIEN
  125. case 1: bien = Resultado.BIEN if resultado < 3 else (Resultado.NO_BIEN if resultado < 4 else Resultado.MAL)
  126. case 2: bien = Resultado.BIEN if resultado < 2 else (Resultado.NO_BIEN if resultado < 3 else Resultado.MAL)
  127. case 3: bien = Resultado.BIEN if resultado < 2 else Resultado.MAL
  128. case 4: bien = Resultado.BIEN if resultado < 1 else Resultado.MAL
  129. if 'audio' in palabra:
  130. print(f" {bcolors.LINK_START}file://{AUDIO_BASE}{palabra['audio']}.aac{bcolors.LINK_MIDDLE}",end="")
  131. else:
  132. print(" ",end="")
  133. if bien == Resultado.CORRECTO:
  134. print(f"{bcolors.OKGREEN}✓{bcolors.ENDC}",end="")
  135. elif bien == Resultado.BIEN:
  136. print(f"{bcolors.WARNING}{palabra['es']}{bcolors.ENDC}",end="")
  137. else:
  138. print(f"{bcolors.FAIL}{palabra['es']}{bcolors.ENDC}",end="")
  139. if 'audio' in palabra:
  140. print(bcolors.LINK_END,end="")
  141. try:
  142. if input(" ") == "+":
  143. bien = Resultado.CORRECTO
  144. except EOFError:
  145. print()
  146. return Resultado.ADIOS
  147. return bien
  148. class Sesion:
  149. def __init__(self, palabras, status):
  150. self.palabras = palabras
  151. self.status = status
  152. self.bien = 0
  153. self.mal = 0
  154. def otra_vez(self, quiero_unidad):
  155. unidad = False
  156. unidad_no = -1
  157. paso = False
  158. cur_palabras = False
  159. for palabra in self.palabras:
  160. if unidad != palabra['unidad']:
  161. unidad_no += 1
  162. unidad = palabra['unidad']
  163. if quiero_unidad == None or quiero_unidad == unidad_no:
  164. if self.status.get(palabra['id'], None) == 5:
  165. self.status[palabra['id']] = 4
  166. def empezar(self, quiero_unidad):
  167. unidad = False
  168. unidad_no = -1
  169. paso = False
  170. cur_palabras = False
  171. for palabra in self.palabras:
  172. if paso != palabra['paso']:
  173. if cur_palabras != False and (quiero_unidad == None or quiero_unidad == unidad_no):
  174. if self.hace_paso(unidad, paso, cur_palabras) == Resultado.ADIOS:
  175. return
  176. cur_palabras = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], None: []}
  177. if unidad != palabra['unidad']:
  178. unidad_no += 1
  179. unidad = palabra['unidad']
  180. paso = palabra['paso']
  181. cur_palabras[self.status.get(palabra['id'])].append(palabra)
  182. if cur_palabras != False and (quiero_unidad == None or quiero_unidad == unidad_no):
  183. if self.hace_paso(unidad, paso, cur_palabras) == Resultado.ADIOS:
  184. return
  185. def hace_paso(self, unidad, paso, cur_palabras):
  186. c = [len(cur_palabras[x]) for x in cur_palabras]
  187. print(f"{bcolors.BOLD}{unidad}{bcolors.ENDC}: {paso} ({c[0] + c[6]}/{c[1]}/{c[2]}/{c[3]}/{c[4]}/{bcolors.OKGREEN}{c[5]}{bcolors.ENDC})")
  188. for n in range(5): # 1..4, no 5
  189. if self.hace_palabras(cur_palabras[n], n) == Resultado.ADIOS:
  190. return Resultado.ADIOS
  191. n = None
  192. if self.hace_palabras(cur_palabras[n], n) == Resultado.ADIOS:
  193. return Resultado.ADIOS
  194. def hace_palabras(self, palabras, status):
  195. random.shuffle(palabras)
  196. for palabra in palabras:
  197. match einzelne_abfrage(palabra, status):
  198. case Resultado.CORRECTO:
  199. self.status[palabra['id']] = (status or 0) + (2 if (status or 0) < 2 else 1)
  200. self.bien += 1
  201. case Resultado.BIEN:
  202. self.status[palabra['id']] = (status or 0) + 1
  203. self.bien += 1
  204. case Resultado.NO_BIEN:
  205. continue
  206. case Resultado.MAL:
  207. self.status[palabra['id']] = status - 1
  208. self.mal += 1
  209. case Resultado.ADIOS:
  210. return Resultado.ADIOS
  211. with open(STATUS_FILE + '.new', 'w') as f:
  212. json.dump(self.status, f)
  213. f.flush()
  214. os.fsync(f.fileno())
  215. with open(STATUS_FILE + '.new', 'r') as f:
  216. pass
  217. os.replace(STATUS_FILE + '.new', STATUS_FILE)
  218. def abfrage(parser, quiero_unidad, otra_vez):
  219. random.seed()
  220. try:
  221. with open(VOKABELN_FILE, 'r') as f:
  222. palabras = json.load(f)
  223. with open(STATUS_FILE, 'r') as f:
  224. status = json.load(f)
  225. except FileNotFoundError:
  226. print(f"{bcolors.FAIL}Daten können nicht geladen werden, hast du sie schon importiert?{bcolors.ENDC}")
  227. print()
  228. parser.print_help()
  229. return
  230. sesion = Sesion(palabras, status)
  231. if otra_vez:
  232. sesion.otra_vez(quiero_unidad)
  233. sesion.empezar(quiero_unidad)
  234. print(f'{bcolors.OKGREEN}+{sesion.bien}{bcolors.ENDC} / {bcolors.FAIL}-{sesion.mal}{bcolors.ENDC}')
  235. parser = argparse.ArgumentParser()
  236. default_data_file = os.environ['PWD'] + '/assets/amf/vokabelTrainer147.amf'
  237. parser.add_argument('--import-data', type=str, help="Path to assets", metavar="DIR")
  238. default_status_file = os.environ['HOME'] + '/klett/1266/vokabeltrainerData147'
  239. parser.add_argument('--import-status', type=str, help="Path to AMF File, defaults to " + default_status_file, metavar="FILE", nargs='?', const=default_status_file)
  240. parser.add_argument('--unidad', type=int)
  241. parser.add_argument('--otra-vez', action='store_true')
  242. args = parser.parse_args()
  243. if args.import_data:
  244. import_vokabeln(args.import_data + '/amf/vokabelTrainer147.amf',
  245. args.import_data + '/amf/medium/')
  246. elif args.import_status:
  247. import_status(args.import_status)
  248. else:
  249. abfrage(parser, args.unidad, args.otra_vez)