Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +48 -0
- .gitignore +19 -0
- UI/safespace/.dart_tool/chrome-device/Default/Accounts/Avatar Images/100679184552698402258 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Affiliation Database +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Google Profile Picture.png +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/History +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422504525250674 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422506231685404 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422658855680597 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422667094602974 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Shared Dictionary/cache/2bfff91f8dd9b032_0 +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Sync Data/Nigori.bin +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/Web Data +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000004.log +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000005.ldb +3 -0
- UI/safespace/.dart_tool/chrome-device/Default/trusted_vault.pb +3 -0
- UI/safespace/.dart_tool/flutter_build/694c124069dece6f3ffa00a5aabab35e/app.dill +3 -0
- UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill +3 -0
- UI/safespace/build/flutter_assets/AssetManifest.bin +3 -0
- UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf +3 -0
- UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf +3 -0
- UI/safespace/build/test_cache/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill +3 -0
- UI/safespace/build/unit_test_assets/AssetManifest.bin +3 -0
- UI/safespace/build/unit_test_assets/NOTICES.Z +3 -0
- UI/safespace/build/unit_test_assets/fonts/MaterialIcons-Regular.otf +3 -0
- UI/safespace/build/unit_test_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf +3 -0
- UI/safespace/build/web/canvaskit/chromium/canvaskit.wasm +3 -0
- UI/safespace/build/web/canvaskit/skwasm.wasm +3 -0
- UI/safespace/build/web/canvaskit/skwasm_heavy.wasm +3 -0
- UI/safespace/build/web/canvaskit/wimp.wasm +3 -0
- UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +3 -0
- api.py +669 -0
- app.py +508 -0
- check_db.py +23 -0
- clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/History +3 -0
- clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/Web Data +3 -0
- clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log +3 -0
- clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/trusted_vault.pb +3 -0
- clean_git_repo/UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill +3 -0
- clean_git_repo/UI/safespace/build/flutter_assets/AssetManifest.bin +3 -0
- clean_git_repo/UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf +3 -0
- clean_git_repo/UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf +3 -0
- clean_git_repo/UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +3 -0
- clean_git_repo/data.csv +3 -0
- clean_git_repo/mental_model.h5 +3 -0
- clean_git_repo/mental_xlmr_final/label_encoder.pkl +3 -0
- clean_git_repo/mental_xlmr_final/tokenizer.json +3 -0
- clean_git_repo/mental_xlmr_final/training_args.bin +3 -0
- clean_git_repo/model_weights.pkl +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,51 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
data.csv filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
mental_xlmr_final/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
clean_git_repo/mental_xlmr_final/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/History filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/Web[[:space:]]Data filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
clean_git_repo/UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
clean_git_repo/UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
clean_git_repo/UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
clean_git_repo/UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
temp_space/mental_xlmr_final/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
temp_space/clean_git_repo/mental_xlmr_final/tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
temp_space/clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/History filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
temp_space/clean_git_repo/UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
temp_space/clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/Web[[:space:]]Data filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
temp_space/clean_git_repo/UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
temp_space/clean_git_repo/UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
temp_space/UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
temp_space/UI/safespace/.dart_tool/chrome-device/Default/History filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
temp_space/UI/safespace/.dart_tool/chrome-device/Default/Web[[:space:]]Data filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
temp_space/UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
temp_space/clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
temp_space/clean_git_repo/UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
temp_space/UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
temp_space/UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
UI/safespace/.dart_tool/chrome-device/Default/Affiliation[[:space:]]Database filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
UI/safespace/.dart_tool/chrome-device/Default/Google[[:space:]]Profile[[:space:]]Picture.png filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
UI/safespace/.dart_tool/chrome-device/Default/History filter=lfs diff=lfs merge=lfs -text
|
| 66 |
+
UI/safespace/.dart_tool/chrome-device/Default/Web[[:space:]]Data filter=lfs diff=lfs merge=lfs -text
|
| 67 |
+
temp_space/UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
UI/safespace/.dart_tool/chrome-device/Default/Shared[[:space:]]Dictionary/cache/2bfff91f8dd9b032_0 filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
UI/safespace/.dart_tool/flutter_build/694c124069dece6f3ffa00a5aabab35e/app.dill filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
UI/safespace/build/unit_test_assets/NOTICES.Z filter=lfs diff=lfs merge=lfs -text
|
| 71 |
+
UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422504525250674 filter=lfs diff=lfs merge=lfs -text
|
| 74 |
+
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422506231685404 filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422658855680597 filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422667094602974 filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000004.log filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000005.ldb filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
UI/safespace/.dart_tool/chrome-device/Default/Accounts/Avatar[[:space:]]Images/100679184552698402258 filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
UI/safespace/build/unit_test_assets/fonts/MaterialIcons-Regular.otf filter=lfs diff=lfs merge=lfs -text
|
| 82 |
+
UI/safespace/build/unit_test_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
UI/safespace/build/test_cache/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
push_to_space.py
|
| 2 |
+
temp_space/
|
| 3 |
+
temp_hf_space/
|
| 4 |
+
scrub_rules.txt
|
| 5 |
+
.ipynb_checkpoints/
|
| 6 |
+
*.ipynb
|
| 7 |
+
repo.zip
|
| 8 |
+
data.csv
|
| 9 |
+
*.h5
|
| 10 |
+
*.pkl
|
| 11 |
+
mental_xlmr_final/
|
| 12 |
+
UI/safespace/build/
|
| 13 |
+
UI/safespace/.dart_tool/
|
| 14 |
+
UI/safespace/.pub-cache/
|
| 15 |
+
UI/safespace/.flutter-plugins
|
| 16 |
+
UI/safespace/.flutter-plugins-dependencies
|
| 17 |
+
UI/safespace/node_modules/
|
| 18 |
+
*.log
|
| 19 |
+
.DS_Store
|
UI/safespace/.dart_tool/chrome-device/Default/Accounts/Avatar Images/100679184552698402258
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0f2264d18025c482b6a86e40d5d048210837422c81b488022c5b77701234b7e6
|
| 3 |
+
size 109914
|
UI/safespace/.dart_tool/chrome-device/Default/Affiliation Database
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:970ee31de145a72835692447fe9f172c59443c794566dbc595ae16f0c30b6c39
|
| 3 |
+
size 102400
|
UI/safespace/.dart_tool/chrome-device/Default/Google Profile Picture.png
ADDED
|
Git LFS Details
|
UI/safespace/.dart_tool/chrome-device/Default/History
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:83e3f671cea193224794e68f6397cfbdcfe7299614f4927e275ba9a284c01c52
|
| 3 |
+
size 294912
|
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422504525250674
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3190858d674bf992e4bf326536b650fddbf4b368e50e6715039c8ae1647a7187
|
| 3 |
+
size 145695
|
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422506231685404
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3bafa05f042888995de97c0169e3cbaca763be8104f86b2aefc48a2350e4783b
|
| 3 |
+
size 103968
|
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422658855680597
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a04b79669055015fa2ed535518c6f99625ca4be895abb7b493e77bbe38d5689
|
| 3 |
+
size 147567
|
UI/safespace/.dart_tool/chrome-device/Default/Sessions/Session_13422667094602974
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0d25e50423a6e1f42667d70bdae7d8bf81ffd7ef9d50a8444a37e5862122dd7e
|
| 3 |
+
size 192253
|
UI/safespace/.dart_tool/chrome-device/Default/Shared Dictionary/cache/2bfff91f8dd9b032_0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:664344ef4ca53efd67808330605a1cb522258b234ec112d03ef10e3b938073bf
|
| 3 |
+
size 198743
|
UI/safespace/.dart_tool/chrome-device/Default/Sync Data/Nigori.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:303a5a630227f630b5704f2289d0469b6c415e4adec542b08e62288fcf15acea
|
| 3 |
+
size 990
|
UI/safespace/.dart_tool/chrome-device/Default/Web Data
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1572acaab352554c98b7cb1765c5a92f838af0da6a3972dc196ecdc4b9fba844
|
| 3 |
+
size 196608
|
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3632466e1e4586ea4ac9a782dbc2fe30bf25db8cd6019be0e705ddc1ef22e851
|
| 3 |
+
size 437109
|
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000004.log
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3c98aca056f3ce1e53e1fd16adb35fb77e9c8376ba3be69a20289942876107bb
|
| 3 |
+
size 352170
|
UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000005.ldb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a8bbd528dee536c8e86216027ae360a15d6ee05d745da3936fc3360d0429b5e8
|
| 3 |
+
size 133527
|
UI/safespace/.dart_tool/chrome-device/Default/trusted_vault.pb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b87bf305aafae1f1ec7dd947176c9529b2a9cbd4a3bf296fce25fc009e725366
|
| 3 |
+
size 84
|
UI/safespace/.dart_tool/flutter_build/694c124069dece6f3ffa00a5aabab35e/app.dill
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e86b5ab568a496d761eedcb4975ead5f277a06bd1c5d652968b2912c9d0b79ae
|
| 3 |
+
size 31744696
|
UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5a39ea79ca44e7928c9a077f63e6c575bd8d1d33efc85fa4a7d9bce7c6da0283
|
| 3 |
+
size 50726856
|
UI/safespace/build/flutter_assets/AssetManifest.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b49a2913e16c75a773c3b3952a15cb6e9ce5df0a46efa5df5f48d870e284e69
|
| 3 |
+
size 27031
|
UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d9865b671a09d683d13a863089d8825e0f61a37696ce5d7d448bc8023aa62453
|
| 3 |
+
size 1645184
|
UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67c44fe9183b002e79dde7f6977e2988661c9a3e4a3c5fce968787efdbed823c
|
| 3 |
+
size 257628
|
UI/safespace/build/test_cache/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6a2e8b59bc8fbd6dd9a3dbcae74342f7c4afcb8853b54ef3d50fc1ed821df7f3
|
| 3 |
+
size 48994592
|
UI/safespace/build/unit_test_assets/AssetManifest.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b49a2913e16c75a773c3b3952a15cb6e9ce5df0a46efa5df5f48d870e284e69
|
| 3 |
+
size 27031
|
UI/safespace/build/unit_test_assets/NOTICES.Z
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:30a3990f4698bce1c4e14242e6f53a47a356585a20d434e1f52378f61682b113
|
| 3 |
+
size 105255
|
UI/safespace/build/unit_test_assets/fonts/MaterialIcons-Regular.otf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d9865b671a09d683d13a863089d8825e0f61a37696ce5d7d448bc8023aa62453
|
| 3 |
+
size 1645184
|
UI/safespace/build/unit_test_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67c44fe9183b002e79dde7f6977e2988661c9a3e4a3c5fce968787efdbed823c
|
| 3 |
+
size 257628
|
UI/safespace/build/web/canvaskit/chromium/canvaskit.wasm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f6c67bf7989fcb1b69a3203617882ac0d1541cc79e38f81c3f5d63ab4d73de33
|
| 3 |
+
size 5686836
|
UI/safespace/build/web/canvaskit/skwasm.wasm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:461a759c657738e573f1c2f69e26489197e43db1bf6ec1e2c3b44c6721314cd1
|
| 3 |
+
size 3549758
|
UI/safespace/build/web/canvaskit/skwasm_heavy.wasm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4b0028e38d865b4673ec38c0c673299dff6b00d6cd8c5e53dda9f800f7bbdda2
|
| 3 |
+
size 5140163
|
UI/safespace/build/web/canvaskit/wimp.wasm
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a9e168fe6556e09414e9ea58badaf13c54b630de13a6e03f92444340b5bec2fd
|
| 3 |
+
size 3461867
|
UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
ADDED
|
|
Git LFS Details
|
api.py
ADDED
|
@@ -0,0 +1,669 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
import hashlib
|
| 4 |
+
import logging
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
import httpx
|
| 8 |
+
from fastapi import FastAPI, HTTPException, Depends, Request
|
| 9 |
+
from fastapi.responses import HTMLResponse
|
| 10 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
+
from pydantic import BaseModel, Field
|
| 12 |
+
from typing import Optional
|
| 13 |
+
|
| 14 |
+
from core_ai import predict_text, predict_survey, fuse_scores
|
| 15 |
+
from recommendations import get_recommendations
|
| 16 |
+
|
| 17 |
+
# --- DATABASE SETUP ---
|
| 18 |
+
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, JSON, Text, Boolean, Index
|
| 19 |
+
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
| 20 |
+
|
| 21 |
+
DATABASE_URL = os.environ.get("DATABASE_URL")
|
| 22 |
+
if DATABASE_URL and DATABASE_URL.startswith("postgres://"):
|
| 23 |
+
DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql://", 1)
|
| 24 |
+
|
| 25 |
+
engine = create_engine(DATABASE_URL, connect_args={'connect_timeout': 5}, pool_pre_ping=True) if DATABASE_URL else None
|
| 26 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) if engine else None
|
| 27 |
+
Base = declarative_base()
|
| 28 |
+
|
| 29 |
+
logging.basicConfig(level=logging.INFO)
|
| 30 |
+
logger = logging.getLogger("safespace.api")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class DBUser(Base):
|
| 34 |
+
__tablename__ = "users"
|
| 35 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 36 |
+
name = Column(String, nullable=True)
|
| 37 |
+
email = Column(String, unique=True, index=True)
|
| 38 |
+
password = Column(String)
|
| 39 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class DBAnalysis(Base):
|
| 43 |
+
__tablename__ = "analyses"
|
| 44 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 45 |
+
user_id = Column(Integer, index=True, nullable=True)
|
| 46 |
+
primary_condition = Column(String)
|
| 47 |
+
clinical_scoring = Column(JSON)
|
| 48 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
| 49 |
+
text_input = Column(Text, nullable=True)
|
| 50 |
+
text_input_hash = Column(Text, nullable=True)
|
| 51 |
+
text_scores = Column(JSON, nullable=True)
|
| 52 |
+
survey_scores = Column(JSON, nullable=True)
|
| 53 |
+
fused_scores = Column(JSON, nullable=True)
|
| 54 |
+
severity = Column(Text, nullable=True)
|
| 55 |
+
cause = Column(Text, nullable=True)
|
| 56 |
+
suicidal_flag = Column(Boolean, default=False)
|
| 57 |
+
model_version = Column(Text, nullable=True)
|
| 58 |
+
app_version = Column(Text, nullable=True)
|
| 59 |
+
locale = Column(Text, nullable=True)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
Index("ix_analyses_user_id_created_at", DBAnalysis.user_id, DBAnalysis.created_at)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class DBCheckin(Base):
|
| 66 |
+
__tablename__ = "checkins"
|
| 67 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 68 |
+
user_id = Column(Integer, index=True, nullable=False)
|
| 69 |
+
mood = Column(Integer, nullable=False)
|
| 70 |
+
sleep = Column(Integer, nullable=False)
|
| 71 |
+
energy = Column(Float, nullable=False)
|
| 72 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
Index("ix_checkins_user_id_created_at", DBCheckin.user_id, DBCheckin.created_at)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class DBJournalEntry(Base):
|
| 79 |
+
__tablename__ = "journal_entries"
|
| 80 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 81 |
+
user_id = Column(Integer, index=True, nullable=True)
|
| 82 |
+
content = Column(Text, nullable=False)
|
| 83 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
| 84 |
+
updated_at = Column(DateTime, nullable=True)
|
| 85 |
+
|
| 86 |
+
# --- APP SETUP ---
|
| 87 |
+
app = FastAPI(title="SafeSpace API", version="1.0.0")
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@app.middleware("http")
|
| 91 |
+
async def log_requests(request: Request, call_next):
|
| 92 |
+
start_time = time.time()
|
| 93 |
+
response = await call_next(request)
|
| 94 |
+
duration_ms = int((time.time() - start_time) * 1000)
|
| 95 |
+
logger.info(
|
| 96 |
+
"%s %s %s %sms",
|
| 97 |
+
request.method,
|
| 98 |
+
request.url.path,
|
| 99 |
+
response.status_code,
|
| 100 |
+
duration_ms,
|
| 101 |
+
)
|
| 102 |
+
return response
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
@app.on_event("startup")
|
| 106 |
+
async def startup_event():
|
| 107 |
+
import asyncio
|
| 108 |
+
if engine:
|
| 109 |
+
try:
|
| 110 |
+
await asyncio.wait_for(
|
| 111 |
+
asyncio.to_thread(Base.metadata.create_all, bind=engine),
|
| 112 |
+
timeout=8.0
|
| 113 |
+
)
|
| 114 |
+
logger.info("Database connected and tables verified.")
|
| 115 |
+
except asyncio.TimeoutError:
|
| 116 |
+
logger.warning("Database connection timed out during startup - server will start without DB verification.")
|
| 117 |
+
except Exception as e:
|
| 118 |
+
logger.exception("Database connection failed during startup: %s", e)
|
| 119 |
+
logger.info("Application startup complete.")
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def get_db():
|
| 123 |
+
if not SessionLocal:
|
| 124 |
+
yield None
|
| 125 |
+
else:
|
| 126 |
+
db = SessionLocal()
|
| 127 |
+
try:
|
| 128 |
+
yield db
|
| 129 |
+
finally:
|
| 130 |
+
db.close()
|
| 131 |
+
|
| 132 |
+
# Add CORS so Flutter app can communicate with it
|
| 133 |
+
app.add_middleware(
|
| 134 |
+
CORSMiddleware,
|
| 135 |
+
allow_origins=["*"],
|
| 136 |
+
allow_credentials=True,
|
| 137 |
+
allow_methods=["*"],
|
| 138 |
+
allow_headers=["*"],
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# --- Password Hashing ---
|
| 142 |
+
def hash_password(password: str) -> str:
|
| 143 |
+
return hashlib.sha256(password.encode()).hexdigest()
|
| 144 |
+
|
| 145 |
+
# --- DASS-42 Clinical Scoring ---
|
| 146 |
+
def calculate_dass_clinical_score(answers: list) -> dict:
|
| 147 |
+
dep_idx = [2, 4, 9, 12, 15, 16, 20, 23, 25, 30, 33, 36, 37, 41]
|
| 148 |
+
anx_idx = [1, 3, 6, 8, 14, 18, 19, 22, 24, 27, 29, 35, 39, 40]
|
| 149 |
+
str_idx = [0, 5, 7, 10, 11, 13, 17, 21, 26, 28, 31, 32, 34, 38]
|
| 150 |
+
|
| 151 |
+
dep_score = sum(answers[i] for i in dep_idx)
|
| 152 |
+
anx_score = sum(answers[i] for i in anx_idx)
|
| 153 |
+
str_score = sum(answers[i] for i in str_idx)
|
| 154 |
+
|
| 155 |
+
def get_severity(score, bounds):
|
| 156 |
+
if score <= bounds[0]: return "Normal"
|
| 157 |
+
if score <= bounds[1]: return "Mild"
|
| 158 |
+
if score <= bounds[2]: return "Moderate"
|
| 159 |
+
if score <= bounds[3]: return "Severe"
|
| 160 |
+
return "Extremely Severe"
|
| 161 |
+
|
| 162 |
+
return {
|
| 163 |
+
"depression": {"score": dep_score, "severity": get_severity(dep_score, [9, 13, 20, 27])},
|
| 164 |
+
"anxiety": {"score": anx_score, "severity": get_severity(anx_score, [7, 9, 14, 19])},
|
| 165 |
+
"stress": {"score": str_score, "severity": get_severity(str_score, [14, 18, 25, 33])}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
# --- API MODELS ---
|
| 169 |
+
class AnalysisRequest(BaseModel):
|
| 170 |
+
user_id: str | int = Field(default=None, description="User identifier")
|
| 171 |
+
text: str = Field(..., min_length=1)
|
| 172 |
+
survey_answers: list[int] = Field(..., min_items=42, max_items=42)
|
| 173 |
+
locale: str = Field(default="en")
|
| 174 |
+
client_ts: str | None = None
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
class AnalyzeRequest(BaseModel):
|
| 178 |
+
text: str = Field(..., description="The user's response in text (Arabic/English)")
|
| 179 |
+
survey_answers: list[int] = Field(..., min_items=42, max_items=42, description="List of 42 integers (0-4) representing DASS-42 survey answers")
|
| 180 |
+
user_id: int | None = Field(default=None, description="Optional user ID to link analysis to a user")
|
| 181 |
+
locale: str = Field(default="en")
|
| 182 |
+
client_ts: str | None = None
|
| 183 |
+
app_version: str | None = None
|
| 184 |
+
model_version: str | None = None
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class ChatRequest(BaseModel):
|
| 188 |
+
message: str
|
| 189 |
+
session_id: Optional[str] = "default"
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
class ChatResponse(BaseModel):
|
| 193 |
+
reply: str
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
class SignupRequest(BaseModel):
|
| 197 |
+
name: str = Field(..., min_length=1)
|
| 198 |
+
email: str = Field(..., min_length=5)
|
| 199 |
+
password: str = Field(..., min_length=4)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class LoginRequest(BaseModel):
|
| 203 |
+
email: str = Field(..., min_length=5)
|
| 204 |
+
password: str = Field(..., min_length=1)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class CheckinRequest(BaseModel):
|
| 208 |
+
mood: int = Field(..., ge=0, le=10)
|
| 209 |
+
sleep: int = Field(..., ge=0, le=10)
|
| 210 |
+
energy: float = Field(..., ge=0, le=10)
|
| 211 |
+
user_id: int | None = Field(default=None, description="Optional user ID to link check-in to a user")
|
| 212 |
+
client_ts: str | None = None
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
class JournalEntryRequest(BaseModel):
|
| 216 |
+
content: str = Field(..., min_length=1)
|
| 217 |
+
user_id: int | None = Field(default=None, description="Optional user ID to link journal entry to a user")
|
| 218 |
+
client_ts: str | None = None
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
class JournalEntryUpdateRequest(BaseModel):
|
| 222 |
+
content: str = Field(..., min_length=1)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
# --- ENDPOINTS ---
|
| 226 |
+
@app.get("/")
|
| 227 |
+
def root():
|
| 228 |
+
return {"status": "ok", "message": "SafeSpace API"}
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
@app.get("/test", response_class=HTMLResponse)
|
| 232 |
+
def test_page():
|
| 233 |
+
html_path = os.path.join(os.path.dirname(__file__), "index.html")
|
| 234 |
+
if not os.path.exists(html_path):
|
| 235 |
+
raise HTTPException(status_code=404, detail="index.html not found")
|
| 236 |
+
with open(html_path, "r", encoding="utf-8") as f:
|
| 237 |
+
return f.read()
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
# --- AUTH ENDPOINTS ---
|
| 241 |
+
@app.post("/api/v1/auth/signup")
|
| 242 |
+
async def signup(request: SignupRequest, db: Session = Depends(get_db)):
|
| 243 |
+
if not db:
|
| 244 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 245 |
+
|
| 246 |
+
# Check if email already exists
|
| 247 |
+
existing = db.query(DBUser).filter(DBUser.email == request.email).first()
|
| 248 |
+
if existing:
|
| 249 |
+
raise HTTPException(status_code=400, detail="Email already registered")
|
| 250 |
+
|
| 251 |
+
# Create new user
|
| 252 |
+
try:
|
| 253 |
+
new_user = DBUser(
|
| 254 |
+
name=request.name,
|
| 255 |
+
email=request.email,
|
| 256 |
+
password=hash_password(request.password),
|
| 257 |
+
)
|
| 258 |
+
db.add(new_user)
|
| 259 |
+
db.commit()
|
| 260 |
+
db.refresh(new_user)
|
| 261 |
+
|
| 262 |
+
return {
|
| 263 |
+
"user_id": new_user.id,
|
| 264 |
+
"email": new_user.email,
|
| 265 |
+
"name": new_user.name,
|
| 266 |
+
"message": "Account created successfully"
|
| 267 |
+
}
|
| 268 |
+
except Exception as e:
|
| 269 |
+
db.rollback()
|
| 270 |
+
raise HTTPException(status_code=500, detail=f"Failed to create account: {str(e)}")
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
@app.post("/api/v1/auth/login")
|
| 274 |
+
async def login(request: LoginRequest, db: Session = Depends(get_db)):
|
| 275 |
+
if not db:
|
| 276 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 277 |
+
|
| 278 |
+
user = db.query(DBUser).filter(DBUser.email == request.email).first()
|
| 279 |
+
if not user:
|
| 280 |
+
raise HTTPException(status_code=401, detail="Email not found")
|
| 281 |
+
|
| 282 |
+
if user.password != hash_password(request.password):
|
| 283 |
+
# Also try plain-text match for legacy users who signed up before hashing
|
| 284 |
+
if user.password != request.password:
|
| 285 |
+
raise HTTPException(status_code=401, detail="Incorrect password")
|
| 286 |
+
|
| 287 |
+
return {
|
| 288 |
+
"user_id": user.id,
|
| 289 |
+
"email": user.email,
|
| 290 |
+
"name": user.name or "",
|
| 291 |
+
"message": "Login successful"
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
# New-style endpoint (used by index.html test page)
|
| 296 |
+
@app.post("/v1/analysis")
|
| 297 |
+
def analyze(payload: AnalysisRequest, db: Session = Depends(get_db)):
|
| 298 |
+
# Shift 0-3 UI scale to 1-4 for the AI model (trained on data.csv)
|
| 299 |
+
shifted_answers = [a + 1 for a in payload.survey_answers]
|
| 300 |
+
text_scores = predict_text(payload.text)
|
| 301 |
+
survey_scores = predict_survey(shifted_answers)
|
| 302 |
+
|
| 303 |
+
final_scores = fuse_scores(text_scores, survey_scores)
|
| 304 |
+
primary = max(final_scores, key=final_scores.get)
|
| 305 |
+
clinical = calculate_dass_clinical_score(payload.survey_answers)
|
| 306 |
+
rec = get_recommendations(primary, final_scores[primary], payload.text)
|
| 307 |
+
created_at_dt = datetime.utcnow()
|
| 308 |
+
if payload.client_ts:
|
| 309 |
+
try:
|
| 310 |
+
created_at_dt = datetime.fromisoformat(payload.client_ts.replace("Z", "+00:00")).replace(tzinfo=None)
|
| 311 |
+
except ValueError:
|
| 312 |
+
pass
|
| 313 |
+
created_at = created_at_dt.isoformat() + "Z"
|
| 314 |
+
|
| 315 |
+
# Save to PostgreSQL if DB is connected
|
| 316 |
+
if db:
|
| 317 |
+
try:
|
| 318 |
+
text_input_hash = hashlib.sha256(payload.text.encode()).hexdigest()
|
| 319 |
+
new_analysis = DBAnalysis(
|
| 320 |
+
user_id=payload.user_id,
|
| 321 |
+
primary_condition=primary,
|
| 322 |
+
clinical_scoring=clinical,
|
| 323 |
+
created_at=created_at_dt,
|
| 324 |
+
text_input=payload.text,
|
| 325 |
+
text_input_hash=text_input_hash,
|
| 326 |
+
text_scores=text_scores,
|
| 327 |
+
survey_scores=survey_scores,
|
| 328 |
+
fused_scores=final_scores,
|
| 329 |
+
severity=rec.get("severity"),
|
| 330 |
+
cause=rec.get("cause"),
|
| 331 |
+
suicidal_flag=rec.get("suicidal_flag", False),
|
| 332 |
+
model_version=None,
|
| 333 |
+
app_version=None,
|
| 334 |
+
locale=payload.locale,
|
| 335 |
+
)
|
| 336 |
+
db.add(new_analysis)
|
| 337 |
+
db.commit()
|
| 338 |
+
except Exception as e:
|
| 339 |
+
logger.exception("DB save error: %s", e)
|
| 340 |
+
|
| 341 |
+
return {
|
| 342 |
+
"analysis_id": None,
|
| 343 |
+
"primary_condition": primary,
|
| 344 |
+
"fused_scores": final_scores,
|
| 345 |
+
"text_scores": text_scores,
|
| 346 |
+
"survey_scores": survey_scores,
|
| 347 |
+
"clinical_scoring": clinical,
|
| 348 |
+
"severity": rec.get("severity"),
|
| 349 |
+
"cause": rec.get("cause"),
|
| 350 |
+
"recommendations": {
|
| 351 |
+
"tips_en": rec.get("tips_en", []),
|
| 352 |
+
"tips_ar": rec.get("tips_ar", []),
|
| 353 |
+
"resources_en": rec.get("resources_en", []),
|
| 354 |
+
"resources_ar": rec.get("resources_ar", []),
|
| 355 |
+
"referral_en": rec.get("referral_en", ""),
|
| 356 |
+
"referral_ar": rec.get("referral_ar", ""),
|
| 357 |
+
},
|
| 358 |
+
"suicidal_flag": rec.get("suicidal_flag", False),
|
| 359 |
+
"created_at": created_at,
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
# Flutter-compatible endpoint (used by api_service.dart)
|
| 363 |
+
@app.post("/api/v1/analyze")
|
| 364 |
+
async def analyze_mental_health(request: AnalyzeRequest, db: Session = Depends(get_db)):
|
| 365 |
+
try:
|
| 366 |
+
start_time = time.time()
|
| 367 |
+
# Shift 0-3 UI scale to 1-4 for the AI model (trained on data.csv)
|
| 368 |
+
shifted_answers = [a + 1 for a in request.survey_answers]
|
| 369 |
+
text_scores = predict_text(request.text)
|
| 370 |
+
survey_scores = predict_survey(shifted_answers)
|
| 371 |
+
|
| 372 |
+
final_scores = fuse_scores(text_scores, survey_scores)
|
| 373 |
+
primary = max(final_scores, key=final_scores.get)
|
| 374 |
+
clinical = calculate_dass_clinical_score(request.survey_answers)
|
| 375 |
+
rec = get_recommendations(primary, final_scores[primary], request.text)
|
| 376 |
+
created_at_dt = datetime.utcnow()
|
| 377 |
+
if request.client_ts:
|
| 378 |
+
try:
|
| 379 |
+
created_at_dt = datetime.fromisoformat(request.client_ts.replace("Z", "+00:00")).replace(tzinfo=None)
|
| 380 |
+
except ValueError:
|
| 381 |
+
pass
|
| 382 |
+
created_at = created_at_dt.isoformat() + "Z"
|
| 383 |
+
|
| 384 |
+
# Save to PostgreSQL if DB is connected
|
| 385 |
+
if db:
|
| 386 |
+
try:
|
| 387 |
+
text_input_hash = hashlib.sha256(request.text.encode()).hexdigest()
|
| 388 |
+
new_analysis = DBAnalysis(
|
| 389 |
+
user_id=request.user_id,
|
| 390 |
+
primary_condition=primary,
|
| 391 |
+
clinical_scoring=clinical,
|
| 392 |
+
created_at=created_at_dt,
|
| 393 |
+
text_input=request.text,
|
| 394 |
+
text_input_hash=text_input_hash,
|
| 395 |
+
text_scores=text_scores,
|
| 396 |
+
survey_scores=survey_scores,
|
| 397 |
+
fused_scores=final_scores,
|
| 398 |
+
severity=rec.get("severity"),
|
| 399 |
+
cause=rec.get("cause"),
|
| 400 |
+
suicidal_flag=rec.get("suicidal_flag", False),
|
| 401 |
+
model_version=request.model_version,
|
| 402 |
+
app_version=request.app_version,
|
| 403 |
+
locale=request.locale,
|
| 404 |
+
)
|
| 405 |
+
db.add(new_analysis)
|
| 406 |
+
db.commit()
|
| 407 |
+
except Exception as e:
|
| 408 |
+
logger.exception("DB save error: %s", e)
|
| 409 |
+
|
| 410 |
+
return {
|
| 411 |
+
"analysis_id": None,
|
| 412 |
+
"primary_condition": primary,
|
| 413 |
+
"fused_scores": final_scores,
|
| 414 |
+
"text_scores": text_scores,
|
| 415 |
+
"survey_scores": survey_scores,
|
| 416 |
+
"clinical_scoring": clinical,
|
| 417 |
+
"severity": rec.get("severity"),
|
| 418 |
+
"cause": rec.get("cause"),
|
| 419 |
+
"recommendations": {
|
| 420 |
+
"tips_en": rec.get("tips_en", []),
|
| 421 |
+
"tips_ar": rec.get("tips_ar", []),
|
| 422 |
+
"resources_en": rec.get("resources_en", []),
|
| 423 |
+
"resources_ar": rec.get("resources_ar", []),
|
| 424 |
+
"referral_en": rec.get("referral_en", ""),
|
| 425 |
+
"referral_ar": rec.get("referral_ar", ""),
|
| 426 |
+
},
|
| 427 |
+
"suicidal_flag": rec.get("suicidal_flag", False),
|
| 428 |
+
"created_at": created_at,
|
| 429 |
+
"duration_ms": int((time.time() - start_time) * 1000),
|
| 430 |
+
}
|
| 431 |
+
except Exception as e:
|
| 432 |
+
logger.exception("Analyze request failed: %s", e)
|
| 433 |
+
raise HTTPException(status_code=500, detail="Failed to analyze")
|
| 434 |
+
|
| 435 |
+
# Flutter-compatible history endpoint
|
| 436 |
+
@app.get("/api/v1/analyses/history")
|
| 437 |
+
async def get_analyses_history(user_id: int = None, db: Session = Depends(get_db)):
|
| 438 |
+
try:
|
| 439 |
+
if not db:
|
| 440 |
+
return []
|
| 441 |
+
|
| 442 |
+
query = db.query(DBAnalysis)
|
| 443 |
+
|
| 444 |
+
# Filter by user_id if provided
|
| 445 |
+
if user_id is not None:
|
| 446 |
+
query = query.filter(DBAnalysis.user_id == user_id)
|
| 447 |
+
|
| 448 |
+
# Get the 10 most recent analyses, sorted by created_at ascending (oldest first for graphing)
|
| 449 |
+
records = query.order_by(DBAnalysis.created_at.desc()).limit(10).all()
|
| 450 |
+
|
| 451 |
+
history = []
|
| 452 |
+
for r in reversed(records): # Reverse so oldest is first
|
| 453 |
+
if r.clinical_scoring:
|
| 454 |
+
history.append({
|
| 455 |
+
"id": r.id,
|
| 456 |
+
"date": r.created_at.strftime("%b %d"),
|
| 457 |
+
"depression": r.clinical_scoring.get("depression", {}).get("score", 0),
|
| 458 |
+
"anxiety": r.clinical_scoring.get("anxiety", {}).get("score", 0),
|
| 459 |
+
"stress": r.clinical_scoring.get("stress", {}).get("score", 0),
|
| 460 |
+
"primary": r.primary_condition
|
| 461 |
+
})
|
| 462 |
+
return history
|
| 463 |
+
except Exception as e:
|
| 464 |
+
logger.exception("Analyze request failed: %s", e)
|
| 465 |
+
raise HTTPException(status_code=500, detail="Failed to analyze")
|
| 466 |
+
|
| 467 |
+
@app.post("/api/v1/chat", response_model=ChatResponse)
|
| 468 |
+
async def chat_with_ai(request: ChatRequest):
|
| 469 |
+
api_url = os.environ.get("AI_API_URL")
|
| 470 |
+
api_key = os.environ.get("AI_API_KEY")
|
| 471 |
+
chatflow_id = os.environ.get("AI_CHATFLOW_ID")
|
| 472 |
+
|
| 473 |
+
if not api_url or not api_key or not chatflow_id:
|
| 474 |
+
raise HTTPException(status_code=500, detail="AI API credentials are not configured in Secrets.")
|
| 475 |
+
|
| 476 |
+
endpoint = f"{api_url}/api/v1/prediction/{chatflow_id}"
|
| 477 |
+
headers = {"Authorization": f"Bearer {api_key}"}
|
| 478 |
+
payload = {"question": request.message, "overrideConfig": {"sessionId": request.session_id}}
|
| 479 |
+
|
| 480 |
+
async with httpx.AsyncClient() as client:
|
| 481 |
+
try:
|
| 482 |
+
response = await client.post(endpoint, json=payload, headers=headers, timeout=30.0)
|
| 483 |
+
response.raise_for_status()
|
| 484 |
+
data = response.json()
|
| 485 |
+
return ChatResponse(reply=data.get("text") or data.get("answer") or str(data))
|
| 486 |
+
except Exception as e:
|
| 487 |
+
raise HTTPException(status_code=502, detail=f"Failed to communicate with AI API: {str(e)}")
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
@app.post("/api/v1/checkin")
|
| 491 |
+
async def create_checkin(request: CheckinRequest, db: Session = Depends(get_db)):
|
| 492 |
+
if not db:
|
| 493 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 494 |
+
|
| 495 |
+
if request.user_id is None:
|
| 496 |
+
raise HTTPException(status_code=400, detail="user_id is required")
|
| 497 |
+
|
| 498 |
+
created_at = datetime.utcnow()
|
| 499 |
+
if request.client_ts:
|
| 500 |
+
try:
|
| 501 |
+
created_at = datetime.fromisoformat(request.client_ts.replace("Z", "+00:00")).replace(tzinfo=None)
|
| 502 |
+
except ValueError:
|
| 503 |
+
pass
|
| 504 |
+
|
| 505 |
+
try:
|
| 506 |
+
new_checkin = DBCheckin(
|
| 507 |
+
user_id=request.user_id,
|
| 508 |
+
mood=request.mood,
|
| 509 |
+
sleep=request.sleep,
|
| 510 |
+
energy=request.energy,
|
| 511 |
+
created_at=created_at,
|
| 512 |
+
)
|
| 513 |
+
db.add(new_checkin)
|
| 514 |
+
db.commit()
|
| 515 |
+
db.refresh(new_checkin)
|
| 516 |
+
|
| 517 |
+
return {
|
| 518 |
+
"id": new_checkin.id,
|
| 519 |
+
"user_id": new_checkin.user_id,
|
| 520 |
+
"mood": new_checkin.mood,
|
| 521 |
+
"sleep": new_checkin.sleep,
|
| 522 |
+
"energy": new_checkin.energy,
|
| 523 |
+
"created_at": new_checkin.created_at.isoformat() + "Z",
|
| 524 |
+
}
|
| 525 |
+
except Exception as e:
|
| 526 |
+
db.rollback()
|
| 527 |
+
logger.exception("Failed to save check-in: %s", e)
|
| 528 |
+
raise HTTPException(status_code=500, detail="Failed to save check-in")
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
@app.get("/api/v1/checkin/history")
|
| 532 |
+
async def get_checkin_history(user_id: int | None = None, db: Session = Depends(get_db)):
|
| 533 |
+
try:
|
| 534 |
+
if not db:
|
| 535 |
+
return []
|
| 536 |
+
|
| 537 |
+
query = db.query(DBCheckin)
|
| 538 |
+
if user_id is not None:
|
| 539 |
+
query = query.filter(DBCheckin.user_id == user_id)
|
| 540 |
+
|
| 541 |
+
records = query.order_by(DBCheckin.created_at.desc()).limit(30).all()
|
| 542 |
+
return [
|
| 543 |
+
{
|
| 544 |
+
"id": r.id,
|
| 545 |
+
"user_id": r.user_id,
|
| 546 |
+
"mood": r.mood,
|
| 547 |
+
"sleep": r.sleep,
|
| 548 |
+
"energy": r.energy,
|
| 549 |
+
"created_at": r.created_at.isoformat() + "Z",
|
| 550 |
+
}
|
| 551 |
+
for r in records
|
| 552 |
+
]
|
| 553 |
+
except Exception as e:
|
| 554 |
+
logger.exception("Failed to fetch check-in history: %s", e)
|
| 555 |
+
raise HTTPException(status_code=500, detail="Failed to fetch history")
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
@app.post("/api/v1/journal")
|
| 559 |
+
async def create_journal_entry(request: JournalEntryRequest, db: Session = Depends(get_db)):
|
| 560 |
+
if not db:
|
| 561 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 562 |
+
|
| 563 |
+
if request.user_id is None:
|
| 564 |
+
raise HTTPException(status_code=400, detail="user_id is required")
|
| 565 |
+
|
| 566 |
+
created_at = datetime.utcnow()
|
| 567 |
+
if request.client_ts:
|
| 568 |
+
try:
|
| 569 |
+
created_at = datetime.fromisoformat(request.client_ts.replace("Z", "+00:00")).replace(tzinfo=None)
|
| 570 |
+
except ValueError:
|
| 571 |
+
pass
|
| 572 |
+
|
| 573 |
+
try:
|
| 574 |
+
entry = DBJournalEntry(
|
| 575 |
+
user_id=request.user_id,
|
| 576 |
+
content=request.content,
|
| 577 |
+
created_at=created_at,
|
| 578 |
+
)
|
| 579 |
+
db.add(entry)
|
| 580 |
+
db.commit()
|
| 581 |
+
db.refresh(entry)
|
| 582 |
+
|
| 583 |
+
return {
|
| 584 |
+
"id": entry.id,
|
| 585 |
+
"user_id": entry.user_id,
|
| 586 |
+
"content": entry.content,
|
| 587 |
+
"created_at": entry.created_at.isoformat() + "Z",
|
| 588 |
+
}
|
| 589 |
+
except Exception as e:
|
| 590 |
+
db.rollback()
|
| 591 |
+
logger.exception("Failed to save journal entry: %s", e)
|
| 592 |
+
raise HTTPException(status_code=500, detail="Failed to save journal entry")
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
@app.get("/api/v1/journal/history")
|
| 596 |
+
async def get_journal_history(user_id: int | None = None, db: Session = Depends(get_db)):
|
| 597 |
+
try:
|
| 598 |
+
if not db:
|
| 599 |
+
return []
|
| 600 |
+
|
| 601 |
+
query = db.query(DBJournalEntry)
|
| 602 |
+
if user_id is not None:
|
| 603 |
+
query = query.filter(DBJournalEntry.user_id == user_id)
|
| 604 |
+
|
| 605 |
+
records = query.order_by(DBJournalEntry.created_at.desc()).limit(50).all()
|
| 606 |
+
return [
|
| 607 |
+
{
|
| 608 |
+
"id": r.id,
|
| 609 |
+
"user_id": r.user_id,
|
| 610 |
+
"content": r.content,
|
| 611 |
+
"created_at": r.created_at.isoformat() + "Z",
|
| 612 |
+
}
|
| 613 |
+
for r in records
|
| 614 |
+
]
|
| 615 |
+
except Exception as e:
|
| 616 |
+
logger.exception("Failed to fetch journal history: %s", e)
|
| 617 |
+
raise HTTPException(status_code=500, detail="Failed to fetch journal history")
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
@app.put("/api/v1/journal/{entry_id}")
|
| 621 |
+
async def update_journal_entry(entry_id: int, request: JournalEntryUpdateRequest, db: Session = Depends(get_db)):
|
| 622 |
+
if not db:
|
| 623 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 624 |
+
|
| 625 |
+
try:
|
| 626 |
+
entry = db.query(DBJournalEntry).filter(DBJournalEntry.id == entry_id).first()
|
| 627 |
+
if not entry:
|
| 628 |
+
raise HTTPException(status_code=404, detail="Journal entry not found")
|
| 629 |
+
|
| 630 |
+
entry.content = request.content
|
| 631 |
+
entry.updated_at = datetime.utcnow()
|
| 632 |
+
db.add(entry)
|
| 633 |
+
db.commit()
|
| 634 |
+
db.refresh(entry)
|
| 635 |
+
|
| 636 |
+
return {
|
| 637 |
+
"id": entry.id,
|
| 638 |
+
"user_id": entry.user_id,
|
| 639 |
+
"content": entry.content,
|
| 640 |
+
"created_at": entry.created_at.isoformat() + "Z",
|
| 641 |
+
"updated_at": entry.updated_at.isoformat() + "Z",
|
| 642 |
+
}
|
| 643 |
+
except HTTPException:
|
| 644 |
+
raise
|
| 645 |
+
except Exception as e:
|
| 646 |
+
db.rollback()
|
| 647 |
+
logger.exception("Failed to update journal entry: %s", e)
|
| 648 |
+
raise HTTPException(status_code=500, detail="Failed to update journal entry")
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
@app.delete("/api/v1/journal/{entry_id}")
|
| 652 |
+
async def delete_journal_entry(entry_id: int, db: Session = Depends(get_db)):
|
| 653 |
+
if not db:
|
| 654 |
+
raise HTTPException(status_code=500, detail="Database not available")
|
| 655 |
+
|
| 656 |
+
try:
|
| 657 |
+
entry = db.query(DBJournalEntry).filter(DBJournalEntry.id == entry_id).first()
|
| 658 |
+
if not entry:
|
| 659 |
+
raise HTTPException(status_code=404, detail="Journal entry not found")
|
| 660 |
+
|
| 661 |
+
db.delete(entry)
|
| 662 |
+
db.commit()
|
| 663 |
+
return {"status": "deleted", "id": entry_id}
|
| 664 |
+
except HTTPException:
|
| 665 |
+
raise
|
| 666 |
+
except Exception as e:
|
| 667 |
+
db.rollback()
|
| 668 |
+
logger.exception("Failed to delete journal entry: %s", e)
|
| 669 |
+
raise HTTPException(status_code=500, detail="Failed to delete journal entry")
|
app.py
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
app.py
|
| 3 |
+
======
|
| 4 |
+
Mental Health AI — Full Pipeline
|
| 5 |
+
Files needed:
|
| 6 |
+
mental_xlmr_final/ ← XLM-R model folder
|
| 7 |
+
mental_model.h5 ← Survey Keras model
|
| 8 |
+
scaler.pkl ← Survey scaler
|
| 9 |
+
recommendations.py ← same directory
|
| 10 |
+
|
| 11 |
+
Install:
|
| 12 |
+
pip install streamlit transformers torch tensorflow scikit-learn deep-translator
|
| 13 |
+
"""
|
| 14 |
+
import sys, os
|
| 15 |
+
sys.path.append(os.path.dirname(__file__))
|
| 16 |
+
import re, pickle, warnings
|
| 17 |
+
import numpy as np
|
| 18 |
+
import streamlit as st
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| 21 |
+
from deep_translator import GoogleTranslator
|
| 22 |
+
sys.path.insert(0, os.path.dirname(__file__))
|
| 23 |
+
from recommendations import get_recommendations
|
| 24 |
+
|
| 25 |
+
warnings.filterwarnings("ignore")
|
| 26 |
+
|
| 27 |
+
# ── PAGE CONFIG ───────────────────────────────────────────────────────────────
|
| 28 |
+
st.set_page_config(page_title="Mental Health AI", page_icon="🧠", layout="wide")
|
| 29 |
+
|
| 30 |
+
st.markdown("""
|
| 31 |
+
<style>
|
| 32 |
+
.stApp { background: linear-gradient(135deg, #0d1117, #161b22, #0d1117); color: #e6edf3; }
|
| 33 |
+
h1 { text-align: center; color: #58a6ff; font-size: 36px; margin-bottom: 4px; }
|
| 34 |
+
h2, h3 { color: #c9d1d9; }
|
| 35 |
+
.section-card {
|
| 36 |
+
background: rgba(22,27,34,0.9);
|
| 37 |
+
border: 1px solid #30363d;
|
| 38 |
+
border-radius: 14px;
|
| 39 |
+
padding: 22px 26px;
|
| 40 |
+
margin-bottom: 18px;
|
| 41 |
+
}
|
| 42 |
+
.result-card {
|
| 43 |
+
background: #161b22;
|
| 44 |
+
border: 1px solid #30363d;
|
| 45 |
+
border-radius: 12px;
|
| 46 |
+
padding: 20px;
|
| 47 |
+
text-align: center;
|
| 48 |
+
margin-bottom: 8px;
|
| 49 |
+
}
|
| 50 |
+
.result-card.primary { border: 2px solid #58a6ff; }
|
| 51 |
+
.result-label { font-size: 15px; color: #8b949e; margin-bottom: 6px; }
|
| 52 |
+
.result-value { font-size: 44px; font-weight: 700; }
|
| 53 |
+
.severity-badge {
|
| 54 |
+
display: inline-block;
|
| 55 |
+
padding: 3px 12px;
|
| 56 |
+
border-radius: 20px;
|
| 57 |
+
font-size: 12px;
|
| 58 |
+
font-weight: 600;
|
| 59 |
+
margin-top: 6px;
|
| 60 |
+
}
|
| 61 |
+
.rec-block {
|
| 62 |
+
background: #161b22;
|
| 63 |
+
border: 1px solid #30363d;
|
| 64 |
+
border-radius: 12px;
|
| 65 |
+
padding: 18px 22px;
|
| 66 |
+
margin-bottom: 14px;
|
| 67 |
+
}
|
| 68 |
+
.rec-title { font-size: 15px; font-weight: 700; margin-bottom: 10px; }
|
| 69 |
+
.rec-item { font-size: 14px; color: #c9d1d9; padding: 4px 0; border-bottom: 1px solid #21262d; }
|
| 70 |
+
.rec-item:last-child { border-bottom: none; }
|
| 71 |
+
.ar-text { font-size: 13px; color: #8b949e; margin-top: 3px; direction: rtl; }
|
| 72 |
+
.referral-box {
|
| 73 |
+
background: rgba(248,81,73,0.1);
|
| 74 |
+
border: 1px solid rgba(248,81,73,0.4);
|
| 75 |
+
border-radius: 10px;
|
| 76 |
+
padding: 14px 18px;
|
| 77 |
+
margin-top: 12px;
|
| 78 |
+
}
|
| 79 |
+
.crisis-box {
|
| 80 |
+
background: rgba(248,81,73,0.2);
|
| 81 |
+
border: 2px solid #f85149;
|
| 82 |
+
border-radius: 12px;
|
| 83 |
+
padding: 20px 24px;
|
| 84 |
+
margin: 16px 0;
|
| 85 |
+
}
|
| 86 |
+
div.stButton > button {
|
| 87 |
+
background: linear-gradient(90deg, #1f6feb, #58a6ff);
|
| 88 |
+
color: white; font-size: 17px; font-weight: 700;
|
| 89 |
+
border-radius: 10px; height: 52px; width: 100%; border: none;
|
| 90 |
+
}
|
| 91 |
+
div.stSlider > label { color: #c9d1d9 !important; font-size: 13px; }
|
| 92 |
+
.stTextArea textarea {
|
| 93 |
+
background: #0d1117 !important;
|
| 94 |
+
color: #e6edf3 !important;
|
| 95 |
+
border: 1px solid #30363d !important;
|
| 96 |
+
border-radius: 8px !important;
|
| 97 |
+
}
|
| 98 |
+
</style>
|
| 99 |
+
""", unsafe_allow_html=True)
|
| 100 |
+
|
| 101 |
+
# ── CONSTANTS ─────────────────────────────────────────────────────────────────
|
| 102 |
+
CLASSES = ["anxiety", "depression", "stress"]
|
| 103 |
+
ARABIC_LABELS = {"anxiety": "القلق", "depression": "الاكتئاب", "stress": "الضغط النفسي"}
|
| 104 |
+
COLORS = {"anxiety": "#ffa657", "depression": "#79c0ff", "stress": "#56d364"}
|
| 105 |
+
SEVERITY_AR = {
|
| 106 |
+
"normal": "طبيعي", "mild": "خفيف", "moderate": "متوسط",
|
| 107 |
+
"severe": "شديد", "extremely_severe": "شديد جداً", "crisis": "أزمة",
|
| 108 |
+
}
|
| 109 |
+
SEVERITY_COLORS = {
|
| 110 |
+
"normal": "#56d364", "mild": "#e3b341", "moderate": "#ffa657",
|
| 111 |
+
"severe": "#f85149", "extremely_severe": "#ff0000", "crisis": "#ff0000",
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
CAUSE_AR = {
|
| 115 |
+
"work": "ضغط العمل", "relationships": "العلاقات", "financial": "الضغط المالي",
|
| 116 |
+
"academic": "الضغط الأكاديمي", "health": "المخاوف الصحية", "social": "القلق الاجتماعي",
|
| 117 |
+
"self_worth": "الثقة بالنفس", "trauma": "الصدمة النفسية", "general": "عام",
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# ── LOAD MODELS ───────────────────────────────────────────────────────────────
|
| 121 |
+
@st.cache_resource
|
| 122 |
+
def load_xlmr():
|
| 123 |
+
token = st.secrets["HF_TOKEN"]
|
| 124 |
+
xlmr_tokenizer = AutoTokenizer.from_pretrained(
|
| 125 |
+
"tasneem33355/mental-xlmr", token=token
|
| 126 |
+
)
|
| 127 |
+
model = AutoModelForSequenceClassification.from_pretrained(
|
| 128 |
+
"tasneem33355/mental-xlmr", token=token
|
| 129 |
+
)
|
| 130 |
+
model.eval()
|
| 131 |
+
# Hardcoded — LabelEncoder على ['anxiety','depression','stress'] دايماً بترتّب alphabetically
|
| 132 |
+
# 0=anxiety, 1=depression, 2=stress
|
| 133 |
+
classes = ["anxiety", "depression", "stress"]
|
| 134 |
+
return xlmr_tokenizer, model, classes
|
| 135 |
+
|
| 136 |
+
@st.cache_resource
|
| 137 |
+
def load_survey():
|
| 138 |
+
scaler = pickle.load(open(os.path.join(os.path.dirname(__file__), "scaler.pkl"), "rb"))
|
| 139 |
+
weights = pickle.load(open(os.path.join(os.path.dirname(__file__), "model_weights.pkl"), "rb"))
|
| 140 |
+
|
| 141 |
+
def predict(x):
|
| 142 |
+
for w in weights:
|
| 143 |
+
if len(w) == 2:
|
| 144 |
+
x = np.dot(x, w[0]) + w[1]
|
| 145 |
+
x = np.maximum(0, x) # ReLU
|
| 146 |
+
x = np.exp(x) / np.sum(np.exp(x)) # Softmax
|
| 147 |
+
return x
|
| 148 |
+
|
| 149 |
+
return scaler, predict
|
| 150 |
+
|
| 151 |
+
xlmr_tokenizer, xlmr_model, le = load_xlmr()
|
| 152 |
+
scaler, survey_predict = load_survey()
|
| 153 |
+
|
| 154 |
+
# ── HELPERS ───────────────────────────────────────────────────────────────────
|
| 155 |
+
def clean_text(text):
|
| 156 |
+
text = re.sub(r'(.)\1{2,}', r'\1\1', text)
|
| 157 |
+
text = re.sub(r'[^\w\s\u0600-\u06FF\[\]]', ' ', text)
|
| 158 |
+
return re.sub(r'\s+', ' ', text).strip()
|
| 159 |
+
|
| 160 |
+
def translate_to_en(text):
|
| 161 |
+
try:
|
| 162 |
+
return GoogleTranslator(source="auto", target="en").translate(text)
|
| 163 |
+
except Exception:
|
| 164 |
+
return ""
|
| 165 |
+
|
| 166 |
+
# ── KEYWORD OVERRIDE ─────────────────────────────────────────────────────────
|
| 167 |
+
DEPRESSION_KEYWORDS = [
|
| 168 |
+
# عربي فصيح
|
| 169 |
+
"اكتئاب", "مكتئب", "مكتئبة", "حزن", "حزين", "حزينة", "يأس", "يائس", "يائسة",
|
| 170 |
+
"فراغ", "إحساس بالفراغ", "بلا معنى", "لا معنى", "مالهاش معنى", "بلا هدف",
|
| 171 |
+
"لا أمل", "مفيش أمل", "تعبت من الحياة", "زهقت من الحياة",
|
| 172 |
+
"مش لاقي معنى", "مش لاقية معنى", "حاسس بالفراغ", "حاسة بالفراغ",
|
| 173 |
+
"مفيش طاقة", "مفيش رغبة", "بكاء", "عايز أبكي", "عايزة أبكي",
|
| 174 |
+
"وحيد", "وحيدة", "عزلة", "منعزل", "منعزلة",
|
| 175 |
+
"إرهاق نفسي", "إرهاق عاطفي", "مش حاسس بحاجة", "مش حاسة بحاجة",
|
| 176 |
+
# عامية مصرية وشامية
|
| 177 |
+
"زهقت", "تعبت", "مش طايق", "مش طايقة", "نفسيتي وحشة", "نفسيتي في الأرض",
|
| 178 |
+
"مش قادر أكمل", "مش قادرة أكمل", "مش عايش", "مش قادر أعيش",
|
| 179 |
+
"مش عايز أصحى", "مش عايزة أصحى", "دموع", "بدمع", "قلبي تقيل",
|
| 180 |
+
"مش حاسس بنفسي", "مش حاسة بنفسي", "ما بحس بشي", "ما في فايدة",
|
| 181 |
+
"مافي امل", "ما في امل", "حياتي خربت", "خسرت كل حاجة",
|
| 182 |
+
# إنجليزي
|
| 183 |
+
"depressed", "depression", "hopeless", "hopelessness", "empty", "emptiness",
|
| 184 |
+
"worthless", "meaningless", "no meaning", "no purpose", "cannot go on",
|
| 185 |
+
"cant go on", "no energy", "no motivation", "crying", "feel nothing",
|
| 186 |
+
"numb", "isolated", "lonely", "loneliness", "sad", "sadness",
|
| 187 |
+
"despair", "grief", "miserable", "broken", "lost all hope",
|
| 188 |
+
]
|
| 189 |
+
|
| 190 |
+
ANXIETY_KEYWORDS = [
|
| 191 |
+
# عربي
|
| 192 |
+
"قلق", "قلقان", "قلقانة", "خوف", "خايف", "خايفة", "توتر", "متوتر", "متوترة",
|
| 193 |
+
"هلع", "مش مرتاح", "مش مرتاحة", "ذعر", "رهاب", "وسواس",
|
| 194 |
+
# إنجليزي
|
| 195 |
+
"panic", "anxious", "anxiety", "worried", "worry", "fear",
|
| 196 |
+
"scared", "nervous", "restless", "tense", "phobia", "ocd",
|
| 197 |
+
]
|
| 198 |
+
|
| 199 |
+
STRESS_KEYWORDS = [
|
| 200 |
+
# عربي
|
| 201 |
+
"ضغط", "ضغوط", "مضغوط", "مضغوطة", "إجهاد", "مجهد", "مجهدة",
|
| 202 |
+
# إنجليزي
|
| 203 |
+
"overwhelmed", "stressed", "stress", "burnout", "exhausted", "overloaded",
|
| 204 |
+
]
|
| 205 |
+
|
| 206 |
+
def keyword_boost(text: str, scores: dict) -> dict:
|
| 207 |
+
"""
|
| 208 |
+
يعوّض الـ stress bias في الموديل عن طريق override قوي
|
| 209 |
+
لما تكون كلمات depression أو anxiety أو stress واضحة في النص.
|
| 210 |
+
"""
|
| 211 |
+
text_lower = text.lower()
|
| 212 |
+
|
| 213 |
+
dep_hits = sum(1 for kw in DEPRESSION_KEYWORDS if kw.lower() in text_lower)
|
| 214 |
+
anx_hits = sum(1 for kw in ANXIETY_KEYWORDS if kw.lower() in text_lower)
|
| 215 |
+
str_hits = sum(1 for kw in STRESS_KEYWORDS if kw.lower() in text_lower)
|
| 216 |
+
|
| 217 |
+
if dep_hits == 0 and anx_hits == 0 and str_hits == 0:
|
| 218 |
+
return scores
|
| 219 |
+
|
| 220 |
+
s = dict(scores)
|
| 221 |
+
|
| 222 |
+
if dep_hits > 0 and dep_hits >= anx_hits and dep_hits >= str_hits:
|
| 223 |
+
# depression كلمات واضحة — override قوي
|
| 224 |
+
boost = min(0.55 + dep_hits * 0.10, 0.85)
|
| 225 |
+
s["depression"] = boost
|
| 226 |
+
remaining = 1.0 - boost
|
| 227 |
+
total_rest = s["anxiety"] + s["stress"]
|
| 228 |
+
if total_rest > 0:
|
| 229 |
+
s["anxiety"] = round(remaining * s["anxiety"] / total_rest, 4)
|
| 230 |
+
s["stress"] = round(remaining * s["stress"] / total_rest, 4)
|
| 231 |
+
s["depression"] = round(boost, 4)
|
| 232 |
+
|
| 233 |
+
elif anx_hits > 0 and anx_hits >= dep_hits and anx_hits >= str_hits:
|
| 234 |
+
# anxiety كلمات واضحة — override قوي
|
| 235 |
+
boost = min(0.55 + anx_hits * 0.10, 0.85)
|
| 236 |
+
s["anxiety"] = boost
|
| 237 |
+
remaining = 1.0 - boost
|
| 238 |
+
total_rest = s["depression"] + s["stress"]
|
| 239 |
+
if total_rest > 0:
|
| 240 |
+
s["depression"] = round(remaining * s["depression"] / total_rest, 4)
|
| 241 |
+
s["stress"] = round(remaining * s["stress"] / total_rest, 4)
|
| 242 |
+
s["anxiety"] = round(boost, 4)
|
| 243 |
+
|
| 244 |
+
elif str_hits > 0 and str_hits >= dep_hits and str_hits >= anx_hits:
|
| 245 |
+
# stress كلمات واضحة — override قوي
|
| 246 |
+
boost = min(0.55 + str_hits * 0.10, 0.85)
|
| 247 |
+
s["stress"] = boost
|
| 248 |
+
remaining = 1.0 - boost
|
| 249 |
+
total_rest = s["depression"] + s["anxiety"]
|
| 250 |
+
if total_rest > 0:
|
| 251 |
+
s["depression"] = round(remaining * s["depression"] / total_rest, 4)
|
| 252 |
+
s["anxiety"] = round(remaining * s["anxiety"] / total_rest, 4)
|
| 253 |
+
s["stress"] = round(boost, 4)
|
| 254 |
+
|
| 255 |
+
# normalize
|
| 256 |
+
total = sum(s.values())
|
| 257 |
+
if total > 0:
|
| 258 |
+
s = {k: round(v / total, 4) for k, v in s.items()}
|
| 259 |
+
|
| 260 |
+
return s
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def predict_text(text: str) -> dict:
|
| 264 |
+
cleaned = clean_text(text)
|
| 265 |
+
text_en = translate_to_en(cleaned)
|
| 266 |
+
combined = (text_en + " [SEP] " + cleaned) if text_en else cleaned
|
| 267 |
+
inputs = xlmr_tokenizer(combined, return_tensors="pt",
|
| 268 |
+
truncation=True, max_length=192, padding=True)
|
| 269 |
+
with torch.no_grad():
|
| 270 |
+
probs = torch.softmax(xlmr_model(**inputs).logits, dim=-1).squeeze().numpy()
|
| 271 |
+
raw_scores = {c: round(float(p), 4) for c, p in zip(le, probs)}
|
| 272 |
+
# طبّق الـ keyword boost على النص الأصلي + الترجمة
|
| 273 |
+
boosted = keyword_boost(text + " " + text_en, raw_scores)
|
| 274 |
+
return boosted
|
| 275 |
+
|
| 276 |
+
def predict_survey(answers: list) -> dict:
|
| 277 |
+
data = scaler.transform(np.array(answers).reshape(1, -1))
|
| 278 |
+
pred = survey_predict(data)[0]
|
| 279 |
+
return {
|
| 280 |
+
"depression": round(float(pred[0]), 4),
|
| 281 |
+
"anxiety": round(float(pred[1]), 4),
|
| 282 |
+
"stress": round(float(pred[2]), 4),
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
def fuse_scores(text_s, survey_s, w_text=0.4, w_survey=0.6):
|
| 286 |
+
return {c: round(w_text * text_s[c] + w_survey * survey_s[c], 4) for c in CLASSES}
|
| 287 |
+
|
| 288 |
+
# ── SURVEY QUESTIONS ─────────────────────────────────────────────────────────
|
| 289 |
+
SURVEY_Q = [
|
| 290 |
+
("I found it hard to wind down", "وجدت صعوبة في الاسترخاء"),
|
| 291 |
+
("I was aware of dryness of my mouth", "لاحظت جفافاً في فمي"),
|
| 292 |
+
("I couldn't seem to experience any positive feeling at all", "لم أستطع الشعور بأي مشاعر إيجابية"),
|
| 293 |
+
("I experienced breathing difficulty", "أحسست بصعوبة في التنفس"),
|
| 294 |
+
("I found it difficult to work up the initiative to do things", "وجدت صعوبة في اتخاذ المبادرة للقيام بالأشياء"),
|
| 295 |
+
("I tended to over-react to situations", "كنت أبالغ في ردود أفعالي تجاه المواقف"),
|
| 296 |
+
("I experienced trembling", "شعرت بالرعشة"),
|
| 297 |
+
("I felt that I was using a lot of nervous energy", "شعرت أنني أستهلك الكثير من الطاقة العصبية"),
|
| 298 |
+
("I was worried about situations in which I might panic", "كنت قلقاً من مواقف قد أصاب فيها بالذعر"),
|
| 299 |
+
("I felt that I had nothing to look forward to", "شعرت أنه لا يوجد شيء أتطلع إليه"),
|
| 300 |
+
("I found myself getting agitated", "وجدت نفسي أشعر بالانفعال"),
|
| 301 |
+
("I found it difficult to relax", "وجدت صعوبة في الاسترخاء"),
|
| 302 |
+
("I felt down-hearted and blue", "شعرت بالإحباط والكآبة"),
|
| 303 |
+
("I was intolerant of anything that kept me from getting on", "كنت غير متسامح مع أي شيء يعيقني"),
|
| 304 |
+
("I felt I was close to panic", "شعرت أنني على وشك الذعر"),
|
| 305 |
+
("I was unable to become enthusiastic", "لم أستطع أن أتحمس لأي شيء"),
|
| 306 |
+
("I felt I wasn't worth much as a person", "شعرت أنني لست شخصاً ذا قيمة"),
|
| 307 |
+
("I felt that I was rather touchy", "شعرت أنني متقلب المزاج"),
|
| 308 |
+
("I was aware of the action of my heart", "كنت واعياً لنبضات قلبي"),
|
| 309 |
+
("I felt scared without any good reason", "شعرت بالخوف دون سبب واضح"),
|
| 310 |
+
("I felt that life was meaningless", "شعرت أن الحياة بلا معنى"),
|
| 311 |
+
("I found it hard to calm down", "وجدت صعوبة في التهدئة"),
|
| 312 |
+
("I felt nervous", "شعرت بالتوتر"),
|
| 313 |
+
("I felt sad and depressed", "شعرت بالحزن والاكتئاب"),
|
| 314 |
+
("I found myself getting impatient", "وجدت نفسي أشعر بنفاد الصبر"),
|
| 315 |
+
("I felt that I was rather emotional", "شعرت أنني عاطفي بشكل مفرط"),
|
| 316 |
+
("I felt restless", "شعرت بعدم الهدوء"),
|
| 317 |
+
("I had difficulty concentrating", "وجدت صعوبة في التركيز"),
|
| 318 |
+
("I felt lonely", "شعرت بالوحدة"),
|
| 319 |
+
("I found it difficult to relax", "وجدت صعوبة في الاسترخاء"),
|
| 320 |
+
("I felt hopeless", "شعرت باليأس"),
|
| 321 |
+
("I felt worried about many things", "كنت قلقاً بشأن أشياء كثيرة"),
|
| 322 |
+
("I felt that I had no energy", "شعرت بعدم وجود طاقة"),
|
| 323 |
+
("I felt tense", "شعرت بالتوتر والضيق"),
|
| 324 |
+
("I felt tired for no reason", "شعرت بالتعب دون سبب"),
|
| 325 |
+
("I felt uneasy", "شعرت بعدم الارتياح"),
|
| 326 |
+
("I felt worthless", "شعرت بأنني لا قيمة لي"),
|
| 327 |
+
("I felt anxious", "شعرت بالقلق"),
|
| 328 |
+
("I felt discouraged", "شعرت بالإحباط"),
|
| 329 |
+
("I felt stressed", "شعرت بالضغط"),
|
| 330 |
+
("I felt overwhelmed", "شعرت بالإرهاق"),
|
| 331 |
+
("I felt emotionally exhausted", "شعرت بالإنهاك العاطفي"),
|
| 332 |
+
]
|
| 333 |
+
|
| 334 |
+
# ── UI ────────────────────────────────────────────────────────────────────────
|
| 335 |
+
st.title("🧠 Mental Health AI")
|
| 336 |
+
st.markdown(
|
| 337 |
+
"<p style='text-align:center;color:#8b949e;font-size:15px;'>"
|
| 338 |
+
"Write how you feel and answer the survey for a complete assessment"
|
| 339 |
+
"<br><span dir='rtl'>اكتب ما تشعر به وأجب على الأسئلة للحصول على تقييم شامل</span></p>",
|
| 340 |
+
unsafe_allow_html=True,
|
| 341 |
+
)
|
| 342 |
+
st.markdown("---")
|
| 343 |
+
|
| 344 |
+
# ── PART 1: TEXT ─────────────────────────────────────────────────────────────
|
| 345 |
+
st.markdown("<div class='section-card'>", unsafe_allow_html=True)
|
| 346 |
+
st.markdown("### 💬 How are you feeling? / كيف تشعر؟")
|
| 347 |
+
st.markdown(
|
| 348 |
+
"<p style='color:#8b949e;font-size:13px;'>"
|
| 349 |
+
"Write in any language — Arabic (any dialect), English, or both<br>"
|
| 350 |
+
"<span dir='rtl'>اكتب بأي لغة — عربي (أي لهجة)، إنجليزي، أو الاتنين</span></p>",
|
| 351 |
+
unsafe_allow_html=True,
|
| 352 |
+
)
|
| 353 |
+
user_text = st.text_area(
|
| 354 |
+
label="",
|
| 355 |
+
placeholder="e.g. I've been feeling very overwhelmed at work and can't sleep...\nمثال: أنا تعبان جداً من الشغل ومش قادر أنام...",
|
| 356 |
+
height=120,
|
| 357 |
+
label_visibility="collapsed",
|
| 358 |
+
)
|
| 359 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
| 360 |
+
|
| 361 |
+
# ── PART 2: SURVEY ────────────────────────────────────────────────────────────
|
| 362 |
+
st.markdown("<div class='section-card'>", unsafe_allow_html=True)
|
| 363 |
+
st.markdown("### 📋 DASS-42 Survey / استبيان DASS-42")
|
| 364 |
+
st.markdown(
|
| 365 |
+
"<p style='color:#8b949e;font-size:13px;'>"
|
| 366 |
+
"0 = Never | 1 = Sometimes | 2 = Often | "
|
| 367 |
+
"3 = Most of the time | 4 = Always<br>"
|
| 368 |
+
"<span dir='rtl'>0 = لم يحدث أبداً | 1 = أحياناً | 2 = كثيراً | 3 = معظم الوقت | 4 = دائماً</span></p>",
|
| 369 |
+
unsafe_allow_html=True,
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
survey_answers = []
|
| 373 |
+
for i in range(0, len(SURVEY_Q), 2):
|
| 374 |
+
cols = st.columns(2)
|
| 375 |
+
for j, (en, ar) in enumerate(SURVEY_Q[i:i+2]):
|
| 376 |
+
with cols[j]:
|
| 377 |
+
val = st.slider(
|
| 378 |
+
f"{i+j+1}. {en}\n{ar}",
|
| 379 |
+
min_value=0, max_value=3, value=0,
|
| 380 |
+
key=f"q_{i+j}",
|
| 381 |
+
)
|
| 382 |
+
survey_answers.append(val)
|
| 383 |
+
|
| 384 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
| 385 |
+
|
| 386 |
+
# ── PREDICT BUTTON ────────────────────────────────────────────────────────────
|
| 387 |
+
_, col_btn, _ = st.columns([1, 2, 1])
|
| 388 |
+
with col_btn:
|
| 389 |
+
predict_btn = st.button("🔍 Analyze / تحليل")
|
| 390 |
+
|
| 391 |
+
# ── RESULTS ──────────────────────────────────────────────────────────────────
|
| 392 |
+
if predict_btn:
|
| 393 |
+
if not user_text.strip():
|
| 394 |
+
st.warning("Please write how you feel first. / من فضلك اكتب ما تشعر به أولاً.")
|
| 395 |
+
st.stop()
|
| 396 |
+
|
| 397 |
+
with st.spinner("Analyzing... / جاري التحليل..."):
|
| 398 |
+
text_scores = predict_text(user_text)
|
| 399 |
+
survey_scores = predict_survey(survey_answers)
|
| 400 |
+
final_scores = fuse_scores(text_scores, survey_scores)
|
| 401 |
+
primary = max(final_scores, key=final_scores.get)
|
| 402 |
+
rec = get_recommendations(primary, final_scores[primary], user_text)
|
| 403 |
+
|
| 404 |
+
st.markdown("---")
|
| 405 |
+
st.markdown("## 📊 Results / النتائج")
|
| 406 |
+
|
| 407 |
+
# ── SCORE CARDS ───────────────────────────────────────────────────────────
|
| 408 |
+
cols = st.columns(3)
|
| 409 |
+
for col, cls in zip(cols, CLASSES):
|
| 410 |
+
pct = int(final_scores[cls] * 100)
|
| 411 |
+
is_primary = cls == primary
|
| 412 |
+
card_class = "result-card primary" if is_primary else "result-card"
|
| 413 |
+
sev = rec["severity"] if is_primary else ""
|
| 414 |
+
badge = ""
|
| 415 |
+
if is_primary and sev:
|
| 416 |
+
sev_color = SEVERITY_COLORS.get(sev, "#8b949e")
|
| 417 |
+
badge = (f"<div class='severity-badge' style='background:{sev_color}20;"
|
| 418 |
+
f"color:{sev_color};border:1px solid {sev_color};'>"
|
| 419 |
+
f"{sev.replace('_',' ').title()} / {SEVERITY_AR.get(sev,'')}</div>")
|
| 420 |
+
col.markdown(f"""
|
| 421 |
+
<div class='{card_class}'>
|
| 422 |
+
<div class='result-label'>{cls.title()} / {ARABIC_LABELS[cls]}</div>
|
| 423 |
+
<div class='result-value' style='color:{COLORS[cls]}'>{pct}%</div>
|
| 424 |
+
{badge}
|
| 425 |
+
</div>""", unsafe_allow_html=True)
|
| 426 |
+
|
| 427 |
+
# ── PRIMARY LABEL ─────────────────────────────────────────────────────────
|
| 428 |
+
if not rec["suicidal_flag"]:
|
| 429 |
+
cause_label = CAUSE_AR.get(rec["cause"], rec["cause"])
|
| 430 |
+
st.markdown(
|
| 431 |
+
f"<p style='text-align:center;margin-top:10px;font-size:17px;color:#8b949e;'>"
|
| 432 |
+
f"Primary: <strong style='color:{COLORS[primary]}'>{primary.title()} / {ARABIC_LABELS[primary]}</strong>"
|
| 433 |
+
f" | Cause detected / السبب المكتشف: "
|
| 434 |
+
f"<strong style='color:#e3b341'>{rec['cause'].replace('_',' ').title()} / {cause_label}</strong></p>",
|
| 435 |
+
unsafe_allow_html=True,
|
| 436 |
+
)
|
| 437 |
+
|
| 438 |
+
# ── CRISIS BOX ────────────────────────────────────────────────────────────
|
| 439 |
+
if rec["suicidal_flag"]:
|
| 440 |
+
st.markdown("""
|
| 441 |
+
<div class='crisis-box'>
|
| 442 |
+
<h3 style='color:#f85149;margin-top:0;'>🚨 Crisis Support Needed / مطلوب دعم أزمة</h3>
|
| 443 |
+
</div>""", unsafe_allow_html=True)
|
| 444 |
+
|
| 445 |
+
# ── SCORE DETAILS ─────────────────────────────────────────────────────────
|
| 446 |
+
with st.expander("Show score breakdown / عرض تفاصيل النتائج"):
|
| 447 |
+
c1, c2 = st.columns(2)
|
| 448 |
+
c1.markdown("**Text model / موديل النص:**")
|
| 449 |
+
for cls in CLASSES:
|
| 450 |
+
c1.markdown(f"- {cls} / {ARABIC_LABELS[cls]}: **{int(text_scores[cls]*100)}%**")
|
| 451 |
+
c2.markdown("**Survey model / موديل السيرفاي:**")
|
| 452 |
+
for cls in CLASSES:
|
| 453 |
+
c2.markdown(f"- {cls} / {ARABIC_LABELS[cls]}: **{int(survey_scores[cls]*100)}%**")
|
| 454 |
+
|
| 455 |
+
# ── RECOMMENDATIONS ───────────────────────────────────────────────────────
|
| 456 |
+
st.markdown("---")
|
| 457 |
+
st.markdown("## 💡 Recommendations / التوصيات")
|
| 458 |
+
|
| 459 |
+
col_tips, col_res = st.columns(2)
|
| 460 |
+
|
| 461 |
+
with col_tips:
|
| 462 |
+
st.markdown("<div class='rec-block'>", unsafe_allow_html=True)
|
| 463 |
+
st.markdown("<div class='rec-title'>✅ Practical Tips / نصائح عملية</div>",
|
| 464 |
+
unsafe_allow_html=True)
|
| 465 |
+
tips_en = rec.get("tips_en", [])
|
| 466 |
+
tips_ar = rec.get("tips_ar", [])
|
| 467 |
+
for en, ar in zip(tips_en, tips_ar):
|
| 468 |
+
st.markdown(
|
| 469 |
+
f"<div class='rec-item'>{en}"
|
| 470 |
+
f"<div class='ar-text' dir='rtl'>• {ar}</div></div>",
|
| 471 |
+
unsafe_allow_html=True,
|
| 472 |
+
)
|
| 473 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
| 474 |
+
|
| 475 |
+
with col_res:
|
| 476 |
+
st.markdown("<div class='rec-block'>", unsafe_allow_html=True)
|
| 477 |
+
st.markdown("<div class='rec-title'>📚 Resources / موارد مفيدة</div>",
|
| 478 |
+
unsafe_allow_html=True)
|
| 479 |
+
res_en = rec.get("resources_en", [])
|
| 480 |
+
res_ar = rec.get("resources_ar", [])
|
| 481 |
+
for en, ar in zip(res_en, res_ar):
|
| 482 |
+
st.markdown(
|
| 483 |
+
f"<div class='rec-item'>{en}"
|
| 484 |
+
f"<div class='ar-text' dir='rtl'>• {ar}</div></div>",
|
| 485 |
+
unsafe_allow_html=True,
|
| 486 |
+
)
|
| 487 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
| 488 |
+
|
| 489 |
+
# ── REFERRAL ───────────────────────────────────────────────────────────���──
|
| 490 |
+
ref_en = rec.get("referral_en", "")
|
| 491 |
+
ref_ar = rec.get("referral_ar", "")
|
| 492 |
+
if ref_en:
|
| 493 |
+
box_class = "crisis-box" if rec["suicidal_flag"] else "referral-box"
|
| 494 |
+
st.markdown(
|
| 495 |
+
f"<div class='{box_class}'>"
|
| 496 |
+
f"<strong>🏥 When to seek help / متى تطلب المساعدة:</strong><br>"
|
| 497 |
+
f"{ref_en}<br>"
|
| 498 |
+
f"<span dir='rtl' style='color:#f0a0a0;font-size:13px;'>{ref_ar}</span>"
|
| 499 |
+
f"</div>",
|
| 500 |
+
unsafe_allow_html=True,
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
st.markdown(
|
| 504 |
+
"<p style='text-align:center;color:#484f58;font-size:12px;margin-top:20px;'>"
|
| 505 |
+
"⚠️ This system is for awareness only and is not a substitute for professional medical diagnosis.<br>"
|
| 506 |
+
"هذا النظام للتوعية فقط وليس بديلاً عن التشخيص الطبي المتخصص.</p>",
|
| 507 |
+
unsafe_allow_html=True,
|
| 508 |
+
)
|
check_db.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from sqlalchemy import create_engine, Column, Integer, String, DateTime
|
| 3 |
+
from sqlalchemy.orm import declarative_base, sessionmaker
|
| 4 |
+
|
| 5 |
+
DATABASE_URL = "postgresql://safespace:AdminAdmin@postgresql-208383-0.cloudclusters.net:19712/safespace"
|
| 6 |
+
|
| 7 |
+
engine = create_engine(DATABASE_URL)
|
| 8 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 9 |
+
Base = declarative_base()
|
| 10 |
+
|
| 11 |
+
class DBUser(Base):
|
| 12 |
+
__tablename__ = "users"
|
| 13 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 14 |
+
name = Column(String, nullable=True)
|
| 15 |
+
email = Column(String, unique=True, index=True)
|
| 16 |
+
password = Column(String)
|
| 17 |
+
|
| 18 |
+
db = SessionLocal()
|
| 19 |
+
users = db.query(DBUser).all()
|
| 20 |
+
print(f"Total users: {len(users)}")
|
| 21 |
+
for u in users:
|
| 22 |
+
print(f"ID: {u.id}, Email: {u.email}, Password Hash: {u.password}")
|
| 23 |
+
db.close()
|
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/History
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:18bb7fac167630752b1cfa38fdbd37883c97701d67639db61141b814498b3388
|
| 3 |
+
size 163840
|
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/Web Data
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3743dd646541935eebc92ec44dc065a71a5beefd8165facae2a5a73eeb6ae656
|
| 3 |
+
size 157696
|
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/shared_proto_db/000003.log
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:224b7af78f931fbc5e380e4970816885ce968e38da0eb46f510b4a661f7b55db
|
| 3 |
+
size 111699
|
clean_git_repo/UI/safespace/.dart_tool/chrome-device/Default/trusted_vault.pb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b87bf305aafae1f1ec7dd947176c9529b2a9cbd4a3bf296fce25fc009e725366
|
| 3 |
+
size 84
|
clean_git_repo/UI/safespace/build/8730b13ca0249799964d0a71255a8f3b.cache.dill.track.dill
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ae0b1b0e267ea854918ca7afbf3c5be6a073deae0894ad6ad67ffd9f78b46c7a
|
| 3 |
+
size 41863640
|
clean_git_repo/UI/safespace/build/flutter_assets/AssetManifest.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:00af55ad3d6f21898fe77e0ff092d1a1cda52c941b6860e9928d45c8af8c095d
|
| 3 |
+
size 117
|
clean_git_repo/UI/safespace/build/flutter_assets/fonts/MaterialIcons-Regular.otf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d9865b671a09d683d13a863089d8825e0f61a37696ce5d7d448bc8023aa62453
|
| 3 |
+
size 1645184
|
clean_git_repo/UI/safespace/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:67c44fe9183b002e79dde7f6977e2988661c9a3e4a3c5fce968787efdbed823c
|
| 3 |
+
size 257628
|
clean_git_repo/UI/safespace/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
ADDED
|
|
Git LFS Details
|
clean_git_repo/data.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d6b39f44704ef71efab7fb6223ffaa68bc84af9c7c42875310c043242dac593d
|
| 3 |
+
size 20819959
|
clean_git_repo/mental_model.h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d83c61efe6d2ff698925835f970cce7d65026107d6adcbe8eb31b58ed1ac4325
|
| 3 |
+
size 259632
|
clean_git_repo/mental_xlmr_final/label_encoder.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7459c4d557907ee7efd7151ef3618a1a47b74750e839c56c50d450f6a2553c20
|
| 3 |
+
size 275
|
clean_git_repo/mental_xlmr_final/tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:542cda5f12d1c653dd46ac4d8c992d71e0047e65e7bf2cdda17c2d43b1d94b37
|
| 3 |
+
size 17781767
|
clean_git_repo/mental_xlmr_final/training_args.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d487b97c54aa8b5c1582f059ef3c809014c6d48eabd7a6c0be396fc96cf835f2
|
| 3 |
+
size 5201
|
clean_git_repo/model_weights.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c7f5e42ca4cda8a43af36c8b72b4ac1dfe500fbab6c7359d26c1eaa9048b37f7
|
| 3 |
+
size 68153
|