File size: 5,996 Bytes
d36a257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de9e92
d36a257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de9e92
d36a257
 
 
 
90d8a83
 
 
 
 
 
 
 
 
 
 
 
d36a257
 
 
90d8a83
8de9e92
d36a257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
document.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('inference-form');
    const fileInput = document.getElementById('image-upload');
    const fileNameDisplay = document.getElementById('file-name');
    const submitBtn = document.getElementById('submit-btn');
    const dropZone = document.getElementById('drop-zone');
    const fileLabel = document.querySelector('.file-label');
    
    const imagePreviewContainer = document.getElementById('image-preview-container');
    const imagePreview = document.getElementById('image-preview');
    const clearImageBtn = document.getElementById('clear-image-btn');
    
    const loadingState = document.getElementById('loading-state');
    const resultsPanel = document.getElementById('results-panel');
    const errorPanel = document.getElementById('error-panel');
    
    const resultGender = document.getElementById('result-gender');
    const resultAge = document.getElementById('result-age');
    const resultRace = document.getElementById('result-race');
    const errorMessage = document.getElementById('error-message');

    // Drag and drop handlers
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        dropZone.addEventListener(eventName, preventDefaults, false);
    });

    function preventDefaults(e) {
        e.preventDefault();
        e.stopPropagation();
    }

    ['dragenter', 'dragover'].forEach(eventName => {
        dropZone.addEventListener(eventName, () => {
            fileLabel.classList.add('dragover');
        }, false);
    });

    ['dragleave', 'drop'].forEach(eventName => {
        dropZone.addEventListener(eventName, () => {
            fileLabel.classList.remove('dragover');
        }, false);
    });

    dropZone.addEventListener('drop', (e) => {
        const dt = e.dataTransfer;
        const files = dt.files;
        
        if (files && files.length > 0) {
            fileInput.files = files;
            handleFileSelection(files[0]);
        }
    }, false);

    // Handle file selection via click
    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        handleFileSelection(file);
    });

    function handleFileSelection(file) {
        if (file && file.type.startsWith('image/')) {
            // Update text
            fileNameDisplay.textContent = file.name;
            fileNameDisplay.style.color = 'var(--text-primary)';
            submitBtn.disabled = false;
            
            // Generate preview
            const reader = new FileReader();
            reader.onload = (e) => {
                imagePreview.src = e.target.result;
                dropZone.classList.add('hidden');
                imagePreviewContainer.classList.remove('hidden');
            };
            reader.readAsDataURL(file);
        } else {
            resetFileState();
        }
        
        // Reset state panels when a new file is chosen
        hideAllPanels();
    }

    // Clear image
    clearImageBtn.addEventListener('click', () => {
        resetFileState();
        hideAllPanels();
    });

    function resetFileState() {
        fileInput.value = '';
        fileNameDisplay.textContent = 'Click or drag an image here';
        fileNameDisplay.style.color = 'var(--text-secondary)';
        submitBtn.disabled = true;
        
        imagePreview.src = '';
        imagePreviewContainer.classList.add('hidden');
        dropZone.classList.remove('hidden');
    }

    // Handle form submission
    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        
        const file = fileInput.files[0];
        if (!file) return;

        // Update UI state
        submitBtn.disabled = true;
        hideAllPanels();
        loadingState.classList.remove('hidden');

        // Prepare data
        const formData = new FormData();
        formData.append('file', file);

        try {
            const response = await fetch('/predict', {
                method: 'POST',
                body: formData
            });

            if (!response.ok) {
                throw new Error(`Server responded with status: ${response.status}`);
            }

            const data = await response.json();
            
            // Validate response format
            if (!('gender' in data) || !('age' in data) || !('race' in data)) {
                throw new Error('Invalid response format from server');
            }

            // Update results UI
            // Show gender with inline confidence like "Male (87%)"
            if (data.gender) {
                if (data.gender_confidence !== undefined) {
                    const pct = Math.round(data.gender_confidence * 100);
                    resultGender.textContent = `${data.gender} (${pct}%)`;
                } else {
                    resultGender.textContent = data.gender;
                }
            } else {
                resultGender.textContent = '-';
            }

            // Format age (round if it's a number, otherwise just display string)
            const formattedAge = typeof data.age === 'number' ? Math.round(data.age) : data.age;
            resultAge.textContent = formattedAge;

            resultRace.textContent = data.race || '-';

            // Show results
            loadingState.classList.add('hidden');
            resultsPanel.classList.remove('hidden');

        } catch (error) {
            console.error('Inference error:', error);
            
            // Show error UI
            errorMessage.textContent = error.message || 'An error occurred during inference.';
            loadingState.classList.add('hidden');
            errorPanel.classList.remove('hidden');
        } finally {
            submitBtn.disabled = false;
        }
    });

    function hideAllPanels() {
        loadingState.classList.add('hidden');
        resultsPanel.classList.add('hidden');
        errorPanel.classList.add('hidden');
    }
});