manage_data_callbacks.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. from dash.dependencies import Input, Output, State, ALL
  2. from dash import html, callback_context
  3. from utils.file_utils import list_files_in_directory, save_file, delete_file, is_valid_wav
  4. from components.hotwords import generate_training_data_table
  5. import os
  6. import base64
  7. TRAINING_HOTWORD_DIR = './training/hotword/'
  8. def register_manage_data_callbacks(app):
  9. @app.callback(
  10. Output('file-preview-list', 'children'),
  11. [
  12. Input('upload-training-data', 'contents'),
  13. Input({'type': 'delete-preview-file', 'index': ALL}, 'n_clicks')
  14. ],
  15. [
  16. State('upload-training-data', 'filename'),
  17. State('upload-training-data', 'last_modified'),
  18. State('file-preview-list', 'children')
  19. ]
  20. )
  21. def manage_file_previews(contents, n_clicks, filenames, last_modified, preview_list):
  22. ctx = callback_context
  23. if not ctx.triggered:
  24. return preview_list
  25. if preview_list is None:
  26. preview_list = []
  27. triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
  28. if 'upload-training-data' in triggered_id and contents:
  29. files = []
  30. for content, filename in zip(contents, filenames):
  31. files.append(html.Div([
  32. html.Audio(src=f'data:audio/wav;base64,{content.split(",")[1]}', controls=True),
  33. html.Button("Delete", id={'type': 'delete-preview-file', 'index': filename}),
  34. html.Div(content, style={'display': 'none'}, id={'type': 'file-content', 'index': filename})
  35. ], id={'type': 'preview-file', 'index': filename}))
  36. return preview_list + files
  37. if 'delete-preview-file' in triggered_id:
  38. filename_to_delete = ctx.triggered[0]['prop_id'].split('.')[1]
  39. new_preview_list = [
  40. item for item in preview_list if item['props']['id']['index'] != filename_to_delete
  41. ]
  42. return new_preview_list
  43. return preview_list
  44. @app.callback(
  45. Output('upload-status', 'children'),
  46. Input('confirm-upload', 'n_clicks'),
  47. State('file-preview-list', 'children'),
  48. State('url', 'pathname')
  49. )
  50. def upload_files(n_clicks, preview_list, pathname):
  51. if n_clicks and preview_list:
  52. model_name = pathname.split('/')[-1]
  53. for item in preview_list:
  54. content = item['props']['children'][2]['props']['children']
  55. filename = item['props']['id']['index']
  56. data = content.split(",")[1]
  57. file_path = f'{TRAINING_HOTWORD_DIR}/{model_name}/{filename}'
  58. save_file(file_path, base64.b64decode(data))
  59. if not is_valid_wav(file_path):
  60. os.remove(file_path)
  61. return f"File {filename} is not a valid WAV file.", True
  62. training_data = list_files_in_directory(f'{TRAINING_HOTWORD_DIR}/{model_name}')
  63. return generate_training_data_table(model_name, training_data)
  64. return html.Div()
  65. @app.callback(
  66. Output('hotword-manage-training-data', 'children'),
  67. [
  68. Input({"type": "delete-button", "index": ALL}, 'n_clicks'),
  69. Input({"type": "toggle-button", 'index': ALL}, 'n_clicks')
  70. ],
  71. [
  72. State({"type": "delete-button", "index": ALL}, 'id'),
  73. State({"type": "toggle-button", "index": ALL}, 'id'),
  74. State('url', 'pathname')
  75. ]
  76. )
  77. def manage_training_data(n_clicks_delete, n_clicks_toggle, delete_ids, toggle_ids, pathname):
  78. ctx = callback_context
  79. if not ctx.triggered:
  80. return html.Div()
  81. model_name = pathname.split('/')[-1]
  82. triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
  83. if 'delete-button' in triggered_id:
  84. for item in delete_ids:
  85. if item['id'] == triggered_id:
  86. file_path = os.path.join(TRAINING_HOTWORD_DIR, model_name, item['index'])
  87. delete_file(file_path)
  88. training_data = list_files_in_directory(f'{TRAINING_HOTWORD_DIR}/{model_name}')
  89. return generate_training_data_table(model_name, training_data)
  90. if 'toggle-button' in triggered_id:
  91. for item in toggle_ids:
  92. if item['id'] == triggered_id:
  93. file_path = os.path.join(TRAINING_HOTWORD_DIR, model_name, item['index'])
  94. new_file_path = os.path.join(TRAINING_HOTWORD_DIR, model_name, '_' + item['index'] if not item['index'].startswith('_') else item['index'][1:])
  95. os.rename(file_path, new_file_path)
  96. training_data = list_files_in_directory(f'{TRAINING_HOTWORD_DIR}/{model_name}')
  97. return generate_training_data_table(model_name, training_data)
  98. return html.Div()