Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import numpy as np
|
| 5 |
+
import random
|
| 6 |
+
import datetime
|
| 7 |
+
import uuid
|
| 8 |
+
import json
|
| 9 |
+
from huggingface_hub import HfApi
|
| 10 |
+
from datasets import Dataset
|
| 11 |
+
|
| 12 |
+
# Configuration
|
| 13 |
+
SAMPLE_PROMPTS = [
|
| 14 |
+
"नमस्ते, मेरो नाम __ हो। म नेपाली बोल्छु।",
|
| 15 |
+
"आज मौसम धेरै राम्रो छ।",
|
| 16 |
+
"नेपाल एक सुन्दर देश हो जहाँ हिमाल, पहाड र तराई छन्।",
|
| 17 |
+
"काठमाडौं नेपालको राजधानी हो।",
|
| 18 |
+
"म आज बिहान स्कूल जाँदैछु।",
|
| 19 |
+
"नेपाली भाषा बोल्ने मानिसहरू विश्वभर छन्।",
|
| 20 |
+
"हिमालमा हिउँ परिरहेको छ।",
|
| 21 |
+
"मलाई नेपाली खाना धेरै मन पर्छ।",
|
| 22 |
+
"बुद्ध नेपालमा जन्मिएका थिए।",
|
| 23 |
+
"सगरमाथा विश्वको सबैभन्दा अग्लो हिमाल हो।"
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
EMOTIONS = ["सामान्य (Neutral)", "खुसी (Happy)", "दुःखी (Sad)", "रिसाएको (Angry)", "अचम्मित (Surprised)"]
|
| 27 |
+
GENDERS = ["पुरुष (Male)", "महिला (Female)", "अन्य (Other)", "भन्न चाहन्न (Prefer not to say)"]
|
| 28 |
+
AGE_GROUPS = ["18 भन्दा कम", "18-24", "25-34", "35-44", "45-54", "55-64", "65+"]
|
| 29 |
+
|
| 30 |
+
# Nepal-specific data
|
| 31 |
+
REGIONS = [
|
| 32 |
+
"प्रदेश १ (Province 1)",
|
| 33 |
+
"मधेश प्रदेश (Madhesh Province)",
|
| 34 |
+
"बागमती प्रदेश (Bagmati Province)",
|
| 35 |
+
"गण्डकी प्रदेश (Gandaki Province)",
|
| 36 |
+
"लुम्बिनी प्रदेश (Lumbini Province)",
|
| 37 |
+
"कर्णाली प्रदेश (Karnali Province)",
|
| 38 |
+
"सुदूरपश्चिम प्रदेश (Sudurpashchim Province)"
|
| 39 |
+
]
|
| 40 |
+
|
| 41 |
+
# Common last names by ethnicity/region for better accent tracking
|
| 42 |
+
COMMON_LAST_NAMES = {
|
| 43 |
+
"पहाडी (Pahadi)": ["शर्मा (Sharma)", "पौडेल (Poudel)", "खनाल (Khanal)", "अधिकारी (Adhikari)", "भट्टराई (Bhattarai)", "अन्य पहाडी (Other Pahadi)"],
|
| 44 |
+
"नेवार (Newar)": ["श्रेष्ठ (Shrestha)", "प्रधान (Pradhan)", "महर्जन (Maharjan)", "बज्राचार्य (Bajracharya)", "अन्य नेवार (Other Newar)"],
|
| 45 |
+
"मधेसी (Madhesi)": ["यादव (Yadav)", "साह (Shah)", "सिंह (Singh)", "गुप्ता (Gupta)", "अन्य मधेसी (Other Madhesi)"],
|
| 46 |
+
"थारु (Tharu)": ["चौधरी (Chaudhary)", "थारु (Tharu)", "अन्य थारु (Other Tharu)"],
|
| 47 |
+
"मगर (Magar)": ["मगर (Magar)", "थापा (Thapa)", "राना (Rana)", "अन्य मगर (Other Magar)"],
|
| 48 |
+
"तामाङ (Tamang)": ["तामाङ (Tamang)", "लामा (Lama)", "अन्य तामाङ (Other Tamang)"],
|
| 49 |
+
"राई (Rai)": ["राई (Rai)", "अन्य राई (Other Rai)"],
|
| 50 |
+
"गुरुङ (Gurung)": ["गुरुङ (Gurung)", "अन्य गुरुङ (Other Gurung)"],
|
| 51 |
+
"लिम्बु (Limbu)": ["लिम्बु (Limbu)", "अन्य लिम्बु (Other Limbu)"],
|
| 52 |
+
"शेर्पा (Sherpa)": ["शेर्पा (Sherpa)", "अन्य शेर्पा (Other Sherpa)"],
|
| 53 |
+
"अन्य (Other)": ["अन्य (Other)"]
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
# Create directory for saved recordings
|
| 57 |
+
os.makedirs("recordings", exist_ok=True)
|
| 58 |
+
os.makedirs("metadata", exist_ok=True)
|
| 59 |
+
os.makedirs("ratings", exist_ok=True)
|
| 60 |
+
|
| 61 |
+
# Initialize metadata file if it doesn't exist
|
| 62 |
+
metadata_file = "metadata/metadata.csv"
|
| 63 |
+
ratings_file = "ratings/ratings.json"
|
| 64 |
+
|
| 65 |
+
if not os.path.exists(metadata_file):
|
| 66 |
+
pd.DataFrame(columns=[
|
| 67 |
+
"id", "text", "audio_path", "gender", "age_group", "ethnicity",
|
| 68 |
+
"last_name", "region", "emotion", "timestamp", "recording_type"
|
| 69 |
+
]).to_csv(metadata_file, index=False)
|
| 70 |
+
|
| 71 |
+
if not os.path.exists(ratings_file):
|
| 72 |
+
with open(ratings_file, 'w') as f:
|
| 73 |
+
json.dump({}, f)
|
| 74 |
+
|
| 75 |
+
def save_recording(audio, text, gender, age_group, ethnicity, last_name, region, emotion, recording_type):
|
| 76 |
+
"""Save the recording and metadata"""
|
| 77 |
+
# Generate unique ID for this recording
|
| 78 |
+
recording_id = str(uuid.uuid4())
|
| 79 |
+
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 80 |
+
|
| 81 |
+
# Check if audio was recorded
|
| 82 |
+
if audio is None:
|
| 83 |
+
return "कृपया पहिले रेकर्डिङ गर्नुहोस्। (Please record audio first)", None
|
| 84 |
+
|
| 85 |
+
# Save audio file
|
| 86 |
+
audio_filename = f"recordings/{recording_id}.wav"
|
| 87 |
+
if isinstance(audio, tuple): # If it's a tuple (sr, data)
|
| 88 |
+
sr, data = audio
|
| 89 |
+
import soundfile as sf
|
| 90 |
+
sf.write(audio_filename, data, sr)
|
| 91 |
+
else: # If it's a path
|
| 92 |
+
# Copy the file
|
| 93 |
+
import shutil
|
| 94 |
+
shutil.copy(audio, audio_filename)
|
| 95 |
+
|
| 96 |
+
# Update metadata
|
| 97 |
+
metadata = pd.read_csv(metadata_file)
|
| 98 |
+
new_row = pd.DataFrame([{
|
| 99 |
+
"id": recording_id,
|
| 100 |
+
"text": text,
|
| 101 |
+
"audio_path": audio_filename,
|
| 102 |
+
"gender": gender,
|
| 103 |
+
"age_group": age_group,
|
| 104 |
+
"ethnicity": ethnicity,
|
| 105 |
+
"last_name": last_name,
|
| 106 |
+
"region": region,
|
| 107 |
+
"emotion": emotion,
|
| 108 |
+
"timestamp": timestamp,
|
| 109 |
+
"recording_type": recording_type
|
| 110 |
+
}])
|
| 111 |
+
|
| 112 |
+
updated_metadata = pd.concat([metadata, new_row], ignore_index=True)
|
| 113 |
+
updated_metadata.to_csv(metadata_file, index=False)
|
| 114 |
+
|
| 115 |
+
# Initialize rating for this recording in the ratings file
|
| 116 |
+
with open(ratings_file, 'r') as f:
|
| 117 |
+
ratings = json.load(f)
|
| 118 |
+
|
| 119 |
+
ratings[recording_id] = {
|
| 120 |
+
"upvotes": 0,
|
| 121 |
+
"downvotes": 0,
|
| 122 |
+
"quality_score": 0, # Average quality rating (1-5)
|
| 123 |
+
"quality_votes": 0, # Number of quality ratings
|
| 124 |
+
"correctness_score": 0, # Average correctness rating (1-5)
|
| 125 |
+
"correctness_votes": 0 # Number of correctness ratings
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
with open(ratings_file, 'w') as f:
|
| 129 |
+
json.dump(ratings, f, indent=2)
|
| 130 |
+
|
| 131 |
+
return f"रेकर्डिङ सफलतापूर्वक सुरक्षित गरियो! (Recording saved successfully!)", audio_filename
|
| 132 |
+
|
| 133 |
+
def get_random_prompt():
|
| 134 |
+
"""Return a random prompt from the list"""
|
| 135 |
+
return random.choice(SAMPLE_PROMPTS)
|
| 136 |
+
|
| 137 |
+
def vote_recording(recording_id, vote_type, vote_value):
|
| 138 |
+
"""Add a vote for a recording"""
|
| 139 |
+
if not os.path.exists(ratings_file):
|
| 140 |
+
return "रेटिङ फाइल भेटिएन। (Rating file not found.)"
|
| 141 |
+
|
| 142 |
+
try:
|
| 143 |
+
with open(ratings_file, 'r') as f:
|
| 144 |
+
ratings = json.load(f)
|
| 145 |
+
|
| 146 |
+
if recording_id not in ratings:
|
| 147 |
+
return "रेकर्डिङ आईडी भेटिएन। (Recording ID not found.)"
|
| 148 |
+
|
| 149 |
+
if vote_type == "upvote":
|
| 150 |
+
ratings[recording_id]["upvotes"] += 1
|
| 151 |
+
elif vote_type == "downvote":
|
| 152 |
+
ratings[recording_id]["downvotes"] += 1
|
| 153 |
+
elif vote_type == "quality":
|
| 154 |
+
# Update quality score (running average)
|
| 155 |
+
current_score = ratings[recording_id]["quality_score"]
|
| 156 |
+
current_votes = ratings[recording_id]["quality_votes"]
|
| 157 |
+
new_votes = current_votes + 1
|
| 158 |
+
new_score = ((current_score * current_votes) + vote_value) / new_votes
|
| 159 |
+
|
| 160 |
+
ratings[recording_id]["quality_score"] = new_score
|
| 161 |
+
ratings[recording_id]["quality_votes"] = new_votes
|
| 162 |
+
elif vote_type == "correctness":
|
| 163 |
+
# Update correctness score (running average)
|
| 164 |
+
current_score = ratings[recording_id]["correctness_score"]
|
| 165 |
+
current_votes = ratings[recording_id]["correctness_votes"]
|
| 166 |
+
new_votes = current_votes + 1
|
| 167 |
+
new_score = ((current_score * current_votes) + vote_value) / new_votes
|
| 168 |
+
|
| 169 |
+
ratings[recording_id]["correctness_score"] = new_score
|
| 170 |
+
ratings[recording_id]["correctness_votes"] = new_votes
|
| 171 |
+
|
| 172 |
+
with open(ratings_file, 'w') as f:
|
| 173 |
+
json.dump(ratings, f, indent=2)
|
| 174 |
+
|
| 175 |
+
return f"मतदान सफलतापूर्वक दर्ता गरियो! (Vote registered successfully!)"
|
| 176 |
+
|
| 177 |
+
except Exception as e:
|
| 178 |
+
return f"त्रुटि: {str(e)}"
|
| 179 |
+
|
| 180 |
+
def get_ethnicity_based_last_names(ethnicity):
|
| 181 |
+
"""Return last name options based on selected ethnicity"""
|
| 182 |
+
if ethnicity in COMMON_LAST_NAMES:
|
| 183 |
+
return COMMON_LAST_NAMES[ethnicity]
|
| 184 |
+
return COMMON_LAST_NAMES["अन्य (Other)"]
|
| 185 |
+
|
| 186 |
+
def upload_to_huggingface(hf_token, dataset_name):
|
| 187 |
+
"""Upload the collected data to Hugging Face"""
|
| 188 |
+
if not os.path.exists(metadata_file):
|
| 189 |
+
return "कुनै डाटा भेटिएन। (No data found.)"
|
| 190 |
+
|
| 191 |
+
try:
|
| 192 |
+
# Read metadata
|
| 193 |
+
metadata = pd.read_csv(metadata_file)
|
| 194 |
+
|
| 195 |
+
if len(metadata) == 0:
|
| 196 |
+
return "कुनै डाटा भेटिएन। (No data found.)"
|
| 197 |
+
|
| 198 |
+
# Read ratings
|
| 199 |
+
with open(ratings_file, 'r') as f:
|
| 200 |
+
ratings = json.load(f)
|
| 201 |
+
|
| 202 |
+
# Add ratings to metadata
|
| 203 |
+
metadata["upvotes"] = metadata["id"].apply(lambda x: ratings.get(x, {}).get("upvotes", 0))
|
| 204 |
+
metadata["downvotes"] = metadata["id"].apply(lambda x: ratings.get(x, {}).get("downvotes", 0))
|
| 205 |
+
metadata["quality_score"] = metadata["id"].apply(lambda x: ratings.get(x, {}).get("quality_score", 0))
|
| 206 |
+
metadata["correctness_score"] = metadata["id"].apply(lambda x: ratings.get(x, {}).get("correctness_score", 0))
|
| 207 |
+
|
| 208 |
+
# Create a dataset dict
|
| 209 |
+
dataset_dict = {
|
| 210 |
+
"id": metadata["id"].tolist(),
|
| 211 |
+
"text": metadata["text"].tolist(),
|
| 212 |
+
"gender": metadata["gender"].tolist(),
|
| 213 |
+
"age_group": metadata["age_group"].tolist(),
|
| 214 |
+
"ethnicity": metadata["ethnicity"].tolist(),
|
| 215 |
+
"last_name": metadata["last_name"].tolist(),
|
| 216 |
+
"region": metadata["region"].tolist(),
|
| 217 |
+
"emotion": metadata["emotion"].tolist(),
|
| 218 |
+
"recording_type": metadata["recording_type"].tolist(),
|
| 219 |
+
"timestamp": metadata["timestamp"].tolist(),
|
| 220 |
+
"upvotes": metadata["upvotes"].tolist(),
|
| 221 |
+
"downvotes": metadata["downvotes"].tolist(),
|
| 222 |
+
"quality_score": metadata["quality_score"].tolist(),
|
| 223 |
+
"correctness_score": metadata["correctness_score"].tolist(),
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
# Create a Dataset object
|
| 227 |
+
dataset = Dataset.from_dict(dataset_dict)
|
| 228 |
+
|
| 229 |
+
# Push to hub
|
| 230 |
+
api = HfApi(token=hf_token)
|
| 231 |
+
dataset.push_to_hub(dataset_name)
|
| 232 |
+
|
| 233 |
+
# Upload audio files
|
| 234 |
+
for _, row in metadata.iterrows():
|
| 235 |
+
audio_path = row["audio_path"]
|
| 236 |
+
if os.path.exists(audio_path):
|
| 237 |
+
api.upload_file(
|
| 238 |
+
path_or_fileobj=audio_path,
|
| 239 |
+
path_in_repo=f"audio/{os.path.basename(audio_path)}",
|
| 240 |
+
repo_id=dataset_name,
|
| 241 |
+
repo_type="dataset"
|
| 242 |
+
)
|
| 243 |
+
|
| 244 |
+
return f"डाटा हगिङफेसमा सफलतापूर्वक अपलोड गरियो! (Data successfully uploaded to Hugging Face at {dataset_name})"
|
| 245 |
+
|
| 246 |
+
except Exception as e:
|
| 247 |
+
return f"त्रुटि: {str(e)}"
|
| 248 |
+
|
| 249 |
+
def update_count():
|
| 250 |
+
"""Update the count of recordings"""
|
| 251 |
+
if os.path.exists(metadata_file):
|
| 252 |
+
metadata = pd.read_csv(metadata_file)
|
| 253 |
+
return f"हालसम्म {len(metadata)} रेकर्डिङहरू संकलन गरिएको छ। (Total recordings collected: {len(metadata)})"
|
| 254 |
+
return "कुनै रेकर्डिङ भेटिएन। (No recordings found.)"
|
| 255 |
+
|
| 256 |
+
def list_recordings(num_items=10):
|
| 257 |
+
"""List recent recordings for review"""
|
| 258 |
+
if not os.path.exists(metadata_file):
|
| 259 |
+
return pd.DataFrame()
|
| 260 |
+
|
| 261 |
+
metadata = pd.read_csv(metadata_file)
|
| 262 |
+
if len(metadata) == 0:
|
| 263 |
+
return pd.DataFrame()
|
| 264 |
+
|
| 265 |
+
# Sort by timestamp (newest first) and get the most recent entries
|
| 266 |
+
metadata['timestamp'] = pd.to_datetime(metadata['timestamp'])
|
| 267 |
+
sorted_metadata = metadata.sort_values('timestamp', ascending=False).head(num_items)
|
| 268 |
+
|
| 269 |
+
# Reset the index for display purposes
|
| 270 |
+
display_df = sorted_metadata[['id', 'text', 'ethnicity', 'region', 'timestamp']].copy()
|
| 271 |
+
display_df['timestamp'] = display_df['timestamp'].dt.strftime('%Y-%m-%d %H:%M')
|
| 272 |
+
display_df = display_df.reset_index(drop=True)
|
| 273 |
+
|
| 274 |
+
return display_df
|
| 275 |
+
|
| 276 |
+
def get_recording_audio(recording_id):
|
| 277 |
+
"""Get audio file path for a specific recording"""
|
| 278 |
+
if not os.path.exists(metadata_file):
|
| 279 |
+
return None, "रेकर्डिङ भेटिएन। (Recording not found.)"
|
| 280 |
+
|
| 281 |
+
metadata = pd.read_csv(metadata_file)
|
| 282 |
+
recording = metadata[metadata['id'] == recording_id]
|
| 283 |
+
|
| 284 |
+
if len(recording) == 0:
|
| 285 |
+
return None, "रेकर्डिङ भेटिएन। (Recording not found.)"
|
| 286 |
+
|
| 287 |
+
audio_path = recording['audio_path'].iloc[0]
|
| 288 |
+
text = recording['text'].iloc[0]
|
| 289 |
+
|
| 290 |
+
if not os.path.exists(audio_path):
|
| 291 |
+
return None, "अडियो फाइल भेटिएन। (Audio file not found.)"
|
| 292 |
+
|
| 293 |
+
return audio_path, text
|
| 294 |
+
|
| 295 |
+
def get_recording_ratings(recording_id):
|
| 296 |
+
"""Get current ratings for a recording"""
|
| 297 |
+
if not os.path.exists(ratings_file):
|
| 298 |
+
return "डाटा भेटिएन। (No data found.)"
|
| 299 |
+
|
| 300 |
+
with open(ratings_file, 'r') as f:
|
| 301 |
+
ratings = json.load(f)
|
| 302 |
+
|
| 303 |
+
if recording_id not in ratings:
|
| 304 |
+
return "रेकर्डिङ आईडी भेटिएन। (Recording ID not found.)"
|
| 305 |
+
|
| 306 |
+
r = ratings[recording_id]
|
| 307 |
+
|
| 308 |
+
# Format the ratings for display
|
| 309 |
+
upvotes = r["upvotes"]
|
| 310 |
+
downvotes = r["downvotes"]
|
| 311 |
+
quality = round(r["quality_score"], 1) if r["quality_votes"] > 0 else 0
|
| 312 |
+
quality_votes = r["quality_votes"]
|
| 313 |
+
correctness = round(r["correctness_score"], 1) if r["correctness_votes"] > 0 else 0
|
| 314 |
+
correctness_votes = r["correctness_votes"]
|
| 315 |
+
|
| 316 |
+
return f"""👍 Upvotes: {upvotes} | 👎 Downvotes: {downvotes}
|
| 317 |
+
गुणस्तर (Quality): {quality}/5 ({quality_votes} मत/votes)
|
| 318 |
+
शुद्धता (Correctness): {correctness}/5 ({correctness_votes} मत/votes)"""
|
| 319 |
+
|
| 320 |
+
def build_ui():
|
| 321 |
+
"""Build the Gradio interface"""
|
| 322 |
+
with gr.Blocks(title="नेपाली ASR डाटा संकलन (Nepali ASR Data Collection)") as app:
|
| 323 |
+
gr.Markdown("""
|
| 324 |
+
# नेपाली ASR डाटा संकलन (Nepali ASR Data Collection)
|
| 325 |
+
|
| 326 |
+
यस प्लेटफर्मले नेपाली भाषाको स्वचालित भाषण पह��चान (ASR) प्रविधिको विकासका लागि आवाज डाटा संकलन गर्दछ।
|
| 327 |
+
कृपया आफ्नो आवाज रेकर्ड गरेर योगदान दिनुहोस्।
|
| 328 |
+
|
| 329 |
+
*This platform collects voice data for the development of Nepali Automatic Speech Recognition (ASR) technology.
|
| 330 |
+
Please contribute by recording your voice.*
|
| 331 |
+
""")
|
| 332 |
+
|
| 333 |
+
with gr.Tab("स्वतन्त्र पाठ (Free Text)"):
|
| 334 |
+
with gr.Row():
|
| 335 |
+
with gr.Column():
|
| 336 |
+
free_text = gr.Textbox(
|
| 337 |
+
label="तपाईंले बोल्न चाहनुभएको पाठ यहाँ लेख्नुहोस् (Type the text you want to speak here)",
|
| 338 |
+
placeholder="यहाँ लेख्नुहोस्...",
|
| 339 |
+
lines=3
|
| 340 |
+
)
|
| 341 |
+
free_audio = gr.Audio(
|
| 342 |
+
label="आफ्नो आवाज रेकर्ड गर्नुहोस् (Record your voice)",
|
| 343 |
+
type="filepath",
|
| 344 |
+
source="microphone"
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
with gr.Column():
|
| 348 |
+
# First row of metadata
|
| 349 |
+
with gr.Row():
|
| 350 |
+
free_gender = gr.Dropdown(
|
| 351 |
+
label="लिङ्ग (Gender)",
|
| 352 |
+
choices=GENDERS,
|
| 353 |
+
value=GENDERS[0]
|
| 354 |
+
)
|
| 355 |
+
free_age = gr.Dropdown(
|
| 356 |
+
label="उमेर समूह (Age Group)",
|
| 357 |
+
choices=AGE_GROUPS,
|
| 358 |
+
value=AGE_GROUPS[1]
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
# Second row of metadata
|
| 362 |
+
with gr.Row():
|
| 363 |
+
free_ethnicity = gr.Dropdown(
|
| 364 |
+
label="जातीयता (Ethnicity)",
|
| 365 |
+
choices=list(COMMON_LAST_NAMES.keys()),
|
| 366 |
+
value=list(COMMON_LAST_NAMES.keys())[0]
|
| 367 |
+
)
|
| 368 |
+
free_last_name = gr.Dropdown(
|
| 369 |
+
label="थर (Last Name)",
|
| 370 |
+
choices=COMMON_LAST_NAMES[list(COMMON_LAST_NAMES.keys())[0]]
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
# Update last name options when ethnicity changes
|
| 374 |
+
free_ethnicity.change(
|
| 375 |
+
fn=get_ethnicity_based_last_names,
|
| 376 |
+
inputs=free_ethnicity,
|
| 377 |
+
outputs=free_last_name
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
# Third row of metadata
|
| 381 |
+
with gr.Row():
|
| 382 |
+
free_region = gr.Dropdown(
|
| 383 |
+
label="क्षेत्र (Region)",
|
| 384 |
+
choices=REGIONS,
|
| 385 |
+
value=REGIONS[2] # Default to Bagmati Province
|
| 386 |
+
)
|
| 387 |
+
free_emotion = gr.Dropdown(
|
| 388 |
+
label="भावना (Emotion)",
|
| 389 |
+
choices=EMOTIONS,
|
| 390 |
+
value=EMOTIONS[0]
|
| 391 |
+
)
|
| 392 |
+
|
| 393 |
+
free_submit = gr.Button("सुरक्षित गर्नुहोस् (Save)")
|
| 394 |
+
free_output = gr.Textbox(label="स्थिति (Status)")
|
| 395 |
+
|
| 396 |
+
free_submit.click(
|
| 397 |
+
fn=save_recording,
|
| 398 |
+
inputs=[
|
| 399 |
+
free_audio, free_text, free_gender, free_age,
|
| 400 |
+
free_ethnicity, free_last_name, free_region, free_emotion,
|
| 401 |
+
gr.Textbox(value="free_text", visible=False)
|
| 402 |
+
],
|
| 403 |
+
outputs=[free_output, free_audio]
|
| 404 |
+
)="filepath",
|
| 405 |
+
source="microphone"
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
with gr.Column():
|
| 409 |
+
free_gender = gr.Dropdown(
|
| 410 |
+
label="लिङ्ग (Gender)",
|
| 411 |
+
choices=GENDERS,
|
| 412 |
+
value=GENDERS[0]
|
| 413 |
+
)
|
| 414 |
+
free_age = gr.Dropdown(
|
| 415 |
+
label="उमेर समूह (Age Group)",
|
| 416 |
+
choices=AGE_GROUPS,
|
| 417 |
+
value=AGE_GROUPS[1]
|
| 418 |
+
)
|
| 419 |
+
free_emotion = gr.Dropdown(
|
| 420 |
+
label="भावना (Emotion)",
|
| 421 |
+
choices=EMOTIONS,
|
| 422 |
+
value=EMOTIONS[0]
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
free_submit = gr.Button("सुरक्षित गर्नुहोस् (Save)")
|
| 426 |
+
free_output = gr.Textbox(label="स्थिति (Status)")
|
| 427 |
+
|
| 428 |
+
free_submit.click(
|
| 429 |
+
fn=save_recording,
|
| 430 |
+
inputs=[free_audio, free_text, free_gender, free_age, free_emotion, gr.Textbox(value="free_text", visible=False)],
|
| 431 |
+
outputs=[free_output, free_audio]
|
| 432 |
+
)
|
| 433 |
+
|
| 434 |
+
with gr.Tab("निर्देशित पाठ (Prompted Text)"):
|
| 435 |
+
with gr.Row():
|
| 436 |
+
with gr.Column():
|
| 437 |
+
prompt_text = gr.Textbox(
|
| 438 |
+
label="कृपया यो पाठ पढ्नुहोस् (Please read this text)",
|
| 439 |
+
value=get_random_prompt(),
|
| 440 |
+
lines=3
|
| 441 |
+
)
|
| 442 |
+
prompt_audio = gr.Audio(
|
| 443 |
+
label="आफ्नो आवाज रेकर्ड गर्नुहोस् (Record your voice)",
|
| 444 |
+
type="filepath",
|
| 445 |
+
source="microphone"
|
| 446 |
+
)
|
| 447 |
+
new_prompt = gr.Button("नयाँ पाठ (New Text)")
|
| 448 |
+
|
| 449 |
+
with gr.Column():
|
| 450 |
+
prompt_gender = gr.Dropdown(
|
| 451 |
+
label="लिङ्ग (Gender)",
|
| 452 |
+
choices=GENDERS,
|
| 453 |
+
value=GENDERS[0]
|
| 454 |
+
)
|
| 455 |
+
prompt_age = gr.Dropdown(
|
| 456 |
+
label="उमेर समूह (Age Group)",
|
| 457 |
+
choices=AGE_GROUPS,
|
| 458 |
+
value=AGE_GROUPS[1]
|
| 459 |
+
)
|
| 460 |
+
prompt_emotion = gr.Dropdown(
|
| 461 |
+
label="भावना (Emotion)",
|
| 462 |
+
choices=EMOTIONS,
|
| 463 |
+
value=EMOTIONS[0]
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
prompt_submit = gr.Button("सुरक्षित गर्नुहोस् (Save)")
|
| 467 |
+
prompt_output = gr.Textbox(label="स्थिति (Status)")
|
| 468 |
+
|
| 469 |
+
new_prompt.click(fn=get_random_prompt, inputs=None, outputs=prompt_text)
|
| 470 |
+
prompt_submit.click(
|
| 471 |
+
fn=save_recording,
|
| 472 |
+
inputs=[prompt_audio, prompt_text, prompt_gender, prompt_age, prompt_emotion, gr.Textbox(value="prompted_text", visible=False)],
|
| 473 |
+
outputs=[prompt_output, prompt_audio]
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
with gr.Tab("प्रगति (Progress)"):
|
| 477 |
+
count_display = gr.Textbox(label="संकलित रेकर्डिङ गणना (Recording Count)")
|
| 478 |
+
refresh_button = gr.Button("ताजा गर्नुहोस् (Refresh)")
|
| 479 |
+
refresh_button.click(fn=update_count, inputs=None, outputs=count_display)
|
| 480 |
+
|
| 481 |
+
# HuggingFace upload section (admin only)
|
| 482 |
+
gr.Markdown("## हगिङफेसमा अपलोड गर्नुहोस् (Upload to Hugging Face)")
|
| 483 |
+
with gr.Row():
|
| 484 |
+
hf_token = gr.Textbox(label="Hugging Face API Token", type="password")
|
| 485 |
+
dataset_name = gr.Textbox(
|
| 486 |
+
label="Dataset Name",
|
| 487 |
+
placeholder="username/nepali-asr-dataset"
|
| 488 |
+
)
|
| 489 |
+
upload_button = gr.Button("अपलोड गर्नुहोस् (Upload)")
|
| 490 |
+
upload_status = gr.Textbox(label="अपलोड स्थिति (Upload Status)")
|
| 491 |
+
|
| 492 |
+
upload_button.click(
|
| 493 |
+
fn=upload_to_huggingface,
|
| 494 |
+
inputs=[hf_token, dataset_name],
|
| 495 |
+
outputs=upload_status
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
with gr.Tab("जानकारी (Information)"):
|
| 499 |
+
gr.Markdown("""
|
| 500 |
+
## नेपाली ASR डाटा संकलन प्रोजेक्टको बारेमा
|
| 501 |
+
|
| 502 |
+
यो प्रोजेक्टले नेपाली भाषाको स्वचालित भाषण पहिचान (ASR) प्रविधिको विकासका लागि आवश्यक डाटा संकलन गर्दछ।
|
| 503 |
+
तपाईंको योगदानले नेपाली भाषा प्रविधिको विकासमा ठूलो मद्दत पुर्याउनेछ।
|
| 504 |
+
|
| 505 |
+
### कसरी योगदान दिने:
|
| 506 |
+
|
| 507 |
+
1. **स्वतन्त्र पाठ (Free Text)** ट्याबमा, तपाईं आफ्नो इच्छा अनुसार पाठ लेखेर त्यसलाई बोल्न सक्नुहुन्छ।
|
| 508 |
+
2. **निर्देशित पाठ (Prompted Text)** ट्याबमा, तपाईंले दिइएको पाठलाई पढेर रेकर्ड गर्न सक्नुहुन्छ।
|
| 509 |
+
3. रेकर्डिङ पछि, "सुरक्षित गर्नुहोस्" बटनमा क्लिक गर्नुहोस्।
|
| 510 |
+
|
| 511 |
+
### गोपनीयता नीति:
|
| 512 |
+
|
| 513 |
+
- त��ाईंको आवाज रेकर्डिङ र मेटाडाटा सार्वजनिक अनुसन्धान उद्देश्यका लागि प्रयोग गरिनेछ।
|
| 514 |
+
- कृपया व्यक्तिगत पहिचान गर्न सकिने जानकारी शेयर नगर्नुहोस्।
|
| 515 |
+
- यो डाटासेट खुला स्रोत हुनेछ र हगिङफेसमा प्रकाशित गरिनेछ।
|
| 516 |
+
|
| 517 |
+
---
|
| 518 |
+
|
| 519 |
+
## About Nepali ASR Data Collection Project
|
| 520 |
+
|
| 521 |
+
This project collects necessary data for the development of Nepali Automatic Speech Recognition (ASR) technology.
|
| 522 |
+
Your contribution will greatly help in advancing Nepali language technology.
|
| 523 |
+
|
| 524 |
+
### How to Contribute:
|
| 525 |
+
|
| 526 |
+
1. In the **Free Text** tab, you can type any text you want and record yourself speaking it.
|
| 527 |
+
2. In the **Prompted Text** tab, you can record yourself reading the provided text.
|
| 528 |
+
3. After recording, click the "Save" button.
|
| 529 |
+
|
| 530 |
+
### Privacy Policy:
|
| 531 |
+
|
| 532 |
+
- Your voice recordings and metadata will be used for public research purposes.
|
| 533 |
+
- Please do not share personally identifiable information.
|
| 534 |
+
- This dataset will be open-source and published on Hugging Face.
|
| 535 |
+
""")
|
| 536 |
+
|
| 537 |
+
# Initialize the count
|
| 538 |
+
app.load(fn=update_count, inputs=None, outputs=count_display)
|
| 539 |
+
|
| 540 |
+
return app
|
| 541 |
+
|
| 542 |
+
# Launch the app
|
| 543 |
+
if __name__ == "__main__":
|
| 544 |
+
app = build_ui()
|
| 545 |
+
app.launch()
|