feat: implement backend image classification with TensorFlow.js model

- Extend shared types for image classification, including PredictionProbability, UploaderMetadata, and ImageClassificationRecord.
- Create image_classifications table in the database with necessary fields and foreign key constraints.
- Implement disease mappers to convert database rows to shared disease records.
- Develop uploader client to handle image uploads to external service.
- Create image model service to load and classify images using TensorFlow.js.
- Add API routes for image classification, including GET for history and POST for new classifications.
- Implement frontend components for image classification form and display results.
- Update dashboard to integrate image classification functionality and display results.
- Document implementation plan for backend image classification.
This commit is contained in:
Asep Haryana Saputra
2026-05-22 15:13:22 +00:00
parent 229db82a08
commit 15c6be9e84
19 changed files with 1750 additions and 33 deletions
+281
View File
@@ -0,0 +1,281 @@
{
"id": "16700e2c-ff8f-4864-a71a-6f4b98a1313a",
"prevId": "71e2eeba-4f8f-48bc-8bcf-63201334aa65",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.app_events": {
"name": "app_events",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"name": {
"name": "name",
"type": "varchar(120)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.disease_catalog": {
"name": "disease_catalog",
"schema": "",
"columns": {
"slug": {
"name": "slug",
"type": "varchar(80)",
"primaryKey": true,
"notNull": true
},
"label": {
"name": "label",
"type": "varchar(80)",
"primaryKey": false,
"notNull": true
},
"common_name": {
"name": "common_name",
"type": "varchar(120)",
"primaryKey": false,
"notNull": true
},
"summary": {
"name": "summary",
"type": "text",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true
},
"symptoms": {
"name": "symptoms",
"type": "text[]",
"primaryKey": false,
"notNull": true
},
"recommendations": {
"name": "recommendations",
"type": "text[]",
"primaryKey": false,
"notNull": true
},
"risk_level": {
"name": "risk_level",
"type": "varchar(20)",
"primaryKey": false,
"notNull": true
},
"accent_color": {
"name": "accent_color",
"type": "varchar(40)",
"primaryKey": false,
"notNull": true
},
"display_order": {
"name": "display_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.image_classifications": {
"name": "image_classifications",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"predicted_disease_slug": {
"name": "predicted_disease_slug",
"type": "varchar(80)",
"primaryKey": false,
"notNull": true
},
"confidence": {
"name": "confidence",
"type": "real",
"primaryKey": false,
"notNull": true
},
"probabilities": {
"name": "probabilities",
"type": "jsonb",
"primaryKey": false,
"notNull": true
},
"image_url": {
"name": "image_url",
"type": "text",
"primaryKey": false,
"notNull": true
},
"original_file_name": {
"name": "original_file_name",
"type": "varchar(240)",
"primaryKey": false,
"notNull": true
},
"uploader_public_id": {
"name": "uploader_public_id",
"type": "varchar(160)",
"primaryKey": false,
"notNull": true
},
"uploader_payload": {
"name": "uploader_payload",
"type": "jsonb",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"image_classifications_predicted_disease_slug_disease_catalog_slug_fk": {
"name": "image_classifications_predicted_disease_slug_disease_catalog_slug_fk",
"tableFrom": "image_classifications",
"tableTo": "disease_catalog",
"columnsFrom": [
"predicted_disease_slug"
],
"columnsTo": [
"slug"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.manual_classifications": {
"name": "manual_classifications",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"disease_slug": {
"name": "disease_slug",
"type": "varchar(80)",
"primaryKey": false,
"notNull": true
},
"observation": {
"name": "observation",
"type": "text",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "varchar(160)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"manual_classifications_disease_slug_disease_catalog_slug_fk": {
"name": "manual_classifications_disease_slug_disease_catalog_slug_fk",
"tableFrom": "manual_classifications",
"tableTo": "disease_catalog",
"columnsFrom": [
"disease_slug"
],
"columnsTo": [
"slug"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
+7
View File
@@ -8,6 +8,13 @@
"when": 1779458573080,
"tag": "0000_harsh_arachne",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1779461286348,
"tag": "0001_cute_lyja",
"breakpoints": true
}
]
}