commit 6f4309a616d62801c61c5efdbcce4718845fd77c Author: Envy_PC Date: Fri May 2 16:30:53 2025 +0900 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a827be6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.venv/ +__pycache__/ +*.pyc +*.pyo +*.pyd +*.pyw +*.pyz +*.log +*.log.* +*.log.*.* +*.log.*.*.* +*.log.*.*.*.* +*.log.*.*.*.*.* \ No newline at end of file diff --git a/Pline1.db b/Pline1.db new file mode 100644 index 0000000..e42e6ea Binary files /dev/null and b/Pline1.db differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..b12617d --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# TRNote - 철도 음성인식 시스템 + +TRNote는 철도 관제사와 기관사의 음성 대화를 인식하고 분석하는 시스템입니다. + +## 주요 기능 + +- 다양한 음원 소스(윈도우 사운드 출력, 스피커, 마이크, LINE-IN 등)에서 오디오 캡처 +- OpenAI Whisper API를 사용한 음성-텍스트 변환 +- 관제사/기관사 화자 구분 및 대화 분석 +- 전동차 편성 정보 및 고장 이력 조회 +- 대화 내용을 기반으로 관련 데이터 검색 및 표시 + +## 시스템 요구사항 + +- Windows 10 이상 +- Python 3.8 이상 +- OpenAI API 키 +- Poetry (패키지 관리) + +## 설치 방법 + +### Poetry 설치 + +``` +pip install poetry +``` + +### 프로젝트 설치 + +1. 저장소 클론 +``` +git clone https://github.com/username/TRNote.git +cd TRNote +``` + +2. Poetry를 사용하여 의존성 설치 +``` +poetry install +``` + +3. OpenAI API 키 설정 +``` +# Windows +set OPENAI_API_KEY=your_api_key_here + +# Linux/MacOS +export OPENAI_API_KEY=your_api_key_here +``` + +## 실행 방법 + +Poetry 환경에서 실행: +``` +poetry run trnote +``` + +또는 직접 실행: +``` +poetry run python main.py +``` + +## 사용 방법 + +1. 음원 소스 선택 드롭다운에서 오디오 소스 선택 +2. '시작' 버튼 클릭하여 음성 인식 시작 +3. 인식된 대화는 왼쪽 대화창에 표시됨 +4. 관련 열차 및 고장 정보는 오른쪽 패널에 표시됨 +5. 과거 대화는 왼쪽 상단 목록에서 선택하여 볼 수 있음 + +## 구조 + +- `main.py`: 애플리케이션 메인 진입점 +- `modules/`: 각 기능별 모듈 + - `audio_source.py`: 오디오 소스 관리 + - `speech_recognition.py`: 음성 인식 처리 + - `conversation_analyzer.py`: 대화 분석 및 화자 구분 + - `database_manager.py`: 데이터베이스 관리 + - `gui_components.py`: GUI 컴포넌트 + +## 라이선스 + +MIT \ No newline at end of file diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..d379004 --- /dev/null +++ b/config.ini @@ -0,0 +1,12 @@ +[api] +openai_api_key = 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 + +[audio] +sample_rate = 16000 +silence_threshold = 0.02 +silence_duration = 60 +buffer_duration = 3 + +[app] +theme = light + diff --git a/main.py b/main.py new file mode 100644 index 0000000..4efd595 --- /dev/null +++ b/main.py @@ -0,0 +1,493 @@ +import flet as ft +import configparser +import os +import numpy as np +import wave +import tempfile +from modules.audio_source import AudioSource +from modules.speech_recognition import SpeechRecognizer +from modules.conversation_analyzer import ConversationAnalyzer +from modules.database_manager import DatabaseManager +from modules.gui_components import ( + AudioSourceSelector, + StatusIndicator, + ConversationView, + PreviousConversationsList, + TrainInfoPanel +) + +def main(page: ft.Page): + # 설정 파일 로드 + config = configparser.ConfigParser() + + # 기본 설정 생성 + if not os.path.exists("config.ini"): + config["api"] = { + "openai_api_key": "your_openai_api_key_here" + } + config["audio"] = { + "sample_rate": "16000", + "silence_threshold": "0.02", + "silence_duration": "60", + "buffer_duration": "3" + } + config["app"] = { + "theme": "light" + } + + with open("config.ini", "w") as configfile: + config.write(configfile) + else: + config.read("config.ini") + + # 테마 설정 + theme = config.get("app", "theme", fallback="light") + page.theme_mode = ft.ThemeMode.LIGHT if theme.lower() == "light" else ft.ThemeMode.DARK + + page.title = "철도 음성인식 시스템" + page.window_width = 1200 + page.window_height = 800 + page.padding = 10 + + # 인스턴스 생성 + audio_source = AudioSource() + speech_recognizer = SpeechRecognizer() + conversation_analyzer = ConversationAnalyzer() + db_manager = DatabaseManager() + + # 테스트 모드 상태 + is_test_mode = False + + # GUI 컴포넌트 생성 + audio_source_selector = AudioSourceSelector(audio_source) + status_indicator = StatusIndicator() + conversation_view = ConversationView() + previous_conversations_list = PreviousConversationsList(conversation_view) + train_info_panel = TrainInfoPanel(db_manager) + + # 실시간 처리 상태 표시기 + realtime_indicator = ft.Text("", italic=True, color=ft.colors.GREY_600) + + # 파일 선택 콜백 함수 정의 + def on_file_picked(e): + if e.files and len(e.files) > 0: + file_path = e.files[0].path + test_status.value = f"선택된 파일: {os.path.basename(file_path)}" + page.update() + + # 선택된 파일 처리 + process_audio_file(file_path) + + # 테스트 모드 컨트롤 + test_mode_switch = ft.Switch(label="테스트 모드", value=False, on_change=lambda e: toggle_test_mode(e.control.value)) + file_picker = ft.FilePicker(on_result=on_file_picked) + page.overlay.append(file_picker) + + test_file_button = ft.ElevatedButton( + "MP3 파일 선택", + icon=ft.icons.UPLOAD_FILE, + on_click=lambda _: file_picker.pick_files( + allow_multiple=False, + allowed_extensions=["mp3", "wav"] + ), + disabled=True + ) + + # 테스트 모드 상태 표시 + test_status = ft.Text("", italic=True, color=ft.colors.ORANGE) + + # 레이아웃 구성 + left_panel = ft.Column([ + ft.Container( + content=previous_conversations_list, + height=320, + border=ft.border.all(1, ft.colors.GREY_400), + border_radius=5, + padding=5 + ), + ft.Container( + content=conversation_view, + height=480, + border=ft.border.all(1, ft.colors.GREY_400), + border_radius=5, + padding=5, + expand=True + ), + ], expand=True) + + right_panel = ft.Container( + content=train_info_panel, + border=ft.border.all(1, ft.colors.GREY_400), + border_radius=5, + padding=10, + expand=True + ) + + # 상단 컨트롤에 설정 버튼 추가 + top_controls = ft.Row([ + audio_source_selector, + status_indicator, + ft.ElevatedButton("시작", on_click=lambda e: start_monitoring()), + ft.ElevatedButton("중지", on_click=lambda e: stop_monitoring()), + ft.IconButton( + icon=ft.icons.SETTINGS, + tooltip="설정", + on_click=lambda e: show_settings_dialog() + ), + ]) + + # 테스트 모드 컨트롤 + test_controls = ft.Row([ + test_mode_switch, + test_file_button, + test_status + ], visible=True) + + # 메인 레이아웃에 실시간 인디케이터 추가 + main_layout = ft.Column([ + top_controls, + test_controls, + realtime_indicator, + ft.Row([ + left_panel, + right_panel, + ], expand=True), + ], expand=True) + + page.add(main_layout) + + # 테스트 모드 토글 함수 + def toggle_test_mode(value): + nonlocal is_test_mode + is_test_mode = value + test_file_button.disabled = not value + + if value: + test_status.value = "테스트 모드: 활성화됨" + audio_source_selector.disabled = True + else: + test_status.value = "테스트 모드: 비활성화됨" + audio_source_selector.disabled = False + + page.update() + + # 오디오 파일 처리 + def process_audio_file(file_path): + try: + status_indicator.set_status("파일 처리 중") + status_indicator.set_detecting(True) + page.update() + + # MP3 또는 WAV 파일 처리 + if file_path.lower().endswith('.mp3'): + # MP3를 WAV로 변환 (librosa 사용) + import librosa + + # MP3 파일 로드 + audio_data, sample_rate = librosa.load(file_path, sr=16000, mono=True) + + # WAV 파일로 변환 + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_file: + temp_wav_path = temp_file.name + + # 16비트 정수로 변환 + audio_data_int = (audio_data * 32767).astype(np.int16) + + # WAV 파일 저장 - 쓰기 모드('wb')로 열기 + with wave.open(temp_wav_path, 'wb') as wf: + wf.setnchannels(1) + wf.setsampwidth(2) # 16-bit + wf.setframerate(16000) + wf.writeframes(audio_data_int.tobytes()) + + # OpenAI의 API를 사용해 음성 인식 + text = speech_recognizer.recognize_file(temp_wav_path) + + # 임시 파일 삭제 + os.unlink(temp_wav_path) + else: + # WAV 파일 직접 처리 + text = speech_recognizer.recognize_file(file_path) + + # 인식 결과 처리 + if text: + realtime_indicator.value = f"파일 인식 결과: {text[:100]}..." + + speakers = conversation_analyzer.identify_speakers(text) + context = conversation_analyzer.analyze_conversation(text, speakers) + + conversation_view.update_conversation(text, speakers) + previous_conversations_list.add_conversation(context) + + # 데이터베이스에서 관련 정보 검색 + train_info = db_manager.get_train_info(context) + fault_info = db_manager.get_fault_info(context) + + train_info_panel.update_info(train_info, fault_info) + else: + realtime_indicator.value = "파일 인식 실패: 텍스트를 추출할 수 없습니다." + + status_indicator.set_detecting(False) + status_indicator.set_status("대기 중") + page.update() + + except Exception as e: + print(f"오디오 파일 처리 오류: {e}") + realtime_indicator.value = f"오류: {str(e)}" + status_indicator.set_detecting(False) + status_indicator.set_status("오류") + page.update() + + # 음원 감시 관련 함수 + def start_monitoring(): + if is_test_mode: + # 테스트 모드에서는 파일 선택을 유도 + file_picker.pick_files( + allow_multiple=False, + allowed_extensions=["mp3", "wav"] + ) + return + + try: + selected_source = audio_source_selector.get_selected_source() + if not selected_source: + page.snack_bar = ft.SnackBar(ft.Text("음원 소스를 선택해주세요")) + page.snack_bar.open = True + page.update() + return + + status_indicator.set_status("녹음 중") + # 일반 및 실시간 콜백 함수 전달 + audio_source.start_recording(selected_source, on_audio_data, on_realtime_audio_data) + page.update() + except Exception as e: + print(f"녹음 시작 오류: {e}") + page.snack_bar = ft.SnackBar(ft.Text(f"녹음 시작 오류: {e}")) + page.snack_bar.open = True + page.update() + + def stop_monitoring(): + try: + audio_source.stop_recording() + + # 실시간 인식 중지 + if hasattr(speech_recognizer, 'stop_realtime_recognition'): + speech_recognizer.stop_realtime_recognition() + + status_indicator.set_status("대기 중") + realtime_indicator.value = "" + page.update() + except Exception as e: + print(f"녹음 중지 오류: {e}") + + def on_audio_data(audio_data): + """완성된 오디오 데이터 처리 콜백""" + try: + if len(audio_data) == 0: + return + + status_indicator.set_detecting(True) + page.update() + + text = speech_recognizer.recognize(audio_data) + if text: + speakers = conversation_analyzer.identify_speakers(text) + context = conversation_analyzer.analyze_conversation(text, speakers) + + conversation_view.update_conversation(text, speakers) + previous_conversations_list.add_conversation(context) + + # 데이터베이스에서 관련 정보 검색 + train_info = db_manager.get_train_info(context) + fault_info = db_manager.get_fault_info(context) + + train_info_panel.update_info(train_info, fault_info) + + status_indicator.set_detecting(False) + page.update() + except Exception as e: + print(f"오디오 데이터 처리 오류: {e}") + status_indicator.set_detecting(False) + page.update() + + def on_realtime_audio_data(audio_data): + """실시간 오디오 데이터 처리 콜백""" + try: + if len(audio_data) == 0: + return + + status_indicator.set_detecting(True) + + # 실시간 인식 결과 처리 콜백 + def on_realtime_result(text): + if text: + realtime_indicator.value = f"실시간 인식 중: {text[:100]}..." + page.update() + + # 실시간 처리 지원 확인 + if hasattr(speech_recognizer, 'add_audio_data') and hasattr(speech_recognizer, 'start_realtime_recognition'): + # 큐에 오디오 데이터 추가 + speech_recognizer.add_audio_data(audio_data) + + # 처리가 시작되지 않았으면 시작 + if hasattr(speech_recognizer, 'is_processing') and not speech_recognizer.is_processing: + speech_recognizer.start_realtime_recognition(on_realtime_result) + else: + # 실시간 변환을 지원하지 않는 경우 일반 변환 사용 + text = speech_recognizer.recognize(audio_data) + if text: + realtime_indicator.value = f"감지: {text[:100]}..." + + page.update() + except Exception as e: + print(f"실시간 오디오 처리 오류: {e}") + + def show_settings_dialog(): + """설정 대화상자 표시""" + try: + # API 키 입력 필드 + api_key_field = ft.TextField( + label="OpenAI API 키", + value=speech_recognizer.api_key, + password=True, + width=400 + ) + + # 오디오 설정 슬라이더 + silence_threshold_slider = ft.Slider( + min=0.01, + max=0.1, + divisions=9, + value=float(config.get("audio", "silence_threshold", fallback="0.02")), + label="{value}", + on_change=lambda e: update_slider_label(e) + ) + + silence_threshold_text = ft.Text(f"소리 감지 임계값: {silence_threshold_slider.value}") + + silence_duration_slider = ft.Slider( + min=10, + max=120, + divisions=11, + value=float(config.get("audio", "silence_duration", fallback="60")), + label="{value}", + on_change=lambda e: update_duration_label(e) + ) + + silence_duration_text = ft.Text(f"대화 구분 시간(초): {silence_duration_slider.value}") + + buffer_duration_slider = ft.Slider( + min=1, + max=10, + divisions=9, + value=float(config.get("audio", "buffer_duration", fallback="3")), + label="{value}", + on_change=lambda e: update_buffer_label(e) + ) + + buffer_duration_text = ft.Text(f"버퍼 길이(초): {buffer_duration_slider.value}") + + # 슬라이더 레이블 업데이트 함수 + def update_slider_label(e): + silence_threshold_text.value = f"소리 감지 임계값: {e.control.value:.2f}" + page.update() + + def update_duration_label(e): + silence_duration_text.value = f"대화 구분 시간(초): {e.control.value:.0f}" + page.update() + + def update_buffer_label(e): + buffer_duration_text.value = f"버퍼 길이(초): {e.control.value:.0f}" + page.update() + + # 테마 선택 라디오 버튼 + theme_radio = ft.RadioGroup( + content=ft.Column([ + ft.Radio(value="light", label="밝은 테마"), + ft.Radio(value="dark", label="어두운 테마"), + ]), + value=theme + ) + + # 설정 저장 함수 + def save_settings(e): + try: + # API 키 저장 + if api_key_field.value: + speech_recognizer.set_api_key(api_key_field.value) + + # 오디오 설정 저장 + if hasattr(audio_source, 'update_settings'): + audio_source.update_settings( + silence_threshold=silence_threshold_slider.value, + silence_duration=silence_duration_slider.value, + buffer_duration=buffer_duration_slider.value + ) + else: + # 직접 속성 설정 + audio_source.silence_threshold = silence_threshold_slider.value + audio_source.silence_duration = silence_duration_slider.value + audio_source.buffer_duration = buffer_duration_slider.value + + # 설정 저장 + config.set("audio", "silence_threshold", str(silence_threshold_slider.value)) + config.set("audio", "silence_duration", str(silence_duration_slider.value)) + config.set("audio", "buffer_duration", str(buffer_duration_slider.value)) + + # 테마 설정 저장 + config.set("app", "theme", theme_radio.value) + with open("config.ini", "w") as config_file: + config.write(config_file) + + # 테마 적용 + page.theme_mode = ft.ThemeMode.LIGHT if theme_radio.value.lower() == "light" else ft.ThemeMode.DARK + + # 대화상자 닫기 + dialog.open = False + page.update() + + # 설정 적용 알림 + page.snack_bar = ft.SnackBar(ft.Text("설정이 저장되었습니다")) + page.snack_bar.open = True + page.update() + except Exception as e: + print(f"설정 저장 오류: {e}") + page.snack_bar = ft.SnackBar(ft.Text(f"설정 저장 오류: {e}")) + page.snack_bar.open = True + page.update() + + # 설정 대화상자 + dialog = ft.AlertDialog( + title=ft.Text("설정"), + content=ft.Column([ + ft.Text("OpenAI API 설정", weight=ft.FontWeight.BOLD), + api_key_field, + ft.Divider(), + ft.Text("오디오 설정", weight=ft.FontWeight.BOLD), + silence_threshold_text, + silence_threshold_slider, + silence_duration_text, + silence_duration_slider, + buffer_duration_text, + buffer_duration_slider, + ft.Divider(), + ft.Text("앱 설정", weight=ft.FontWeight.BOLD), + ft.Text("테마"), + theme_radio, + ], scroll=ft.ScrollMode.AUTO, height=500), + actions=[ + ft.TextButton("취소", on_click=lambda e: setattr(dialog, "open", False)), + ft.TextButton("저장", on_click=save_settings), + ], + actions_alignment=ft.MainAxisAlignment.END, + ) + + # 대화상자 표시 + page.dialog = dialog + dialog.open = True + page.update() + except Exception as e: + print(f"설정 대화상자 표시 오류: {e}") + +if __name__ == "__main__": + ft.app(target=main) \ No newline at end of file diff --git a/modules/__init__.py b/modules/__init__.py new file mode 100644 index 0000000..bfce48c --- /dev/null +++ b/modules/__init__.py @@ -0,0 +1,5 @@ +""" +TRNote - 철도 음성인식 시스템 +""" + +__version__ = "0.1.0" \ No newline at end of file diff --git a/modules/audio_source.py b/modules/audio_source.py new file mode 100644 index 0000000..61b6209 --- /dev/null +++ b/modules/audio_source.py @@ -0,0 +1,271 @@ +import sounddevice as sd +import numpy as np +import threading +import time +import configparser +import os +from typing import Callable, List, Dict, Any + +class AudioSource: + def __init__(self, config_path="config.ini"): + # 설정 파일 로드 + self.config = configparser.ConfigParser() + + if os.path.exists(config_path): + self.config.read(config_path) + + self.is_recording = False + self.stream = None + self.callback = None + self.realtime_callback = None + self.thread = None + + # 설정 파일에서 오디오 설정 읽기 + self.silence_threshold = self.config.getfloat("audio", "silence_threshold", fallback=0.02) + self.silence_duration = self.config.getfloat("audio", "silence_duration", fallback=60) + self.buffer_duration = self.config.getfloat("audio", "buffer_duration", fallback=3) + self.sample_rate = self.config.getint("audio", "sample_rate", fallback=16000) + + self.last_sound_time = 0 + self.buffer = np.array([]) + self.realtime_buffer = np.array([]) + self.realtime_buffer_size = int(self.sample_rate * 2) # 2초 버퍼 + self.last_realtime_process = 0 + self.realtime_interval = 2.0 # 2초마다 실시간 처리 + + def get_available_sources(self) -> List[Dict[str, Any]]: + """사용 가능한 모든 오디오 소스 목록을 반환합니다.""" + try: + devices = sd.query_devices() + sources = [] + + for i, device in enumerate(devices): + if isinstance(device, dict) and device.get('max_input_channels', 0) > 0: + sources.append({ + 'id': i, + 'name': device['name'], + 'channels': device['max_input_channels'], + 'type': self._determine_device_type(device['name']) + }) + + # 윈도우 사운드 출력 캡처 옵션 추가 + sources.append({ + 'id': 'wasapi_loopback', + 'name': '시스템 소리 출력 (WASAPI Loopback)', + 'channels': 2, + 'type': 'system_output' + }) + + return sources + except Exception as e: + print(f"오디오 소스 목록 가져오기 오류: {e}") + # 기본 소스라도 반환 + return [{ + 'id': 0, + 'name': '기본 마이크', + 'channels': 1, + 'type': 'microphone' + }] + + def _determine_device_type(self, name: str) -> str: + """장치 이름을 기반으로 장치 유형을 결정합니다.""" + name_lower = name.lower() + + if 'mic' in name_lower or 'microphone' in name_lower: + return 'microphone' + elif 'line' in name_lower and 'in' in name_lower: + return 'line_in' + elif 'speaker' in name_lower or 'headphone' in name_lower: + return 'speaker' + else: + return 'unknown' + + def start_recording(self, source_id, callback: Callable, realtime_callback: Callable = None): + """오디오 소스 녹음을 시작합니다.""" + if self.is_recording: + self.stop_recording() + + self.callback = callback + self.realtime_callback = realtime_callback + self.is_recording = True + self.buffer = np.array([]) + self.realtime_buffer = np.array([]) + self.last_sound_time = time.time() + self.last_realtime_process = time.time() + + try: + # WASAPI 루프백일 경우 특별 처리 + if source_id == 'wasapi_loopback': + try: + device_info = sd.query_devices() + device_idx = None + + # 출력 장치 찾기 + for i, device in enumerate(device_info): + if isinstance(device, dict) and device.get('hostapi', None) == 0 and device.get('max_output_channels', 0) > 0: + device_idx = i + break + + if device_idx is None: + device_idx = sd.default.device[1] # 기본 출력 장치 + + self.stream = sd.InputStream( + device=f'{device_idx}:loopback', + samplerate=self.sample_rate, + channels=2, + callback=self._audio_callback + ) + except Exception as e: + print(f"WASAPI 루프백 설정 오류: {e}") + # 기본 장치로 대체 + self.stream = sd.InputStream( + samplerate=self.sample_rate, + channels=1, + callback=self._audio_callback + ) + else: + # 일반 입력 장치 + try: + self.stream = sd.InputStream( + device=source_id, + samplerate=self.sample_rate, + channels=1, + callback=self._audio_callback + ) + except Exception as e: + print(f"입력 장치 설정 오류: {e}") + # 기본 장치로 대체 + self.stream = sd.InputStream( + samplerate=self.sample_rate, + channels=1, + callback=self._audio_callback + ) + + self.stream.start() + + # 침묵 감지 스레드 시작 + self.thread = threading.Thread(target=self._silence_detection_thread) + self.thread.daemon = True + self.thread.start() + + except Exception as e: + print(f"녹음 시작 오류: {e}") + self.is_recording = False + + def stop_recording(self): + """녹음을 중지합니다.""" + self.is_recording = False + + if self.stream: + try: + self.stream.stop() + self.stream.close() + except Exception as e: + print(f"녹음 중지 오류: {e}") + finally: + self.stream = None + + self.thread = None + + def _audio_callback(self, indata, frames, time, status): + """오디오 데이터가 들어올 때 호출되는 콜백 함수입니다.""" + if status: + print(f"상태: {status}") + + try: + # 모노로 변환 (필요한 경우) + if indata.shape[1] > 1: + audio_data = np.mean(indata, axis=1) + else: + audio_data = indata[:, 0] + + # 음량 레벨 계산 + volume_norm = np.linalg.norm(audio_data) / len(audio_data) + + # 소리가 감지된 경우 + if volume_norm > self.silence_threshold: + self.last_sound_time = time.time() + + # 메인 버퍼에 데이터 추가 + if len(self.buffer) == 0: + self.buffer = audio_data + else: + self.buffer = np.append(self.buffer, audio_data) + + # 실시간 버퍼에 데이터 추가 + if len(self.realtime_buffer) == 0: + self.realtime_buffer = audio_data + else: + self.realtime_buffer = np.append(self.realtime_buffer, audio_data) + + # 실시간 버퍼가 최대 크기를 초과하면 오래된 데이터 제거 + if len(self.realtime_buffer) > self.realtime_buffer_size: + self.realtime_buffer = self.realtime_buffer[-self.realtime_buffer_size:] + + # 실시간 처리 (일정 시간 간격으로) + current_time = time.time() + if (current_time - self.last_realtime_process) > self.realtime_interval and self.realtime_callback: + self.last_realtime_process = current_time + + # 데이터 복사 후 처리 + realtime_data = self.realtime_buffer.copy() + if self.realtime_callback: + # 별도 쓰레드에서 실행하지 않고 직접 콜백 호출 + self.realtime_callback(realtime_data) + + # 메인 버퍼가 최대 길이를 초과하면 콜백 호출 및 버퍼 초기화 + max_buffer_size = self.sample_rate * self.buffer_duration + if len(self.buffer) >= max_buffer_size: + if self.callback: + buffer_copy = self.buffer.copy() + self.callback(buffer_copy) + self.buffer = np.array([]) + except Exception as e: + print(f"오디오 처리 오류: {e}") + + def _silence_detection_thread(self): + """침묵 감지 스레드입니다. 침묵이 일정 시간 이상 지속되면 현재 버퍼의 데이터를 처리합니다.""" + while self.is_recording: + try: + current_time = time.time() + + # 현재 버퍼에 데이터가 있고 일정 시간 동안 소리가 없는 경우 + if len(self.buffer) > 0 and (current_time - self.last_sound_time) > 1.0: + if self.callback: + buffer_copy = self.buffer.copy() + self.callback(buffer_copy) + self.buffer = np.array([]) + + # 실시간 버퍼도 처리하고 초기화 + if self.realtime_callback and len(self.realtime_buffer) > 0: + realtime_data = self.realtime_buffer.copy() + self.realtime_callback(realtime_data) + self.realtime_buffer = np.array([]) + + # 장시간 침묵이 지속되면 새로운 대화로 간주 + if (current_time - self.last_sound_time) > self.silence_duration: + # 새로운 대화 시작을 알림 + pass + + time.sleep(0.5) # 스레드 부하 감소 + except Exception as e: + print(f"침묵 감지 오류: {e}") + time.sleep(1) # 오류 시 더 긴 대기 + + def update_settings(self, silence_threshold=None, silence_duration=None, buffer_duration=None): + """오디오 설정을 업데이트합니다.""" + if silence_threshold is not None: + self.silence_threshold = silence_threshold + self.config.set("audio", "silence_threshold", str(silence_threshold)) + + if silence_duration is not None: + self.silence_duration = silence_duration + self.config.set("audio", "silence_duration", str(silence_duration)) + + if buffer_duration is not None: + self.buffer_duration = buffer_duration + self.config.set("audio", "buffer_duration", str(buffer_duration)) + + # 설정 파일 저장 + with open("config.ini", "w") as config_file: + self.config.write(config_file) \ No newline at end of file diff --git a/modules/conversation_analyzer.py b/modules/conversation_analyzer.py new file mode 100644 index 0000000..8375cee --- /dev/null +++ b/modules/conversation_analyzer.py @@ -0,0 +1,233 @@ +import re +import time +import os +from typing import Dict, List, Tuple, Any, Optional +from datetime import datetime + +class ConversationAnalyzer: + def __init__(self): + # 대화 구분을 위한 패턴 + self.controller_patterns = [ + r"전철\s*(?:신평|보안)", + r"관제\s*(?:\d+)?", + ] + + self.driver_patterns = [ + r"(\d{4})\s*열차\s*(?:(\d+)\s*편성)?", + r"다대\s*회차선\s*(\d{4})\s*열차\s*(?:(\d+)\s*편성)?", + ] + + # 현재 및 최근 대화 저장 + self.current_conversation = { + "id": "", + "start_time": None, + "last_update": None, + "train_number": None, + "train_formation": None, + "speakers": [], + "messages": [], + "faults": [] + } + + self.conversations = [] + self.max_conversation_history = 50 + + # 로그 디렉토리 생성 + self.log_dir = "conversation_logs" + try: + if not os.path.exists(self.log_dir): + os.makedirs(self.log_dir) + except Exception as e: + print(f"로그 디렉토리 생성 오류: {e}") + + def identify_speakers(self, text: str) -> Dict[str, str]: + """텍스트에서 화자를 식별합니다.""" + result = {"role": "unknown", "name": "알 수 없음"} + + # 관제사 패턴 확인 + for pattern in self.controller_patterns: + match = re.search(pattern, text) + if match: + result["role"] = "controller" + result["name"] = "관제사" + break + + # 기관사 패턴 확인 + for pattern in self.driver_patterns: + match = re.search(pattern, text) + if match: + result["role"] = "driver" + result["name"] = "기관사" + + # 열차 번호와 편성 추출 + train_number = match.group(1) if match.groups() and len(match.groups()) > 0 else None + train_formation = match.group(2) if match.groups() and len(match.groups()) > 1 else None + + if train_number: + result["train_number"] = train_number + if train_formation: + result["train_formation"] = train_formation + break + + return result + + def identify_faults(self, text: str) -> List[str]: + """텍스트에서 고장 관련 키워드를 식별합니다.""" + fault_patterns = [ + r"HVAC", + r"배기팬", + r"고장", + r"장애", + r"오류", + r"문제", + r"에러", + r"점검", + r"이상", + ] + + found_faults = [] + for pattern in fault_patterns: + if re.search(pattern, text, re.IGNORECASE): + found_faults.append(pattern) + + return found_faults + + def analyze_conversation(self, text: str, speaker_info: Dict[str, str]) -> Dict[str, Any]: + """대화 내용을 분석하고 현재 대화 컨텍스트를 업데이트합니다.""" + try: + current_time = time.time() + + # 새 대화 시작 여부 확인 + if (self.current_conversation["last_update"] is None or + current_time - self.current_conversation["last_update"] > 60): # 1분 이상 침묵 + # 이전 대화가 있으면 저장 + if self.current_conversation["start_time"] is not None: + # 대화 로그 저장 + self._save_conversation_log(self.current_conversation) + + self.conversations.insert(0, self.current_conversation.copy()) + # 최대 저장 개수 제한 + if len(self.conversations) > self.max_conversation_history: + self.conversations = self.conversations[:self.max_conversation_history] + + # 새 대화 초기화 + conversation_id = datetime.now().strftime("%Y%m%d%H%M%S") + self.current_conversation = { + "id": conversation_id, + "start_time": current_time, + "last_update": current_time, + "train_number": None, + "train_formation": None, + "speakers": [], + "messages": [], + "faults": [] + } + else: + # 기존 대화 업데이트 + self.current_conversation["last_update"] = current_time + + # 화자 정보 업데이트 + speaker_role = speaker_info.get("role", "unknown") + speaker_name = speaker_info.get("name", "알 수 없음") + + speaker_exists = False + for s in self.current_conversation["speakers"]: + if s["role"] == speaker_role: + speaker_exists = True + break + + if not speaker_exists: + self.current_conversation["speakers"].append({ + "role": speaker_role, + "name": speaker_name + }) + + # 열차 번호와 편성 업데이트 + if "train_number" in speaker_info and self.current_conversation["train_number"] is None: + self.current_conversation["train_number"] = speaker_info["train_number"] + + if "train_formation" in speaker_info and self.current_conversation["train_formation"] is None: + self.current_conversation["train_formation"] = speaker_info["train_formation"] + + # 고장 정보 식별 및 업데이트 + faults = self.identify_faults(text) + for fault in faults: + if fault not in self.current_conversation["faults"]: + self.current_conversation["faults"].append(fault) + + # 메시지 추가 + timestamp = datetime.fromtimestamp(current_time).strftime("%H:%M:%S") + self.current_conversation["messages"].append({ + "time": timestamp, + "speaker": speaker_name, + "text": text + }) + + return self.current_conversation + + except Exception as e: + print(f"대화 분석 오류: {e}") + # 오류 발생 시 기본 대화 객체 반환 + return { + "id": datetime.now().strftime("%Y%m%d%H%M%S"), + "start_time": time.time(), + "last_update": time.time(), + "train_number": None, + "train_formation": None, + "speakers": [{ + "role": "unknown", + "name": "알 수 없음" + }], + "messages": [{ + "time": datetime.now().strftime("%H:%M:%S"), + "speaker": "알 수 없음", + "text": text + }], + "faults": [] + } + + def _save_conversation_log(self, conversation: Dict[str, Any]): + """대화 내용을 로그 파일로 저장합니다.""" + try: + if not conversation or not conversation.get("messages"): + return + + log_filename = f"{self.log_dir}/conversation_{conversation['id']}.txt" + + with open(log_filename, "w", encoding="utf-8") as f: + # 헤더 정보 + f.write(f"대화 ID: {conversation['id']}\n") + f.write(f"시작 시간: {datetime.fromtimestamp(conversation['start_time']).strftime('%Y-%m-%d %H:%M:%S')}\n") + + if conversation.get("train_number"): + f.write(f"열차 번호: {conversation['train_number']}\n") + + if conversation.get("train_formation"): + f.write(f"편성 번호: {conversation['train_formation']}\n") + + if conversation.get("faults"): + f.write(f"감지된 고장: {', '.join(conversation['faults'])}\n") + + f.write("\n--- 대화 내용 ---\n\n") + + # 대화 내용 + for msg in conversation["messages"]: + f.write(f"[{msg['time']}] {msg['speaker']}: {msg['text']}\n") + + except Exception as e: + print(f"대화 로그 저장 오류: {e}") + + def get_conversation_history(self) -> List[Dict[str, Any]]: + """대화 기록을 반환합니다.""" + return self.conversations + + def get_conversation_by_id(self, conversation_id: str) -> Optional[Dict[str, Any]]: + """ID로 대화를 찾아 반환합니다.""" + if self.current_conversation["id"] == conversation_id: + return self.current_conversation + + for conv in self.conversations: + if conv["id"] == conversation_id: + return conv + + return None \ No newline at end of file diff --git a/modules/database_manager.py b/modules/database_manager.py new file mode 100644 index 0000000..41c4b9e --- /dev/null +++ b/modules/database_manager.py @@ -0,0 +1,273 @@ +import sqlite3 +import os +import json +from typing import Dict, List, Any, Optional +from datetime import datetime + +class DatabaseManager: + def __init__(self, db_path: str = "railway_data.db"): + self.db_path = db_path + self.initialize_db() + + # 임시 데이터 생성 (실제 구현에서는 DB에서 로드) + self.sample_data = { + "train_schedules": { + "2212": {"formation": "24", "type": "일반", "line": "1호선"}, + "2242": {"formation": "24", "type": "일반", "line": "1호선"}, + "2312": {"formation": "31", "type": "급행", "line": "1호선"}, + }, + "train_formations": { + "24": { + "manufacturer": "현대로템", + "introduction_date": "2019-05", + "recent_faults": [ + {"date": "2023-11-15", "car": "3", "component": "HVAC", "detail": "배기팬 고장", "status": "완료"}, + {"date": "2023-12-03", "car": "5", "component": "출입문", "detail": "개방 지연", "status": "완료"}, + ], + "maintenance": [ + {"type": "일상", "date": "2023-12-20", "details": "각종 센서 점검 및 배터리 교체"}, + {"type": "월상", "date": "2023-11-25", "details": "공조장치 종합 점검"}, + {"type": "중정비", "date": "2023-10-05", "details": "차량 종합 정밀검사"} + ], + "reports": [ + {"type": "동향", "date": "2023-12-01", "file": "reports/24_trend_202312.pdf"}, + {"type": "조치결과", "date": "2023-11-20", "file": "reports/24_fix_20231120.pdf"} + ] + }, + "31": { + "manufacturer": "대우중공업", + "introduction_date": "2012-08", + "recent_faults": [ + {"date": "2023-10-25", "car": "2", "component": "제동장치", "detail": "공기압 저하", "status": "완료"}, + ], + "maintenance": [ + {"type": "일상", "date": "2023-12-15", "details": "전기장치 점검"}, + {"type": "월상", "date": "2023-11-10", "details": "차체 및 대차 검사"}, + {"type": "중정비", "date": "2023-06-20", "details": "차량 전체 검사 및 부품 교체"} + ], + "reports": [ + {"type": "동향", "date": "2023-12-01", "file": "reports/31_trend_202312.pdf"} + ] + } + }, + "fault_history": [ + {"date": "2023-12-05", "formation": "15", "car": "3", "component": "HVAC", "detail": "냉방 불량", "status": "완료"}, + {"date": "2023-12-01", "formation": "22", "car": "1", "component": "HVAC", "detail": "과열", "status": "완료"}, + {"date": "2023-11-28", "formation": "17", "car": "4", "component": "HVAC", "detail": "배기팬 고장", "status": "완료"}, + {"date": "2023-11-25", "formation": "24", "car": "3", "component": "HVAC", "detail": "배기팬 고장", "status": "완료"}, + {"date": "2023-11-20", "formation": "08", "car": "2", "component": "HVAC", "detail": "제어기 오류", "status": "완료"}, + {"date": "2023-11-15", "formation": "19", "car": "5", "component": "HVAC", "detail": "필터 교체 필요", "status": "완료"}, + {"date": "2023-11-10", "formation": "28", "car": "6", "component": "HVAC", "detail": "모터 소음", "status": "완료"}, + ] + } + + def initialize_db(self): + """데이터베이스 초기화 및 필요한 테이블 생성""" + if not os.path.exists(self.db_path): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + # 열차다이아표 테이블 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS train_schedules ( + train_number TEXT PRIMARY KEY, + formation_number TEXT, + train_type TEXT, + line TEXT + ) + ''') + + # 편성데이터 테이블 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS train_formations ( + formation_number TEXT PRIMARY KEY, + manufacturer TEXT, + introduction_date TEXT, + details TEXT + ) + ''') + + # 고장이력 테이블 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS fault_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + date TEXT, + formation_number TEXT, + car_number TEXT, + component TEXT, + detail TEXT, + status TEXT + ) + ''') + + # 대화 기록 테이블 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS conversation_history ( + id TEXT PRIMARY KEY, + timestamp TEXT, + train_number TEXT, + formation_number TEXT, + content TEXT + ) + ''') + + conn.commit() + conn.close() + + # 샘플 데이터 추가 + self._add_sample_data() + + def _add_sample_data(self): + """샘플 데이터를 DB에 추가합니다.""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + # 열차다이아표 데이터 + for train_number, data in self.sample_data["train_schedules"].items(): + cursor.execute( + "INSERT OR REPLACE INTO train_schedules VALUES (?, ?, ?, ?)", + (train_number, data["formation"], data["type"], data["line"]) + ) + + # 편성데이터 + for formation_number, data in self.sample_data["train_formations"].items(): + cursor.execute( + "INSERT OR REPLACE INTO train_formations VALUES (?, ?, ?, ?)", + (formation_number, data["manufacturer"], data["introduction_date"], json.dumps(data)) + ) + + # 고장이력 데이터 + for fault in self.sample_data["fault_history"]: + cursor.execute( + "INSERT OR IGNORE INTO fault_history (date, formation_number, car_number, component, detail, status) VALUES (?, ?, ?, ?, ?, ?)", + (fault["date"], fault["formation"], fault["car"], fault["component"], fault["detail"], fault["status"]) + ) + + conn.commit() + conn.close() + + def get_train_formation(self, train_number: str) -> Optional[str]: + """열차번호로 편성번호를 조회합니다.""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + cursor.execute("SELECT formation_number FROM train_schedules WHERE train_number = ?", (train_number,)) + result = cursor.fetchone() + + conn.close() + + if result: + return result[0] + return None + + def get_formation_details(self, formation_number: str) -> Optional[Dict[str, Any]]: + """편성번호로 편성 상세정보를 조회합니다.""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + cursor.execute("SELECT details FROM train_formations WHERE formation_number = ?", (formation_number,)) + result = cursor.fetchone() + + conn.close() + + if result: + return json.loads(result[0]) + return None + + def get_fault_history(self, component: str = None, limit: int = 50) -> List[Dict[str, Any]]: + """특정 컴포넌트의 고장이력을 조회합니다.""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + query = "SELECT * FROM fault_history" + params = [] + + if component: + query += " WHERE component = ?" + params.append(component) + + query += " ORDER BY date DESC LIMIT ?" + params.append(limit) + + cursor.execute(query, params) + results = cursor.fetchall() + + conn.close() + + fault_history = [] + for row in results: + fault_history.append({ + "id": row[0], + "date": row[1], + "formation": row[2], + "car": row[3], + "component": row[4], + "detail": row[5], + "status": row[6] + }) + + return fault_history + + def save_conversation(self, conversation: Dict[str, Any]): + """대화 기록을 저장합니다.""" + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + + cursor.execute( + "INSERT OR REPLACE INTO conversation_history VALUES (?, ?, ?, ?, ?)", + ( + conversation["id"], + datetime.fromtimestamp(conversation["start_time"]).isoformat() if conversation["start_time"] else None, + conversation["train_number"], + conversation["train_formation"], + json.dumps(conversation) + ) + ) + + conn.commit() + conn.close() + + def get_train_info(self, conversation: Dict[str, Any]) -> Dict[str, Any]: + """대화 컨텍스트를 기반으로 열차 정보를 검색합니다.""" + train_info = {"found": False} + + train_number = conversation.get("train_number") + if not train_number: + return train_info + + # 편성번호 조회 + formation_number = conversation.get("train_formation") + if not formation_number: + formation_number = self.get_train_formation(train_number) + + if formation_number: + # 편성 상세정보 조회 + formation_details = self.get_formation_details(formation_number) + if formation_details: + train_info.update({ + "found": True, + "train_number": train_number, + "formation_number": formation_number, + "details": formation_details + }) + + return train_info + + def get_fault_info(self, conversation: Dict[str, Any]) -> Dict[str, Any]: + """대화 컨텍스트를 기반으로 고장 정보를 검색합니다.""" + fault_info = {"found": False, "faults": []} + + if not conversation.get("faults"): + return fault_info + + for fault_type in conversation["faults"]: + component = "HVAC" if fault_type.upper() == "HVAC" else fault_type + fault_history = self.get_fault_history(component) + + if fault_history: + fault_info["found"] = True + fault_info["component"] = component + fault_info["faults"] = fault_history + break + + return fault_info \ No newline at end of file diff --git a/modules/gui_components.py b/modules/gui_components.py new file mode 100644 index 0000000..c4ee44f --- /dev/null +++ b/modules/gui_components.py @@ -0,0 +1,404 @@ +import flet as ft +from typing import Callable, Dict, List, Any, Optional +from datetime import datetime + +class AudioSourceSelector(ft.UserControl): + def __init__(self, audio_source): + super().__init__() + self.audio_source = audio_source + self.sources = [] + self.dropdown = None + + def did_mount(self): + self.sources = self.audio_source.get_available_sources() + self.dropdown.options = [ + ft.dropdown.Option(key=str(src["id"]), text=src["name"]) + for src in self.sources + ] + self.update() + + def build(self): + self.dropdown = ft.Dropdown( + label="음원 소스 선택", + width=300, + options=[] + ) + + return ft.Row([ + self.dropdown, + ft.IconButton( + icon=ft.icons.REFRESH, + tooltip="소스 새로고침", + on_click=self.refresh_sources + ) + ]) + + def refresh_sources(self, e): + self.sources = self.audio_source.get_available_sources() + self.dropdown.options = [ + ft.dropdown.Option(key=str(src["id"]), text=src["name"]) + for src in self.sources + ] + self.update() + + def get_selected_source(self): + if not self.dropdown.value: + return None + + if self.dropdown.value == "wasapi_loopback": + return "wasapi_loopback" + + return int(self.dropdown.value) + + +class StatusIndicator(ft.UserControl): + def __init__(self): + super().__init__() + self.status = "대기 중" + self.is_detecting = False + self.status_text = None + self.indicator = None + + def build(self): + self.status_text = ft.Text(self.status, size=14) + self.indicator = ft.Container( + width=20, + height=20, + border_radius=10, + bgcolor=ft.colors.GREY_400 + ) + + return ft.Row([ + self.status_text, + self.indicator, + ], spacing=5) + + def set_status(self, status: str): + self.status = status + self.status_text.value = status + + if status == "녹음 중": + self.indicator.bgcolor = ft.colors.GREEN + elif status == "오류": + self.indicator.bgcolor = ft.colors.RED + else: + self.indicator.bgcolor = ft.colors.GREY_400 + + self.update() + + def set_detecting(self, is_detecting: bool): + self.is_detecting = is_detecting + + if is_detecting: + self.indicator.bgcolor = ft.colors.YELLOW + elif self.status == "녹음 중": + self.indicator.bgcolor = ft.colors.GREEN + + self.update() + + +class ConversationView(ft.UserControl): + def __init__(self): + super().__init__() + self.conversation = [] + self.text_area = None + + def build(self): + self.text_area = ft.TextField( + multiline=True, + read_only=True, + min_lines=15, + max_lines=20, + expand=True, + shift_enter=True, + border_color=ft.colors.GREY_300, + ) + + return ft.Column([ + ft.Text("현재 대화", size=16, weight=ft.FontWeight.BOLD), + self.text_area, + ]) + + def update_conversation(self, text: str, speaker_info: Dict[str, str]): + speaker_name = speaker_info.get("name", "알 수 없음") + + if self.conversation: + self.conversation.append(f"{speaker_name}: {text}") + else: + self.conversation = [f"{speaker_name}: {text}"] + + self.text_area.value = "\n\n".join(self.conversation) + self.update() + + def set_conversation(self, messages: List[Dict[str, str]]): + self.conversation = [ + f"{msg['speaker']}: {msg['text']}" + for msg in messages + ] + + self.text_area.value = "\n\n".join(self.conversation) + self.update() + + +class PreviousConversationsList(ft.UserControl): + def __init__(self, conversation_view: ConversationView): + super().__init__() + self.conversation_view = conversation_view + self.conversations = [] + self.list_view = None + + def build(self): + self.list_view = ft.ListView( + expand=1, + spacing=2, + padding=10, + auto_scroll=False, + ) + + return ft.Column([ + ft.Text("이전 대화", size=16, weight=ft.FontWeight.BOLD), + self.list_view, + ]) + + def add_conversation(self, conversation: Dict[str, Any]): + if not conversation or "id" not in conversation: + return + + # 이미 있는지 확인 + for i, conv in enumerate(self.conversations): + if conv["id"] == conversation["id"]: + self.conversations[i] = conversation + self._refresh_list() + return + + # 새 대화 추가 + self.conversations.insert(0, conversation) + + # 최대 개수 제한 + if len(self.conversations) > 50: + self.conversations = self.conversations[:50] + + self._refresh_list() + + def _refresh_list(self): + self.list_view.controls = [] + + for conv in self.conversations: + # 대화 요약 생성 + train_info = "" + if conv.get("train_number"): + train_info = f"[{conv['train_number']}열차" + if conv.get("train_formation"): + train_info += f" {conv['train_formation']}편성]" + else: + train_info += "]" + + time_info = "" + if conv.get("start_time"): + time_info = datetime.fromtimestamp(conv["start_time"]).strftime("%H:%M:%S") + + # 대화 내용 요약 (첫 번째 메시지 또는 최대 30자) + summary = "" + if conv.get("messages") and len(conv["messages"]) > 0: + first_msg = conv["messages"][0]["text"] + summary = first_msg[:30] + ("..." if len(first_msg) > 30 else "") + + self.list_view.controls.append( + ft.ListTile( + title=ft.Text(f"{time_info} {train_info}"), + subtitle=ft.Text(summary), + on_click=lambda e, conv_id=conv["id"]: self._on_conversation_selected(conv_id) + ) + ) + + self.update() + + def _on_conversation_selected(self, conversation_id: str): + # 선택된 대화 찾기 + selected_conv = None + for conv in self.conversations: + if conv["id"] == conversation_id: + selected_conv = conv + break + + if selected_conv and selected_conv.get("messages"): + self.conversation_view.set_conversation(selected_conv["messages"]) + + +class TrainInfoPanel(ft.UserControl): + def __init__(self, db_manager): + super().__init__() + self.db_manager = db_manager + self.train_info = None + self.fault_info = None + self.container = None + + def build(self): + self.container = ft.Column( + [ft.Text("열차 정보가 표시됩니다.", italic=True, color=ft.colors.GREY_600)], + scroll=ft.ScrollMode.AUTO, + spacing=10, + ) + + return self.container + + def update_info(self, train_info: Dict[str, Any], fault_info: Dict[str, Any]): + self.train_info = train_info + self.fault_info = fault_info + + self.container.controls = [] + + # 열차 정보 표시 + if train_info and train_info.get("found"): + details = train_info.get("details", {}) + + train_header = ft.Row([ + ft.Text( + f"{train_info['train_number']}열차 {train_info['formation_number']}편성", + size=18, + weight=ft.FontWeight.BOLD + ) + ]) + + # 기본 정보 + basic_info = ft.Column([ + ft.Row([ + ft.Text("제작사:", weight=ft.FontWeight.BOLD), + ft.Text(details.get("manufacturer", "-")) + ]), + ft.Row([ + ft.Text("도입일:", weight=ft.FontWeight.BOLD), + ft.Text(details.get("introduction_date", "-")) + ]), + ]) + + self.container.controls.extend([ + train_header, + ft.Divider(), + ft.Text("기본 정보", weight=ft.FontWeight.BOLD), + basic_info, + ft.Divider(), + ]) + + # 고장 이력 + if details.get("recent_faults"): + fault_list = ft.Column([ + ft.DataTable( + columns=[ + ft.DataColumn(ft.Text("날짜")), + ft.DataColumn(ft.Text("호차")), + ft.DataColumn(ft.Text("부품")), + ft.DataColumn(ft.Text("내용")), + ], + rows=[ + ft.DataRow( + cells=[ + ft.DataCell(ft.Text(fault["date"])), + ft.DataCell(ft.Text(fault["car"])), + ft.DataCell(ft.Text(fault["component"])), + ft.DataCell(ft.Text(fault["detail"])), + ] + ) + for fault in details["recent_faults"] + ] + ) + ]) + + self.container.controls.extend([ + ft.Text("최근 고장 이력", weight=ft.FontWeight.BOLD), + fault_list, + ft.Divider(), + ]) + + # 정비 이력 + if details.get("maintenance"): + maintenance_list = ft.Column([ + ft.DataTable( + columns=[ + ft.DataColumn(ft.Text("유형")), + ft.DataColumn(ft.Text("날짜")), + ft.DataColumn(ft.Text("내용")), + ], + rows=[ + ft.DataRow( + cells=[ + ft.DataCell(ft.Text(maint["type"])), + ft.DataCell(ft.Text(maint["date"])), + ft.DataCell(ft.Text(maint["details"])), + ] + ) + for maint in details["maintenance"] + ] + ) + ]) + + self.container.controls.extend([ + ft.Text("정비 이력", weight=ft.FontWeight.BOLD), + maintenance_list, + ft.Divider(), + ]) + + # 보고서 + if details.get("reports"): + report_list = ft.Column([ + ft.Row([ + ft.Text(f"{report['type']} 보고서: "), + ft.Text(report["date"]), + ft.FilledButton( + "보기", + icon=ft.icons.DESCRIPTION, + on_click=lambda e, file_path=report["file"]: self._open_report(file_path) + ) + ]) + for report in details["reports"] + ]) + + self.container.controls.extend([ + ft.Text("보고서", weight=ft.FontWeight.BOLD), + report_list, + ]) + + # HVAC 또는 다른 고장 정보 표시 + if fault_info and fault_info.get("found"): + component = fault_info.get("component", "") + faults = fault_info.get("faults", []) + + if faults: + component_header = ft.Text( + f"{component} 관련 최근 고장 이력", + size=16, + weight=ft.FontWeight.BOLD + ) + + fault_list = ft.DataTable( + columns=[ + ft.DataColumn(ft.Text("날짜")), + ft.DataColumn(ft.Text("편성")), + ft.DataColumn(ft.Text("호차")), + ft.DataColumn(ft.Text("내용")), + ], + rows=[ + ft.DataRow( + cells=[ + ft.DataCell(ft.Text(fault["date"])), + ft.DataCell(ft.Text(fault["formation"])), + ft.DataCell(ft.Text(fault["car"])), + ft.DataCell(ft.Text(fault["detail"])), + ] + ) + for fault in faults[:10] # 최대 10개만 표시 + ] + ) + + self.container.controls.extend([ + ft.Divider(height=30), + component_header, + fault_list, + ]) + + self.update() + + def _open_report(self, file_path: str): + """보고서 파일을 엽니다.""" + # 실제 구현에서는 파일 열기 로직 추가 + print(f"보고서 파일 열기: {file_path}") \ No newline at end of file diff --git a/modules/speech_recognition.py b/modules/speech_recognition.py new file mode 100644 index 0000000..89c171f --- /dev/null +++ b/modules/speech_recognition.py @@ -0,0 +1,234 @@ +import numpy as np +import os +import tempfile +import openai +import configparser +import wave +import struct +import threading +import queue +import time +from typing import Optional, Callable + +class SpeechRecognizer: + def __init__(self, config_path="config.ini"): + self.config = configparser.ConfigParser() + + if os.path.exists(config_path): + self.config.read(config_path) + + self.api_key = self.config.get("api", "openai_api_key", fallback="") + + if not self.api_key or self.api_key == "your_openai_api_key_here": + print("경고: OpenAI API 키가 설정되지 않았습니다. config.ini 파일을 확인하세요.") + + openai.api_key = self.api_key + + # 실시간 처리를 위한 설정 + self.is_processing = False + self.audio_queue = queue.Queue() + self.process_thread = None + self.callback = None + + def recognize(self, audio_data: np.ndarray) -> Optional[str]: + """오디오 데이터를 텍스트로 변환합니다.""" + if len(audio_data) == 0: + return None + + try: + # 임시 파일로 오디오 저장 + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_file: + # WAV 파일 형식으로 저장 + with wave.open(temp_file.name, 'wb') as wf: + wf.setnchannels(1) + wf.setsampwidth(2) # 16-bit + wf.setframerate(16000) + + # float32 데이터를 int16으로 변환 + audio_data_int = (audio_data * 32767).astype(np.int16) + wf.writeframes(audio_data_int.tobytes()) + + # OpenAI Whisper API를 사용하여 음성 인식 + with open(temp_file.name, "rb") as audio_file: + try: + if not self.api_key: + print("API 키가 설정되지 않았습니다.") + return None + + # 최신 OpenAI API 사용 방식 + try: + client = openai.OpenAI(api_key=self.api_key) + result = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.text + except (AttributeError, ImportError, NameError): + # 이전 버전 OpenAI 라이브러리 지원 (최대한 호환성 유지) + try: + # audio 모듈 사용 (최신 버전) + result = openai.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.text + except (AttributeError, ImportError, NameError): + # Audio 클래스 사용 (이전 버전) + try: + result = openai.Audio.transcribe( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.get("text", "") + except: + print("OpenAI API 호출 방식을 찾을 수 없습니다.") + return None + + # 임시 파일 삭제 + os.unlink(temp_file.name) + + return text_result + + except Exception as e: + print(f"API 호출 오류: {e}") + # 임시 파일 삭제 시도 + try: + os.unlink(temp_file.name) + except: + pass + return None + + except Exception as e: + print(f"음성 인식 오류: {e}") + return None + + def recognize_file(self, file_path: str) -> Optional[str]: + """오디오 파일을 텍스트로 변환합니다.""" + if not os.path.exists(file_path): + print(f"파일이 존재하지 않습니다: {file_path}") + return None + + try: + # OpenAI Whisper API를 사용하여 음성 인식 + with open(file_path, "rb") as audio_file: + try: + if not self.api_key: + print("API 키가 설정되지 않았습니다.") + return None + + # 최신 OpenAI API 사용 방식 + try: + client = openai.OpenAI(api_key=self.api_key) + result = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.text + except (AttributeError, ImportError, NameError): + # 이전 버전 OpenAI 라이브러리 지원 (최대한 호환성 유지) + try: + # audio 모듈 사용 (최신 버전) + result = openai.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.text + except (AttributeError, ImportError, NameError): + # Audio 클래스 사용 (이전 버전) + try: + result = openai.Audio.transcribe( + model="whisper-1", + file=audio_file, + language="ko", + response_format="text" + ) + text_result = result if isinstance(result, str) else result.get("text", "") + except: + print("OpenAI API 호출 방식을 찾을 수 없습니다.") + return None + + return text_result + + except Exception as e: + print(f"파일 API 호출 오류: {e}") + return None + + except Exception as e: + print(f"파일 음성 인식 오류: {e}") + return None + + def start_realtime_recognition(self, result_callback: Callable[[str], None]): + """실시간 음성 인식을 시작합니다.""" + if self.is_processing: + return + + self.is_processing = True + self.callback = result_callback + + # 오디오 처리 스레드 시작 + self.process_thread = threading.Thread(target=self._process_audio_queue) + self.process_thread.daemon = True + self.process_thread.start() + + def stop_realtime_recognition(self): + """실시간 음성 인식을 중지합니다.""" + self.is_processing = False + + # 큐 비우기 + while not self.audio_queue.empty(): + try: + self.audio_queue.get_nowait() + except queue.Empty: + break + + self.process_thread = None + + def add_audio_data(self, audio_data: np.ndarray): + """오디오 큐에 데이터를 추가합니다.""" + if self.is_processing: + self.audio_queue.put(audio_data) + + def _process_audio_queue(self): + """백그라운드에서 오디오 큐의 데이터를 처리합니다.""" + while self.is_processing: + try: + # 큐에서 오디오 데이터 가져오기 (1초 타임아웃) + audio_data = self.audio_queue.get(timeout=1.0) + + # 오디오 데이터 변환 + result = self.recognize(audio_data) + + # 결과가 있으면 콜백 호출 + if result and self.callback: + self.callback(result) + + except queue.Empty: + # 타임아웃 - 계속 진행 + time.sleep(0.1) + continue + except Exception as e: + print(f"오디오 처리 오류: {e}") + time.sleep(0.5) # 오류 시 잠시 대기 + + def set_api_key(self, api_key: str): + """OpenAI API 키를 설정합니다.""" + self.api_key = api_key + openai.api_key = api_key + + # config.ini 파일 업데이트 + self.config.set("api", "openai_api_key", api_key) + try: + with open("config.ini", "w") as config_file: + self.config.write(config_file) + except Exception as e: + print(f"설정 파일 저장 오류: {e}") \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..7c63da8 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1305 @@ +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. + +[[package]] +name = "anyio" +version = "4.5.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "certifi" +version = "2025.4.26" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, + {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "copier" +version = "8.1.0" +description = "A library for rendering project templates." +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "copier-8.1.0-py3-none-any.whl", hash = "sha256:56537163b1b24441a63504ebf56b8db588dee20b48ef4fc374a6c5a2b43010c1"}, + {file = "copier-8.1.0.tar.gz", hash = "sha256:902b4eb65fafe7a1621991234d2ebf3bc3fc9323e64e3a2560a00c05c73f6229"}, +] + +[package.dependencies] +colorama = ">=0.4.3" +decorator = ">=5.1.1" +dunamai = ">=1.7.0" +funcy = ">=1.17" +jinja2 = ">=3.1.1" +jinja2-ansible-filters = ">=1.3.1" +packaging = ">=23.0" +pathspec = ">=0.9.0" +plumbum = ">=1.6.9" +pydantic = ">=1.10.2,<2" +pygments = ">=2.7.1" +pyyaml = ">=5.3.1" +pyyaml-include = ">=1.2" +questionary = ">=1.8.1" + +[[package]] +name = "decorator" +version = "5.2.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, +] + +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + +[[package]] +name = "dunamai" +version = "1.23.1" +description = "Dynamic version generation" +optional = false +python-versions = ">=3.5" +groups = ["main"] +files = [ + {file = "dunamai-1.23.1-py3-none-any.whl", hash = "sha256:2611b0b9105a5797149ef82f4968a01dd912bdac857d49fc06856a4cfa58cf78"}, + {file = "dunamai-1.23.1.tar.gz", hash = "sha256:0b5712fc63bfb235263d912bfc5eb84590ba2201bb737268d25a5dbad7085489"}, +] + +[package.dependencies] +packaging = ">=20.9" + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "flet" +version = "0.9.0" +description = "Flet for Python - easily build interactive multi-platform apps in Python" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "flet-0.9.0-py3-none-any.whl", hash = "sha256:ce9843c5c3bc29b8bb57e8c7340d4f32dbf9ba4c1c8f8c875118297ed34be264"}, + {file = "flet-0.9.0-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:cd6133c149586b2c16e3701e35d1b1da18c1188331eeec689648a6ad68a9750d"}, + {file = "flet-0.9.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:1c3409e0c0d103e7d8f746f666e7cb7aa922c869f1fb7ad656f7a0821fb9feac"}, + {file = "flet-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56598e840ef02e6c37f5075b802e83ad77900aa1b74e632cf489d9d76d9fe21f"}, + {file = "flet-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4591e05cdd6a1edc5ce2252d91cfb6eff77a3e5f613be405b2fca06829936706"}, + {file = "flet-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec98488088fd9bccdd5d89dd6d1d3538939c2d8a41615284f8700049c74c2a3a"}, + {file = "flet-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:04e552c2c6e95e7b5374a5ed9d3d363b390afa6fe02666dfa79388ce12853a24"}, + {file = "flet-0.9.0-py3-none-win32.whl", hash = "sha256:7d8206f10763596b470e9982608503f7adb0a9ca5f4ed5ccf2762c52fdffaece"}, + {file = "flet-0.9.0-py3-none-win_amd64.whl", hash = "sha256:1178373e779fe3f2000a320eeafcc92032ba11098287ec56e28151a5557dad68"}, + {file = "flet-0.9.0.tar.gz", hash = "sha256:f06e20d79590bdfdc2c02267605b32d0551ee434f2aeb44053f2a73f88ab1654"}, +] + +[package.dependencies] +copier = ">=8.0.0,<9.0.0" +flet-runtime = "0.9.0" +packaging = ">=23.1,<24.0" +pydantic = "<2" +qrcode = ">=7.4.2,<8.0.0" +watchdog = ">=3.0.0,<4.0.0" +websocket-client = ">=1.5.2,<2.0.0" +websockets = ">=11.0.3,<12.0.0" + +[[package]] +name = "flet-core" +version = "0.9.0" +description = "Flet core library" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "flet_core-0.9.0-py3-none-any.whl", hash = "sha256:a03bc7099433a7170ebc99bc3c5200763fbe90fbd3cf1e999ffd07fb3abe4b9a"}, + {file = "flet_core-0.9.0.tar.gz", hash = "sha256:c90a000c1fbcf75d4aa4ac488d8d1b37986d6382d2bef408097d5fb07607eb1f"}, +] + +[package.dependencies] +repath = ">=0.9.0,<0.10.0" + +[[package]] +name = "flet-runtime" +version = "0.9.0" +description = "Flet Runtime - a base package for Flet desktop and Flet mobile." +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "flet_runtime-0.9.0-py3-none-any.whl", hash = "sha256:c6ace96e054b3456ce44786596008db5ff618b0981ad63294e2ee3c74e8500ae"}, + {file = "flet_runtime-0.9.0.tar.gz", hash = "sha256:76af913a74aaa38a07d93c77816685991d6e25563fd9885b34ad057906498e66"}, +] + +[package.dependencies] +flet-core = "0.9.0" +httpx = ">=0.24.1,<0.25.0" +oauthlib = ">=3.2.2,<4.0.0" + +[[package]] +name = "fsspec" +version = "2025.3.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3"}, + {file = "fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "funcy" +version = "2.0" +description = "A fancy and practical functional tools" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "funcy-2.0-py2.py3-none-any.whl", hash = "sha256:53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0"}, + {file = "funcy-2.0.tar.gz", hash = "sha256:3963315d59d41c6f30c04bc910e10ab50a3ac4a225868bfa96feed133df075cb"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "0.17.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, + {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.24.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, + {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.18.0" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.9\"" +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jinja2-ansible-filters" +version = "1.3.2" +description = "A port of Ansible's jinja2 filters without requiring ansible core." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "jinja2-ansible-filters-1.3.2.tar.gz", hash = "sha256:07c10cf44d7073f4f01102ca12d9a2dc31b41d47e4c61ed92ef6a6d2669b356b"}, + {file = "jinja2_ansible_filters-1.3.2-py3-none-any.whl", hash = "sha256:e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34"}, +] + +[package.dependencies] +Jinja2 = "*" +PyYAML = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "jiter" +version = "0.9.0" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad"}, + {file = "jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51"}, + {file = "jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708"}, + {file = "jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5"}, + {file = "jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678"}, + {file = "jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4"}, + {file = "jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322"}, + {file = "jiter-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6c4d99c71508912a7e556d631768dcdef43648a93660670986916b297f1c54af"}, + {file = "jiter-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f60fb8ce7df529812bf6c625635a19d27f30806885139e367af93f6e734ef58"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c4e1a4f8ea84d98b7b98912aa4290ac3d1eabfde8e3c34541fae30e9d1f08b"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f4c677c424dc76684fea3e7285a7a2a7493424bea89ac441045e6a1fb1d7b3b"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2221176dfec87f3470b21e6abca056e6b04ce9bff72315cb0b243ca9e835a4b5"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c7adb66f899ffa25e3c92bfcb593391ee1947dbdd6a9a970e0d7e713237d572"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98d27330fdfb77913c1097a7aab07f38ff2259048949f499c9901700789ac15"}, + {file = "jiter-0.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eda3f8cc74df66892b1d06b5d41a71670c22d95a1ca2cbab73654745ce9d0419"}, + {file = "jiter-0.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd5ab5ddc11418dce28343123644a100f487eaccf1de27a459ab36d6cca31043"}, + {file = "jiter-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42f8a68a69f047b310319ef8e2f52fdb2e7976fb3313ef27df495cf77bcad965"}, + {file = "jiter-0.9.0-cp311-cp311-win32.whl", hash = "sha256:a25519efb78a42254d59326ee417d6f5161b06f5da827d94cf521fed961b1ff2"}, + {file = "jiter-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:923b54afdd697dfd00d368b7ccad008cccfeb1efb4e621f32860c75e9f25edbd"}, + {file = "jiter-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7b46249cfd6c48da28f89eb0be3f52d6fdb40ab88e2c66804f546674e539ec11"}, + {file = "jiter-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:609cf3c78852f1189894383cf0b0b977665f54cb38788e3e6b941fa6d982c00e"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d726a3890a54561e55a9c5faea1f7655eda7f105bd165067575ace6e65f80bb2"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e89dc075c1fef8fa9be219e249f14040270dbc507df4215c324a1839522ea75"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e8ffa3c353b1bc4134f96f167a2082494351e42888dfcf06e944f2729cbe1d"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203f28a72a05ae0e129b3ed1f75f56bc419d5f91dfacd057519a8bd137b00c42"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fca1a02ad60ec30bb230f65bc01f611c8608b02d269f998bc29cca8619a919dc"}, + {file = "jiter-0.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:237e5cee4d5d2659aaf91bbf8ec45052cc217d9446070699441a91b386ae27dc"}, + {file = "jiter-0.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:528b6b71745e7326eed73c53d4aa57e2a522242320b6f7d65b9c5af83cf49b6e"}, + {file = "jiter-0.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9f48e86b57bc711eb5acdfd12b6cb580a59cc9a993f6e7dcb6d8b50522dcd50d"}, + {file = "jiter-0.9.0-cp312-cp312-win32.whl", hash = "sha256:699edfde481e191d81f9cf6d2211debbfe4bd92f06410e7637dffb8dd5dfde06"}, + {file = "jiter-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:099500d07b43f61d8bd780466d429c45a7b25411b334c60ca875fa775f68ccb0"}, + {file = "jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7"}, + {file = "jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d"}, + {file = "jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3"}, + {file = "jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5"}, + {file = "jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d"}, + {file = "jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53"}, + {file = "jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7"}, + {file = "jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001"}, + {file = "jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a"}, + {file = "jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf"}, + {file = "jiter-0.9.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4a2d16360d0642cd68236f931b85fe50288834c383492e4279d9f1792e309571"}, + {file = "jiter-0.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e84ed1c9c9ec10bbb8c37f450077cbe3c0d4e8c2b19f0a49a60ac7ace73c7452"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f3c848209ccd1bfa344a1240763975ca917de753c7875c77ec3034f4151d06c"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7825f46e50646bee937e0f849d14ef3a417910966136f59cd1eb848b8b5bb3e4"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d82a811928b26d1a6311a886b2566f68ccf2b23cf3bfed042e18686f1f22c2d7"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c058ecb51763a67f019ae423b1cbe3fa90f7ee6280c31a1baa6ccc0c0e2d06e"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9897115ad716c48f0120c1f0c4efae348ec47037319a6c63b2d7838bb53aaef4"}, + {file = "jiter-0.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:351f4c90a24c4fb8c87c6a73af2944c440494ed2bea2094feecacb75c50398ae"}, + {file = "jiter-0.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d45807b0f236c485e1e525e2ce3a854807dfe28ccf0d013dd4a563395e28008a"}, + {file = "jiter-0.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1537a890724ba00fdba21787010ac6f24dad47f763410e9e1093277913592784"}, + {file = "jiter-0.9.0-cp38-cp38-win32.whl", hash = "sha256:e3630ec20cbeaddd4b65513fa3857e1b7c4190d4481ef07fb63d0fad59033321"}, + {file = "jiter-0.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:2685f44bf80e95f8910553bf2d33b9c87bf25fceae6e9f0c1355f75d2922b0ee"}, + {file = "jiter-0.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9ef340fae98065071ccd5805fe81c99c8f80484e820e40043689cf97fb66b3e2"}, + {file = "jiter-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:efb767d92c63b2cd9ec9f24feeb48f49574a713870ec87e9ba0c2c6e9329c3e2"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113f30f87fb1f412510c6d7ed13e91422cfd329436364a690c34c8b8bd880c42"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8793b6df019b988526f5a633fdc7456ea75e4a79bd8396a3373c371fc59f5c9b"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a9aaa5102dba4e079bb728076fadd5a2dca94c05c04ce68004cfd96f128ea34"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d838650f6ebaf4ccadfb04522463e74a4c378d7e667e0eb1865cfe3990bfac49"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0194f813efdf4b8865ad5f5c5f50f8566df7d770a82c51ef593d09e0b347020"}, + {file = "jiter-0.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7954a401d0a8a0b8bc669199db78af435aae1e3569187c2939c477c53cb6a0a"}, + {file = "jiter-0.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4feafe787eb8a8d98168ab15637ca2577f6ddf77ac6c8c66242c2d028aa5420e"}, + {file = "jiter-0.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:27cd1f2e8bb377f31d3190b34e4328d280325ad7ef55c6ac9abde72f79e84d2e"}, + {file = "jiter-0.9.0-cp39-cp39-win32.whl", hash = "sha256:161d461dcbe658cf0bd0aa375b30a968b087cdddc624fc585f3867c63c6eca95"}, + {file = "jiter-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e8b36d8a16a61993be33e75126ad3d8aa29cf450b09576f3c427d27647fcb4aa"}, + {file = "jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "numpy" +version = "1.24.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "openai" +version = "1.76.2" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "openai-1.76.2-py3-none-any.whl", hash = "sha256:9c1d9ad59e6e3bea7205eedc9ca66eeebae18d47b527e505a2b0d2fb1538e26e"}, + {file = "openai-1.76.2.tar.gz", hash = "sha256:f430c8b848775907405c6eff54621254c96f6444c593c097e0cc3a9f8fdda96f"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.11,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +realtime = ["websockets (>=13,<16)"] +voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "plumbum" +version = "1.9.0" +description = "Plumbum: shell combinators library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "plumbum-1.9.0-py3-none-any.whl", hash = "sha256:9fd0d3b0e8d86e4b581af36edf3f3bbe9d1ae15b45b8caab28de1bcb27aaa7f5"}, + {file = "plumbum-1.9.0.tar.gz", hash = "sha256:e640062b72642c3873bd5bdc3effed75ba4d3c70ef6b6a7b907357a84d909219"}, +] + +[package.dependencies] +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +pywin32 = {version = "*", markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""} + +[package.extras] +dev = ["coverage[toml]", "paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"] +docs = ["sphinx (>=4.0.0)", "sphinx-rtd-theme (>=1.0.0)"] +ssh = ["paramiko"] +test = ["coverage[toml]", "paramiko", "psutil", "pytest (>=6.0)", "pytest-cov", "pytest-mock", "pytest-timeout"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, + {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "pyaudio" +version = "0.2.14" +description = "Cross-platform audio I/O with PortAudio" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "PyAudio-0.2.14-cp310-cp310-win32.whl", hash = "sha256:126065b5e82a1c03ba16e7c0404d8f54e17368836e7d2d92427358ad44fefe61"}, + {file = "PyAudio-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:2a166fc88d435a2779810dd2678354adc33499e9d4d7f937f28b20cc55893e83"}, + {file = "PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289"}, + {file = "PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903"}, + {file = "PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b"}, + {file = "PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3"}, + {file = "PyAudio-0.2.14-cp313-cp313-win32.whl", hash = "sha256:95328285b4dab57ea8c52a4a996cb52be6d629353315be5bfda403d15932a497"}, + {file = "PyAudio-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:692d8c1446f52ed2662120bcd9ddcb5aa2b71f38bda31e58b19fb4672fffba69"}, + {file = "PyAudio-0.2.14-cp38-cp38-win32.whl", hash = "sha256:858caf35b05c26d8fc62f1efa2e8f53d5fa1a01164842bd622f70ddc41f55000"}, + {file = "PyAudio-0.2.14-cp38-cp38-win_amd64.whl", hash = "sha256:2dac0d6d675fe7e181ba88f2de88d321059b69abd52e3f4934a8878e03a7a074"}, + {file = "PyAudio-0.2.14-cp39-cp39-win32.whl", hash = "sha256:f745109634a7c19fa4d6b8b7d6967c3123d988c9ade0cd35d4295ee1acdb53e9"}, + {file = "PyAudio-0.2.14-cp39-cp39-win_amd64.whl", hash = "sha256:009f357ee5aa6bc8eb19d69921cd30e98c42cddd34210615d592a71d09c4bd57"}, + {file = "PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87"}, +] + +[package.extras] +test = ["numpy"] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "1.10.22" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pydantic-1.10.22-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:57889565ccc1e5b7b73343329bbe6198ebc472e3ee874af2fa1865cfe7048228"}, + {file = "pydantic-1.10.22-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90729e22426de79bc6a3526b4c45ec4400caf0d4f10d7181ba7f12c01bb3897d"}, + {file = "pydantic-1.10.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8684d347f351554ec94fdcb507983d3116dc4577fb8799fed63c65869a2d10"}, + {file = "pydantic-1.10.22-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8dad498ceff2d9ef1d2e2bc6608f5b59b8e1ba2031759b22dfb8c16608e1802"}, + {file = "pydantic-1.10.22-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fac529cc654d4575cf8de191cce354b12ba705f528a0a5c654de6d01f76cd818"}, + {file = "pydantic-1.10.22-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4148232aded8dd1dd13cf910a01b32a763c34bd79a0ab4d1ee66164fcb0b7b9d"}, + {file = "pydantic-1.10.22-cp310-cp310-win_amd64.whl", hash = "sha256:ece68105d9e436db45d8650dc375c760cc85a6793ae019c08769052902dca7db"}, + {file = "pydantic-1.10.22-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e530a8da353f791ad89e701c35787418605d35085f4bdda51b416946070e938"}, + {file = "pydantic-1.10.22-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:654322b85642e9439d7de4c83cb4084ddd513df7ff8706005dada43b34544946"}, + {file = "pydantic-1.10.22-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8bece75bd1b9fc1c32b57a32831517943b1159ba18b4ba32c0d431d76a120ae"}, + {file = "pydantic-1.10.22-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eccb58767f13c6963dcf96d02cb8723ebb98b16692030803ac075d2439c07b0f"}, + {file = "pydantic-1.10.22-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7778e6200ff8ed5f7052c1516617423d22517ad36cc7a3aedd51428168e3e5e8"}, + {file = "pydantic-1.10.22-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffe02767d27c39af9ca7dc7cd479c00dda6346bb62ffc89e306f665108317a2"}, + {file = "pydantic-1.10.22-cp311-cp311-win_amd64.whl", hash = "sha256:23bc19c55427091b8e589bc08f635ab90005f2dc99518f1233386f46462c550a"}, + {file = "pydantic-1.10.22-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:92d0f97828a075a71d9efc65cf75db5f149b4d79a38c89648a63d2932894d8c9"}, + {file = "pydantic-1.10.22-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af5a2811b6b95b58b829aeac5996d465a5f0c7ed84bd871d603cf8646edf6ff"}, + {file = "pydantic-1.10.22-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cf06d8d40993e79af0ab2102ef5da77b9ddba51248e4cb27f9f3f591fbb096e"}, + {file = "pydantic-1.10.22-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:184b7865b171a6057ad97f4a17fbac81cec29bd103e996e7add3d16b0d95f609"}, + {file = "pydantic-1.10.22-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:923ad861677ab09d89be35d36111156063a7ebb44322cdb7b49266e1adaba4bb"}, + {file = "pydantic-1.10.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:82d9a3da1686443fb854c8d2ab9a473251f8f4cdd11b125522efb4d7c646e7bc"}, + {file = "pydantic-1.10.22-cp312-cp312-win_amd64.whl", hash = "sha256:1612604929af4c602694a7f3338b18039d402eb5ddfbf0db44f1ebfaf07f93e7"}, + {file = "pydantic-1.10.22-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b259dc89c9abcd24bf42f31951fb46c62e904ccf4316393f317abeeecda39978"}, + {file = "pydantic-1.10.22-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9238aa0964d80c0908d2f385e981add58faead4412ca80ef0fa352094c24e46d"}, + {file = "pydantic-1.10.22-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8029f05b04080e3f1a550575a1bca747c0ea4be48e2d551473d47fd768fc1b"}, + {file = "pydantic-1.10.22-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c06918894f119e0431a36c9393bc7cceeb34d1feeb66670ef9b9ca48c073937"}, + {file = "pydantic-1.10.22-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e205311649622ee8fc1ec9089bd2076823797f5cd2c1e3182dc0e12aab835b35"}, + {file = "pydantic-1.10.22-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:815f0a73d5688d6dd0796a7edb9eca7071bfef961a7b33f91e618822ae7345b7"}, + {file = "pydantic-1.10.22-cp313-cp313-win_amd64.whl", hash = "sha256:9dfce71d42a5cde10e78a469e3d986f656afc245ab1b97c7106036f088dd91f8"}, + {file = "pydantic-1.10.22-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3ecaf8177b06aac5d1f442db1288e3b46d9f05f34fd17fdca3ad34105328b61a"}, + {file = "pydantic-1.10.22-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb36c2de9ea74bd7f66b5481dea8032d399affd1cbfbb9bb7ce539437f1fce62"}, + {file = "pydantic-1.10.22-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6b8d14a256be3b8fff9286d76c532f1a7573fbba5f189305b22471c6679854d"}, + {file = "pydantic-1.10.22-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:1c33269e815db4324e71577174c29c7aa30d1bba51340ce6be976f6f3053a4c6"}, + {file = "pydantic-1.10.22-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:8661b3ab2735b2a9ccca2634738534a795f4a10bae3ab28ec0a10c96baa20182"}, + {file = "pydantic-1.10.22-cp37-cp37m-win_amd64.whl", hash = "sha256:22bdd5fe70d4549995981c55b970f59de5c502d5656b2abdfcd0a25be6f3763e"}, + {file = "pydantic-1.10.22-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e3f33d1358aa4bc2795208cc29ff3118aeaad0ea36f0946788cf7cadeccc166b"}, + {file = "pydantic-1.10.22-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:813f079f9cd136cac621f3f9128a4406eb8abd2ad9fdf916a0731d91c6590017"}, + {file = "pydantic-1.10.22-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab618ab8dca6eac7f0755db25f6aba3c22c40e3463f85a1c08dc93092d917704"}, + {file = "pydantic-1.10.22-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d128e1aaa38db88caca920d5822c98fc06516a09a58b6d3d60fa5ea9099b32cc"}, + {file = "pydantic-1.10.22-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:cc97bbc25def7025e55fc9016080773167cda2aad7294e06a37dda04c7d69ece"}, + {file = "pydantic-1.10.22-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dda5d7157d543b1fa565038cae6e952549d0f90071c839b3740fb77c820fab8"}, + {file = "pydantic-1.10.22-cp38-cp38-win_amd64.whl", hash = "sha256:a093fe44fe518cb445d23119511a71f756f8503139d02fcdd1173f7b76c95ffe"}, + {file = "pydantic-1.10.22-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec54c89b2568b258bb30d7348ac4d82bec1b58b377fb56a00441e2ac66b24587"}, + {file = "pydantic-1.10.22-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8f1d1a1532e4f3bcab4e34e8d2197a7def4b67072acd26cfa60e92d75803a48"}, + {file = "pydantic-1.10.22-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ad83ca35508c27eae1005b6b61f369f78aae6d27ead2135ec156a2599910121"}, + {file = "pydantic-1.10.22-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53cdb44b78c420f570ff16b071ea8cd5a477635c6b0efc343c8a91e3029bbf1a"}, + {file = "pydantic-1.10.22-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:16d0a5ae9d98264186ce31acdd7686ec05fd331fab9d68ed777d5cb2d1514e5e"}, + {file = "pydantic-1.10.22-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8aee040e25843f036192b1a1af62117504a209a043aa8db12e190bb86ad7e611"}, + {file = "pydantic-1.10.22-cp39-cp39-win_amd64.whl", hash = "sha256:7f691eec68dbbfca497d3c11b92a3e5987393174cbedf03ec7a4184c35c2def6"}, + {file = "pydantic-1.10.22-py3-none-any.whl", hash = "sha256:343037d608bcbd34df937ac259708bfc83664dadf88afe8516c4f282d7d471a9"}, + {file = "pydantic-1.10.22.tar.gz", hash = "sha256:ee1006cebd43a8e7158fb7190bb8f4e2da9649719bff65d0c287282ec38dec6d"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygments" +version = "2.19.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pypng" +version = "0.20220715.0" +description = "Pure Python library for saving and loading PNG images" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"}, + {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, +] + +[[package]] +name = "pywin32" +version = "310" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +groups = ["main"] +markers = "platform_system == \"Windows\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, + {file = "pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d"}, + {file = "pywin32-310-cp310-cp310-win_arm64.whl", hash = "sha256:33babed0cf0c92a6f94cc6cc13546ab24ee13e3e800e61ed87609ab91e4c8213"}, + {file = "pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd"}, + {file = "pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c"}, + {file = "pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582"}, + {file = "pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d"}, + {file = "pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060"}, + {file = "pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966"}, + {file = "pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab"}, + {file = "pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e"}, + {file = "pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33"}, + {file = "pywin32-310-cp38-cp38-win32.whl", hash = "sha256:0867beb8addefa2e3979d4084352e4ac6e991ca45373390775f7084cc0209b9c"}, + {file = "pywin32-310-cp38-cp38-win_amd64.whl", hash = "sha256:30f0a9b3138fb5e07eb4973b7077e1883f558e40c578c6925acc7a94c34eaa36"}, + {file = "pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a"}, + {file = "pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "pyyaml-include" +version = "2.2" +description = "An extending constructor of PyYAML: include other YAML files into current YAML document" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyyaml_include-2.2-py3-none-any.whl", hash = "sha256:489fff69f78bad8b9509d006297a0140fd91382a66775b8b1da0ce7e126c1815"}, + {file = "pyyaml_include-2.2.tar.gz", hash = "sha256:6f0c7e2ac56cdd9cc305b04122817b55514e6ce8584869fae2bc2a4ef2e0d40f"}, +] + +[package.dependencies] +fsspec = ">=2021.04.0" +PyYAML = ">=6.0,<7.0" +typing-extensions = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "qrcode" +version = "7.4.2" +description = "QR Code image generator" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"}, + {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +pypng = "*" +typing-extensions = "*" + +[package.extras] +all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"] +dev = ["pytest", "pytest-cov", "tox"] +maintainer = ["zest.releaser[recommended]"] +pil = ["pillow (>=9.1.0)"] +test = ["coverage", "pytest"] + +[[package]] +name = "questionary" +version = "2.1.0" +description = "Python library to build pretty command line user prompts ⭐️" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, + {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, +] + +[package.dependencies] +prompt_toolkit = ">=2.0,<4.0" + +[[package]] +name = "repath" +version = "0.9.0" +description = "Generate regular expressions form ExpressJS path patterns" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "repath-0.9.0-py3-none-any.whl", hash = "sha256:ee079d6c91faeb843274d22d8f786094ee01316ecfe293a1eb6546312bb6a318"}, + {file = "repath-0.9.0.tar.gz", hash = "sha256:8292139bac6a0e43fd9d70605d4e8daeb25d46672e484ed31a24c7ce0aef0fb7"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sounddevice" +version = "0.4.7" +description = "Play and Record Sound with Python" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sounddevice-0.4.7-py3-none-any.whl", hash = "sha256:1c3f18bfa4d9a257f5715f2ab83f2c0eb412a09f3e6a9fa73720886ca88f6bc7"}, + {file = "sounddevice-0.4.7-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d6ddfd341ad7412b14ca001f2c4dbf5fa2503bdc9eb15ad2c3105f6c260b698a"}, + {file = "sounddevice-0.4.7-py3-none-win32.whl", hash = "sha256:1ec1df094c468a210113aa22c4f390d5b4d9c7a73e41a6cb6ecfec83db59b380"}, + {file = "sounddevice-0.4.7-py3-none-win_amd64.whl", hash = "sha256:0c8b3543da1496f282b66a7bc54b755577ba638b1af06c146d4ac7f39d86b548"}, + {file = "sounddevice-0.4.7.tar.gz", hash = "sha256:69b386818d50a2d518607d4b973442e8d524760c7cd6c8b8be03d8c98fc4bce7"}, +] + +[package.dependencies] +CFFI = ">=1.0" + +[package.extras] +numpy = ["NumPy"] + +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[[package]] +name = "watchdog" +version = "3.0.0" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, + {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, + {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, + {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, + {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, + {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, + {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, + {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, + {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] + +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.9\"" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[metadata] +lock-version = "2.1" +python-versions = "^3.8" +content-hash = "45cc5a82edeb1d93670ba5ee2e56925e68d129abeca2481bf890f70a48ed5ed2" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9ffc8cf --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "trnote" +version = "0.1.0" +description = "철도 음성인식 시스템" +authors = ["Your Name "] +readme = "README.md" +packages = [{include = "."}] + +[tool.poetry.dependencies] +python = "^3.8" +flet = "^0.9.0" +openai = "^1.0.0" +numpy = "^1.22.0" +sounddevice = "^0.4.6" +pyaudio = "^0.2.13" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +trnote = "main:main" \ No newline at end of file diff --git a/railway_data.db b/railway_data.db new file mode 100644 index 0000000..77ea82e Binary files /dev/null and b/railway_data.db differ diff --git a/updater/__init__.py b/updater/__init__.py new file mode 100644 index 0000000..e69de29