restructure the project for packaging
This commit is contained in:
0
src/api_client/__init__.py
Normal file
0
src/api_client/__init__.py
Normal file
62
src/api_client/client.py
Normal file
62
src/api_client/client.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
API client functions for interacting with the Airport Announcement System backend.
|
||||
"""
|
||||
import requests
|
||||
from typing import List, Optional
|
||||
|
||||
# This can be overridden through environment variables
|
||||
API_BASE_URL = "http://localhost:7999"
|
||||
|
||||
def get_groups() -> List[dict]:
|
||||
"""Get all endpoint groups."""
|
||||
response = requests.get(f"{API_BASE_URL}/groups")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_group(group_id: int) -> Optional[dict]:
|
||||
"""Get a specific endpoint group by ID."""
|
||||
response = requests.get(f"{API_BASE_URL}/groups/{group_id}")
|
||||
if response.status_code == 404:
|
||||
return None
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def create_group(group: dict) -> dict:
|
||||
"""Create a new endpoint group."""
|
||||
response = requests.post(f"{API_BASE_URL}/groups", json=group)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def update_group(group_id: int, updated_group: dict) -> dict:
|
||||
"""Update an existing endpoint group."""
|
||||
response = requests.put(f"{API_BASE_URL}/groups/{group_id}", json=updated_group)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def delete_group(group_id: int) -> None:
|
||||
"""Delete an endpoint group."""
|
||||
response = requests.delete(f"{API_BASE_URL}/groups/{group_id}")
|
||||
response.raise_for_status()
|
||||
|
||||
def start_announcement(text: str, group_id: int) -> None:
|
||||
"""Start a new announcement."""
|
||||
response = requests.post(f"{API_BASE_URL}/announcements", params={"text": text, "group_id": group_id})
|
||||
response.raise_for_status()
|
||||
|
||||
def get_announcement_status() -> dict:
|
||||
"""Get the status of the current announcement."""
|
||||
response = requests.get(f"{API_BASE_URL}/announcements/status")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_available_endpoints() -> List[str]:
|
||||
"""Get all available endpoints."""
|
||||
response = requests.get(f"{API_BASE_URL}/endpoints")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_available_languages() -> List[str]:
|
||||
"""Get all available languages for announcements."""
|
||||
response = requests.get(f"{API_BASE_URL}/languages")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
35
src/api_client/models.py
Normal file
35
src/api_client/models.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
API models for the Airport Announcement System.
|
||||
"""
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
|
||||
class AnnouncementStates(Enum):
|
||||
IDLE: str = "Ready"
|
||||
TRANSLATING: str = "Translating"
|
||||
GENERATING_VOICE: str = "Generating voice synthesis"
|
||||
ROUTING: str = "Routing to endpoints"
|
||||
ACTIVE: str = "Broadcasting announcement"
|
||||
COMPLETE: str = "Complete"
|
||||
ERROR: str = "Error"
|
||||
|
||||
class AnnouncementParameters(BaseModel):
|
||||
text: Optional[str] = None
|
||||
languages: List[str] = []
|
||||
start_time: Optional[float] = None
|
||||
|
||||
class AnnouncementProgress(BaseModel):
|
||||
current_state: str = AnnouncementStates.IDLE.value
|
||||
progress: float = Field(default=0.0, ge=0.0, le=1.0)
|
||||
error: Optional[str] = None
|
||||
|
||||
class EndpointGroup(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
endpoints: List[str]
|
||||
languages: List[str]
|
||||
|
||||
# Announcement parameters and progress as nested models
|
||||
parameters: AnnouncementParameters = Field(default_factory=AnnouncementParameters)
|
||||
progress: AnnouncementProgress = Field(default_factory=AnnouncementProgress)
|
||||
0
src/auracaster_webui/__init__.py
Normal file
0
src/auracaster_webui/__init__.py
Normal file
248
src/auracaster_webui/app.py
Normal file
248
src/auracaster_webui/app.py
Normal file
@@ -0,0 +1,248 @@
|
||||
"""
|
||||
Airport Announcement System Streamlit frontend application.
|
||||
"""
|
||||
import streamlit as st
|
||||
|
||||
# Page setup must be first
|
||||
st.set_page_config(page_title="Airport Announcement System", page_icon="✈️")
|
||||
|
||||
import time
|
||||
import requests
|
||||
from api_client.client import (
|
||||
get_groups, get_available_languages, get_announcement_status,
|
||||
start_announcement, update_group, get_available_endpoints
|
||||
)
|
||||
|
||||
# Initialize session state for configuration
|
||||
if "endpoint_groups" not in st.session_state:
|
||||
try:
|
||||
st.session_state.endpoint_groups = get_groups()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to load endpoint groups: {str(e)}")
|
||||
st.session_state.endpoint_groups = []
|
||||
|
||||
# Initialize session state for available languages
|
||||
if "available_languages" not in st.session_state:
|
||||
try:
|
||||
st.session_state.available_languages = get_available_languages()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to load available languages: {str(e)}")
|
||||
st.session_state.available_languages = ["German", "English"] # Fallback languages
|
||||
|
||||
# Initialize session state for announcement text and status tracking
|
||||
if "announcement_text" not in st.session_state:
|
||||
st.session_state.announcement_text = "Hallo Welt."
|
||||
if "show_success_message" not in st.session_state:
|
||||
st.session_state.show_success_message = False
|
||||
if "announcement_id" not in st.session_state:
|
||||
st.session_state.announcement_id = 0
|
||||
if "status_container_key" not in st.session_state:
|
||||
st.session_state.status_container_key = 0
|
||||
|
||||
|
||||
def show_announcement_status():
|
||||
try:
|
||||
status_data = get_announcement_status()
|
||||
if status_data["state"] != "Ready":
|
||||
# Create a container with a unique key for each announcement
|
||||
# This ensures we get a fresh container for each new announcement
|
||||
with st.container(key=f"status_container_{st.session_state.status_container_key}"):
|
||||
# Create the status without a key parameter
|
||||
status = st.status("**Airport PA System Status**", expanded=True)
|
||||
|
||||
with status:
|
||||
# Progress elements
|
||||
progress_bar = st.progress(status_data["progress"])
|
||||
time_col, stage_col = st.columns([1, 3])
|
||||
|
||||
# Track last displayed state
|
||||
last_state = None
|
||||
|
||||
# Update loop
|
||||
while status_data["state"] not in ["Complete", "Error"]:
|
||||
# Update time elapsed continuously
|
||||
|
||||
# Only update stage display if state changed
|
||||
if status_data["state"] != last_state:
|
||||
stage_col.write(f"**Stage:** {status_data['state']}")
|
||||
last_state = status_data["state"]
|
||||
time_col.write(f"⏱️ Time elapsed: {time.time() - status_data['details']['start_time']:.1f}s")
|
||||
|
||||
# Update progress bar
|
||||
progress_bar.progress(status_data["progress"])
|
||||
time.sleep(0.3)
|
||||
|
||||
# Refresh status data
|
||||
status_data = get_announcement_status()
|
||||
|
||||
# Make sure to update the progress bar one final time with the final state's progress value
|
||||
progress_bar.progress(status_data["progress"])
|
||||
|
||||
# Final state
|
||||
if status_data["state"] == "Error":
|
||||
st.error(f"❌ Error: {status_data['error']}")
|
||||
else:
|
||||
st.success("✅ Announcement completed successfully")
|
||||
st.write(f"📢 Announcement made to group {status_data['details']['group']['name']}:")
|
||||
st.write(f"📡 Endpoints: {', '.join(status_data['details']['group']['endpoints'])}")
|
||||
st.write(f"🗣️ '{status_data['details']['text']}'")
|
||||
st.write(f"🌐 Languages: {', '.join(status_data['details']['languages'])}")
|
||||
|
||||
# Clear the success message when announcement completes
|
||||
st.session_state.show_success_message = False
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to get announcement status: {str(e)}")
|
||||
|
||||
|
||||
# Main interface
|
||||
st.title("Airport Announcement System ✈️")
|
||||
|
||||
# Announcements section
|
||||
with st.container():
|
||||
st.header("Announcements")
|
||||
|
||||
# Predefined announcements
|
||||
st.write("**Predefined Announcements** (click to autofill)")
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
if st.button("Final Boarding Call"):
|
||||
st.session_state.announcement_text = "This is the final boarding call for flight LX-380 to New York"
|
||||
with col2:
|
||||
if st.button("Security Reminder"):
|
||||
st.session_state.announcement_text = "Please keep your luggage with you at all times"
|
||||
with col3:
|
||||
if st.button("Delay Notice"):
|
||||
st.session_state.announcement_text = "We regret to inform you of a 30-minute delay"
|
||||
|
||||
# Custom announcement
|
||||
with st.form("custom_announcement"):
|
||||
# Get all groups with their names and IDs
|
||||
group_options = [(g["name"], g["id"]) for g in st.session_state.endpoint_groups]
|
||||
selected_group_name = st.selectbox(
|
||||
"Select announcement area",
|
||||
options=[g[0] for g in group_options]
|
||||
)
|
||||
message = st.text_area("Enter announcement text", st.session_state.announcement_text)
|
||||
|
||||
if st.form_submit_button("Make Announcement"):
|
||||
try:
|
||||
selected_group_id = next(g[1] for g in group_options if g[0] == selected_group_name)
|
||||
start_announcement(message, selected_group_id)
|
||||
|
||||
# Set flag to show success message
|
||||
st.session_state.show_success_message = True
|
||||
|
||||
# Increment announcement ID to ensure a fresh status container
|
||||
st.session_state.announcement_id += 1
|
||||
st.session_state.status_container_key = st.session_state.announcement_id
|
||||
|
||||
# Clear the announcement text after successful submission
|
||||
st.session_state.announcement_text = ""
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to start announcement: {str(e)}")
|
||||
|
||||
# Configuration section in sidebar
|
||||
with st.sidebar:
|
||||
st.header("Configuration")
|
||||
|
||||
with st.expander("Endpoint Groups"):
|
||||
for i, group in enumerate(st.session_state.endpoint_groups):
|
||||
cols = st.columns([4, 1])
|
||||
with cols[0]:
|
||||
# Use a unique key for the text input
|
||||
input_key = f"group_name_{i}"
|
||||
|
||||
# Initialize the previous value in session state if not present
|
||||
if f"prev_{input_key}" not in st.session_state:
|
||||
st.session_state[f"prev_{input_key}"] = group["name"]
|
||||
|
||||
new_name = st.text_input(
|
||||
f"Group Name",
|
||||
value=group["name"],
|
||||
key=input_key,
|
||||
on_change=lambda: None # Prevent automatic callbacks
|
||||
)
|
||||
|
||||
# Only update if the name has changed and it's different from the previous value
|
||||
if new_name != group["name"] and new_name != st.session_state[f"prev_{input_key}"]:
|
||||
try:
|
||||
updated_group = group.copy()
|
||||
updated_group["name"] = new_name
|
||||
update_group(group["id"], updated_group)
|
||||
# Update the session state with the latest groups
|
||||
st.session_state.endpoint_groups = get_groups()
|
||||
# Update the previous value before rerunning
|
||||
st.session_state[f"prev_{input_key}"] = new_name
|
||||
st.rerun()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to update group name: {str(e)}")
|
||||
|
||||
try:
|
||||
available_endpoints = get_available_endpoints()
|
||||
# Use a unique key for the endpoints multiselect
|
||||
endpoints_key = f"endpoints_select_{i}"
|
||||
|
||||
# Initialize the previous value in session state if not present
|
||||
if f"prev_{endpoints_key}" not in st.session_state:
|
||||
st.session_state[f"prev_{endpoints_key}"] = group["endpoints"]
|
||||
|
||||
selected_endpoints = st.multiselect(
|
||||
f"Endpoints",
|
||||
options=available_endpoints,
|
||||
default=group["endpoints"],
|
||||
key=endpoints_key
|
||||
)
|
||||
|
||||
# Only update if endpoints have changed and they're different from previous value
|
||||
if selected_endpoints != group["endpoints"] and selected_endpoints != st.session_state[f"prev_{endpoints_key}"]:
|
||||
updated_group = group.copy()
|
||||
updated_group["endpoints"] = selected_endpoints
|
||||
update_group(group["id"], updated_group)
|
||||
# Update the previous value before rerunning
|
||||
st.session_state[f"prev_{endpoints_key}"] = selected_endpoints
|
||||
st.session_state.endpoint_groups = get_groups()
|
||||
st.rerun()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to get available endpoints: {str(e)}")
|
||||
|
||||
with cols[1]:
|
||||
if st.button("Delete", key=f"delete_group_{i}"):
|
||||
try:
|
||||
# This is assumed to exist in the API client module
|
||||
from api_client.client import delete_group
|
||||
delete_group(group["id"])
|
||||
# Update the session state with the latest groups
|
||||
st.session_state.endpoint_groups = get_groups()
|
||||
st.rerun()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to delete group: {str(e)}")
|
||||
|
||||
# Add new group
|
||||
st.subheader("Add New Group")
|
||||
with st.form("add_group_form"):
|
||||
new_group_name = st.text_input("New Group Name")
|
||||
|
||||
try:
|
||||
available_endpoints = get_available_endpoints()
|
||||
new_group_endpoints = st.multiselect("Endpoints", options=available_endpoints)
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to get available endpoints: {str(e)}")
|
||||
new_group_endpoints = []
|
||||
|
||||
if st.form_submit_button("Add Group"):
|
||||
if new_group_name:
|
||||
try:
|
||||
from api_client.client import create_group
|
||||
new_group = {"name": new_group_name, "endpoints": new_group_endpoints}
|
||||
create_group(new_group)
|
||||
# Update the session state with the latest groups
|
||||
st.session_state.endpoint_groups = get_groups()
|
||||
st.rerun()
|
||||
except requests.exceptions.RequestException as e:
|
||||
st.error(f"Failed to create group: {str(e)}")
|
||||
else:
|
||||
st.error("Group name cannot be empty")
|
||||
|
||||
# Show the announcement status
|
||||
show_announcement_status()
|
||||
27
src/auracaster_webui/main_ui.py
Normal file
27
src/auracaster_webui/main_ui.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Main entry point for the Airport Announcement System frontend.
|
||||
"""
|
||||
import streamlit.web.cli as stcli
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
def run_app():
|
||||
"""Run the Streamlit app."""
|
||||
# Get the directory of this file
|
||||
current_dir = Path(__file__).parent
|
||||
|
||||
# Set the path to the Streamlit app file
|
||||
app_path = current_dir / "app.py"
|
||||
|
||||
# Change directory to the app's directory
|
||||
os.chdir(current_dir)
|
||||
|
||||
# Use sys.argv[0] as the program name
|
||||
sys.argv = ["streamlit", "run", str(app_path)]
|
||||
|
||||
# Run the Streamlit CLI
|
||||
sys.exit(stcli.main())
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_app()
|
||||
0
src/mock_backend/__init__.py
Normal file
0
src/mock_backend/__init__.py
Normal file
72
src/mock_backend/mock_api.py
Normal file
72
src/mock_backend/mock_api.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
FastAPI implementation of the Airport Announcement System mock backend API.
|
||||
"""
|
||||
from fastapi import FastAPI, HTTPException
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to the Python path
|
||||
src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if src_path not in sys.path:
|
||||
sys.path.insert(0, src_path)
|
||||
|
||||
# Use absolute imports instead of relative imports
|
||||
from mock_backend.mock_backend import announcement_system
|
||||
from api_client.models import EndpointGroup
|
||||
from typing import List
|
||||
import uvicorn
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/groups", response_model=List[EndpointGroup])
|
||||
def get_groups():
|
||||
return announcement_system.get_endpoint_groups()
|
||||
|
||||
@app.post("/groups", response_model=EndpointGroup)
|
||||
def create_group(group: EndpointGroup):
|
||||
try:
|
||||
return announcement_system.add_endpoint_group(group)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@app.put("/groups/{group_id}", response_model=EndpointGroup)
|
||||
def update_group(group_id: int, updated_group: EndpointGroup):
|
||||
try:
|
||||
return announcement_system.update_endpoint_group(group_id, updated_group)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
@app.delete("/groups/{group_id}")
|
||||
def delete_group(group_id: int):
|
||||
try:
|
||||
announcement_system.delete_endpoint_group(group_id)
|
||||
return {"message": "Group deleted successfully"}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
@app.post("/announcements")
|
||||
def start_announcement(text: str, group_id: int):
|
||||
group = announcement_system.get_endpoint_group(group_id)
|
||||
if not group:
|
||||
raise HTTPException(status_code=404, detail=f"Group with ID {group_id} not found")
|
||||
|
||||
try:
|
||||
announcement_system.start_announcement(text, group)
|
||||
return {"message": "Announcement started successfully"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@app.get("/announcements/status")
|
||||
def get_announcement_status():
|
||||
return announcement_system.get_announcement_status()
|
||||
|
||||
@app.get("/endpoints")
|
||||
def get_available_endpoints():
|
||||
return announcement_system.get_available_endpoints()
|
||||
|
||||
@app.get("/languages")
|
||||
def get_available_languages():
|
||||
return announcement_system.get_available_languages()
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run('mock_backend.mock_api:app', host="0.0.0.0", port=7999, reload=True)
|
||||
238
src/mock_backend/mock_backend.py
Normal file
238
src/mock_backend/mock_backend.py
Normal file
@@ -0,0 +1,238 @@
|
||||
"""
|
||||
Mock implementation of the Airport Announcement System backend.
|
||||
"""
|
||||
import time
|
||||
import threading
|
||||
from typing import Optional, List
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to the Python path
|
||||
src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
if src_path not in sys.path:
|
||||
sys.path.insert(0, src_path)
|
||||
|
||||
from api_client.models import EndpointGroup, AnnouncementStates
|
||||
|
||||
AVAILABLE_ENDPOINTS = [f"endpoint{i}" for i in range(1, 4)] # Predefined endpoints
|
||||
AVAILABLE_LANGUAGES = ["German", "English", "French", "Spanish", "Italian"]
|
||||
|
||||
class AnnouncementSystem:
|
||||
def __init__(self):
|
||||
self.available_endpoints = AVAILABLE_ENDPOINTS
|
||||
self.available_languages = AVAILABLE_LANGUAGES
|
||||
self.endpoint_groups = [
|
||||
EndpointGroup(
|
||||
id=1,
|
||||
name="Gate1",
|
||||
endpoints=["endpoint1", "endpoint2"],
|
||||
languages=["German", "English"]
|
||||
),
|
||||
EndpointGroup(
|
||||
id=2,
|
||||
name="Gate2",
|
||||
endpoints=["endpoint3"],
|
||||
languages=["German", "English"]
|
||||
)
|
||||
]
|
||||
self.active_group_id = None
|
||||
self._thread = None
|
||||
self.last_completed_group_id = None
|
||||
|
||||
def get_endpoint_groups(self) -> List[EndpointGroup]:
|
||||
"""Get all endpoint groups."""
|
||||
return self.endpoint_groups
|
||||
|
||||
def get_endpoint_group(self, group_id: int) -> Optional[EndpointGroup]:
|
||||
"""Get a specific endpoint group by ID."""
|
||||
for group in self.endpoint_groups:
|
||||
if group.id == group_id:
|
||||
return group
|
||||
return None
|
||||
|
||||
def add_endpoint_group(self, group: EndpointGroup) -> EndpointGroup:
|
||||
"""Add a new endpoint group."""
|
||||
# Ensure group with this ID doesn't already exist
|
||||
for existing_group in self.endpoint_groups:
|
||||
if existing_group.id == group.id:
|
||||
raise ValueError(f"Group with ID {group.id} already exists")
|
||||
|
||||
# If no ID is provided or ID is 0, auto-assign the next available ID
|
||||
if group.id == 0:
|
||||
max_id = max([g.id for g in self.endpoint_groups]) if self.endpoint_groups else 0
|
||||
group.id = max_id + 1
|
||||
|
||||
self.endpoint_groups.append(group)
|
||||
return group
|
||||
|
||||
def update_endpoint_group(self, group_id: int, updated_group: EndpointGroup) -> EndpointGroup:
|
||||
"""Update an existing endpoint group."""
|
||||
for i, group in enumerate(self.endpoint_groups):
|
||||
if group.id == group_id:
|
||||
# Ensure the ID doesn't change
|
||||
updated_group.id = group_id
|
||||
self.endpoint_groups[i] = updated_group
|
||||
return updated_group
|
||||
|
||||
raise ValueError(f"Group with ID {group_id} not found")
|
||||
|
||||
def delete_endpoint_group(self, group_id: int) -> None:
|
||||
"""Delete an endpoint group."""
|
||||
for i, group in enumerate(self.endpoint_groups):
|
||||
if group.id == group_id:
|
||||
del self.endpoint_groups[i]
|
||||
return
|
||||
|
||||
raise ValueError(f"Group with ID {group_id} not found")
|
||||
|
||||
def get_available_endpoints(self) -> List[str]:
|
||||
"""Get all available endpoints."""
|
||||
return self.available_endpoints
|
||||
|
||||
def get_available_languages(self) -> List[str]:
|
||||
"""Get all available languages for announcements."""
|
||||
return self.available_languages
|
||||
|
||||
def _simulate_announcement(self, text: str, group: EndpointGroup) -> None:
|
||||
"""
|
||||
Simulate an announcement being made.
|
||||
This runs in a separate thread to allow the API to continue serving requests.
|
||||
"""
|
||||
# Set start time
|
||||
group.parameters.text = text
|
||||
group.parameters.languages = ["German", "English"] # Default languages
|
||||
group.parameters.start_time = time.time()
|
||||
|
||||
# Track the active group
|
||||
self.active_group_id = group.id
|
||||
|
||||
# Update status to translating
|
||||
group.progress.current_state = AnnouncementStates.TRANSLATING.value
|
||||
group.progress.progress = 0.2
|
||||
time.sleep(1.5) # Simulate translation time
|
||||
|
||||
# Check if we should stop (e.g. if another announcement started)
|
||||
if self.active_group_id != group.id:
|
||||
return
|
||||
|
||||
# Update status to generating voice
|
||||
group.progress.current_state = AnnouncementStates.GENERATING_VOICE.value
|
||||
group.progress.progress = 0.4
|
||||
time.sleep(2) # Simulate voice generation time
|
||||
|
||||
# Check if we should stop
|
||||
if self.active_group_id != group.id:
|
||||
return
|
||||
|
||||
# Update status to routing
|
||||
group.progress.current_state = AnnouncementStates.ROUTING.value
|
||||
group.progress.progress = 0.6
|
||||
time.sleep(1) # Simulate routing time
|
||||
|
||||
# Check if we should stop
|
||||
if self.active_group_id != group.id:
|
||||
return
|
||||
|
||||
# Update status to active
|
||||
group.progress.current_state = AnnouncementStates.ACTIVE.value
|
||||
group.progress.progress = 0.8
|
||||
time.sleep(2.5) # Simulate announcement playing time
|
||||
|
||||
# Check if we should stop
|
||||
if self.active_group_id != group.id:
|
||||
return
|
||||
|
||||
# Update status to complete
|
||||
group.progress.current_state = AnnouncementStates.COMPLETE.value
|
||||
group.progress.progress = 1.0
|
||||
|
||||
# Record the last completed group
|
||||
self.last_completed_group_id = group.id
|
||||
|
||||
# Reset active group if this is still the active one
|
||||
if self.active_group_id == group.id:
|
||||
self.active_group_id = None
|
||||
|
||||
# After a while, reset to idle state
|
||||
def reset_to_idle():
|
||||
time.sleep(10) # Keep completed state visible for 10 seconds
|
||||
if group.progress.current_state == AnnouncementStates.COMPLETE.value:
|
||||
group.progress.current_state = AnnouncementStates.IDLE.value
|
||||
group.progress.progress = 0.0
|
||||
|
||||
reset_thread = threading.Thread(target=reset_to_idle)
|
||||
reset_thread.daemon = True
|
||||
reset_thread.start()
|
||||
|
||||
def start_announcement(self, text: str, group: EndpointGroup) -> None:
|
||||
"""Start a new announcement to the specified endpoint group."""
|
||||
# Check if an announcement is already in progress
|
||||
if self.active_group_id is not None:
|
||||
# Cancel the current announcement
|
||||
self.active_group_id = None
|
||||
|
||||
# Start a new thread to handle the announcement
|
||||
self._thread = threading.Thread(target=self._simulate_announcement, args=(text, group))
|
||||
self._thread.daemon = True
|
||||
self._thread.start()
|
||||
|
||||
def get_announcement_status(self) -> dict:
|
||||
"""Get the status of the current announcement."""
|
||||
# If an announcement is active, return its status
|
||||
if self.active_group_id is not None:
|
||||
group = self.get_endpoint_group(self.active_group_id)
|
||||
return {
|
||||
"state": group.progress.current_state,
|
||||
"progress": group.progress.progress,
|
||||
"error": group.progress.error,
|
||||
"details": {
|
||||
"group": {
|
||||
"id": group.id,
|
||||
"name": group.name,
|
||||
"endpoints": group.endpoints
|
||||
},
|
||||
"text": group.parameters.text,
|
||||
"languages": group.parameters.languages,
|
||||
"start_time": group.parameters.start_time
|
||||
}
|
||||
}
|
||||
|
||||
# If no announcement is active but we have a last completed one
|
||||
elif self.last_completed_group_id is not None:
|
||||
group = self.get_endpoint_group(self.last_completed_group_id)
|
||||
if group and group.progress.current_state == AnnouncementStates.COMPLETE.value:
|
||||
return {
|
||||
"state": group.progress.current_state,
|
||||
"progress": group.progress.progress,
|
||||
"error": group.progress.error,
|
||||
"details": {
|
||||
"group": {
|
||||
"id": group.id,
|
||||
"name": group.name,
|
||||
"endpoints": group.endpoints
|
||||
},
|
||||
"text": group.parameters.text,
|
||||
"languages": group.parameters.languages,
|
||||
"start_time": group.parameters.start_time
|
||||
}
|
||||
}
|
||||
|
||||
# Default: no active announcement
|
||||
return {
|
||||
"state": AnnouncementStates.IDLE.value,
|
||||
"progress": 0.0,
|
||||
"error": None,
|
||||
"details": {
|
||||
"group": {
|
||||
"id": 0,
|
||||
"name": "",
|
||||
"endpoints": []
|
||||
},
|
||||
"text": "",
|
||||
"languages": [],
|
||||
"start_time": time.time()
|
||||
}
|
||||
}
|
||||
|
||||
# Singleton instance
|
||||
announcement_system = AnnouncementSystem()
|
||||
Reference in New Issue
Block a user