deepsync/translator-admin-dashboard
0
1import os2import gradio as gr3import pymongo4import pandas as pd5from bson import ObjectId6from dotenv import load_dotenv7import requests8from datetime import timedelta9from collections import defaultdict10 11load_dotenv()12 13video_status = {14 "Not Assigned": "NOT_STARTED",15 "Under Review": "NOT_STARTED",16 "Dubbing Started": "DUBBING_STARTED",17 "Waiting QA": "WAITING_QA",18 "Sent to Client": "SENT_TO_CLIENT",19 "Approved": "APPROVED",20 "Rejected": "REJECTED",21 "Churned": "CHURNED",22 "Re-review Requested": "RE_REVIEW_REQUESTED"23}24 25HEADERS = {"x-api-key": os.environ.get('X-API-KEY'), "Content-Type": "application/json"}26 27ALL_LANGUAGES = ['Hindi', 'English', 'Bengali', 'Kannada', 'Spanish', 'French', 'German', 'Italian', 'Tamil', 'Telugu']28 29language_maps = {30 "Hindi": "hi",31 "English": "en",32 "Spanish": "es",33 "French": "fr",34 "German": "de",35 "Italian": "it",36 "Tamil": "ta",37 "Telugu": "te",38 "Bengali": "bn",39 "Kannada": "kn",40 "hi": "Hindi",41 "en": "English",42 "es": "Spanish",43 "fr": "French",44 "de": "German",45 "it": "Italian",46 "ta": "Tamil",47 "te": "Telugu",48 "bn": "Bengali",49 "kn": "Kannada"50}51 52 53def print_slack(platform, video_id, translator_name, assignment="translator"):54 if platform == "dev":55 return56 else:57 client = pymongo.MongoClient(os.environ.get("MONGO_URI"))58 source_db = client.deepsync_prod59 60 headers = {61 'Content-type': 'application/json',62 }63 64 video = source_db.dubbedvideos.find_one({"_id": ObjectId(video_id)})65 video_name = video['title']66 source_language = language_maps[video['sourceLanguage'][:2]]67 target_language = language_maps[video['targetLanguage'][:2]]68 video_duration = str(timedelta(seconds=video['duration']))69 translator_details = "/".join(translator_name.split("/")[:3])70 71 message = f"""```{'Translator' if assignment == 'translator' else 'QA'} Assigned72Video Name : {video_name}73Source Language : {source_language}74Target Language : {target_language}75Video Duration : {video_duration} hours76Details : {translator_details}77```78"""79 80 json_data = {81 'text': message,82 }83 84 response = requests.post(85 os.environ.get('SLACK_WEBHOOK'),86 headers=headers,87 json=json_data,88 )89 client.close()90 91 92def get_translators(platform):93 if platform == "dev":94 api_url = "https://api.dev-env.deepsync.co/api/v1"95 client = pymongo.MongoClient(os.environ.get("MONGO_DEV_URI"))96 source_db = client.deepsync_stage97 else:98 api_url = "https://api.deepsync.co/api/v1"99 client = pymongo.MongoClient(os.environ.get("MONGO_URI"))100 source_db = client.deepsync_prod101 102 response = requests.get(f"{api_url}/dashboard/translators", headers=HEADERS)103 translators = list(filter(lambda x: not x.get('isDeleted', False), response.json()['data']))104 translators_data = []105 106 if len(translators):107 desired_ids = [ObjectId(t["translator"]) for t in translators]108 companies_cursor = source_db.companies.find({'_id': {'$in': desired_ids}})109 companies = {c["_id"]: c for c in companies_cursor}110 for translator in translators:111 try:112 data = companies[ObjectId(translator["translator"])]113 translators_data.append({114 "_id": str(translator["_id"]),115 "Name": data["name"],116 "Email": data["email"],117 "Source Language": ", ".join(list(map(lambda x: language_maps[x], translator["sourceAccent"]))),118 "Target Language": ", ".join(list(map(lambda x: language_maps[x], translator["targetAccent"])))119 })120 except Exception as e:121 print(e)122 pass123 client.close()124 return pd.DataFrame(translators_data)125 126 127def add_translator(platform, email_id, source_langs, target_langs, contact_num, pricing):128 if platform == "dev":129 api_url = "https://api.dev-env.deepsync.co/api/v1"130 client = pymongo.MongoClient(os.environ.get("MONGO_DEV_URI"))131 source_db = client.deepsync_stage132 else:133 api_url = "https://api.deepsync.co/api/v1"134 client = pymongo.MongoClient(os.environ.get("MONGO_URI"))135 source_db = client.deepsync_prod136 137 try:138 company_id_validity = source_db.companies.find_one({"email": email_id})139 except:140 company_id_validity = None141 if company_id_validity is None:142 return "Invalid Company ID. Unable to add translator."143 144 if source_langs is None:145 source_accents = []146 else:147 source_accents = [language_maps[l] for l in source_langs]148 if target_langs is None:149 target_accents = []150 else:151 target_accents = [language_maps[l] for l in target_langs]152 153 main_data = {154 "targetAccent": target_accents,155 "sourceAccent": source_accents,156 "contactNum": None if len(contact_num.strip()) == 0 else contact_num.strip(),157 "pricing": None if pricing is None or pricing == -1 else pricing158 }159 160 data = {"translator": str(company_id_validity["_id"]), **main_data}161 162 response = requests.post(f"{api_url}/dashboard/translator", json=data, headers=HEADERS)163 164 if str(response.status_code).startswith('4') or str(response.status_code).startswith('5'):165 if response.json()['message'].strip() == "Translator already exists":166 translator_main_id = source_db.translators.find_one({"translator": company_id_validity['_id']})167 response = requests.patch(f"{api_url}/dashboard/translators/{translator_main_id['_id']}", json={"isDeleted": False, **main_data}, headers=HEADERS)168 if str(response.status_code).startswith('4') or str(response.status_code).startswith('5'):169 return f"{response.json()['message']} ({response.status_code})"170 return f"Translator added succesfully, ID : {response.json()['data']['_id']}"171 return f"{response.json()['message']} ({response.status_code})"172 173 client.close()174 return f"Translator added succesfully, ID : {response.json()['data']['_id']}"175 176 177def get_translator_names(platform):178 if platform == "dev":179 api_url = "https://api.dev-env.deepsync.co/api/v1"180 client = pymongo.MongoClient(os.environ.get("MONGO_DEV_URI"))181 source_db = client.deepsync_stage182 else:183 api_url = "https://api.deepsync.co/api/v1"184 client = pymongo.MongoClient(os.environ.get("MONGO_URI"))185 source_db = client.deepsync_prod186 187 response = requests.get(f"{api_url}/dashboard/translators", headers=HEADERS)188 translators = list(filter(lambda x: not x.get('isDeleted', False), response.json()['data']))189 translators_names = []190 191 if len(translators):192 desired_ids = [ObjectId(t["translator"]) for t in translators]193 companies_cursor = source_db.companies.find({'_id': {'$in': desired_ids}})194 companies = {c["_id"]: c for c in companies_cursor}195 for translator in translators:196 try:197 data = companies[ObjectId(translator["translator"])]198 translators_names.append(f'{data["name"]} / {data["email"]} / {translator["_id"]}')199 except Exception as e:200 print(e)201 pass202 client.close()203 return gr.Dropdown.update(choices=translators_names)204 205 206def select_current_data(platform, translator_name):207 if translator_name is None:208 return "Please select a translator."209 210 name, email, translator_id = list(map(lambda x: x.strip(), translator_name.split("/")))211 212 if platform == "dev":213 api_url = "https://api.dev-env.deepsync.co/api/v1"214 else:215 api_url = "https://api.deepsync.co/api/v1"216 217 response = requests.get(f"{api_url}/dashboard/translators", headers=HEADERS)218 translators = list(filter(lambda x: not x.get('isDeleted', False) and x["_id"] == translator_id, response.json()['data']))219 if len(translators):220 translator = translators[0]221 else:222 raise Exception("Translator", translator_name, "not found.")223 return gr.CheckboxGroup.update(value=[language_maps[l] for l in translator["sourceAccent"]]), gr.CheckboxGroup.update(value=[language_maps[l] for l in translator["targetAccent"]]), gr.Textbox.update(value="" if translator["contactNum"] is None else translator["contactNum"]), gr.Textbox.update(value=-1 if translator["pricing"] is None else translator["pricing"])224 225 226def update_translator(platform, translator_name, source_languages, target_languages, contact_num, pricing):227 if translator_name is None:228 return "Please select a translator."229 230 name, email, translator_id = list(map(lambda x: x.strip(), translator_name.split("/")))231 232 if platform == "dev":233 api_url = "https://api.dev-env.deepsync.co/api/v1"234 else:235 api_url = "https://api.deepsync.co/api/v1"236 237 response = requests.get(f"{api_url}/dashboard/translators", headers=HEADERS)238 translators = list(filter(lambda x: not x.get('isDeleted', False), response.json()['data']))239 if len(translators):240 translator = translators[0]241 else:242 raise Exception("Translator", translator_name, "not found.")243 244 source_accents = [language_maps[l] for l in source_languages]245 target_accents = [language_maps[l] for l in target_languages]246 247 updateable_data = {248 "sourceAccent": source_accents,249 "targetAccent": target_accents,250 "contactNum": "" if contact_num is None else contact_num.strip(),251 "pricing": 0 if pricing is None else pricing252 }253 254 response = requests.patch(f"{api_url}/dashboard/translators/{translator_id}", json=updateable_data, headers=HEADERS)255 if str(response.status_code).startswith('4') or str(response.status_code).startswith('5'):256 return response.json()['message']257 258 return f"Updated translator details succesfully."259 260 261def get_videos_under_review(platform, status_key):262 status_keys = [status_key]263 if platform == "dev":264 api_url = "https://api.dev-env.deepsync.co/api/v1"265 else:266 api_url = "https://api.deepsync.co/api/v1"267 268 if status_keys is None or len(status_keys) == 0:269 API_URL = f"{api_url}/dashboard/dubbed/videos/review/requested"270 response = requests.get(API_URL, headers=HEADERS)271 if response.status_code == 200:272 options = [f"{str(v['_id'])} / {v['title']}" for v in response.json()['data'] if 'title' in v]273 return gr.Dropdown.update(choices=options, value=None), ""274 else:275 return gr.Dropdown.update(choices=[], value=None), ""276 else:277 options = []278 count = {}279 for status in status_keys:280 API_URL = f"{api_url}/dashboard/dubbed/videos/review/requested/{video_status[status]}"281 response = requests.get(API_URL, headers=HEADERS)282 if response.status_code == 200:283 response_data = response.json()['data']284 if status == "Not Assigned":285 response_data = list(filter(lambda x: x["videoReviewInformation"].get("assignedTranslator") is None, response_data))286 if status == "Under Review":287 response_data = list(filter(lambda x: x["videoReviewInformation"].get("assignedTranslator") is not None, response_data))288 current_options = [f"{str(v['_id'])} / {v['title']}" for v in response_data if 'title' in v]289 290 count[status] = len(current_options)291 options.extend(current_options)292 data_text = "\n".join([f"{k} : {v} videos" for k,v in count.items()])293 return gr.Dropdown.update(choices=options, value=None), data_text294 295 296def delete_translator_func(platform, translator_name):297 if translator_name is None:298 return "Please select a translator."299 300 name, email, translator_id = list(map(lambda x: x.strip(), translator_name.split("/")))301 302 if platform == "dev":303 api_url = "https://api.dev-env.deepsync.co/api/v1"304 else:305 api_url = "https://api.deepsync.co/api/v1"306 307 response = requests.patch(f"{api_url}/dashboard/translators/{translator_id}", json={"isDeleted": True}, headers=HEADERS)308 if str(response.status_code).startswith('4') or str(response.status_code).startswith('5'):309 return response.json()['message']310 return "Translator deleted."311 312 313 314def get_traslators_assign(platform, video):315 video_id = video.split("/", maxsplit=1)[0].strip()316 317 if platform == "dev":318 api_url = "https://api.dev-env.deepsync.co/api/v1"319 client = pymongo.MongoClient(os.environ.get("MONGO_DEV_URI"))320 source_db = client.deepsync_stage321 else:322 api_url = "https://api.deepsync.co/api/v1"323 client = pymongo.MongoClient(os.environ.get("MONGO_URI"))324 source_db = client.deepsync_prod325 326 video = source_db.dubbedvideos.find_one({"_id": ObjectId(video_id)})327 source_language = video["sourceLanguage"].split("-")[0]328 target_language = video["targetLanguage"].split("-")[0]329 330 translators = list(source_db.translators.find({"sourceAccent": {"$elemMatch": {"$eq": source_language}}, "targetAccent": {"$elemMatch": {"$eq": target_language}}}))331 translators_list = []332 333 pending_videos = []334 for status in ["NOT_STARTED", "DUBBING_STARTED", "WAITING_QA"]:335 API_URL = f"{api_url}/dashboard/dubbed/videos/review/requested/{status}"336 response = requests.get(API_URL, headers=HEADERS)337 if response.status_code == 200:338 response_data = response.json()['data']339 pending_videos.extend(response_data)340 translator_pv_count = defaultdict(lambda: 0)341 for pv in pending_videos:342 if pv.get("videoReviewInformation", {}).get("assignedTranslator") is not None:343 trs = str(pv.get("videoReviewInformation", {}).get("assignedTranslator"))344 translator_pv_count[trs] += 1345 346 if len(translators):347 desired_ids = [t["translator"] for t in translators]348 companies_cursor = source_db.companies.find({'_id': {'$in': desired_ids}})349 companies = {c["_id"]: c for c in companies_cursor}350 for translator in translators:351 company = companies[translator["translator"]]352 translators_list.append(f"{company['name']} / {company['email']} / {str(translator['_id'])} / Pending Videos: {translator_pv_count[str(translator['_id'])]}")353 354 button = gr.Button.update(interactive=False)355 current_translator = None 356 current_qa = None357 if "videoReviewInformation" in video:358 if video["videoReviewInformation"]["status"] in {"NOT_STARTED", "DUBBING_STARTED", "WAITING_QA"}:359 button = gr.Button.update(interactive=True)360 if video["videoReviewInformation"].get("assignedTranslator") is not None:361 str_id = str(video["videoReviewInformation"].get("assignedTranslator"))362 value = [v for v in translators_list if str_id in v]363 current_translator = value[0] if len(value) else None364 if video["videoReviewInformation"].get("assignedQA") is not None:365 str_id = str(video["videoReviewInformation"].get("assignedQA"))366 value = [v for v in translators_list if str_id in v]367 current_qa = value[0] if len(value) else None368 client.close()369 return gr.Dropdown.update(choices=translators_list, value=current_translator), gr.Dropdown.update(choices=translators_list, value=current_qa), language_maps[source_language], language_maps[target_language], button, button370 371 372def assign_translator(platform, translator, video):373 translator_id = translator.split("/")[2].strip()374 video_id = video.split("/", maxsplit=1)[0].strip()375 role = "translator"376 if platform == "dev":377 api_url = "https://api.dev-env.deepsync.co/api/v1"378 else:379 api_url = "https://api.deepsync.co/api/v1"380 data = {381 "videoId": video_id,382 "userId": translator_id383 }384 response = requests.post(f"{api_url}/dashboard/dubbed/video/assign/{role}", json=data, headers=HEADERS)385 if str(response.status_code).startswith('2'):386 print_slack(platform, video_id, translator, "translator")387 return "Successfully assigned."388 else:389 return str(response.status_code)390 391 392def assign_qa(platform, qa, video):393 translator_id = qa.split("/")[2].strip()394 video_id = video.split("/", maxsplit=1)[0].strip()395 role = "QA"396 if platform == "dev":397 api_url = "https://api.dev-env.deepsync.co/api/v1"398 else:399 api_url = "https://api.deepsync.co/api/v1"400 data = {401 "videoId": video_id,402 "userId": translator_id403 }404 response = requests.post(f"{api_url}/dashboard/dubbed/video/assign/{role}", json=data, headers=HEADERS)405 if str(response.status_code).startswith('2'):406 print_slack(platform, video_id, qa, "qa")407 return "Successfully assigned."408 else:409 return str(response.status_code)410 411 412with gr.Blocks() as demo:413 gr.Markdown("# Translators Admin Dashboard")414 with gr.Tab("List"):415 source_platform = gr.Radio(["dev", "prod"], value="prod", label="Source Platform")416 refresh_list = gr.Button("Load")417 translators_list = gr.Dataframe(label="Translators", interactive=False, max_rows=20, overflow_row_behaviour="paginate")418 with gr.Tab("Add"):419 gr.Markdown("Add a new translator")420 source_platform_add = gr.Radio(["dev", "prod"], value="prod", label="Source Platform")421 email_id = gr.Textbox(label="Email ID", lines=1)422 source_languages = gr.CheckboxGroup(ALL_LANGUAGES, label="Source Languages")423 target_languages = gr.CheckboxGroup(ALL_LANGUAGES, label="Target Languages")424 contact_num_box = gr.Textbox(label="Contact Number", lines=1)425 pricing_box = gr.Number(label="Pricing", value=-1)426 submit = gr.Button("Submit")427 result = gr.Textbox(label="Logs")428 with gr.Tab("Update"):429 gr.Markdown("Update Translator")430 source_platform_update = gr.Radio(["dev", "prod"], value="prod", label="Source Platform")431 refresh_update_platform = gr.Button("Load")432 translator_name = gr.Dropdown([], label="Translators")433 source_languages_update = gr.CheckboxGroup(ALL_LANGUAGES, label="Source Languages")434 target_languages_update = gr.CheckboxGroup(ALL_LANGUAGES, label="Target Languages")435 contact_num_update = gr.Textbox(label="Contact Number", lines=1)436 pricing_update = gr.Number(label="Pricing")437 update_language = gr.Button("Update")438 delete_translator = gr.Button("Delete Translator")439 result_update = gr.Textbox(label="Logs")440 with gr.Tab("Assign"):441 gr.Markdown("Assign Translator / QA")442 source_platform_assign = gr.Radio(["dev", "prod"], value="prod", label="Source Platform")443 filter_videos_assign = gr.Radio(['Not Assigned', 'Under Review', 'Dubbing Started', 'Waiting QA', 'Sent to Client', 'Approved', 'Rejected', 'Churned', 'Re-review Requested'], label="Filter videos. Do not select any of them for videos without request info.")444 video_details = gr.Textbox(label="Video Details")445 refresh_assign = gr.Button("Load Videos")446 under_review_videos_list = gr.Dropdown([], label="Video IDs")447 with gr.Row():448 source_language_box = gr.Textbox(label="Source Language")449 target_language_box = gr.Textbox(label="Target Language")450 with gr.Row():451 translator_name_assign = gr.Dropdown([], label="Translator")452 submit_tr_assign_button = gr.Button("Assign Translator")453 with gr.Row():454 qa_name_assign = gr.Dropdown([], label="QA")455 submit_qa_assign_button = gr.Button("Assign QA")456 assign_logs = gr.Textbox(label="Logs")457 458 459 # For list460 refresh_list.click(get_translators, source_platform, translators_list)461 source_platform.change(get_translators, source_platform, translators_list)462 463 # For add464 submit.click(add_translator, [source_platform_add, email_id, source_languages, target_languages, contact_num_box, pricing_box], result)465 466 # For Update467 refresh_update_platform.click(get_translator_names, source_platform_update, translator_name)468 source_platform_update.change(get_translator_names, source_platform_update, translator_name)469 translator_name.change(select_current_data, [source_platform_update, translator_name], [source_languages_update, target_languages_update, contact_num_update, pricing_update])470 update_language.click(update_translator, [source_platform_update, translator_name, source_languages_update, target_languages_update, contact_num_update, pricing_update], result_update)471 delete_translator.click(delete_translator_func, [source_platform_update, translator_name], result_update)472 473 # For Assign474 refresh_assign.click(get_videos_under_review, [source_platform_assign, filter_videos_assign], [under_review_videos_list, video_details])475 under_review_videos_list.change(get_traslators_assign, [source_platform_assign, under_review_videos_list], [translator_name_assign, qa_name_assign, source_language_box, target_language_box, submit_tr_assign_button, submit_qa_assign_button])476 submit_tr_assign_button.click(assign_translator, [source_platform_assign, translator_name_assign, under_review_videos_list], assign_logs)477 submit_qa_assign_button.click(assign_qa, [source_platform_assign, qa_name_assign, under_review_videos_list], assign_logs)478 479 480if __name__=="__main__":481 demo.launch(auth=(os.environ.get("GRADIO_USERNAME"), os.environ.get("GRADIO_PASSWORD")))