Compare commits

..

No commits in common. "43ef57f1b655e92f78652db441be53ca3307f37b" and "f219effc33154334e95663eee73ff1b9e0b4fa7c" have entirely different histories.

273 changed files with 58 additions and 316 deletions

4
.gitignore vendored
View File

@ -97,6 +97,4 @@ database_export/
# Lock files
*.tar.gz
dump.rdb
.~lock.*
node_modules
.~lock.*

View File

@ -1,95 +0,0 @@
# Move Inquiry AI Analysis to Celery Task
## Summary
Replace synchronous AI calls in 3 inquiry creation views with the existing `analyze_inquiry_with_ai.delay()` Celery task.
## Changes
### 1. `apps/complaints/ui_views.py``inquiry_create` (lines 1554-1567)
**Replace:**
```python
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=inquiry.get_category_display(),
hospital_id=inquiry.hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on inquiry creation: {e}")
```
**With:**
```python
from apps.complaints.tasks import analyze_inquiry_with_ai
analyze_inquiry_with_ai.delay(str(inquiry.id))
```
Also update line 1572 — change `"Inquiry created with AI analysis."` to `"Inquiry created."`
---
### 2. `apps/complaints/ui_views.py``public_inquiry_submit` (lines 2315-2328)
**Replace:**
```python
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=category,
hospital_id=hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on public inquiry creation: {e}")
```
**With:**
```python
from apps.complaints.tasks import analyze_inquiry_with_ai
analyze_inquiry_with_ai.delay(str(inquiry.id))
```
---
### 3. `apps/complaints/views.py``InquiryViewSet.perform_create` (lines 2750-2763)
**Replace:**
```python
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=inquiry.get_category_display(),
hospital_id=inquiry.hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on inquiry creation: {e}")
```
**With:**
```python
from apps.complaints.tasks import analyze_inquiry_with_ai
analyze_inquiry_with_ai.delay(str(inquiry.id))
```
Also update docstring on line 2747 from `"Auto-set created_by from request.user and run AI analysis synchronously"` to `"Auto-set created_by from request.user and trigger AI analysis in background"`.

View File

@ -1551,14 +1551,25 @@ def inquiry_create(request):
inquiry.save()
from apps.complaints.tasks import analyze_inquiry_with_ai
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
analyze_inquiry_with_ai.delay(str(inquiry.id))
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=inquiry.get_category_display(),
hospital_id=inquiry.hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on inquiry creation: {e}")
InquiryUpdate.objects.create(
inquiry=inquiry,
update_type="note",
message="Inquiry created.",
message="Inquiry created with AI analysis.",
created_by=request.user,
)
@ -2301,9 +2312,20 @@ def public_inquiry_submit(request):
reference_number=reference_number,
)
from apps.complaints.tasks import analyze_inquiry_with_ai
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
analyze_inquiry_with_ai.delay(str(inquiry.id))
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=category,
hospital_id=hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on public inquiry creation: {e}")
AuditService.log_event(
event_type="inquiry_created_public",

View File

@ -2744,12 +2744,23 @@ class InquiryViewSet(viewsets.ModelViewSet):
ordering = ["-created_at"]
def perform_create(self, serializer):
"""Auto-set created_by from request.user and trigger AI analysis in background"""
"""Auto-set created_by from request.user and run AI analysis synchronously"""
inquiry = serializer.save(created_by=self.request.user)
from apps.complaints.tasks import analyze_inquiry_with_ai
from apps.complaints.tasks import _apply_inquiry_ai_analysis
from apps.core.ai_service import AIService
analyze_inquiry_with_ai.delay(str(inquiry.id))
try:
analysis = AIService.analyze_inquiry(
subject=inquiry.subject,
message=inquiry.message,
category=inquiry.get_category_display(),
hospital_id=inquiry.hospital.id,
)
emotion_analysis = AIService.analyze_emotion(text=inquiry.message)
_apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis)
except Exception as e:
logger.error(f"AI analysis failed on inquiry creation: {e}")
AuditService.log_from_request(
event_type="inquiry_created",

View File

@ -13,8 +13,6 @@ from celery import shared_task
from django.utils import timezone
from django.core.files.base import ContentFile
from celery import shared_task
from datetime import timedelta
import logging
logger = logging.getLogger(__name__)

View File

@ -222,7 +222,7 @@ def observation_create(request):
title=form.cleaned_data.get("title", ""),
location_text=form.cleaned_data.get("location_text", ""),
incident_datetime=form.cleaned_data.get("incident_datetime"),
reporter_staff_id=user.employee_id or "",
reporter_staff_id=user.staff_id or "",
reporter_name=user.get_full_name(),
reporter_phone="",
reporter_email=user.email,

37
node_modules/.package-lock.json generated vendored
View File

@ -30,15 +30,6 @@
"node": ">=12"
}
},
"node_modules/@fontsource/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz",
"integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@ -229,12 +220,6 @@
"node": ">= 8"
}
},
"node_modules/apexcharts": {
"version": "5.10.6",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.10.6.tgz",
"integrity": "sha512-FJQGbso3iRuOwUYnj0yUhkWeKeJE6aboVol+ae09lsc+lbLMWZqSRbrAWVa/qishLiaeG2icxdvmVkm+9n6kOQ==",
"license": "SEE LICENSE IN LICENSE"
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@ -469,12 +454,6 @@
"node": ">= 0.4"
}
},
"node_modules/htmx.org": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.8.tgz",
"integrity": "sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==",
"license": "0BSD"
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -568,12 +547,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/lucide": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/lucide/-/lucide-1.8.0.tgz",
"integrity": "sha512-JjV/QnadgFLj1Pyu9IKl0lknrolFEzo04B64QcYLLeRzZl/iEHpdbSrRRKbyXcv45SZNv+WGjIUCT33e7xHO6Q==",
"license": "ISC"
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1038,16 +1011,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sweetalert2": {
"version": "11.26.24",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.24.tgz",
"integrity": "sha512-SLgukW4wicewpW5VOukSXY5Z6DL/z7HCOK2ODSjmQPiSphCN8gJAmh9npoceXOtBRNoDN0xIz+zHYthtfiHmjg==",
"license": "MIT",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/tailwindcss": {
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",

44
package-lock.json generated
View File

@ -7,13 +7,6 @@
"": {
"name": "hh",
"version": "0.1.0",
"dependencies": {
"@fontsource/inter": "^5.2.8",
"apexcharts": "^5.10.6",
"htmx.org": "^2.0.8",
"lucide": "^1.8.0",
"sweetalert2": "^11.26.24"
},
"devDependencies": {
"@playwright/test": "^1.59.1",
"@types/node": "^25.5.2",
@ -48,15 +41,6 @@
"node": ">=12"
}
},
"node_modules/@fontsource/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz",
"integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@ -247,12 +231,6 @@
"node": ">= 8"
}
},
"node_modules/apexcharts": {
"version": "5.10.6",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.10.6.tgz",
"integrity": "sha512-FJQGbso3iRuOwUYnj0yUhkWeKeJE6aboVol+ae09lsc+lbLMWZqSRbrAWVa/qishLiaeG2icxdvmVkm+9n6kOQ==",
"license": "SEE LICENSE IN LICENSE"
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@ -502,12 +480,6 @@
"node": ">= 0.4"
}
},
"node_modules/htmx.org": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.8.tgz",
"integrity": "sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==",
"license": "0BSD"
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -601,12 +573,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/lucide": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/lucide/-/lucide-1.8.0.tgz",
"integrity": "sha512-JjV/QnadgFLj1Pyu9IKl0lknrolFEzo04B64QcYLLeRzZl/iEHpdbSrRRKbyXcv45SZNv+WGjIUCT33e7xHO6Q==",
"license": "ISC"
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1071,16 +1037,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sweetalert2": {
"version": "11.26.24",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.24.tgz",
"integrity": "sha512-SLgukW4wicewpW5VOukSXY5Z6DL/z7HCOK2ODSjmQPiSphCN8gJAmh9npoceXOtBRNoDN0xIz+zHYthtfiHmjg==",
"license": "MIT",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/tailwindcss": {
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",

View File

@ -26,12 +26,5 @@
"tailwindcss": "^3.4.19",
"ts-node": "^10.9.2",
"typescript": "^6.0.2"
},
"dependencies": {
"@fontsource/inter": "^5.2.8",
"apexcharts": "^5.10.6",
"htmx.org": "^2.0.8",
"lucide": "^1.8.0",
"sweetalert2": "^11.26.24"
}
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More