DaniGoua commited on
Commit
91440dd
·
verified ·
1 Parent(s): 4141851

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +814 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Pdf6
3
- emoji: 🚀
4
- colorFrom: indigo
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: pdf6
3
+ emoji: 🐳
4
+ colorFrom: pink
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,814 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>UMR - Contrôle Qualité PDF</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
9
+ <style>
10
+ .pdf-container {
11
+ height: 500px;
12
+ overflow-y: auto;
13
+ border: 1px solid #e5e7eb;
14
+ }
15
+ .chat-container {
16
+ height: 400px;
17
+ overflow-y: auto;
18
+ }
19
+ .highlight {
20
+ background-color: rgba(255, 255, 0, 0.3);
21
+ }
22
+ .page-selector {
23
+ position: absolute;
24
+ bottom: 10px;
25
+ left: 10px;
26
+ z-index: 100;
27
+ background: white;
28
+ padding: 5px;
29
+ border-radius: 5px;
30
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
31
+ }
32
+ .control-zone {
33
+ position: absolute;
34
+ border: 2px dashed rgba(59, 130, 246, 0.5);
35
+ background-color: rgba(59, 130, 246, 0.2);
36
+ z-index: 10;
37
+ }
38
+ #zoneCreator {
39
+ position: relative;
40
+ }
41
+ </style>
42
+ </head>
43
+ <body class="bg-gray-50">
44
+ <div class="container mx-auto px-4 py-8">
45
+ <header class="mb-8">
46
+ <h1 class="text-3xl font-bold text-blue-800">UMR - Contrôle Qualité des Courriers PDF</h1>
47
+ <p class="text-gray-600 mt-2">Comparez vos documents PDF avec les modèles de référence</p>
48
+ </header>
49
+
50
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
51
+ <!-- Document de Référence -->
52
+ <div class="bg-white rounded-lg shadow p-4">
53
+ <h2 class="text-xl font-semibold mb-4 text-blue-700">Document de Référence</h2>
54
+ <div class="mb-4">
55
+ <input type="file" id="referenceFile" accept=".pdf" class="block w-full text-sm text-gray-500
56
+ file:mr-4 file:py-2 file:px-4
57
+ file:rounded-md file:border-0
58
+ file:text-sm file:font-semibold
59
+ file:bg-blue-50 file:text-blue-700
60
+ hover:file:bg-blue-100">
61
+ </div>
62
+ <div class="relative pdf-container" id="referenceViewer">
63
+ <canvas id="referenceCanvas"></canvas>
64
+ <div class="page-selector hidden" id="refPageSelector">
65
+ Page <span id="refCurrentPage">1</span> sur <span id="refTotalPages">0</span>
66
+ <button class="ml-2 px-2 py-1 bg-blue-100 rounded" id="refPrevPage">←</button>
67
+ <button class="ml-1 px-2 py-1 bg-blue-100 rounded" id="refNextPage">→</button>
68
+ </div>
69
+ </div>
70
+ <button id="defineZonesBtn" class="mt-4 hidden bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded">
71
+ Définir les zones de contrôle
72
+ </button>
73
+ </div>
74
+
75
+ <!-- Document à Contrôler -->
76
+ <div class="bg-white rounded-lg shadow p-4">
77
+ <h2 class="text-xl font-semibold mb-4 text-blue-700">Document à Contrôler</h2>
78
+ <div class="mb-4">
79
+ <input type="file" id="controlFile" accept=".pdf" class="block w-full text-sm text-gray-500
80
+ file:mr-4 file:py-2 file:px-4
81
+ file:rounded-md file:border-0
82
+ file:text-sm file:font-semibold
83
+ file:bg-blue-50 file:text-blue-700
84
+ hover:file:bg-blue-100">
85
+ </div>
86
+ <div class="relative pdf-container" id="controlViewer">
87
+ <canvas id="controlCanvas"></canvas>
88
+ <div class="page-selector hidden" id="ctrlPageSelector">
89
+ Page <span id="ctrlCurrentPage">1</span> sur <span id="ctrlTotalPages">0</span>
90
+ <button class="ml-2 px-2 py-1 bg-blue-100 rounded" id="ctrlPrevPage">←</button>
91
+ <button class="ml-1 px-2 py-1 bg-blue-100 rounded" id="ctrlNextPage">→</button>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+
97
+ <!-- Zone Creator (hidden by default) -->
98
+ <div id="zoneCreator" class="hidden bg-white rounded-lg shadow p-4 mb-6">
99
+ <h2 class="text-xl font-semibold mb-4 text-blue-700">Définition des Zones de Contrôle</h2>
100
+ <div class="mb-4">
101
+ <label class="block text-sm font-medium text-gray-700 mb-1">Type de document</label>
102
+ <select id="docType" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
103
+ <option value="lettre">Lettre du Président</option>
104
+ <option value="lexique">Lexique</option>
105
+ <option value="attestation_alloc">Attestation Fiscale Allocataire</option>
106
+ <option value="attestation_cotis">Attestation Fiscale Cotisant</option>
107
+ <option value="releve">Relevé de Situation</option>
108
+ <option value="contact">Page de Contact</option>
109
+ </select>
110
+ </div>
111
+ <div class="mb-4">
112
+ <label class="block text-sm font-medium text-gray-700 mb-1">Nom de la zone</label>
113
+ <input type="text" id="zoneName" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
114
+ </div>
115
+ <div class="mb-4">
116
+ <label class="block text-sm font-medium text-gray-700 mb-1">Page</label>
117
+ <input type="number" id="zonePage" min="1" value="1" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
118
+ </div>
119
+ <div class="flex space-x-4">
120
+ <button id="saveZoneBtn" class="bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded">
121
+ Enregistrer la zone
122
+ </button>
123
+ <button id="cancelZoneBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-medium py-2 px-4 rounded">
124
+ Annuler
125
+ </button>
126
+ </div>
127
+ </div>
128
+
129
+ <!-- Control Interface -->
130
+ <div class="bg-white rounded-lg shadow p-4 mb-6">
131
+ <div class="flex justify-between items-center mb-4">
132
+ <h2 class="text-xl font-semibold text-blue-700">Interface de Contrôle</h2>
133
+ <button id="startControlBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded">
134
+ Lancer le contrôle
135
+ </button>
136
+ </div>
137
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
138
+ <!-- Document Types -->
139
+ <div class="bg-gray-50 p-4 rounded-lg">
140
+ <h3 class="font-medium text-gray-800 mb-3">Types de documents détectés</h3>
141
+ <ul id="docTypesList" class="space-y-2">
142
+ <li class="text-sm text-gray-500 italic">Aucun document chargé</li>
143
+ </ul>
144
+ </div>
145
+
146
+ <!-- Anomalies -->
147
+ <div class="bg-gray-50 p-4 rounded-lg">
148
+ <h3 class="font-medium text-gray-800 mb-3">Anomalies détectées</h3>
149
+ <ul id="anomaliesList" class="space-y-2">
150
+ <li class="text-sm text-gray-500 italic">Aucune anomalie détectée</li>
151
+ </ul>
152
+ </div>
153
+
154
+ <!-- Actions -->
155
+ <div class="bg-gray-50 p-4 rounded-lg">
156
+ <h3 class="font-medium text-gray-800 mb-3">Actions</h3>
157
+ <div class="space-y-3">
158
+ <button id="exportCSVBtn" class="w-full bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded">
159
+ Exporter en CSV
160
+ </button>
161
+ <button id="generatePDFBtn" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-medium py-2 px-4 rounded">
162
+ Générer PDF corrigé
163
+ </button>
164
+ <button id="resetBtn" class="w-full bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded">
165
+ Réinitialiser
166
+ </button>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ <!-- Chat Interface -->
173
+ <div class="bg-white rounded-lg shadow p-4">
174
+ <h2 class="text-xl font-semibold mb-4 text-blue-700">Interface Conversationnelle</h2>
175
+ <div class="chat-container mb-4 p-4 bg-gray-50 rounded-lg" id="chatMessages">
176
+ <div class="text-center text-gray-500 italic">Bienvenue dans l'interface de contrôle qualité. Chargez vos documents pour commencer.</div>
177
+ </div>
178
+ <div class="flex">
179
+ <input type="text" id="chatInput" placeholder="Posez votre question..." class="flex-1 px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-blue-500 focus:border-blue-500">
180
+ <button id="sendMessageBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-r-md">
181
+ Envoyer
182
+ </button>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <script>
188
+ // Initialize PDF.js
189
+ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js';
190
+
191
+ // Global variables
192
+ let referencePdf = null;
193
+ let controlPdf = null;
194
+ let referenceZones = [];
195
+ let currentZone = null;
196
+ let isCreatingZone = false;
197
+ let startX, startY;
198
+ let chatHistory = [];
199
+ let detectedAnomalies = [];
200
+ let detectedDocTypes = [];
201
+
202
+ // DOM elements
203
+ const referenceFileInput = document.getElementById('referenceFile');
204
+ const controlFileInput = document.getElementById('controlFile');
205
+ const referenceCanvas = document.getElementById('referenceCanvas');
206
+ const controlCanvas = document.getElementById('controlCanvas');
207
+ const refPageSelector = document.getElementById('refPageSelector');
208
+ const ctrlPageSelector = document.getElementById('ctrlPageSelector');
209
+ const refCurrentPage = document.getElementById('refCurrentPage');
210
+ const refTotalPages = document.getElementById('refTotalPages');
211
+ const ctrlCurrentPage = document.getElementById('ctrlCurrentPage');
212
+ const ctrlTotalPages = document.getElementById('ctrlTotalPages');
213
+ const refPrevPage = document.getElementById('refPrevPage');
214
+ const refNextPage = document.getElementById('refNextPage');
215
+ const ctrlPrevPage = document.getElementById('ctrlPrevPage');
216
+ const ctrlNextPage = document.getElementById('ctrlNextPage');
217
+ const defineZonesBtn = document.getElementById('defineZonesBtn');
218
+ const zoneCreator = document.getElementById('zoneCreator');
219
+ const docTypeSelect = document.getElementById('docType');
220
+ const zoneNameInput = document.getElementById('zoneName');
221
+ const zonePageInput = document.getElementById('zonePage');
222
+ const saveZoneBtn = document.getElementById('saveZoneBtn');
223
+ const cancelZoneBtn = document.getElementById('cancelZoneBtn');
224
+ const startControlBtn = document.getElementById('startControlBtn');
225
+ const docTypesList = document.getElementById('docTypesList');
226
+ const anomaliesList = document.getElementById('anomaliesList');
227
+ const exportCSVBtn = document.getElementById('exportCSVBtn');
228
+ const generatePDFBtn = document.getElementById('generatePDFBtn');
229
+ const resetBtn = document.getElementById('resetBtn');
230
+ const chatMessages = document.getElementById('chatMessages');
231
+ const chatInput = document.getElementById('chatInput');
232
+ const sendMessageBtn = document.getElementById('sendMessageBtn');
233
+
234
+ // Event listeners
235
+ referenceFileInput.addEventListener('change', handleReferenceFile);
236
+ controlFileInput.addEventListener('change', handleControlFile);
237
+ defineZonesBtn.addEventListener('click', startZoneCreation);
238
+ saveZoneBtn.addEventListener('click', saveZone);
239
+ cancelZoneBtn.addEventListener('click', cancelZoneCreation);
240
+ startControlBtn.addEventListener('click', startControlProcess);
241
+ exportCSVBtn.addEventListener('click', exportToCSV);
242
+ generatePDFBtn.addEventListener('click', generateCorrectedPDF);
243
+ resetBtn.addEventListener('click', resetApplication);
244
+ sendMessageBtn.addEventListener('click', sendChatMessage);
245
+ chatInput.addEventListener('keypress', (e) => {
246
+ if (e.key === 'Enter') sendChatMessage();
247
+ });
248
+
249
+ // Navigation buttons
250
+ refPrevPage.addEventListener('click', () => navigatePage('ref', -1));
251
+ refNextPage.addEventListener('click', () => navigatePage('ref', 1));
252
+ ctrlPrevPage.addEventListener('click', () => navigatePage('ctrl', -1));
253
+ ctrlNextPage.addEventListener('click', () => navigatePage('ctrl', 1));
254
+
255
+ // Functions
256
+ function handleReferenceFile(e) {
257
+ const file = e.target.files[0];
258
+ if (!file) return;
259
+
260
+ const reader = new FileReader();
261
+ reader.onload = async function() {
262
+ try {
263
+ const typedArray = new Uint8Array(reader.result);
264
+ referencePdf = await pdfjsLib.getDocument(typedArray).promise;
265
+
266
+ refTotalPages.textContent = referencePdf.numPages;
267
+ refPageSelector.classList.remove('hidden');
268
+
269
+ await renderPage('ref', 1);
270
+ defineZonesBtn.classList.remove('hidden');
271
+
272
+ addChatMessage("Système", "Document de référence chargé avec succès. Vous pouvez maintenant définir les zones de contrôle.");
273
+ } catch (error) {
274
+ console.error('Error loading PDF:', error);
275
+ addChatMessage("Erreur", "Erreur lors du chargement du PDF de référence.");
276
+ }
277
+ };
278
+ reader.readAsArrayBuffer(file);
279
+ }
280
+
281
+ function handleControlFile(e) {
282
+ const file = e.target.files[0];
283
+ if (!file) return;
284
+
285
+ const reader = new FileReader();
286
+ reader.onload = async function() {
287
+ try {
288
+ const typedArray = new Uint8Array(reader.result);
289
+ controlPdf = await pdfjsLib.getDocument(typedArray).promise;
290
+
291
+ ctrlTotalPages.textContent = controlPdf.numPages;
292
+ ctrlPageSelector.classList.remove('hidden');
293
+
294
+ await renderPage('ctrl', 1);
295
+
296
+ addChatMessage("Système", "Document à contrôler chargé avec succès. Vous pouvez maintenant lancer le contrôle.");
297
+ } catch (error) {
298
+ console.error('Error loading PDF:', error);
299
+ addChatMessage("Erreur", "Erreur lors du chargement du PDF à contrôler.");
300
+ }
301
+ };
302
+ reader.readAsArrayBuffer(file);
303
+ }
304
+
305
+ async function renderPage(type, pageNum) {
306
+ try {
307
+ const pdf = type === 'ref' ? referencePdf : controlPdf;
308
+ const canvas = type === 'ref' ? referenceCanvas : controlCanvas;
309
+ const pageInfo = type === 'ref' ? refCurrentPage : ctrlCurrentPage;
310
+
311
+ if (!pdf) return;
312
+
313
+ const page = await pdf.getPage(pageNum);
314
+ const viewport = page.getViewport({ scale: 1.0 });
315
+
316
+ // Adjust canvas size
317
+ canvas.height = viewport.height;
318
+ canvas.width = viewport.width;
319
+
320
+ // Update current page display
321
+ pageInfo.textContent = pageNum;
322
+
323
+ // Render PDF page
324
+ await page.render({
325
+ canvasContext: canvas.getContext('2d'),
326
+ viewport: viewport
327
+ }).promise;
328
+
329
+ // Update zone display if reference
330
+ if (type === 'ref') {
331
+ displayZonesForPage(pageNum);
332
+ }
333
+
334
+ return viewport;
335
+ } catch (error) {
336
+ console.error('Error rendering page:', error);
337
+ }
338
+ }
339
+
340
+ function navigatePage(type, direction) {
341
+ const currentPage = type === 'ref' ? parseInt(refCurrentPage.textContent) : parseInt(ctrlCurrentPage.textContent);
342
+ const totalPages = type === 'ref' ? referencePdf.numPages : controlPdf.numPages;
343
+
344
+ let newPage = currentPage + direction;
345
+ if (newPage < 1) newPage = 1;
346
+ if (newPage > totalPages) newPage = totalPages;
347
+
348
+ renderPage(type, newPage);
349
+ }
350
+
351
+ function startZoneCreation() {
352
+ isCreatingZone = true;
353
+ zoneCreator.classList.remove('hidden');
354
+ defineZonesBtn.classList.add('hidden');
355
+
356
+ // Set up canvas for zone creation
357
+ referenceCanvas.style.cursor = 'crosshair';
358
+ referenceCanvas.addEventListener('mousedown', startZoneDrawing);
359
+ referenceCanvas.addEventListener('mousemove', drawZone);
360
+ referenceCanvas.addEventListener('mouseup', endZoneDrawing);
361
+
362
+ addChatMessage("Système", "Mode création de zones activé. Cliquez et glissez pour définir une zone de contrôle.");
363
+ }
364
+
365
+ function startZoneDrawing(e) {
366
+ if (!isCreatingZone) return;
367
+
368
+ const rect = referenceCanvas.getBoundingClientRect();
369
+ startX = e.clientX - rect.left;
370
+ startY = e.clientY - rect.top;
371
+
372
+ // Remove any existing temporary zone
373
+ if (currentZone) {
374
+ currentZone.remove();
375
+ currentZone = null;
376
+ }
377
+
378
+ // Create new zone element
379
+ currentZone = document.createElement('div');
380
+ currentZone.className = 'control-zone';
381
+ currentZone.style.left = startX + 'px';
382
+ currentZone.style.top = startY + 'px';
383
+ currentZone.style.width = '0';
384
+ currentZone.style.height = '0';
385
+
386
+ document.getElementById('referenceViewer').appendChild(currentZone);
387
+ }
388
+
389
+ function drawZone(e) {
390
+ if (!isCreatingZone || !currentZone) return;
391
+
392
+ const rect = referenceCanvas.getBoundingClientRect();
393
+ const currentX = e.clientX - rect.left;
394
+ const currentY = e.clientY - rect.top;
395
+
396
+ const width = currentX - startX;
397
+ const height = currentY - startY;
398
+
399
+ currentZone.style.width = Math.abs(width) + 'px';
400
+ currentZone.style.height = Math.abs(height) + 'px';
401
+
402
+ if (width < 0) {
403
+ currentZone.style.left = currentX + 'px';
404
+ }
405
+ if (height < 0) {
406
+ currentZone.style.top = currentY + 'px';
407
+ }
408
+ }
409
+
410
+ function endZoneDrawing() {
411
+ if (!isCreatingZone || !currentZone) return;
412
+
413
+ // Clean up event listeners
414
+ referenceCanvas.removeEventListener('mousemove', drawZone);
415
+ referenceCanvas.removeEventListener('mouseup', endZoneDrawing);
416
+ }
417
+
418
+ function saveZone() {
419
+ if (!currentZone) return;
420
+
421
+ const rect = currentZone.getBoundingClientRect();
422
+ const canvasRect = referenceCanvas.getBoundingClientRect();
423
+
424
+ // Calculate relative coordinates
425
+ const x = rect.left - canvasRect.left;
426
+ const y = rect.top - canvasRect.top;
427
+ const width = rect.width;
428
+ const height = rect.height;
429
+
430
+ const zone = {
431
+ type: docTypeSelect.value,
432
+ name: zoneNameInput.value || 'Zone sans nom',
433
+ page: parseInt(zonePageInput.value),
434
+ x: x,
435
+ y: y,
436
+ width: width,
437
+ height: height
438
+ };
439
+
440
+ referenceZones.push(zone);
441
+
442
+ // Clean up
443
+ currentZone.remove();
444
+ currentZone = null;
445
+ isCreatingZone = false;
446
+
447
+ // Reset UI
448
+ referenceCanvas.style.cursor = 'default';
449
+ zoneCreator.classList.add('hidden');
450
+ defineZonesBtn.classList.remove('hidden');
451
+
452
+ // Display saved zones
453
+ displayZonesForPage(parseInt(zonePageInput.value));
454
+
455
+ addChatMessage("Système", `Zone "${zone.name}" enregistrée pour le type "${docTypeSelect.options[docTypeSelect.selectedIndex].text}".`);
456
+ }
457
+
458
+ function cancelZoneCreation() {
459
+ if (currentZone) {
460
+ currentZone.remove();
461
+ currentZone = null;
462
+ }
463
+
464
+ isCreatingZone = false;
465
+ referenceCanvas.style.cursor = 'default';
466
+ zoneCreator.classList.add('hidden');
467
+ defineZonesBtn.classList.remove('hidden');
468
+
469
+ addChatMessage("Système", "Création de zone annulée.");
470
+ }
471
+
472
+ function displayZonesForPage(pageNum) {
473
+ // Remove all existing zone displays
474
+ document.querySelectorAll('.control-zone').forEach(zone => {
475
+ if (zone !== currentZone) zone.remove();
476
+ });
477
+
478
+ // Display zones for this page
479
+ referenceZones.filter(zone => zone.page === pageNum).forEach(zone => {
480
+ const zoneElement = document.createElement('div');
481
+ zoneElement.className = 'control-zone';
482
+ zoneElement.style.left = zone.x + 'px';
483
+ zoneElement.style.top = zone.y + 'px';
484
+ zoneElement.style.width = zone.width + 'px';
485
+ zoneElement.style.height = zone.height + 'px';
486
+ zoneElement.title = `${zone.name} (${zone.type})`;
487
+
488
+ document.getElementById('referenceViewer').appendChild(zoneElement);
489
+ });
490
+ }
491
+
492
+ async function startControlProcess() {
493
+ if (!referencePdf || !controlPdf || referenceZones.length === 0) {
494
+ addChatMessage("Erreur", "Veuillez charger les deux documents et définir des zones de contrôle avant de lancer le processus.");
495
+ return;
496
+ }
497
+
498
+ addChatMessage("Système", "Début du processus de contrôle...");
499
+
500
+ // Reset previous results
501
+ detectedAnomalies = [];
502
+ detectedDocTypes = [];
503
+
504
+ // Simulate document type detection (in a real app, this would analyze the PDF)
505
+ detectDocumentTypes();
506
+
507
+ // Simulate comparison (in a real app, this would compare text in the zones)
508
+ simulateComparison();
509
+
510
+ // Update UI with results
511
+ updateResultsUI();
512
+
513
+ addChatMessage("Système", "Contrôle terminé. " + detectedAnomalies.length + " anomalies détectées.");
514
+ }
515
+
516
+ function detectDocumentTypes() {
517
+ // In a real app, this would analyze the PDF content to detect document types
518
+ // For this demo, we'll just use some sample data
519
+ detectedDocTypes = [
520
+ { type: 'lettre', page: 1, status: 'détecté' },
521
+ { type: 'lexique', page: 2, status: 'détecté' },
522
+ { type: 'attestation_alloc', page: 3, status: 'détecté' },
523
+ { type: 'releve', page: 4, status: 'détecté' },
524
+ { type: 'contact', page: 5, status: 'détecté' }
525
+ ];
526
+ }
527
+
528
+ function simulateComparison() {
529
+ // In a real app, this would compare text in the defined zones
530
+ // For this demo, we'll generate some sample anomalies
531
+
532
+ // Sample anomalies for different document types
533
+ detectedAnomalies = [
534
+ {
535
+ docType: 'lettre',
536
+ page: 1,
537
+ location: 'Haut de la page',
538
+ description: 'Le logo en en-tête est décalé de 5px vers la droite',
539
+ severity: 'Majeure',
540
+ refText: 'Logo UMR positionné à x:50, y:50',
541
+ controlText: 'Logo UMR positionné à x:55, y:50'
542
+ },
543
+ {
544
+ docType: 'lettre',
545
+ page: 1,
546
+ location: 'Bas de la page',
547
+ description: 'La mention légale est incomplète',
548
+ severity: 'Mineure',
549
+ refText: 'UMR est une SA au capital de 247 668 709 €. Siège social : 12 Rue de Cornulier - 75 000 PARIS.',
550
+ controlText: 'UMR est une SA au capital de 247 668 709 €.'
551
+ },
552
+ {
553
+ docType: 'attestation_alloc',
554
+ page: 3,
555
+ location: 'Milieu de la page',
556
+ description: 'La phrase "Au titre des pensions : 1 632 €" est absente',
557
+ severity: 'Majeure',
558
+ refText: 'Au titre des pensions : 1 632 €',
559
+ controlText: ''
560
+ },
561
+ {
562
+ docType: 'releve',
563
+ page: 4,
564
+ location: 'Bas de la page',
565
+ description: 'La valeur du point affichée diffère',
566
+ severity: 'Variable',
567
+ refText: 'valeur de service du point* de votre contrat PER Corem a été revalorisée à hauteur de 3,5% au 01/01/2025',
568
+ controlText: 'valeur de service du point* de votre contrat PER Corem a été revalorisée à hauteur de 2,5% au 01/01/2025',
569
+ needsConfirmation: true
570
+ }
571
+ ];
572
+ }
573
+
574
+ function updateResultsUI() {
575
+ // Update document types list
576
+ docTypesList.innerHTML = '';
577
+ if (detectedDocTypes.length === 0) {
578
+ docTypesList.innerHTML = '<li class="text-sm text-gray-500 italic">Aucun type de document détecté</li>';
579
+ } else {
580
+ detectedDocTypes.forEach(doc => {
581
+ const li = document.createElement('li');
582
+ li.className = 'text-sm';
583
+ li.innerHTML = `<span class="font-medium">${getDocTypeName(doc.type)}</span> (Page ${doc.page}) - <span class="text-green-600">${doc.status}</span>`;
584
+ docTypesList.appendChild(li);
585
+ });
586
+ }
587
+
588
+ // Update anomalies list
589
+ anomaliesList.innerHTML = '';
590
+ if (detectedAnomalies.length === 0) {
591
+ anomaliesList.innerHTML = '<li class="text-sm text-gray-500 italic">Aucune anomalie détectée</li>';
592
+ } else {
593
+ detectedAnomalies.forEach((anomaly, index) => {
594
+ const li = document.createElement('li');
595
+ li.className = 'text-sm';
596
+
597
+ let severityClass = 'text-yellow-600';
598
+ if (anomaly.severity === 'Majeure') severityClass = 'text-red-600';
599
+ if (anomaly.severity === 'Variable') severityClass = 'text-blue-600';
600
+
601
+ li.innerHTML = `
602
+ <div class="font-medium">${getDocTypeName(anomaly.docType)} (${anomaly.location}, Page ${anomaly.page})</div>
603
+ <div>${anomaly.description} <span class="${severityClass}">(${anomaly.severity})</span></div>
604
+ ${anomaly.needsConfirmation ? '<div class="text-blue-600 mt-1">[Nécessite confirmation]</div>' : ''}
605
+ `;
606
+
607
+ li.addEventListener('click', () => showAnomalyDetails(index));
608
+ anomaliesList.appendChild(li);
609
+ });
610
+ }
611
+ }
612
+
613
+ function getDocTypeName(typeCode) {
614
+ const types = {
615
+ 'lettre': 'Lettre du Président',
616
+ 'lexique': 'Lexique',
617
+ 'attestation_alloc': 'Attestation Fiscale Allocataire',
618
+ 'attestation_cotis': 'Attestation Fiscale Cotisant',
619
+ 'releve': 'Relevé de Situation',
620
+ 'contact': 'Page de Contact'
621
+ };
622
+ return types[typeCode] || typeCode;
623
+ }
624
+
625
+ function showAnomalyDetails(index) {
626
+ const anomaly = detectedAnomalies[index];
627
+
628
+ let message = `
629
+ <strong>${getDocTypeName(anomaly.docType)} (Page ${anomaly.page})</strong><br>
630
+ <span class="${anomaly.severity === 'Majeure' ? 'text-red-600' : anomaly.severity === 'Variable' ? 'text-blue-600' : 'text-yellow-600'}">
631
+ ${anomaly.severity}: ${anomaly.description}
632
+ </span><br><br>
633
+ <strong>Document de référence:</strong><br>
634
+ ${anomaly.refText || 'Aucun texte correspondant'}<br><br>
635
+ <strong>Document à contrôler:</strong><br>
636
+ ${anomaly.controlText || 'Aucun texte correspondant'}
637
+ `;
638
+
639
+ if (anomaly.needsConfirmation) {
640
+ message += `<br><br><div class="mt-4">
641
+ <button onclick="confirmAnomaly(${index}, true)" class="mr-2 bg-green-600 hover:bg-green-700 text-white font-medium py-1 px-3 rounded text-sm">
642
+ Confirmer la différence
643
+ </button>
644
+ <button onclick="confirmAnomaly(${index}, false)" class="bg-red-600 hover:bg-red-700 text-white font-medium py-1 px-3 rounded text-sm">
645
+ Rejeter la différence
646
+ </button>
647
+ </div>`;
648
+ }
649
+
650
+ addChatMessage("Détail de l'anomalie", message);
651
+ }
652
+
653
+ function confirmAnomaly(index, isConfirmed) {
654
+ const anomaly = detectedAnomalies[index];
655
+
656
+ if (isConfirmed) {
657
+ addChatMessage("Système", `Vous avez confirmé que la différence dans "${anomaly.description}" est normale.`);
658
+ // In a real app, you might mark this as confirmed in your data structure
659
+ } else {
660
+ addChatMessage("Système", `Vous avez indiqué que la différence dans "${anomaly.description}" est anormale.`);
661
+ // In a real app, you might mark this as a real anomaly
662
+ }
663
+
664
+ // Remove the needsConfirmation flag
665
+ detectedAnomalies[index].needsConfirmation = false;
666
+
667
+ // Update the UI
668
+ updateResultsUI();
669
+ }
670
+
671
+ function exportToCSV() {
672
+ if (detectedAnomalies.length === 0) {
673
+ addChatMessage("Erreur", "Aucune anomalie à exporter.");
674
+ return;
675
+ }
676
+
677
+ // Create CSV content
678
+ let csvContent = "Type de document,Page,Localisation,Description,Sévérité,Texte de référence,Texte du document\n";
679
+
680
+ detectedAnomalies.forEach(anomaly => {
681
+ csvContent += `"${getDocTypeName(anomaly.docType)}",${anomaly.page},"${anomaly.location}","${anomaly.description}","${anomaly.severity}","${anomaly.refText.replace(/"/g, '""')}","${anomaly.controlText.replace(/"/g, '""')}"\n`;
682
+ });
683
+
684
+ // Create download link
685
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
686
+ const url = URL.createObjectURL(blob);
687
+ const link = document.createElement('a');
688
+ link.setAttribute('href', url);
689
+ link.setAttribute('download', 'anomalies_detectees.csv');
690
+ link.style.visibility = 'hidden';
691
+ document.body.appendChild(link);
692
+ link.click();
693
+ document.body.removeChild(link);
694
+
695
+ addChatMessage("Système", "Export CSV des anomalies terminé.");
696
+ }
697
+
698
+ function generateCorrectedPDF() {
699
+ // In a real app, this would generate a corrected PDF
700
+ // For this demo, we'll just show a message
701
+ addChatMessage("Système", "Génération du PDF corrigé en cours...");
702
+
703
+ setTimeout(() => {
704
+ addChatMessage("Système", "PDF corrigé généré avec succès. Les anomalies ont été surlignées.");
705
+
706
+ // Simulate download (in a real app, this would be the actual PDF)
707
+ const link = document.createElement('a');
708
+ link.setAttribute('href', '#');
709
+ link.setAttribute('download', 'document_corrige.pdf');
710
+ link.style.visibility = 'hidden';
711
+ document.body.appendChild(link);
712
+ link.click();
713
+ document.body.removeChild(link);
714
+ }, 2000);
715
+ }
716
+
717
+ function resetApplication() {
718
+ // Reset file inputs
719
+ referenceFileInput.value = '';
720
+ controlFileInput.value = '';
721
+
722
+ // Reset PDF viewers
723
+ referenceCanvas.getContext('2d').clearRect(0, 0, referenceCanvas.width, referenceCanvas.height);
724
+ controlCanvas.getContext('2d').clearRect(0, 0, controlCanvas.width, controlCanvas.height);
725
+
726
+ // Hide page selectors
727
+ refPageSelector.classList.add('hidden');
728
+ ctrlPageSelector.classList.add('hidden');
729
+
730
+ // Reset zone creation
731
+ if (currentZone) {
732
+ currentZone.remove();
733
+ currentZone = null;
734
+ }
735
+ isCreatingZone = false;
736
+ referenceCanvas.style.cursor = 'default';
737
+ zoneCreator.classList.add('hidden');
738
+ defineZonesBtn.classList.add('hidden');
739
+
740
+ // Clear zones
741
+ referenceZones = [];
742
+ document.querySelectorAll('.control-zone').forEach(zone => zone.remove());
743
+
744
+ // Reset results
745
+ detectedAnomalies = [];
746
+ detectedDocTypes = [];
747
+ docTypesList.innerHTML = '<li class="text-sm text-gray-500 italic">Aucun type de document détecté</li>';
748
+ anomaliesList.innerHTML = '<li class="text-sm text-gray-500 italic">Aucune anomalie détectée</li>';
749
+
750
+ // Clear chat
751
+ chatHistory = [];
752
+ chatMessages.innerHTML = '<div class="text-center text-gray-500 italic">Bienvenue dans l\'interface de contrôle qualité. Chargez vos documents pour commencer.</div>';
753
+
754
+ // Reset PDF objects
755
+ referencePdf = null;
756
+ controlPdf = null;
757
+
758
+ addChatMessage("Système", "Application réinitialisée. Vous pouvez charger de nouveaux documents.");
759
+ }
760
+
761
+ function addChatMessage(sender, message) {
762
+ const messageDiv = document.createElement('div');
763
+ messageDiv.className = 'mb-3';
764
+
765
+ const senderSpan = document.createElement('span');
766
+ senderSpan.className = 'font-medium text-blue-700';
767
+ senderSpan.textContent = sender + ': ';
768
+
769
+ const contentDiv = document.createElement('div');
770
+ contentDiv.className = 'mt-1 ml-4 p-3 bg-white rounded-lg shadow-sm';
771
+ contentDiv.innerHTML = message;
772
+
773
+ messageDiv.appendChild(senderSpan);
774
+ messageDiv.appendChild(contentDiv);
775
+
776
+ chatMessages.appendChild(messageDiv);
777
+ chatMessages.scrollTop = chatMessages.scrollHeight;
778
+
779
+ // Add to history
780
+ chatHistory.push({ sender, message });
781
+ }
782
+
783
+ function sendChatMessage() {
784
+ const message = chatInput.value.trim();
785
+ if (!message) return;
786
+
787
+ addChatMessage("Vous", message);
788
+ chatInput.value = '';
789
+
790
+ // Simulate AI response
791
+ setTimeout(() => {
792
+ let response = "Je suis désolé, je ne peux pas répondre à cette question dans cette version de démonstration. Dans une version complète, je pourrais analyser les documents et répondre à vos questions sur les anomalies détectées.";
793
+
794
+ // Simple keyword responses for demo
795
+ if (message.toLowerCase().includes('bonjour') || message.toLowerCase().includes('salut')) {
796
+ response = "Bonjour ! Comment puis-je vous aider avec le contrôle de vos documents aujourd'hui ?";
797
+ } else if (message.toLowerCase().includes('aide') || message.toLowerCase().includes('comment')) {
798
+ response = "Pour utiliser cette application :<br>1. Chargez un document de référence<br>2. Définissez les zones de contrôle<br>3. Chargez un document à vérifier<br>4. Lancez le contrôle<br>Je vous signalerai alors les anomalies détectées.";
799
+ } else if (message.toLowerCase().includes('anomalie') && detectedAnomalies.length > 0) {
800
+ response = `Il y a actuellement ${detectedAnomalies.length} anomalies détectées. Cliquez sur une anomalie dans la liste pour voir les détails.`;
801
+ } else if (message.toLowerCase().includes('type') && detectedDocTypes.length > 0) {
802
+ const typesList = detectedDocTypes.map(doc => getDocTypeName(doc.type)).join(', ');
803
+ response = `Les types de documents détectés sont : ${typesList}.`;
804
+ }
805
+
806
+ addChatMessage("Assistant", response);
807
+ }, 500);
808
+ }
809
+
810
+ // Make confirmAnomaly available globally for the chat messages
811
+ window.confirmAnomaly = confirmAnomaly;
812
+ </script>
813
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=DaniGoua/pdf6" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
814
+ </html>