AI-Powered Medication Management System
An intelligent platform that digitizes prescriptions via OCR and fine-tuned LLMs, providing a personalized AI pharmacist for safe medication management.
- πΈ AI Vision Prescription Parsing
- Simply snap a photo of a prescription or pill envelope. Our AI instantly extracts structured data (hospital name, prescription date, medication names, and dosage).
- Automatically masks Personally Identifiable Information (PII) to ensure data privacy.
- π©ββοΈ Personalized AI Pharmacist
- Leverages public health data to translate complex medical jargon into friendly, easy-to-understand instructions.
- ποΈ Smart Pillbox Management
- Easily track and manage your current medications, their efficacy, and important precautions in one place.
flowchart TD
%% Custom Styling
classDef client fill:#02569B,stroke:#fff,stroke-width:2px,color:#fff,rx:8px,ry:8px
classDef server fill:#005571,stroke:#fff,stroke-width:2px,color:#fff,rx:8px,ry:8px
classDef ai fill:#8E75B2,stroke:#fff,stroke-width:2px,color:#fff,rx:8px,ry:8px
classDef db fill:#07405e,stroke:#fff,stroke-width:2px,color:#fff,rx:8px,ry:8px
subgraph Frontend [π± Client Tier - Flutter]
C1(πΈ Capture<br/>Prescription):::client
C2(π₯οΈ Structured<br/>UI Display):::client
C3(π Request<br/>Detail & Save):::client
C4(ποΈ Update<br/>Pillbox UI):::client
end
subgraph Backend [π Server Tier - FastAPI]
S1(βοΈ Image Preprocessing<br/>OpenCV):::server
S2(π‘οΈ PII Masking &<br/>Data Structuring):::server
S3(π§ Routing &<br/>Business Logic):::server
DB[(πΎ SQLite<br/>Database)]:::db
end
subgraph External [π€ AI & External APIs]
AI1{{β¨ Fine-Tuned<br/>LLM Vision}}:::ai
PUB{{ποΈ Public Drug<br/>Safety API}}:::ai
AI2{{β¨ Fine-Tuned<br/>LLM Text}}:::ai
end
%% Flow 1: Vision Parsing (Current)
C1 -->|Multipart Image| S1
S1 -->|Processed Bytes| AI1
AI1 -->|Raw JSON| S2
S2 -->|Response DTO| C2
%% Flow 2: Detail & Save (Planned)
C2 -.->|User Click| C3
C3 -->|Drug Name| S3
S3 -->|Query| PUB
PUB -->|Raw Efficacy Data| AI2
AI2 -->|Pharmacist Summary| S3
S3 -->|ORM Entity| DB
S3 -->|Success Response| C4
πClass Diagram
classDiagram
direction TD
skinparam classAttributeIconSize 0
%% ==========================================
%% π± 1. FRONTEND TIER (Flutter Dart Files)
%% ==========================================
namespace Frontend_Application {
class main_dart {
<<Entry Point : main.dart>>
+main() void
}
class MedBuddyApp {
<<Widget : main.dart>>
+build(context) Widget
}
class HomeScreen {
<<View : home_screen.dart>>
+build(context) Widget
-_buildInfoBadge(label, value) Widget
}
class PillboxScreen {
<<View : pillbox_screen.dart>>
+createState() _PillboxScreenState
}
class _PillboxScreenState {
<<State : pillbox_screen.dart>>
+initState() void
+build(context) Widget
}
class MedicationViewModel {
<<ViewModel : medication_viewmodel.dart>>
-_apiService : ApiService
-_picker : ImagePicker
-_apiUrl : String
-_isLoading : bool
-_statusMessage : String
-_hospitalName : String
-_prescriptionDate : String
-_parsedDrugList : List~dynamic~
-_savedDrugs : List~DrugInfo~
+processMedicationImage() void
-_setLoading(value) void
+saveDrugToPillbox(drug) bool
+fetchPillbox() void
+removeDrugFromPillbox(id) void
}
class ApiService {
<<Service : api_service.dart>>
+baseUrl : String
+identifyMedication(text) List~DrugInfo~
+saveMedication(drug) bool
+getSavedMedications() List~DrugInfo~
+deleteMedication(id) bool
+parsePrescription(ocrText) Map
}
class VisionService {
<<Service : vision_service.dart>>
-_picker : ImagePicker
-_textRecognizer : TextRecognizer
+captureAndRecognizeText() String
+dispose() void
}
class PrescriptionParser_Dart {
<<Utility : prescription_parser.dart>>
+maskPrivacyInfo(text) String
+extractDosageInfo(text) Map
}
class DrugInfo_Dart {
<<Model : drug_info.dart>>
+itemName : String
+efficacy : String
+useMethod : String
+warningMessage : String
+aiGuide : String
+id : int
+fromJson(json) DrugInfo
}
}
%% ==========================================
%% π 2. BACKEND API & CONTROLLER TIER
%% ==========================================
namespace Backend_API {
class FastAPIApp {
<<Entry Point : main.py>>
+app : FastAPI
+include_router() void
}
class MedicationRouter {
<<Controller : api/router.py>>
+identify_medication(request, ocr, drug) MedicationResponse
+save_medication(medication, db) dict
+get_saved_medications(db) dict
+delete_medication(drug_id, db) dict
+parse_prescription_endpoint(request, ocr) dict
+upload_and_parse_prescription(file, ocr) PrescriptionData
}
}
%% ==========================================
%% π§ 3. BACKEND BUSINESS LOGIC TIER
%% ==========================================
namespace Backend_Services {
class Settings {
<<Config : core/config.py>>
+GEMINI_API_KEY : str
+PUBLIC_DATA_API_KEY : str
+BASIC_DRUG_API_BASE_URL : str
+ADVANCED_DRUG_API_BASE_URL : str
}
class OCRService {
<<Service : services/ocr_service.py>>
+process_text(raw_text) str
+split_lines(raw_text) List~str~
+parse_prescription_text(raw_text) dict
+extract_prescription_data(image_bytes) PrescriptionData
-_apply_secondary_masking(data) dict
}
class DrugService {
<<Service : services/drug_service.py>>
+api_key : str
+basic_url : str
+advanced_url : str
+fetch_drug_info(drug_name) List~DrugInfo~
}
class PrescriptionParser_Python {
<<Utility : utils/prescription_parser.py>>
+normalize_text(text) str
+normalize_date(text) str
+extract_patient_name(line) str
+parse_medication_line(line) dict
+parse_prescription(lines) dict
}
}
%% ==========================================
%% ποΈ 4. BACKEND DATA TIER (DB & Schemas)
%% ==========================================
namespace Backend_Data {
class DatabaseModule {
<<Config : core/database.py>>
+engine : Engine
+SessionLocal : sessionmaker
+Base : declarative_base
+get_db() Iterator~Session~
}
class SavedMedication {
<<Entity : models/db_models.py>>
+id : Integer
+item_name : String
+efficacy : String
+use_method : String
+warning_message : String
+ai_guide : String
}
class MedicationSchemas {
<<DTO : schemas/medication.py>>
+class MedicationRequest
+class DrugInfo
+class SavedMedicationCreate
+class MedicationResponse
}
class OCRSchemas {
<<DTO : schemas/ocr.py>>
+class MedicationItem
+class PrescriptionData
}
}
%% ==========================================
%% π RELATIONSHIPS (κ΄κ³λ§ μ°κ²°)
%% ==========================================
%% Frontend Relationships
main_dart ..> MedBuddyApp : Runs
MedBuddyApp ..> HomeScreen : Uses
MedBuddyApp ..> MedicationViewModel : Provides
PillboxScreen ..> _PillboxScreenState : Creates
HomeScreen ..> MedicationViewModel : Observes
_PillboxScreenState ..> MedicationViewModel : Observes
MedicationViewModel --> ApiService : Uses
MedicationViewModel o-- DrugInfo_Dart : Aggregation
ApiService ..> DrugInfo_Dart : Maps Data
VisionService ..> PrescriptionParser_Dart : Can Use
%% Client-Server Connection
MedicationViewModel ..> FastAPIApp : HTTP Multipart Request
ApiService ..> FastAPIApp : HTTP Network Protocol
%% Backend Controller Relationships
FastAPIApp --> MedicationRouter : Registers
FastAPIApp ..> DatabaseModule : Creates Tables
MedicationRouter ..> DatabaseModule : Uses get_db()
MedicationRouter --> SavedMedication : DB CRUD Operations
MedicationRouter --> OCRService : Injects
MedicationRouter --> DrugService : Injects
MedicationRouter ..> MedicationSchemas : Request/Response
MedicationRouter ..> OCRSchemas : Request/Response
%% Backend Service Dependencies
OCRService --> PrescriptionParser_Python : Uses
OCRService ..> OCRSchemas : Returns
DrugService ..> Settings : Reads Config
DrugService ..> MedicationSchemas : Returns
πSequence Diagram
sequenceDiagram
autonumber
actor User as μ¬μ©μ
participant View as HomeScreen
participant VM as MedicationViewModel
participant API as FastAPI (main.py)
participant Router as MedicationRouter
participant OCR as OCRService
participant AI as Gemini Vision API
User->>View: 'μ²λ°©μ 촬μ' λ²νΌ ν΄λ¦
activate View
View->>VM: processMedicationImage()
activate VM
VM->>VM: _picker.pickImage(camera) νΈμΆ
alt 촬μ μλ£ μ
VM->>VM: _setLoading(true)
VM-->>View: notifyListeners() (μ€νΌλ λ λλ§)
Note over VM, API: HTTP Multipart POST /upload-prescription
VM->>API: μ²λ°©μ μ΄λ―Έμ§ νμΌ(imageFile) μ μ‘
activate API
API->>Router: upload_and_parse_prescription()
activate Router
Router->>OCR: extract_prescription_data(image_bytes)
activate OCR
OCR->>OCR: preprocess_prescription_image() μ μ²λ¦¬
OCR->>AI: model.generate_content_async() νΈμΆ
activate AI
Note over OCR, AI: 보μ ν둬ννΈ + μ΄λ―Έμ§ μ μ‘
AI-->>OCR: ꡬ쑰νλ JSON Text μλ΅ λ°ν
deactivate AI
OCR->>OCR: λ§ν¬λ€μ΄ μ κ±° λ° JSON λμ½λ©
OCR->>OCR: _apply_secondary_masking() (μ κ·μ 2μ°¨ λ§μ€νΉ)
OCR-->>Router: PrescriptionData (DTO κ°μ²΄) λ°ν
deactivate OCR
Router-->>API: HTTP 200 OK
deactivate Router
API-->>VM: JSON μλ΅ μμ (decodedBody)
deactivate API
VM->>VM: _parsedDrugList μ
λ°μ΄νΈ & _setLoading(false)
VM-->>View: notifyListeners() (κ²°κ³Ό νλ©΄ λ λλ§)
end
deactivate VM
View-->>User: λ³μ μ 보 λ° μΆμΆλ μ½ν λͺ©λ‘ UI νμ
deactivate View
Note over User, View: μ¬μ©μκ° νλ©΄μ μ½ν 리μ€νΈλ₯Ό νμΈνλ©° λκΈ°
sequenceDiagram
autonumber
actor User as μ¬μ©μ
participant View as HomeScreen
participant VM as MedicationViewModel
participant ApiS as ApiService (Dart)
participant Router as MedicationRouter
participant OCR as OCRService
participant DrugS as DrugService
participant PubAPI as 곡곡λ°μ΄ν° API
participant AI as Gemini Text API
participant DB as SQLite DB
Note over User, View: Phase 1μμ μΆμΆλ μ½ν λͺ©λ‘ μ€ νλλ₯Ό μ ννμ¬ μ§ν
User->>View: μ½ν 리μ€νΈμμ 'μμΈ λΆμ & μ μ₯' ν΄λ¦
activate View
View->>VM: saveDrugToPillbox(DrugInfo)
activate VM
VM-->>View: μν λ©μμ§ μ
λ°μ΄νΈ (μ μ₯ μ€...)
VM->>ApiS: identifyMedication(text) νΈμΆ
activate ApiS
Note over ApiS, Router: HTTP POST /identify
ApiS->>Router: μ½νλͺ
(extracted_text) μ μ‘
activate Router
Router->>OCR: process_text() λ
Έμ΄μ¦ μ κ±°
OCR-->>Router: μ μ λ κ²μμ΄(search_keyword) λ°ν
Router->>DrugS: fetch_drug_info(search_keyword)
activate DrugS
DrugS->>PubAPI: HTTP GET eμ½μμ API νΈμΆ
PubAPI-->>DrugS: XML/JSON μλ³Έ λ°μ΄ν° λ°ν
DrugS-->>Router: List<DrugInfo> λ°ν (ν¨λ₯, μ£Όμμ¬ν ν¬ν¨)
deactivate DrugS
Router->>AI: model.generate_content_async(μμ½μ² μλ³Έ λ°μ΄ν°)
activate AI
Note over Router, AI: "μΉμ ν μ½μ¬ λ§ν¬λ‘ 1~2μ€ μμ½ν΄μ€" ν둬ννΈ μ μ‘
AI-->>Router: μΉμ ν AI κ°μ΄λ ν
μ€νΈ(ai_guide) λ°ν
deactivate AI
Router-->>ApiS: MedicationResponse JSON λ°ν
deactivate Router
ApiS->>ApiS: DrugInfo.fromJson() λͺ¨λΈ λ§€ν
Note over ApiS, Router: HTTP POST /save
ApiS->>Router: μμ±λ DrugInfo μ μ‘
activate Router
Router->>DB: db.add(SavedMedication) & db.commit()
activate DB
DB-->>Router: DB μ μ₯ μ±κ³΅ νμΈ
deactivate DB
Router-->>ApiS: μ μ₯ μ±κ³΅ μλ΅
deactivate Router
ApiS-->>VM: boolean(success) λ°ν
deactivate ApiS
VM->>VM: fetchPillbox() νΈμΆ (λ°μ΄ν° μ΅μ ν)
VM-->>View: μ μ₯ μλ£ λ©μμ§ νμ
deactivate VM
View-->>User: SnackBar μλ¦Ό ("μ±κ³΅μ μΌλ‘ μ μ₯λμμ΅λλ€!")
deactivate View
$ cd backend
$ pip install -r requirements.txt
$ uvicorn main:app --reload$ cd frontend
$ flutter pub get
$ flutter run| Profile | Name | Role | GitHub |
|---|---|---|---|
![]() |
1window2 | Lead Full-Stack Developer & AI Pipeline Architecture | @1window2 |
![]() |
tmdgusdl9647 | Backend Developer & AI Logic | @tmdgusdl9647 |
![]() |
jeeon0318 | Backend Developer & Compliance Specialist | @jeeon0318 |
![]() |
onlyone130 | Frontend Designer & UI/UX Lead | @onlyone130 |



