AI-Powered Medication Management System
A Flutter and FastAPI medication assistant that analyzes prescriptions and loose-pill photos, supports direct medication entry, enriches medication information with Korean public data and Gemini, and helps patients and caregivers manage schedules, reminders, linked medication context, and nearby-pharmacy and chat flows.
- Read the Korean user guide on GitHub.
- Open the illustrated Korean PDF guide.
- The guide covers the v0.2.3 beta interface; its text was brought up to the v0.2.4 line on 2026-10-21 while the PDF and screenshots still await a rebuild. Capture and version notes list what changed.
- Keep a 2x2 home menu: medication registration and identification, health recommendations, nearby hospitals and pharmacies, and settings. Reminder settings remain available from Schedule and Settings.
- Open one latest-first notification list from the header bell, with an unread badge, received chat previews, read actions, and notification-only deletion.
- History is stored per account on the current device, not in a server mailbox. Chat previews respect content-visibility settings; reminder entries reflect scheduled events rather than proof of delivery or medication intake.
- See the notification inbox design note for storage and lifecycle boundaries.
- Capture or select prescription and pill-envelope images, with a guided prescription camera that supports portrait and landscape framing only while the camera is active.
- Perform Korean OCR and privacy masking on-device, then send only de-identified text to the authenticated FastAPI analysis pipeline.
- Review recognized fields, correct or add medications, confirm schedule details, and save verified results to the medication list.
- Resolve normalized medication names through the local catalog, Redis cache, and Korean public drug APIs.
- Present validated medication information, images, patient-friendly guidance, and focused TTS playback.
- Compare front and optional reverse-side photos against the MFDS pill-identification catalog using visible attributes and deterministic ranking.
- Treat results as candidates requiring explicit user confirmation, with guidance to verify packaging or consult a pharmacist.
- Users can register a medication without a prescription or pill-identification request by entering its name, dose, unit, start date, end date, and schedule slots.
- An optional camera or gallery image is copied into app-owned patient storage. Orphaned local images are removed when they are no longer referenced.
- Direct entries reuse the same saved-medication and schedule contracts as analyzed medications, so filtering, reminders, completion tracking, caregiver views, and medication-context chat do not require a separate data path.
-
Open pharmacy search from the home menu's nearby hospitals-and-pharmacies shortcut.
-
Pharmacy search is a standard feature and needs no setting; choices made in the retired laboratory menu do not restrict access.
-
Users can request nearby pharmacies after granting foreground location permission. Location is requested only while this feature is in use.
-
The Flutter client sends coordinates to the authenticated MedBuddy API. The backend keeps the public-data credential private and adapts the National Emergency Medical Center pharmacy response into the app contract.
-
Results are filtered on the server before the 30-result limit is applied. The default view shows pharmacies that are open now without asking the user to enter a time. One filter button offers open-now, late-hours, exact-date weekend/holiday, and all-nearby views; the late-hours view combines officially designated public late-night pharmacies with pharmacies whose reported schedules run late. Date-based views ask only for a date. A Naver Map view appears above the filter; selecting either a pharmacy card or marker synchronizes the selection and centers the map on that pharmacy. Open pharmacies are ranked before closed pharmacies and late-hours pharmacies are prioritized next, followed by user-scoped favorites and distance within the same operating group.
-
Weekly National Emergency Medical Center schedules, Korean legal holidays, and exact-date NEMC holiday emergency rosters are cached in PostgreSQL. Responses expose catalog freshness and whether an exact roster, a bounded stale cache, or a weekly fallback supplied the schedule. Official designation records retain their authority URL and verification date and are not inferred from closing time alone.
-
Naver Dynamic Map is initialized with a compile-time client identifier. Each pharmacy card lets the user choose an installed map app or Google Maps; if neither can be opened, MedBuddy copies the validated pharmacy address for use in another map or notes app.
-
The screen distinguishes pharmacies that are closing soon from closed pharmacies with a known next regular opening, and shows the public-catalog refresh time when available. If optional map configuration is unavailable, the list, phone, and directions actions remain usable with a clear configuration message instead of a misleading coordinate error.
-
Manual refresh uses a cooldown to prevent accidental repeated public-data requests. Telephone and directions launches are centralized in one validated external-action service.
-
Operating hours are informational public data and may change on holidays or at short notice, so the screen asks users to confirm by phone before visiting. A selected pharmacy can be shared into an authorized medication conversation, with phone-confirmed sharing represented separately from an ordinary location share.
- Persist medication, caregiver, and chat notification preferences; lock-screen privacy; schedule defaults; font size; reading speed; language; and time format with a local fallback.
- Apply display and voice preferences consistently without changing existing confirmed medication schedules.
- Save patient-scoped medications with prescription dates, dosage details, treatment periods, and schedule slots.
- Filter active and completed courses, track each daily dose independently, and undo accidental completion updates.
- Surface the next dose and daily progress on the home screen while retaining completed medication history.
- Link patients and caregivers through temporary codes backed by authenticated, server-derived ownership.
- Let caregivers view linked medication data, unlink safely, and configure per-slot completion or missed-dose alerts.
- Deliver transition-based FCM alerts in beta mode without exposing internal patient identifiers.
- Support medication-context chat as a standard v0.2.0 feature with server-verified schedules, medication cards, pharmacy snapshots, idempotent events, and participant-scoped unread state.
- Show a Chat bottom tab only when the current user has an active patient-caregiver connection. The conversation list shows peer names and recent messages, supports multiple linked patients or caregivers, and opens the existing context chat. Local link changes refresh the tab immediately; foreground checks and app resume also detect remote changes.
- Select chat messages for private deletion at any time, or redact your own messages for both participants within 24 hours of sending. Server-side authorization and time checks apply; medication records remain unchanged.
- Generate patient-scoped health recommendations from saved medication context.
- Schedule persistent per-slot medication reminders and caregiver notification preferences.
- v0.2.4 exhibition line and carried-over verification: v0.2.3-beta is published as a limited off-Play pre-release; v0.2.4 prepares the app for the graduation exhibition (2026-11-26 to 11-27) and is to be finished by 2026-11-20. Remaining physical reboot/offline recovery, two-device caregiver/chat checks, and measured recognition validation are tracked in the release TODO.
- Android production verification: Validate the dedicated FastAPI/PostgreSQL/Redis production host behind Cloudflare Tunnel, complete backup and restore rehearsal, and finish authenticated two-device, Wi-Fi, cellular, outage-recovery, and signed-device smoke tests.
- Local pill-vision model: Evaluate a licensed or locally trained lightweight model against the current
PillVisualFeaturesboundary before replacing the external visual-attribute adapter. The current MFDS ranking and mandatory confirmation contract must remain unchanged.
MedBuddy is implemented around the project UML diagrams and follows a Boundary-Control-Entity style structure:
- Boundary/UI classes render screens and collect user input.
- Frontend boundary/service classes wrap on-device prescription OCR, text-region mapping, privacy filtering, camera-guide cropping, local manual-entry images, foreground location, embedded pharmacy-map rendering, pharmacy favorites and validated external actions, local notifications, TTS, and linked-chat WebSocket events.
- Backend boundaries receive de-identified prescription text and isolate public drug and pharmacy APIs, Gemini text recovery, loose-pill vision extraction, and FCM delivery from the use-case controls.
- Control classes coordinate use cases, API calls, scope resolution, persistence, OCR correction policy, bounded multi-pill work, nearby-pharmacy queries, and linked chat without placing domain logic in screens or routers.
- Entity/Model classes preserve application data contracts such as prescription analysis results, medication schedules, saved medication snapshots, manual entries, nearby pharmacies, chat messages and medication context, user settings, notification preferences, and patient-caregiver links.
- Backend medication, pharmacy, and chat routers remain thin boundary adapters around cohesive controls.
The implementation-grounded class view is maintained in
docs/UML/class/README.md.
The frozen Android beta boundary and planned security architecture are defined
in docs/MedBuddy - Beta Scope.md and
docs/MedBuddy - Beta Security Architecture.md.
Contribution rules for preserving the UML-aligned structure are documented in
CONTRIBUTING.md.
Figma: MedBuddy Design
- Designed by @onlyone130
- Flutter UI colors are centralized in
frontend/lib/theme/medbuddy_theme.dartfrom the Figma palette, including primary greens, mint borders, schedule slot colors, surface shades, dividers, and text colors.
- Python 3.11 for the backend CI target
- Flutter SDK and Android Studio
- A running Android emulator or physical Android device
- Gemini API key
- Korean public data portal API key authorized for the drug, NEMC pharmacy/hospital, and KASI holiday services listed below
- Redis server, optional for local cache and rate-limit testing; required for distributed production quotas
- Optional local medication catalog database at
backend/medbuddy.db
From the repository root:
cd backend
py -3.11 -m venv ..\.venv
..\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
Copy-Item .env.example .envOpen backend/.env and set at least:
GEMINI_API_KEY=your_gemini_api_key
PUBLIC_DATA_API_KEY=your_public_data_api_key
# Optional: leave blank to reuse PUBLIC_DATA_API_KEY for hospital requests.
HOSPITAL_API_KEY=
APP_ENV=development
AUTH_MODE=disabled
AUTO_CREATE_SCHEMA=true
PILL_IMAGE_API_ENABLED=true
PILL_IMAGE_API_TIMEOUT_SECONDS=8
PILL_IDENTIFICATION_MODEL_NAME=gemini-3.1-flash-lite
PILL_IDENTIFICATION_TIMEOUT_SECONDS=20
PILL_IDENTIFICATION_CATALOG_TTL_HOURS=168
PILL_IDENTIFICATION_CATALOG_REFRESH_TIMEOUT_SECONDS=30The public-data key must be authorized for the e약은요, medication approval,
medication pill-identification, and pharmacy-location APIs. Pill images are optional; lookups keep
working with the existing placeholder when that API is unavailable or the dosage
form has no public pill image. Set PILL_IMAGE_API_ENABLED=false only when the
optional saved-medication image enrichment must be disabled. The experimental
loose-pill flow still requires the MFDS identification catalog.
The values above are placeholders, not working credentials. Enter your own keys
only in the ignored backend/.env (or deploy/backend.env on the server).
Do not commit either file, service-account JSON, Firebase configuration originals,
local demo settings, or logs containing credentials. Never put public-data or
Gemini keys in Flutter --dart-define values or client source.
| Service to apply for | Used for | Backend setting |
|---|---|---|
| 국립중앙의료원 전국 병·의원 찾기 서비스 | Hospital coordinates, departments, contact information and weekly consultation hours | HOSPITAL_API_KEY; when blank, reuses PUBLIC_DATA_API_KEY |
| 한국천문연구원 특일 정보 | Determines whether a selected Korean date is a legal holiday, for both hospital and pharmacy hours | PUBLIC_DATA_API_KEY |
국립중앙의료원 전국 약국 정보 조회 서비스 (ErmctInsttInfoInqireService) |
Pharmacy catalog and weekly business hours | PUBLIC_DATA_API_KEY |
| 국립중앙의료원 명절 비상진료기관 및 약국 조회 서비스 | Exact-date holiday pharmacy rosters | PUBLIC_DATA_API_KEY |
Apply for each service separately in the Public Data Portal and check approval
for the key used by the running backend. A key that works for medication or
hospital data does not automatically authorize holiday queries. HOSPITAL_API_KEY
does not override the key used by the KASI calendar or pharmacy services.
The backend URL-encodes request parameters; use the portal's decoded key value.
Restart the backend after editing its local environment. For a 403 or
SERVICE_KEY_IS_NOT_REGISTERED_ERROR, check service approval and the configured
key without copying the real key or a credential-bearing request URL into an
issue. Public-data failures are not treated as proof that a hospital is closed
or that a date is an ordinary weekday. Visit-time availability still requires
confirmation with the institution.
The hospital request/cache limits and service base URLs are documented in
backend/.env.example; retain those defaults for initial testing. Map rendering
uses the existing Naver client-ID setup below, independently of backend data keys.
DB 초기화 및 갱신 정책:
AUTO_CREATE_SCHEMA=true는 빈 DB만 Alembichead로 초기화한다. 기존 DB에create_all()이나 임의의 컬럼 보정을 적용하지 않는다.- 이미 Alembic으로 관리되는 DB는 Backend/worker를 중지하고 백업한 뒤,
backend/에서 아래 명령으로 갱신한다. 개발·staging·production 모두 같은 migration 체인을 사용하며, 운영에서는AUTO_CREATE_SCHEMA=false를 유지한다.
python -m alembic current
python -m alembic heads
python -m alembic upgrade head- 기존 테이블은 있지만
alembic_version이 없는 구형 로컬 DB는 자동 변환하지 않는다. 먼저 SQLite backup API로 WAL까지 포함한 일관된 백업을 만들고, 복사본의 테이블·컬럼·제약조건·데이터 무결성을 특정 migration revision과 대조한다. 확인된 baseline에만alembic stamp <검증한_revision>을 수행하고alembic upgrade head를 적용해 데이터 보존을 확인한 후 실제 DB에 적용한다. 다른 DB의 revision을 그대로 사용하거나stamp head로 누락된 migration을 건너뛰지 않는다. DB 삭제·수동ALTER TABLE은 필요하지 않다. - 앱은 migration revision 및 ORM 필수 컬럼 불일치 시 worker 시작 전에 중단한다.
따라서
AUTO_CREATE_SCHEMA=true도 기존 DB의 migration을 대체하지 않는다. - v0.2.2 통합 migration head는
f2c8a6d4e901이다. Catalog 갱신 상태c5e1a7f3b902와 ID·Push 등록 보호d19e7c4b2a60을 합치며 기존 revision을 바꾸지 않는다. d19e7c4b2a60적용 전 각 기기의 복용 대기 기록 동기화를 완료하고 실패·보류 항목을 확인한다. 기록을 일괄 폐기하거나 조용히 삭제하지 않으며, 대상이 불명확하면 자동 재전송하지 않고 사용자 확인 후 진행한다. 앱과 Backend/worker를 중지하고 일관된 백업 및 복사본 검증 후 전용 Alembic 연결로 갱신한다. SQLite의foreign_keys=OFF는 이 전용 migration 연결에만 해당하며 서비스 연결은 끄지 않는다. SQLite 약·채팅 ID의 현재 행과 적용 후 발급 이력은 재사용을 방지하지만, 적용 전 이미 삭제된 최고 ID는 복원할 수 없다. 큰 timestamp ID를 시딩하거나 기존 ID를 바꾸지 않는다. 상세 절차와 rollback 제한은 운영 갱신 안내를 따른다.
Start the API server:
python -m uvicorn main:app --host 127.0.0.1 --port 8000 --reloadAPI documentation is available at:
http://127.0.0.1:8000/docs
This loopback-only server is retained for backend development and automated testing only. The Android application does not use it as its default API endpoint.
Production runs on the dedicated Ubuntu host behind Cloudflare Tunnel. See MedBuddy Production Deployment.
Local development stores medication, pill-reference, and weekly pharmacy schedule rows in backend/medbuddy.db. Production uses the same ORM mappings in Alembic-managed PostgreSQL, and the catalog synchronization job is the sole production writer. Generated .db files are intentionally ignored by Git.
Build or refresh the optional local medication catalog from the public drug APIs:
python scripts/sync_drug_catalog.py --dataset all --page-size 500 --max-retries 5
python scripts/sync_pharmacy_catalog.py --page-size 1000 --max-retries 5Resume an interrupted long-running sync from a known API page:
python scripts/sync_drug_catalog.py --dataset approval --page-size 500 --start-page 120 --max-retries 5Use Flutter 3.41.5 (stable), matching both CI and the signed Android release
workflow. The application commits frontend/pubspec.lock; do not delete it or
regenerate it with a different SDK to work around a resolution failure.
Open another terminal from the repository root:
cd frontend
flutter --version
flutter pub get --enforce-lockfile
flutter devices
flutter run -d "[your-device-id]"Use the device id shown by flutter devices.
For an intentional dependency change, use Flutter 3.41.5, run flutter pub get
without enforcement, review and commit the lockfile diff, then verify
flutter pub get --enforce-lockfile, flutter analyze --no-pub, and
flutter test --no-pub. SDK upgrades must update both workflow pins and the
lockfile together. CI and signed builds reject dependency drift rather than
silently resolving different versions.
The Android application defaults to the production API endpoint:
https://api.medbuddy.pp.ua/api/v1/medication
ADB debugging, Flutter hot reload, breakpoints, and physical-device testing do not require a backend process on the development laptop or a device on the same LAN. The Android client reaches the production API over ordinary HTTPS.
MEDBUDDY_API_BASE_URL remains a compile-time String.fromEnvironment value.
Profile and release builds accept only a public HTTPS endpoint whose path is
/api/v1/medication. Debug builds additionally accept only the local demo
hosts 10.0.2.2, 127.0.0.1, localhost, or ::1 over port 8000 when
MEDBUDDY_ALLOW_LOCAL_HTTP=true is supplied. Other private-network and
clear-text endpoints remain rejected.
For authenticated beta testing, keep the real google-services.json in
frontend/android/app and out of Git. Provide the Firebase configuration that
matches the registered com.medbuddy.app Android application:
flutter run -d "[your-device-id]" `
--dart-define=MEDBUDDY_AUTH_MODE=firebase `
--dart-define=MEDBUDDY_FIREBASE_API_KEY=your_api_key `
--dart-define=MEDBUDDY_FIREBASE_APP_ID=your_android_app_id `
--dart-define=MEDBUDDY_FIREBASE_MESSAGING_SENDER_ID=your_sender_id `
--dart-define=MEDBUDDY_FIREBASE_PROJECT_ID=medbuddy-26 `
--dart-define=MEDBUDDY_NAVER_MAP_CLIENT_ID=your_naver_dynamic_map_client_id `
--dart-define=MEDBUDDY_PHONE_AUTH_ENABLED=falseRelease and profile builds require Firebase authentication and the same public
HTTPS backend contract. Production deployment details are documented in
docs/Production Deployment.md, and
authentication, App Check, and signing requirements are documented in
docs/MedBuddy - Beta Security Architecture.md.
For an already installed physical-device beta, increment frontend/pubspec.yaml
version code and use frontend/tool/install_update_preserving_data.ps1. The
helper verifies the package/signing identity, performs only adb install -r,
and refuses any fallback that would uninstall the app or clear Firebase and
reminder state. Server-side medication history remains in the named PostgreSQL
Compose volume; never use docker compose down -v during a rebuild. See the
self-hosted guide above for the complete update and backup procedure.
Development workflow, verification commands, UML alignment rules, documentation standards, and commit message conventions are maintained in CONTRIBUTING.md.
| Profile | Name | Role | GitHub |
|---|---|---|---|
![]() |
1window2 | Full-Stack Architecture & AI Pipeline Lead | @1window2 |
![]() |
tmdgusdl9647 | Team Lead & Developer | @tmdgusdl9647 |
![]() |
jeeon0318 | UML Documentation & Legal Compliance Lead | @jeeon0318 |
![]() |
onlyone130 | UI/UX Design Lead | @onlyone130 |



