Spaces:
Runtime error
Runtime error
| import logging | |
| import aiohttp | |
| import os,time | |
| from aiohttp import FormData | |
| from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup | |
| from telegram.ext import ( | |
| Application, CommandHandler, MessageHandler, | |
| filters, CallbackQueryHandler, ContextTypes | |
| ) | |
| from dotenv import load_dotenv | |
| load_dotenv(dotenv_path='env.env') | |
| #config | |
| TOKEN = "8320924107:AAH505mhHkOxeY3aLk0GObIpO_KCtY9hhLM" | |
| API_ENDPOINT = os.getenv("API_ENDPOINT") | |
| logging.basicConfig(level=logging.INFO) | |
| #category mappings | |
| VIEW_CATEGORIES = { | |
| "crl": { | |
| "Maxilla": "mx", | |
| "Mandible-MDS": "mds", | |
| "Mandible-MLS": "mls", | |
| "Lateral ventricle": "lv", | |
| "Head": "head", | |
| "Gestational sac": "gsac", | |
| "Thorax": "thorax", | |
| "Abdomen": "ab", | |
| "Body(Biparietal diameter)": "bd", | |
| "Rhombencephalon": "rbp", | |
| "Diencephalon": "dp", | |
| "NTAPS": "ntaps", | |
| "Nasal bone": "nb" | |
| }, | |
| "nt": { | |
| "Maxilla": "mx", | |
| "Mandible-MDS": "mds", | |
| "Mandible-MLS": "mls", | |
| "Lateral ventricle": "lv", | |
| "Head": "head", | |
| "Thorax": "thorax", | |
| "Abdomen": "ab", | |
| "Rhombencephalon": "rbp", | |
| "Diencephalon": "dp", | |
| "Nuchal translucency": "nt", | |
| "NTAPS": "ntaps", | |
| "Nasal bone": "nb" | |
| } | |
| } | |
| #start | |
| async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| await update.message.reply_text("Send an ultrasound image (JPG/png only) to begin. See /instructions first") | |
| async def instructions(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| instruction_message = """ | |
| π **Instructions** | |
| πΈ **Step 1:** Send a cropped ultrasound image of a structure you want to analyze. See all structures with /list command | |
| πΈ **Step 2:** Select the ultrasound view (CRL or NT) | |
| πΈ **Step 3:** Choose the anatomical category to analyze | |
| πΈ **Step 4:** Wait for the AI analysis results | |
| π **Important notes:** | |
| β’ Only JPG/PNG images are supported | |
| β’ Ensure the ultrasound image is clear and properly oriented | |
| β’ Results are for reference only - always consult a medical professional | |
| β’ Processing may take a few seconds | |
| β’ Stop bot with /stop | |
| π‘ **Tips:** | |
| β’ Use high-quality, well-lit images for better accuracy | |
| β’ Make sure the anatomical structure is clearly visible | |
| β’ Different views (CRL/NT) have different category options | |
| π **Need help?** Contact support if you encounter any issues @d3ikshr. | |
| """ | |
| await update.message.reply_text(instruction_message, parse_mode='Markdown') | |
| async def list_categories(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| list_message = """ | |
| π **Available Categories by View** | |
| π **CRL view categories:** | |
| β’ Maxilla β’ Mandible-MDS β’ Mandible-MLS | |
| β’ Lateral ventricle β’ Head β’ Gestational sac | |
| β’ Thorax β’ Abdomen β’ Body(Biparietal diameter) | |
| β’ Rhombencephalon β’ Diencephalon β’ NTAPS | |
| β’ Nasal bone | |
| π **NT view categories:** | |
| β’ Maxilla β’ Mandible-MDS β’ Mandible-MLS | |
| β’ Lateral ventricle β’ Head β’ Thorax | |
| β’ Abdomen β’ Rhombencephalon β’ Diencephalon | |
| β’ Nuchal translucency β’ NTAPS β’ Nasal bone | |
| π‘ **Note:** Categories will be shown automatically based on your selected view during analysis. | |
| """ | |
| await update.message.reply_text(list_message, parse_mode='Markdown') | |
| async def stop(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| await update.message.reply_text("π Bot stopped for this chat. Use /start to begin again.") | |
| # Clear user data | |
| context.user_data.clear() | |
| # Receive image | |
| async def handle_image(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| photo = update.message.photo[-1] | |
| file = await photo.get_file() | |
| file_path = f"{update.message.from_user.id}_ultrasound.jpg" | |
| await file.download_to_drive(file_path) | |
| context.user_data["image_path"] = file_path | |
| # get view | |
| buttons = [ | |
| [InlineKeyboardButton("CRL", callback_data="view:crl"), | |
| InlineKeyboardButton("NT", callback_data="view:nt")] | |
| ] | |
| await update.message.reply_text( | |
| "Select the ultrasound view:", | |
| reply_markup=InlineKeyboardMarkup(buttons) | |
| ) | |
| #selcet view | |
| async def handle_view(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| query = update.callback_query | |
| await query.answer() | |
| view = query.data.split(":")[1] | |
| context.user_data["selected_view"] = view | |
| #get categories for the selected view | |
| categories = list(VIEW_CATEGORIES[view].keys()) | |
| buttons = [[InlineKeyboardButton(cat, callback_data=f"category:{cat}")] | |
| for cat in categories] | |
| await query.edit_message_text( | |
| "Select anatomical category:", | |
| reply_markup=InlineKeyboardMarkup(buttons) | |
| ) | |
| #get category then upload | |
| async def handle_category(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| query = update.callback_query | |
| await query.answer() | |
| category_display = query.data.split(":")[1] | |
| context.user_data["selected_category"] = category_display | |
| image_path = context.user_data.get("image_path") | |
| view = context.user_data.get("selected_view") | |
| if not image_path or not view: | |
| await query.edit_message_text("Missing image or view.") | |
| return | |
| await query.edit_message_text("π Processing image...") | |
| time.sleep(3) | |
| await query.edit_message_text("π₯ Building diagnosis, please wait...") | |
| try: | |
| #read the image file into memory first | |
| with open(image_path, "rb") as f: | |
| image_data = f.read() | |
| #mapping | |
| category_value = VIEW_CATEGORIES[view][category_display] | |
| #create form data | |
| form = FormData() | |
| form.add_field("view", view) | |
| form.add_field("category", category_value) | |
| form.add_field("source", "telegram") | |
| form.add_field("image", image_data, filename="image.jpg", content_type="image/jpeg") | |
| #send request | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post(API_ENDPOINT, data=form) as resp: | |
| if resp.status == 200: | |
| result = await resp.json() | |
| message = f""" | |
| π **Analysis Results** | |
| π **View:** {result.get('view', view).upper()} | |
| π₯ **Category:** {category_display} | |
| π **Confidence:** {result.get('confidence', 0):.2f}% | |
| β οΈ **Reconstruction error:** {result.get('error', 0):.5f} | |
| π **Status:** {result.get('comment', 'No comment')} | |
| π©Ί **Diagnosis:** {result.get('diagnosis', 'No diagnosis')}* | |
| """ | |
| await query.edit_message_text(message, parse_mode='Markdown') | |
| else: | |
| error_text = await resp.text() | |
| await query.edit_message_text(f"β Upload failed. Status: {resp.status}\nError: {error_text}") | |
| except Exception as e: | |
| logging.error(f"Error processing request: {e}") | |
| await query.edit_message_text(f"β Error: {str(e)}") | |
| finally: | |
| try: | |
| if os.path.exists(image_path): | |
| os.remove(image_path) | |
| logging.info(f"Cleaned up image file: {image_path}") | |
| except Exception as e: | |
| logging.error(f"Error cleaning up file: {e}") | |
| def main(): | |
| app = Application.builder().token(TOKEN).build() | |
| app.add_handler(CommandHandler("start", start)) | |
| app.add_handler(CommandHandler("instructions", instructions)) | |
| app.add_handler(CommandHandler("list", list_categories)) | |
| app.add_handler(CommandHandler("stop", stop)) | |
| app.add_handler(MessageHandler(filters.PHOTO, handle_image)) | |
| app.add_handler(CallbackQueryHandler(handle_view, pattern="^view:")) | |
| app.add_handler(CallbackQueryHandler(handle_category, pattern="^category:")) | |
| app.run_polling() | |
| if __name__ == "__main__": | |
| main() |