Spaces:
Runtime error
Runtime error
File size: 7,928 Bytes
fc2fc56 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
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() |