pltnhan07 commited on
Commit
96794d4
·
1 Parent(s): 5b131a2
.firebase/hosting.cHVibGlj.cache ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ index.html,1741591499789,a164687b08f5e91f6befe297edcde59d6005c1d8ff9a124bba47226efd0adf1c
2
+ 404.html,1741591499640,762bf484ba67404bd1a3b181546ea28d60dfddf18e9dd4795d8d25bcf3c1a890
.firebaserc ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "projects": {
3
+ "default": "skincancerscreening-a9c4d"
4
+ }
5
+ }
.gcloudignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file specifies files that are *not* uploaded to Google Cloud
2
+ # using gcloud. It follows the same syntax as .gitignore, with the addition of
3
+ # "#!include" directives (which insert the entries of the given .gitignore-style
4
+ # file at that point).
5
+ #
6
+ # For more information, run:
7
+ # $ gcloud topic gcloudignore
8
+ #
9
+ .gcloudignore
10
+ # If you would like to upload your .git directory, .gitignore file or files
11
+ # from your .gitignore file, remove the corresponding line
12
+ # below:
13
+ .git
14
+ .gitignore
15
+
16
+ node_modules
17
+ #!include:.gitignore
__pycache__/main.cpython-39.pyc ADDED
Binary file (10.7 kB). View file
 
firebase-debug.log ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [debug] [2025-03-10T08:17:30.321Z] ----------------------------------------------------------------------
2
+ [debug] [2025-03-10T08:17:30.325Z] Command: C:\Program Files\nodejs\node.exe C:\Users\phaml\AppData\Roaming\npm\node_modules\firebase-tools\lib\bin\firebase.js serve --only hosting
3
+ [debug] [2025-03-10T08:17:30.325Z] CLI Version: 13.33.0
4
+ [debug] [2025-03-10T08:17:30.325Z] Platform: win32
5
+ [debug] [2025-03-10T08:17:30.326Z] Node Version: v20.17.0
6
+ [debug] [2025-03-10T08:17:30.326Z] Time: Mon Mar 10 2025 15:17:30 GMT+0700 (Indochina Time)
7
+ [debug] [2025-03-10T08:17:30.326Z] ----------------------------------------------------------------------
8
+ [debug]
9
+ [debug] [2025-03-10T08:17:30.987Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
10
+ [debug] [2025-03-10T08:17:30.987Z] > authorizing via signed-in user (phamlethanhnhan0707@gmail.com)
11
+ [debug] [2025-03-10T08:17:30.987Z] [iam] checking project skincancerscreening-a9c4d for permissions ["firebase.projects.get"]
12
+ [debug] [2025-03-10T08:17:30.989Z] Checked if tokens are valid: false, expires at: 1741594954977
13
+ [debug] [2025-03-10T08:17:30.989Z] Checked if tokens are valid: false, expires at: 1741594954977
14
+ [debug] [2025-03-10T08:17:30.989Z] > refreshing access token with scopes: []
15
+ [debug] [2025-03-10T08:17:30.992Z] >>> [apiv2][query] POST https://www.googleapis.com/oauth2/v3/token [none]
16
+ [debug] [2025-03-10T08:17:30.992Z] >>> [apiv2][body] POST https://www.googleapis.com/oauth2/v3/token [omitted]
17
+ [debug] [2025-03-10T08:17:31.169Z] <<< [apiv2][status] POST https://www.googleapis.com/oauth2/v3/token 200
18
+ [debug] [2025-03-10T08:17:31.170Z] <<< [apiv2][body] POST https://www.googleapis.com/oauth2/v3/token [omitted]
19
+ [debug] [2025-03-10T08:17:31.222Z] >>> [apiv2][query] POST https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d:testIamPermissions [none]
20
+ [debug] [2025-03-10T08:17:31.222Z] >>> [apiv2][(partial)header] POST https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d:testIamPermissions x-goog-quota-user=projects/skincancerscreening-a9c4d
21
+ [debug] [2025-03-10T08:17:31.223Z] >>> [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d:testIamPermissions {"permissions":["firebase.projects.get"]}
22
+ [debug] [2025-03-10T08:17:32.345Z] <<< [apiv2][status] POST https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d:testIamPermissions 200
23
+ [debug] [2025-03-10T08:17:32.346Z] <<< [apiv2][body] POST https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d:testIamPermissions {"permissions":["firebase.projects.get"]}
24
+ [debug] [2025-03-10T08:17:32.347Z] Checked if tokens are valid: true, expires at: 1741598250170
25
+ [debug] [2025-03-10T08:17:32.347Z] Checked if tokens are valid: true, expires at: 1741598250170
26
+ [debug] [2025-03-10T08:17:32.347Z] >>> [apiv2][query] GET https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d [none]
27
+ [debug] [2025-03-10T08:17:32.609Z] <<< [apiv2][status] GET https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d 200
28
+ [debug] [2025-03-10T08:17:32.609Z] <<< [apiv2][body] GET https://cloudresourcemanager.googleapis.com/v1/projects/skincancerscreening-a9c4d {"projectNumber":"830343088745","projectId":"skincancerscreening-a9c4d","lifecycleState":"ACTIVE","name":"skincancerscreening","labels":{"firebase":"enabled","firebase-core":"disabled"},"createTime":"2025-03-10T07:20:38.909685Z"}
29
+ [debug] [2025-03-10T08:17:32.612Z] Checked if tokens are valid: true, expires at: 1741598250170
30
+ [debug] [2025-03-10T08:17:32.612Z] Checked if tokens are valid: true, expires at: 1741598250170
31
+ [debug] [2025-03-10T08:17:32.613Z] >>> [apiv2][query] GET https://firebasehosting.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/sites
32
+ [debug] [2025-03-10T08:17:33.344Z] <<< [apiv2][status] GET https://firebasehosting.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/sites 200
33
+ [debug] [2025-03-10T08:17:33.345Z] <<< [apiv2][body] GET https://firebasehosting.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/sites {"sites":[{"name":"projects/skincancerscreening-a9c4d/sites/skincancerscreening-a9c4d","defaultUrl":"https://skincancerscreening-a9c4d.web.app","type":"DEFAULT_SITE"}]}
34
+ [debug] [2025-03-10T08:17:33.345Z] Checked if tokens are valid: true, expires at: 1741598250170
35
+ [debug] [2025-03-10T08:17:33.346Z] Checked if tokens are valid: true, expires at: 1741598250170
36
+ [debug] [2025-03-10T08:17:33.346Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/webApps/-/config [none]
37
+ [debug] [2025-03-10T08:17:33.869Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/webApps/-/config 200
38
+ [debug] [2025-03-10T08:17:33.869Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d/webApps/-/config {"projectId":"skincancerscreening-a9c4d","storageBucket":"skincancerscreening-a9c4d.firebasestorage.app","apiKey":"AIzaSyBmT9PuKj5MnWE5mD1ivG4e09ENfb_ZObM","authDomain":"skincancerscreening-a9c4d.firebaseapp.com","messagingSenderId":"830343088745"}
39
+ [debug] [2025-03-10T08:17:33.909Z] Checked if tokens are valid: true, expires at: 1741598250170
40
+ [debug] [2025-03-10T08:17:33.909Z] Checked if tokens are valid: true, expires at: 1741598250170
41
+ [debug] [2025-03-10T08:17:33.910Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d [none]
42
+ [debug] [2025-03-10T08:17:35.294Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d 200
43
+ [debug] [2025-03-10T08:17:35.294Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/skincancerscreening-a9c4d {"projectId":"skincancerscreening-a9c4d","projectNumber":"830343088745","displayName":"skincancerscreening","name":"projects/skincancerscreening-a9c4d","resources":{"hostingSite":"skincancerscreening-a9c4d"},"state":"ACTIVE","etag":"1_b7dc9c78-635e-464c-a390-955983ce6ca1"}
44
+ [info] i hosting[skincancerscreening-a9c4d]: Serving hosting files from: public {"metadata":{"emulator":{"name":"hosting"},"message":"Serving hosting files from: \u001b[1mpublic\u001b[22m"}}
45
+ [info] + hosting[skincancerscreening-a9c4d]: Local server: http://localhost:5000 {"metadata":{"emulator":{"name":"hosting"},"message":"Local server: \u001b[4m\u001b[1mhttp://localhost:5000\u001b[22m\u001b[24m"}}
46
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET / HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
47
+ [debug] [2025-03-10T08:18:29.064Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-app-compat.js [none]
48
+ [debug] [2025-03-10T08:18:29.073Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-auth-compat.js [none]
49
+ [debug] [2025-03-10T08:18:29.148Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-database-compat.js [none]
50
+ [debug] [2025-03-10T08:18:29.155Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-firestore-compat.js [none]
51
+ [debug] [2025-03-10T08:18:29.161Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-functions-compat.js [none]
52
+ [debug] [2025-03-10T08:18:29.166Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-messaging-compat.js [none]
53
+ [debug] [2025-03-10T08:18:29.244Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-app-compat.js 200
54
+ [debug] [2025-03-10T08:18:29.244Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-app-compat.js [stream]
55
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-app-compat.js HTTP/1.1" 200 10177 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-app-compat.js HTTP/1.1\" 200 10177 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
56
+ [debug] [2025-03-10T08:18:29.261Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-auth-compat.js 200
57
+ [debug] [2025-03-10T08:18:29.261Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-auth-compat.js [stream]
58
+ [debug] [2025-03-10T08:18:29.267Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-storage-compat.js [none]
59
+ [debug] [2025-03-10T08:18:29.287Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-database-compat.js 200
60
+ [debug] [2025-03-10T08:18:29.287Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-database-compat.js [stream]
61
+ [debug] [2025-03-10T08:18:29.293Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-messaging-compat.js 200
62
+ [debug] [2025-03-10T08:18:29.293Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-messaging-compat.js [stream]
63
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-messaging-compat.js HTTP/1.1" 200 9968 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-messaging-compat.js HTTP/1.1\" 200 9968 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
64
+ [debug] [2025-03-10T08:18:29.303Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-analytics-compat.js [none]
65
+ [debug] [2025-03-10T08:18:29.304Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-functions-compat.js 200
66
+ [debug] [2025-03-10T08:18:29.304Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-functions-compat.js [stream]
67
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-functions-compat.js HTTP/1.1" 200 3974 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-functions-compat.js HTTP/1.1\" 200 3974 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
68
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-auth-compat.js HTTP/1.1" 200 40259 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-auth-compat.js HTTP/1.1\" 200 40259 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
69
+ [debug] [2025-03-10T08:18:29.311Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-storage-compat.js 200
70
+ [debug] [2025-03-10T08:18:29.311Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-storage-compat.js [stream]
71
+ [debug] [2025-03-10T08:18:29.315Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-remote-config-compat.js [none]
72
+ [debug] [2025-03-10T08:18:29.319Z] >>> [apiv2][query] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-performance-compat.js [none]
73
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-storage-compat.js HTTP/1.1" 200 13023 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-storage-compat.js HTTP/1.1\" 200 13023 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
74
+ [debug] [2025-03-10T08:18:29.324Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-firestore-compat.js 200
75
+ [debug] [2025-03-10T08:18:29.324Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-firestore-compat.js [stream]
76
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/init.js?useEmulator=true HTTP/1.1" 200 - "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/init.js?useEmulator=true HTTP/1.1\" 200 - \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
77
+ [debug] [2025-03-10T08:18:29.340Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-analytics-compat.js 200
78
+ [debug] [2025-03-10T08:18:29.340Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-analytics-compat.js [stream]
79
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-analytics-compat.js HTTP/1.1" 200 9132 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-analytics-compat.js HTTP/1.1\" 200 9132 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
80
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-database-compat.js HTTP/1.1" 200 48150 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-database-compat.js HTTP/1.1\" 200 48150 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
81
+ [debug] [2025-03-10T08:18:29.359Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-remote-config-compat.js 200
82
+ [debug] [2025-03-10T08:18:29.359Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-remote-config-compat.js [stream]
83
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-remote-config-compat.js HTTP/1.1" 200 9172 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-remote-config-compat.js HTTP/1.1\" 200 9172 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
84
+ [debug] [2025-03-10T08:18:29.366Z] <<< [apiv2][status] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-performance-compat.js 200
85
+ [debug] [2025-03-10T08:18:29.366Z] <<< [apiv2][body] GET https://www.gstatic.com/firebasejs/11.4.0/firebase-performance-compat.js [stream]
86
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-performance-compat.js HTTP/1.1" 200 13495 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-performance-compat.js HTTP/1.1\" 200 13495 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
87
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /__/firebase/11.4.0/firebase-firestore-compat.js HTTP/1.1" 200 102688 "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /__/firebase/11.4.0/firebase-firestore-compat.js HTTP/1.1\" 200 102688 \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
88
+ [info] i hosting: ::1 - - [10/Mar/2025:08:18:29 +0000] "GET /favicon.ico HTTP/1.1" 404 - "http://localhost:5000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:18:29 +0000] \"GET /favicon.ico HTTP/1.1\" 404 - \"http://localhost:5000/\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
89
+ [info] i hosting: ::1 - - [10/Mar/2025:08:22:07 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:22:07 +0000] \"GET / HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
90
+ [info] i hosting: ::1 - - [10/Mar/2025:08:25:59 +0000] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36" {"metadata":{"emulator":{"name":"hosting"},"message":"::1 - - [10/Mar/2025:08:25:59 +0000] \"GET / HTTP/1.1\" 200 - \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\""}}
firebase.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "hosting": {
3
+ "public": "public",
4
+ "ignore": [
5
+ "firebase.json",
6
+ "**/.*",
7
+ "**/node_modules/**"
8
+ ]
9
+ }
10
+ }
main.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from torchvision import transforms, models
7
+ from PIL import Image
8
+ import cv2
9
+ import numpy as np
10
+ from flask import jsonify, request
11
+ import functions_framework
12
+
13
+ # Set device (Cloud Functions generally use CPU, but GPU will be used if available)
14
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
15
+
16
+ ###############################################################################
17
+ # Utility Functions for Image Preprocessing (Optional)
18
+ ###############################################################################
19
+ def remove_hair_and_markings(img_bgr, kernel_size=17):
20
+ """Remove thin hair and markings using morphological operations."""
21
+ gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
22
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
23
+ blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel)
24
+ _, mask = cv2.threshold(blackhat, 10, 255, cv2.THRESH_BINARY)
25
+ inpainted = cv2.inpaint(img_bgr, mask, 3, cv2.INPAINT_TELEA)
26
+ return inpainted
27
+
28
+ def normalize_color(img_bgr):
29
+ """Per-channel normalization: subtract mean and divide by std for each channel."""
30
+ img_float = img_bgr.astype(np.float32)
31
+ b, g, r = cv2.split(img_float)
32
+ for channel in (b, g, r):
33
+ mean_val = np.mean(channel)
34
+ std_val = np.std(channel) + 1e-8
35
+ channel[:] = (channel - mean_val) / std_val
36
+ normalized = cv2.merge([b, g, r])
37
+ return normalized
38
+
39
+ def mmwf_filter(gray_image, window_size=3, noise_variance=0.01):
40
+ """Apply the Median–Modified Wiener Filter (MMWF) on a grayscale image."""
41
+ if gray_image.dtype != np.float32:
42
+ gray_image = gray_image.astype(np.float32)
43
+ pad_size = window_size // 2
44
+ padded = np.pad(gray_image, pad_size, mode='reflect')
45
+ filtered = np.zeros_like(gray_image, dtype=np.float32)
46
+ rows, cols = gray_image.shape
47
+ for i in range(rows):
48
+ for j in range(cols):
49
+ local_patch = padded[i:i+window_size, j:j+window_size]
50
+ mu_m = np.median(local_patch)
51
+ sigma_sq = local_patch.var()
52
+ a_val = gray_image[i, j]
53
+ if sigma_sq < 1e-12:
54
+ filtered[i, j] = mu_m
55
+ else:
56
+ filtered[i, j] = mu_m + ((sigma_sq - noise_variance) / sigma_sq) * (a_val - mu_m)
57
+ return filtered
58
+
59
+ def mmwf_filter_color(img_bgr, window_size=3, noise_variance=0.01):
60
+ """Apply MMWF to each channel of a BGR image."""
61
+ if img_bgr.dtype != np.float32:
62
+ img_bgr = img_bgr.astype(np.float32)
63
+ b, g, r = cv2.split(img_bgr)
64
+ b_denoised = mmwf_filter(b, window_size, noise_variance)
65
+ g_denoised = mmwf_filter(g, window_size, noise_variance)
66
+ r_denoised = mmwf_filter(r, window_size, noise_variance)
67
+ denoised_bgr = cv2.merge([b_denoised, g_denoised, r_denoised])
68
+ return denoised_bgr
69
+
70
+ def preprocess_image(image):
71
+ """
72
+ Optionally, apply preprocessing steps:
73
+ 1. Convert from PIL RGB to NumPy BGR.
74
+ 2. Remove hair/markings.
75
+ 3. Normalize color.
76
+ 4. Apply MMWF filter.
77
+ 5. Convert back to PIL RGB.
78
+ """
79
+ image_np = np.array(image)
80
+ image_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
81
+ image_bgr = remove_hair_and_markings(image_bgr, kernel_size=17)
82
+ image_bgr = normalize_color(image_bgr)
83
+ image_bgr = cv2.normalize(image_bgr, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)
84
+ # image_bgr = mmwf_filter_color(image_bgr, window_size=3, noise_variance=0.01)
85
+ image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
86
+ return Image.fromarray(image_rgb)
87
+
88
+ ###############################################################################
89
+ # Helper Modules for the Classifier
90
+ ###############################################################################
91
+ def create_classification_head(input_dim, num_classes):
92
+ return nn.Sequential(
93
+ nn.Linear(input_dim, 512),
94
+ nn.ReLU(),
95
+ nn.Dropout(0.5),
96
+ nn.Linear(512, num_classes)
97
+ )
98
+
99
+ class MetadataEncoder(nn.Module):
100
+ def __init__(self, input_size, output_size):
101
+ super(MetadataEncoder, self).__init__()
102
+ self.encoder = nn.Sequential(
103
+ nn.Conv1d(1, 16, kernel_size=3, padding=1),
104
+ nn.BatchNorm1d(16),
105
+ nn.ReLU(),
106
+ nn.Conv1d(16, 32, kernel_size=3, padding=1),
107
+ nn.BatchNorm1d(32),
108
+ nn.ReLU(),
109
+ nn.Flatten(),
110
+ nn.Linear(32 * input_size, output_size),
111
+ nn.ReLU()
112
+ )
113
+ def forward(self, x):
114
+ x = x.unsqueeze(1)
115
+ return self.encoder(x)
116
+
117
+ class GraphAttentionLayer(nn.Module):
118
+ def __init__(self, in_features, out_features, dropout=0.0, alpha=0.2):
119
+ super(GraphAttentionLayer, self).__init__()
120
+ self.W = nn.Linear(in_features, out_features, bias=False)
121
+ self.a = nn.Parameter(torch.empty(2 * out_features, 1))
122
+ nn.init.xavier_uniform_(self.W.weight.data, gain=1.414)
123
+ nn.init.xavier_uniform_(self.a.data, gain=1.414)
124
+ self.leakyrelu = nn.LeakyReLU(alpha)
125
+ self.dropout = nn.Dropout(dropout)
126
+ def forward(self, h, adj=None):
127
+ Wh = self.W(h)
128
+ batch_size, N, _ = Wh.size()
129
+ Wh_i = Wh.unsqueeze(2).repeat(1, 1, N, 1)
130
+ Wh_j = Wh.unsqueeze(1).repeat(1, N, 1, 1)
131
+ e = self.leakyrelu(torch.matmul(torch.cat([Wh_i, Wh_j], dim=-1), self.a).squeeze(-1))
132
+ attention = F.softmax(e, dim=-1)
133
+ attention = self.dropout(attention)
134
+ h_prime = torch.matmul(attention, Wh)
135
+ return h_prime, attention
136
+
137
+ class MultiHeadGraphAttentionLayer(nn.Module):
138
+ def __init__(self, in_features, out_features, num_heads=4, dropout=0.0, alpha=0.2):
139
+ super(MultiHeadGraphAttentionLayer, self).__init__()
140
+ self.num_heads = num_heads
141
+ self.heads = nn.ModuleList([
142
+ GraphAttentionLayer(in_features, out_features, dropout, alpha)
143
+ for _ in range(num_heads)
144
+ ])
145
+ self.linear = nn.Linear(num_heads * out_features, out_features)
146
+ def forward(self, h):
147
+ head_outputs = [head(h)[0] for head in self.heads]
148
+ h_concat = torch.cat(head_outputs, dim=-1)
149
+ return self.linear(h_concat)
150
+
151
+ class EnhancedGraphFusion(nn.Module):
152
+ def __init__(self, in_features, out_features, num_heads=4, num_layers=2, dropout=0.0, alpha=0.2):
153
+ super(EnhancedGraphFusion, self).__init__()
154
+ self.global_init = nn.Parameter(torch.zeros(in_features))
155
+ self.layers = nn.ModuleList([
156
+ MultiHeadGraphAttentionLayer(in_features, in_features, num_heads, dropout, alpha)
157
+ for _ in range(num_layers)
158
+ ])
159
+ self.norm = nn.LayerNorm(in_features)
160
+ self.proj = nn.Linear(in_features, out_features)
161
+ def forward(self, image_nodes, metadata_feat):
162
+ batch_size = image_nodes.size(0)
163
+ metadata_node = metadata_feat.unsqueeze(1)
164
+ global_node = self.global_init.unsqueeze(0).expand(batch_size, -1).unsqueeze(1)
165
+ h = torch.cat([image_nodes, metadata_node, global_node], dim=1)
166
+ for layer in self.layers:
167
+ residual = h
168
+ h = layer(h)
169
+ h = F.elu(h)
170
+ h = self.norm(h + residual)
171
+ fused = h[:, -1, :]
172
+ return self.proj(fused)
173
+
174
+ ###############################################################################
175
+ # DenseNet201-based Classifier for Skin Lesion Classification
176
+ ###############################################################################
177
+ class DenseNet201Classifier(nn.Module):
178
+ def __init__(self, num_classes=6, metadata_input_size=3, metadata_output_size=768):
179
+ super(DenseNet201Classifier, self).__init__()
180
+ self.densenet = models.densenet201(pretrained=True)
181
+ self.num_features = self.densenet.classifier.in_features
182
+ self.img_proj = nn.Linear(self.num_features, metadata_output_size)
183
+ self.metadata_encoder = MetadataEncoder(metadata_input_size, metadata_output_size)
184
+ self.enhanced_graph_fusion = EnhancedGraphFusion(
185
+ in_features=metadata_output_size,
186
+ out_features=metadata_output_size,
187
+ num_heads=4,
188
+ num_layers=2,
189
+ dropout=0.1,
190
+ alpha=0.2
191
+ )
192
+ self.head = create_classification_head(metadata_output_size, num_classes)
193
+ def forward_feature_map(self, x):
194
+ features = self.densenet.features(x)
195
+ return F.relu(features, inplace=True)
196
+ def forward(self, x, metadata):
197
+ fmap = self.forward_feature_map(x)
198
+ batch_size, C, H, W = fmap.shape
199
+ image_nodes = fmap.view(batch_size, C, H * W).transpose(1, 2)
200
+ image_nodes = self.img_proj(image_nodes)
201
+ metadata_features = self.metadata_encoder(metadata)
202
+ fused_features = self.enhanced_graph_fusion(image_nodes, metadata_features)
203
+ return self.head(fused_features)
204
+
205
+ ###############################################################################
206
+ # Inference Pipeline Setup
207
+ ###############################################################################
208
+ # Define image transformation for inference
209
+ transform = transforms.Compose([
210
+ transforms.Resize((224, 224)),
211
+ transforms.ToTensor(),
212
+ transforms.Normalize(mean=[0.485, 0.456, 0.406],
213
+ std=[0.229, 0.224, 0.225])
214
+ ])
215
+
216
+ def load_model(model_path='pd4_model.pth'):
217
+ """Load the pre-trained DenseNet201Classifier model."""
218
+ num_classes = 6
219
+ metadata_input_size = 3 # Expecting: age, gender, skin_cancer_history
220
+ model = DenseNet201Classifier(num_classes=num_classes,
221
+ metadata_input_size=metadata_input_size,
222
+ metadata_output_size=768)
223
+ state_dict = torch.load(model_path, map_location='cpu')
224
+ model.load_state_dict(state_dict)
225
+ model.to(device)
226
+ model.eval()
227
+ return model
228
+
229
+ # Load the model once at function startup
230
+ model = load_model()
231
+
232
+ ###############################################################################
233
+ # Cloud Function HTTP Endpoint (Using functions_framework)
234
+ ###############################################################################
235
+ @functions_framework.http
236
+ def predict(request):
237
+ """
238
+ HTTP-triggered Cloud Function that accepts a POST request with an image file.
239
+ Optionally, you can uncomment the preprocessing step if needed.
240
+ Returns the predicted class and confidence as JSON.
241
+ """
242
+ if request.method != 'POST':
243
+ return jsonify({'error': 'Only POST method is supported.'}), 405
244
+
245
+ if 'file' not in request.files:
246
+ return jsonify({'error': 'No file provided.'}), 400
247
+
248
+ file = request.files['file']
249
+ try:
250
+ image = Image.open(file.stream).convert('RGB')
251
+ except Exception as e:
252
+ return jsonify({'error': 'Invalid image file.'}), 400
253
+
254
+ # Optionally apply advanced preprocessing:
255
+ image = preprocess_image(image)
256
+
257
+ image_tensor = transform(image).unsqueeze(0).to(device)
258
+ # Use default metadata values (e.g., zeros for [age, gender, skin_cancer_history])
259
+ default_metadata = torch.zeros((1, 3), dtype=torch.float).to(device)
260
+
261
+ with torch.no_grad():
262
+ outputs = model(image_tensor, default_metadata)
263
+ probabilities = F.softmax(outputs, dim=1)
264
+ confidence, predicted = torch.max(probabilities, dim=1)
265
+
266
+ result = {
267
+ 'predicted_class': predicted.item(),
268
+ 'confidence': confidence.item()
269
+ }
270
+ return jsonify(result)
271
+
272
+ if __name__ == '__main__':
273
+ import os
274
+ port = int(os.environ.get("PORT", 8080))
275
+ # The functions_framework creates a Flask app that exposes your target function.
276
+ from functions_framework import create_app
277
+ app = create_app(target="predict")
278
+ app.run(host="0.0.0.0", port=port, debug=True)
package-lock.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "name": "skincancerscreening",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {}
6
+ }
pd4_model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cd2aacb12d67299548ad2905a9f11f7bf4ae1d1a4a41b37aa06169a1e9f8820c
3
+ size 129438302
public/404.html ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Page Not Found</title>
7
+
8
+ <style media="screen">
9
+ body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
10
+ #message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
11
+ #message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
12
+ #message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
13
+ #message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
14
+ #message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
15
+ #message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
16
+ #message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
17
+ #load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
18
+ @media (max-width: 600px) {
19
+ body, #message { margin-top: 0; background: white; box-shadow: none; }
20
+ body { border-top: 16px solid #ffa100; }
21
+ }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <div id="message">
26
+ <h2>404</h2>
27
+ <h1>Page Not Found</h1>
28
+ <p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
29
+ <h3>Why am I seeing this?</h3>
30
+ <p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
31
+ </div>
32
+ </body>
33
+ </html>
public/index.html ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Skin Lesion Screening</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <style>
7
+ body {
8
+ font-family: Arial, sans-serif;
9
+ margin: 40px;
10
+ background-color: #f9f9f9;
11
+ }
12
+ .container {
13
+ max-width: 500px;
14
+ margin: auto;
15
+ background: #fff;
16
+ padding: 20px;
17
+ border-radius: 8px;
18
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
19
+ text-align: center;
20
+ }
21
+ input, select, button {
22
+ width: 100%;
23
+ padding: 10px;
24
+ margin: 10px 0;
25
+ font-size: 1em;
26
+ }
27
+ button {
28
+ background-color: #007BFF;
29
+ border: none;
30
+ color: white;
31
+ cursor: pointer;
32
+ border-radius: 4px;
33
+ }
34
+ button:hover {
35
+ background-color: #0056b3;
36
+ }
37
+ .result {
38
+ margin-top: 20px;
39
+ font-size: 1.2em;
40
+ color: #333;
41
+ white-space: pre-line;
42
+ }
43
+ </style>
44
+ </head>
45
+ <body>
46
+ <div class="container">
47
+ <h1>Skin Lesion Screening</h1>
48
+ <form id="upload-form">
49
+ <!-- Image File Input -->
50
+ <label for="file">Upload Image:</label>
51
+ <input type="file" id="file" accept="image/*" capture="camera" required>
52
+
53
+ <!-- Metadata: Age -->
54
+ <label for="age">Age:</label>
55
+ <input type="number" id="age" placeholder="Enter your age" required>
56
+
57
+ <!-- Metadata: Gender -->
58
+ <label for="gender">Gender:</label>
59
+ <select id="gender" required>
60
+ <option value="" disabled selected>Select your gender</option>
61
+ <option value="0">Male</option>
62
+ <option value="1">Female</option>
63
+ <option value="2">Other</option>
64
+ </select>
65
+
66
+ <!-- Metadata: Skin Cancer History -->
67
+ <label for="cancerHistory">History of Skin Cancer:</label>
68
+ <select id="cancerHistory" required>
69
+ <option value="" disabled selected>Do you have a history of skin cancer?</option>
70
+ <option value="0">No</option>
71
+ <option value="1">Yes</option>
72
+ </select>
73
+
74
+ <button type="submit">Analyze</button>
75
+ </form>
76
+ <div id="result" class="result"></div>
77
+ </div>
78
+ <script>
79
+ document.getElementById('upload-form').addEventListener('submit', async function(e) {
80
+ e.preventDefault();
81
+
82
+ // Get the inputs
83
+ const fileInput = document.getElementById('file');
84
+ const ageInput = document.getElementById('age');
85
+ const genderInput = document.getElementById('gender');
86
+ const cancerHistoryInput = document.getElementById('cancerHistory');
87
+
88
+ if (!fileInput.files.length) {
89
+ alert("Please select an image file.");
90
+ return;
91
+ }
92
+
93
+ // Create a FormData object and append file and metadata
94
+ const formData = new FormData();
95
+ formData.append('file', fileInput.files[0]);
96
+ formData.append('age', ageInput.value);
97
+ formData.append('gender', genderInput.value);
98
+ formData.append('cancer_history', cancerHistoryInput.value);
99
+
100
+ // Replace this URL with your actual Cloud Function endpoint URL
101
+ const functionUrl = 'https://us-central1-skincancerscreening.cloudfunctions.net/predict';
102
+
103
+ // Send POST request to the backend
104
+ try {
105
+ const response = await fetch(functionUrl, {
106
+ method: 'POST',
107
+ body: formData
108
+ });
109
+
110
+ if (!response.ok) {
111
+ throw new Error("Network response was not ok");
112
+ }
113
+
114
+ const data = await response.json();
115
+ document.getElementById('result').innerText =
116
+ 'Predicted Class: ' + data.predicted_class + '\nConfidence: ' + (data.confidence * 100).toFixed(2) + '%';
117
+ } catch (error) {
118
+ document.getElementById('result').innerText = 'Error: ' + error;
119
+ }
120
+ });
121
+ </script>
122
+ </body>
123
+ </html>
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ functions-framework==3.1.0
2
+ torch==1.13.1
3
+ torchvision==0.14.1
4
+ Pillow==9.4.0
5
+ opencv-python-headless==4.7.0.72