amirmadjour commited on
Commit
13a47ae
·
1 Parent(s): 81477fe
Files changed (4) hide show
  1. app.py +10 -39
  2. static/css/style.css +180 -0
  3. static/js/main.js +107 -0
  4. templates/index.html +57 -0
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from fastapi import FastAPI, UploadFile, File, HTTPException, Form
2
  from fastapi.responses import HTMLResponse, FileResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from typing import Optional
@@ -6,6 +6,7 @@ import os
6
  import tempfile
7
  from pathlib import Path
8
  from document_processor import process_document, supported_formats
 
9
  from transformers import pipeline
10
 
11
  app = FastAPI()
@@ -13,45 +14,15 @@ app = FastAPI()
13
  # Mount static files for CSS/JS if needed
14
  app.mount("/static", StaticFiles(directory="static"), name="static")
15
 
 
 
 
16
  @app.get("/", response_class=HTMLResponse)
17
- async def upload_form():
18
- return f"""
19
- <html>
20
- <head>
21
- <title>Document Translation Service</title>
22
- <style>
23
- body {{ font-family: Arial, sans-serif; max-width: 799px; margin: 0 auto; padding: 20px; }}
24
- .container {{ border: 1px dashed #ccc; padding: 20px; text-align: center; border-radius: 5px; }}
25
- .form-group {{ margin-bottom: 14px; }}
26
- select, button {{ padding: 7px 15px; font-size: 16px; }}
27
- .supported {{ margin-top: 19px; font-size: 14px; color: #666; }}
28
- </style>
29
- </head>
30
- <body>
31
- <h1>Document Translation Service</h1>
32
- <div class="container">
33
- <form action="/translate" method="post" enctype="multipart/form-data">
34
- <div class="form-group">
35
- <input type="file" name="file" required>
36
- </div>
37
- <div class="form-group">
38
- <select name="target_language">
39
- <option value="es">Spanish</option>
40
- <option value="fr">French</option>
41
- <option value="de">German</option>
42
- <option value="it">Italian</option>
43
- <option value="pt">Portuguese</option>
44
- </select>
45
- </div>
46
- <button type="submit">Translate Document</button>
47
- </form>
48
- </div>
49
- <div class="supported">
50
- <p>Supported formats: {', '.join(supported_formats())}</p>
51
- </div>
52
- </body>
53
- </html>
54
- """
55
 
56
  @app.post("/translate")
57
  async def translate_file(
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Request
2
  from fastapi.responses import HTMLResponse, FileResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from typing import Optional
 
6
  import tempfile
7
  from pathlib import Path
8
  from document_processor import process_document, supported_formats
9
+ from fastapi.templating import Jinja2Templates
10
  from transformers import pipeline
11
 
12
  app = FastAPI()
 
14
  # Mount static files for CSS/JS if needed
15
  app.mount("/static", StaticFiles(directory="static"), name="static")
16
 
17
+ # Setup templates
18
+ templates = Jinja2Templates(directory="templates")
19
+
20
  @app.get("/", response_class=HTMLResponse)
21
+ async def upload_form(request: Request):
22
+ return templates.TemplateResponse("index.html", {
23
+ "request": request,
24
+ "supported_formats": supported_formats()
25
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  @app.post("/translate")
28
  async def translate_file(
static/css/style.css ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #4361ee;
3
+ --secondary-color: #3f37c9;
4
+ --accent-color: #4cc9f0;
5
+ --light-color: #f8f9fa;
6
+ --dark-color: #212529;
7
+ --success-color: #4bb543;
8
+ }
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
19
+ min-height: 100vh;
20
+ display: flex;
21
+ justify-content: center;
22
+ align-items: center;
23
+ padding: 2rem;
24
+ }
25
+
26
+ .container {
27
+ max-width: 800px;
28
+ width: 100%;
29
+ background: white;
30
+ border-radius: 15px;
31
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
32
+ padding: 2.5rem;
33
+ animation: fadeIn 0.8s ease-out;
34
+ }
35
+
36
+ @keyframes fadeIn {
37
+ from { opacity: 0; transform: translateY(20px); }
38
+ to { opacity: 1; transform: translateY(0); }
39
+ }
40
+
41
+ h1 {
42
+ color: var(--primary-color);
43
+ text-align: center;
44
+ margin-bottom: 1.5rem;
45
+ font-weight: 600;
46
+ }
47
+
48
+ .upload-area {
49
+ border: 2px dashed var(--primary-color);
50
+ border-radius: 10px;
51
+ padding: 2rem;
52
+ text-align: center;
53
+ margin-bottom: 1.5rem;
54
+ transition: all 0.3s ease;
55
+ position: relative;
56
+ overflow: hidden;
57
+ }
58
+
59
+ .upload-area:hover {
60
+ border-color: var(--secondary-color);
61
+ transform: translateY(-3px);
62
+ }
63
+
64
+ .upload-area.active {
65
+ background-color: rgba(67, 97, 238, 0.05);
66
+ }
67
+
68
+ .file-input {
69
+ display: none;
70
+ }
71
+
72
+ .file-label {
73
+ display: block;
74
+ cursor: pointer;
75
+ padding: 1rem;
76
+ }
77
+
78
+ .file-label i {
79
+ font-size: 3rem;
80
+ color: var(--primary-color);
81
+ margin-bottom: 1rem;
82
+ display: block;
83
+ }
84
+
85
+ .file-label h3 {
86
+ color: var(--dark-color);
87
+ margin-bottom: 0.5rem;
88
+ }
89
+
90
+ .file-label p {
91
+ color: #6c757d;
92
+ font-size: 0.9rem;
93
+ }
94
+
95
+ .form-group {
96
+ margin-bottom: 1.5rem;
97
+ }
98
+
99
+ select {
100
+ width: 100%;
101
+ padding: 0.8rem 1rem;
102
+ border: 1px solid #ced4da;
103
+ border-radius: 8px;
104
+ font-size: 1rem;
105
+ color: var(--dark-color);
106
+ background-color: white;
107
+ transition: all 0.3s;
108
+ }
109
+
110
+ select:focus {
111
+ border-color: var(--primary-color);
112
+ outline: none;
113
+ box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2);
114
+ }
115
+
116
+ .btn {
117
+ background-color: var(--primary-color);
118
+ color: white;
119
+ border: none;
120
+ padding: 0.8rem 1.5rem;
121
+ border-radius: 8px;
122
+ font-size: 1rem;
123
+ cursor: pointer;
124
+ transition: all 0.3s;
125
+ width: 100%;
126
+ font-weight: 500;
127
+ }
128
+
129
+ .btn:hover {
130
+ background-color: var(--secondary-color);
131
+ transform: translateY(-2px);
132
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
133
+ }
134
+
135
+ .supported {
136
+ text-align: center;
137
+ margin-top: 1.5rem;
138
+ color: #6c757d;
139
+ font-size: 0.9rem;
140
+ animation: fadeIn 1s ease-out;
141
+ }
142
+
143
+ .progress-bar {
144
+ width: 0;
145
+ height: 4px;
146
+ background-color: var(--accent-color);
147
+ position: absolute;
148
+ bottom: 0;
149
+ left: 0;
150
+ transition: width 0.3s ease;
151
+ }
152
+
153
+ /* Loading animation */
154
+ .loading {
155
+ display: none;
156
+ text-align: center;
157
+ margin: 1rem 0;
158
+ }
159
+
160
+ .spinner {
161
+ width: 40px;
162
+ height: 40px;
163
+ margin: 0 auto;
164
+ border: 4px solid rgba(0, 0, 0, 0.1);
165
+ border-radius: 50%;
166
+ border-top-color: var(--primary-color);
167
+ animation: spin 1s ease-in-out infinite;
168
+ }
169
+
170
+ @keyframes spin {
171
+ to { transform: rotate(360deg); }
172
+ }
173
+
174
+ .success-message {
175
+ display: none;
176
+ text-align: center;
177
+ color: var(--success-color);
178
+ margin: 1rem 0;
179
+ animation: fadeIn 0.5s ease-out;
180
+ }
static/js/main.js ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ const form = document.querySelector('form');
3
+ const fileInput = document.getElementById('file-input');
4
+ const fileLabel = document.querySelector('.file-label');
5
+ const uploadArea = document.querySelector('.upload-area');
6
+ const fileName = document.getElementById('file-name');
7
+ const submitBtn = document.querySelector('.btn');
8
+ const loading = document.querySelector('.loading');
9
+ const successMessage = document.querySelector('.success-message');
10
+ const progressBar = document.querySelector('.progress-bar');
11
+
12
+ // File input change handler
13
+ fileInput.addEventListener('change', function(e) {
14
+ if (e.target.files.length) {
15
+ const file = e.target.files[0];
16
+ fileName.textContent = file.name;
17
+ uploadArea.classList.add('active');
18
+
19
+ // Animate progress bar
20
+ let progress = 0;
21
+ const interval = setInterval(() => {
22
+ progress += 10;
23
+ progressBar.style.width = `${progress}%`;
24
+ if (progress >= 100) clearInterval(interval);
25
+ }, 100);
26
+ }
27
+ });
28
+
29
+ // Drag and drop functionality
30
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
31
+ uploadArea.addEventListener(eventName, preventDefaults, false);
32
+ });
33
+
34
+ function preventDefaults(e) {
35
+ e.preventDefault();
36
+ e.stopPropagation();
37
+ }
38
+
39
+ ['dragenter', 'dragover'].forEach(eventName => {
40
+ uploadArea.addEventListener(eventName, highlight, false);
41
+ });
42
+
43
+ ['dragleave', 'drop'].forEach(eventName => {
44
+ uploadArea.addEventListener(eventName, unhighlight, false);
45
+ });
46
+
47
+ function highlight() {
48
+ uploadArea.classList.add('highlight');
49
+ }
50
+
51
+ function unhighlight() {
52
+ uploadArea.classList.remove('highlight');
53
+ }
54
+
55
+ uploadArea.addEventListener('drop', handleDrop, false);
56
+
57
+ function handleDrop(e) {
58
+ const dt = e.dataTransfer;
59
+ const files = dt.files;
60
+ fileInput.files = files;
61
+ const event = new Event('change');
62
+ fileInput.dispatchEvent(event);
63
+ }
64
+
65
+ // Form submission
66
+ form.addEventListener('submit', function(e) {
67
+ e.preventDefault();
68
+
69
+ if (!fileInput.files.length) return;
70
+
71
+ loading.style.display = 'block';
72
+ submitBtn.disabled = true;
73
+
74
+ // Simulate processing delay (remove in production)
75
+ setTimeout(() => {
76
+ const formData = new FormData(form);
77
+
78
+ fetch('/translate', {
79
+ method: 'POST',
80
+ body: formData
81
+ })
82
+ .then(response => {
83
+ if (!response.ok) throw new Error('Translation failed');
84
+ return response.blob();
85
+ })
86
+ .then(blob => {
87
+ const url = window.URL.createObjectURL(blob);
88
+ const a = document.createElement('a');
89
+ a.href = url;
90
+ a.download = `translated_${fileInput.files[0].name.split('.')[0]}.txt`;
91
+ document.body.appendChild(a);
92
+ a.click();
93
+ window.URL.revokeObjectURL(url);
94
+
95
+ loading.style.display = 'none';
96
+ successMessage.style.display = 'block';
97
+ submitBtn.disabled = false;
98
+ })
99
+ .catch(error => {
100
+ console.error('Error:', error);
101
+ loading.style.display = 'none';
102
+ submitBtn.disabled = false;
103
+ alert('Translation failed: ' + error.message);
104
+ });
105
+ }, 1500);
106
+ });
107
+ });
templates/index.html ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Document Translation Service</title>
7
+ <link rel="stylesheet" href="/static/css/style.css">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
+ </head>
10
+ <body>
11
+ <div class="container">
12
+ <h1>Document Translation Service</h1>
13
+
14
+ <form action="/translate" method="post" enctype="multipart/form-data">
15
+ <div class="upload-area">
16
+ <div class="progress-bar"></div>
17
+ <input type="file" name="file" id="file-input" class="file-input" required>
18
+ <label for="file-input" class="file-label">
19
+ <i class="fas fa-cloud-upload-alt"></i>
20
+ <h3>Click to upload or drag and drop</h3>
21
+ <p id="file-name">No file selected</p>
22
+ <p>PDF, DOCX, PPTX, TXT (Max. 10MB)</p>
23
+ </label>
24
+ </div>
25
+
26
+ <div class="form-group">
27
+ <select name="target_language" required>
28
+ <option value="" disabled selected>Select target language</option>
29
+ <option value="es">Spanish</option>
30
+ <option value="fr">French</option>
31
+ <option value="de">German</option>
32
+ <option value="it">Italian</option>
33
+ <option value="pt">Portuguese</option>
34
+ </select>
35
+ </div>
36
+
37
+ <button type="submit" class="btn">Translate Document</button>
38
+
39
+ <div class="loading">
40
+ <div class="spinner"></div>
41
+ <p>Processing your document...</p>
42
+ </div>
43
+
44
+ <div class="success-message">
45
+ <i class="fas fa-check-circle"></i>
46
+ <p>Translation complete! Your download will start shortly.</p>
47
+ </div>
48
+ </form>
49
+
50
+ <div class="supported">
51
+ <p>Supported formats: PDF, DOCX, PPTX, TXT, XLSX</p>
52
+ </div>
53
+ </div>
54
+
55
+ <script src="/static/js/main.js"></script>
56
+ </body>
57
+ </html>