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.

207 lines
7.7 KiB

8 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
8 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
8 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
8 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['XDG_DATA_HOME'] + '/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. expecta = re.sub(' \((irr.|pl.)\)', '', expecta)
  69. respuesta = unicodedata.normalize('NFD', respuesta)
  70. expecta = unicodedata.normalize('NFD', expecta)
  71. if respuesta == expecta: return 0
  72. respuesta = re.sub('[!?¿¡…. ]', '', respuesta)
  73. expecta = re.sub('[!?¿¡…. ]', '', expecta)
  74. if respuesta == expecta: return 1
  75. respuesta = respuesta.encode('ASCII', 'ignore').decode('ASCII')
  76. expecta = expecta.encode('ASCII', 'ignore').decode('ASCII')
  77. if respuesta == expecta: return 2
  78. respuesta = respuesta.casefold()
  79. expecta = expecta.casefold()
  80. if respuesta == expecta: return 3
  81. return 4
  82. assert vergleiche("e", "e") == 0
  83. assert vergleiche("é", "e") == 2
  84. assert vergleiche("Bien", "bien") == 3
  85. assert vergleiche("quétal", "Qué tal") == 3
  86. assert vergleiche("Buenas noches", "¡Buenas noches!") == 1
  87. assert vergleiche("ser", "ser (irr.)") == 0
  88. assert vergleiche("hacer algo", "hacer (irr.) algo") == 0
  89. assert vergleiche("padres", "padres (pl.)") == 0
  90. class Resultado:
  91. BIEN = 1
  92. MAL = 2
  93. ADIOS = 3
  94. def einzelne_abfrage(palabra, status):
  95. status_text = f"({status})" if status > 0 else f"{bcolors.OKGREEN}neu{bcolors.ENDC}"
  96. try:
  97. respuesta = input(f"{status_text} {bcolors.BOLD}{palabra['de']}{bcolors.ENDC} {bcolors.OKCYAN}")
  98. except EOFError:
  99. print()
  100. return Resultado.ADIOS
  101. finally:
  102. print(bcolors.ENDC, end="")
  103. print('\033[{}C\033[1A'.format(4 + len(palabra['de']) + 1 + len(respuesta)), end="")
  104. resultado = vergleiche(respuesta, palabra['es'])
  105. match status:
  106. case 0: bien = resultado < 4
  107. case 1: bien = resultado < 3
  108. case 2: bien = resultado < 3
  109. case 3: bien = resultado < 2
  110. case 4: bien = resultado < 1
  111. print(f" {bcolors.LINK_START}file://{AUDIO_BASE}{palabra['audio']}.aac{bcolors.LINK_MIDDLE}",end="")
  112. if bien and resultado == 0:
  113. print(f"{bcolors.OKGREEN}✓{bcolors.ENDC}",end="")
  114. elif bien:
  115. print(f"{bcolors.WARNING}{palabra['es']}{bcolors.ENDC}",end="")
  116. else:
  117. print(f"{bcolors.FAIL}{palabra['es']}{bcolors.ENDC}",end="")
  118. print(bcolors.LINK_END,end="")
  119. try:
  120. input(" ")
  121. except EOFError:
  122. print()
  123. return Resultado.ADIOS
  124. return Resultado.BIEN if bien else Resultado.MAL
  125. def abfrage(parser, quiero_unidad):
  126. random.seed()
  127. try:
  128. with open(VOKABELN_FILE, 'r') as f:
  129. palabras = json.load(f)
  130. with open(STATUS_FILE, 'r') as f:
  131. status = json.load(f)
  132. except FileNotFoundError:
  133. print(f"{bcolors.FAIL}Daten können nicht geladen werden, hast du sie schon importiert?{bcolors.ENDC}")
  134. print()
  135. parser.print_help()
  136. return
  137. unidad = False
  138. unidad_no = -1
  139. paso = False
  140. cur_palabras = False
  141. bien = 0
  142. mal = 0
  143. for palabra in palabras:
  144. if paso != palabra['paso']:
  145. if cur_palabras != False and (quiero_unidad == None or quiero_unidad == unidad_no):
  146. c = [len(cur_palabras[x]) for x in cur_palabras]
  147. print(f"{bcolors.BOLD}{unidad}{bcolors.ENDC}: {paso} (",end="")
  148. print(*c, sep="/", end=")")
  149. print()
  150. for n in range(5): # 1..4, no 5
  151. random.shuffle(cur_palabras[n])
  152. for a_palabra in cur_palabras[n]:
  153. s = status.get(a_palabra['id'], 0)
  154. match einzelne_abfrage(a_palabra, s):
  155. case Resultado.BIEN:
  156. status[a_palabra['id']] = s + 1
  157. bien+=1
  158. case Resultado.MAL:
  159. if s > 0:
  160. status[a_palabra['id']] = s - 1
  161. mal+=1
  162. continue
  163. case Resultado.ADIOS:
  164. print(f'{bcolors.OKGREEN}+{bien}{bcolors.ENDC} / {bcolors.FAIL}-{mal}{bcolors.ENDC}')
  165. return
  166. with open(STATUS_FILE + '.new', 'w') as f:
  167. json.dump(status, f)
  168. f.flush()
  169. os.fsync(f.fileno())
  170. with open(STATUS_FILE + '.new', 'r') as f:
  171. status = json.load(f)
  172. os.replace(STATUS_FILE + '.new', STATUS_FILE)
  173. cur_palabras = {0: [], 1: [], 2: [], 3: [], 4: [], 5: []}
  174. if unidad != palabra['unidad']:
  175. unidad_no += 1
  176. unidad = palabra['unidad']
  177. paso = palabra['paso']
  178. cur_palabras[status.get(palabra['id'], 0)].append(palabra)
  179. parser = argparse.ArgumentParser()
  180. default_data_file = os.environ['PWD'] + '/assets/amf/vokabelTrainer147.amf'
  181. parser.add_argument('--import-data', type=str, help="Path to assets", metavar="DIR")
  182. default_status_file = os.environ['HOME'] + '/klett/1266/vokabeltrainerData147'
  183. parser.add_argument('--import-status', type=str, help="Path to AMF File, defaults to " + default_status_file, metavar="FILE", nargs='?', const=default_status_file)
  184. parser.add_argument('--unidad', type=int)
  185. args = parser.parse_args()
  186. if args.import_data:
  187. import_vokabeln(args.import_data + '/amf/vokabelTrainer147.amf',
  188. args.import_data + '/amf/medium/')
  189. elif args.import_status:
  190. import_status(args.import_status)
  191. else:
  192. abfrage(parser, args.unidad)