ouhenio commited on
Commit
76138e1
verified
1 Parent(s): 4886dc4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -626
app.py CHANGED
@@ -3,647 +3,116 @@ import random
3
  import json
4
  import fastapi
5
  from fastapi import FastAPI
 
 
6
 
7
- # Create a FastAPI app
8
- app = FastAPI()
 
 
 
9
 
10
- # Sample country data with random progress percentages - using the proper ISO codes
11
- def generate_data():
12
- return {
13
- "MEX": {"name": "Mexico", "percent": random.randint(10, 90)},
14
- "ARG": {"name": "Argentina", "percent": random.randint(10, 90)},
15
- "COL": {"name": "Colombia", "percent": random.randint(10, 90)},
16
- "CHL": {"name": "Chile", "percent": random.randint(10, 90)},
17
- "PER": {"name": "Peru", "percent": random.randint(10, 90)},
18
- "ESP": {"name": "Spain", "percent": random.randint(10, 90)},
19
- "BRA": {"name": "Brazil", "percent": random.randint(10, 90)},
20
- "VEN": {"name": "Venezuela", "percent": random.randint(10, 90)},
21
- "ECU": {"name": "Ecuador", "percent": random.randint(10, 90)},
22
- "BOL": {"name": "Bolivia", "percent": random.randint(10, 90)},
23
- "PRY": {"name": "Paraguay", "percent": random.randint(10, 90)},
24
- "URY": {"name": "Uruguay", "percent": random.randint(10, 90)},
25
- "CRI": {"name": "Costa Rica", "percent": random.randint(10, 90)},
26
- "PAN": {"name": "Panama", "percent": random.randint(10, 90)},
27
- "DOM": {"name": "Dominican Republic", "percent": random.randint(10, 90)},
28
- "GTM": {"name": "Guatemala", "percent": random.randint(10, 90)},
29
- "HND": {"name": "Honduras", "percent": random.randint(10, 90)},
30
- "SLV": {"name": "El Salvador", "percent": random.randint(10, 90)},
31
- "NIC": {"name": "Nicaragua", "percent": random.randint(10, 90)},
32
- "CUB": {"name": "Cuba", "percent": random.randint(10, 90)}
33
- }
34
 
35
- # HTML template - avoiding f-strings with JavaScript template literals
36
- HTML_TEMPLATE = """
37
- <!DOCTYPE html>
38
- <html>
39
- <head>
40
- <meta charset="utf-8">
41
- <title>Cartograf铆a de anotaci贸n</title>
42
- <script src="https://d3js.org/d3.v7.min.js"></script>
43
- <style>
44
- body {
45
- margin: 0;
46
- padding: 20px;
47
- background-color: #0f1218;
48
- color: #fff;
49
- font-family: system-ui, -apple-system, sans-serif;
50
- }
51
- h1 {
52
- margin-bottom: 20px;
53
- }
54
- .container {
55
- display: flex;
56
- width: 100%;
57
- }
58
- .map-container {
59
- flex: 3;
60
- height: 600px;
61
- position: relative;
62
- background-color: #0f1218;
63
- }
64
- .stats-container {
65
- flex: 1;
66
- padding: 20px;
67
- background-color: #161b22;
68
- border-radius: 8px;
69
- margin-right: 20px;
70
- }
71
- #tooltip {
72
- position: absolute;
73
- background-color: rgba(0, 0, 0, 0.8);
74
- border-radius: 5px;
75
- padding: 8px;
76
- color: white;
77
- font-size: 12px;
78
- pointer-events: none;
79
- opacity: 0;
80
- transition: opacity 0.3s;
81
- border: 1px solid rgba(255, 255, 255, 0.2);
82
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
83
- z-index: 1000;
84
- }
85
- .country {
86
- cursor: pointer;
87
- transition: opacity 0.3s;
88
- }
89
- .country:hover {
90
- opacity: 0.8;
91
- }
92
- .stat-title {
93
- font-size: 1.2rem;
94
- margin-bottom: 20px;
95
- font-weight: bold;
96
- }
97
- .stat-item {
98
- margin-bottom: 10px;
99
- color: #abb4c2;
100
- }
101
- .stat-value {
102
- font-weight: bold;
103
- color: white;
104
- }
105
- .stat-bar-container {
106
- width: 100%;
107
- height: 8px;
108
- background-color: #30363d;
109
- border-radius: 4px;
110
- margin-top: 5px;
111
- overflow: hidden;
112
- }
113
- .stat-bar {
114
- height: 100%;
115
- background: linear-gradient(to right, #4a1942, #f32b7b);
116
- border-radius: 4px;
117
- }
118
- .top-countries {
119
- margin-top: 30px;
120
- }
121
- .country-stat {
122
- display: flex;
123
- justify-content: space-between;
124
- margin-bottom: 8px;
125
- align-items: center;
126
- font-size: 14px;
127
- }
128
- .country-bar {
129
- flex: 1;
130
- height: 6px;
131
- background-color: #30363d;
132
- border-radius: 3px;
133
- overflow: hidden;
134
- margin: 0 10px;
135
- }
136
- .country-bar-fill {
137
- height: 100%;
138
- background: linear-gradient(to right, #4a1942, #f32b7b);
139
- border-radius: 3px;
140
- }
141
- .country-value {
142
- width: 80px;
143
- text-align: right;
144
- }
145
- .legend {
146
- margin-top: 20px;
147
- }
148
- .footer-note {
149
- margin-top: 30px;
150
- font-style: italic;
151
- font-size: 0.9em;
152
- color: #abb4c2;
153
- }
154
- </style>
155
- </head>
156
- <body>
157
 
158
- <div class="container">
159
- <div class="stats-container">
160
- <div class="stat-title">Resumen General</div>
161
-
162
- <div class="stat-item">
163
- Pa铆ses en la base de datos:<br>
164
- <span class="stat-value">20</span>
165
- </div>
166
-
167
- <div class="stat-item">
168
- Total de documentos:<br>
169
- <span class="stat-value" id="total-docs">0</span>
170
- </div>
171
-
172
- <div class="stat-item">
173
- Promedio de completitud:<br>
174
- <span class="stat-value" id="avg-percent">0%</span>
175
- </div>
176
-
177
- <div class="top-countries">
178
- <div class="stat-item">Los 5 pa铆ses con mayor recolecci贸n:</div>
179
- <div id="top-countries-list">
180
- <!-- Will be populated by JavaScript -->
181
- </div>
182
- </div>
183
-
184
- <div class="footer-note">
185
- Selecciona un pa铆s en el mapa para ver informaci贸n detallada.
186
- </div>
187
- </div>
188
-
189
- <div class="map-container" id="map-container"></div>
190
- </div>
191
 
192
- <div id="tooltip"></div>
 
193
 
194
- <script>
195
- // Country data from Python - will be replaced
196
- const countryData = COUNTRY_DATA_PLACEHOLDER;
197
 
198
- document.addEventListener('DOMContentLoaded', function() {
199
- console.log('Document loaded, initializing map...');
200
-
201
- // Set up dimensions
202
- const container = document.getElementById('map-container');
203
- const width = container.clientWidth;
204
- const height = container.clientHeight;
205
-
206
- console.log('Container dimensions:', width, height);
207
-
208
- // Create SVG
209
- const svg = d3.select('#map-container')
210
- .append('svg')
211
- .attr('width', width)
212
- .attr('height', height);
213
-
214
- console.log('SVG created');
215
-
216
- // Create color scale
217
- const colorScale = d3.scaleLinear()
218
- .domain([0, 100])
219
- .range(['#4a1942', '#f32b7b']);
220
-
221
- // Set up projection with specific focus
222
- const projection = d3.geoMercator()
223
- .center([-60, -15]) // Centered on South America
224
- .scale(width / 3)
225
- .translate([width / 2, height / 2]);
226
-
227
- const path = d3.geoPath().projection(projection);
228
-
229
- // Tooltip setup
230
- const tooltip = d3.select('#tooltip');
231
-
232
- console.log('Loading GeoJSON data...');
233
-
234
- // Load GeoJSON data
235
- d3.json('https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson')
236
- .then(function(data) {
237
- console.log('GeoJSON data loaded');
238
-
239
- // The relevant country codes
240
- const relevantCountryCodes = Object.keys(countryData);
241
-
242
- // Log countries in data
243
- console.log('Countries in countryData:', relevantCountryCodes);
244
-
245
- // Log some sample features to check the properties structure
246
- const sampleFeatures = data.features.slice(0, 3);
247
- console.log('Sample feature properties:', sampleFeatures.map(f => f.properties));
248
-
249
- // Add ocean background
250
- svg.append('rect')
251
- .attr('width', width)
252
- .attr('height', height)
253
- .attr('fill', '#0f1218');
254
-
255
- // Filter the features - check for id match
256
- const relevantFeatures = data.features.filter(d =>
257
- relevantCountryCodes.includes(d.id)
258
- );
259
-
260
- console.log('Filtered features count:', relevantFeatures.length);
261
-
262
- // If we still don't have matches, look more deeply at the data structure
263
- if (relevantFeatures.length === 0) {
264
- console.log('No matches found using id, checking all feature properties');
265
-
266
- // Get the first 10 features to examine their structure
267
- const firstTen = data.features.slice(0, 10);
268
- console.log('First ten features:', firstTen);
269
-
270
- // Look for our countries in all features
271
- const latinAmericanFeatures = data.features.filter(f => {
272
- // Check various properties that might contain the country code
273
- return relevantCountryCodes.includes(f.id) ||
274
- (f.properties && relevantCountryCodes.includes(f.properties.iso_a3)) ||
275
- (f.properties && relevantCountryCodes.includes(f.properties.name));
276
- });
277
-
278
- console.log('Latin American features found:', latinAmericanFeatures.length);
279
-
280
- // If there are still no matches, just use all features and filter visually
281
- if (latinAmericanFeatures.length === 0) {
282
- console.log('Still no matches, using all features and hiding non-Latin American countries');
283
-
284
- // Draw all countries but only color our target ones
285
- svg.selectAll('.country')
286
- .data(data.features)
287
- .enter()
288
- .append('path')
289
- .attr('class', 'country')
290
- .attr('d', path)
291
- .attr('fill', function(d) {
292
- // Try to match with id or iso_a3
293
- if (d.id && countryData[d.id]) {
294
- return colorScale(countryData[d.id].percent);
295
- } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
296
- return colorScale(countryData[d.properties.iso_a3].percent);
297
- } else if (d.properties && d.properties.name && countryData[d.properties.name]) {
298
- return colorScale(countryData[d.properties.name].percent);
299
- } else {
300
- // Try checking if it's a Latin American country by name
301
- const latinAmericanCountries = [
302
- 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
303
- 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
304
- 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
305
- 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
306
- ];
307
-
308
- if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
309
- // Find the matching country in our data
310
- for (const code in countryData) {
311
- if (countryData[code].name === d.properties.name) {
312
- return colorScale(countryData[code].percent);
313
- }
314
- }
315
- }
316
-
317
- // If not a target country, make it transparent
318
- return 'transparent';
319
- }
320
- })
321
- .attr('stroke', function(d) {
322
- // Only show outlines for Latin American countries
323
- const latinAmericanCountries = [
324
- 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
325
- 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
326
- 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
327
- 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
328
- ];
329
-
330
- if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
331
- return '#0f1218';
332
- } else {
333
- return 'transparent';
334
- }
335
- })
336
- .attr('stroke-width', 1)
337
- .on('mouseover', function(event, d) {
338
- // Only enable hover for target countries
339
- if (d.id && countryData[d.id]) {
340
- const iso = d.id;
341
- showTooltip(event, iso);
342
- } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
343
- const iso = d.properties.iso_a3;
344
- showTooltip(event, iso);
345
- } else if (d.properties && d.properties.name) {
346
- // Find the matching country in our data by name
347
- for (const code in countryData) {
348
- if (countryData[code].name === d.properties.name) {
349
- showTooltip(event, code);
350
- break;
351
- }
352
- }
353
- }
354
- })
355
- .on('mousemove', function(event) {
356
- tooltip.style('left', (event.pageX + 15) + 'px')
357
- .style('top', (event.pageY + 15) + 'px');
358
- })
359
- .on('mouseout', function() {
360
- d3.select(this)
361
- .attr('stroke', function(d) {
362
- const latinAmericanCountries = [
363
- 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
364
- 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
365
- 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
366
- 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
367
- ];
368
-
369
- if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
370
- return '#0f1218';
371
- } else {
372
- return 'transparent';
373
- }
374
- })
375
- .attr('stroke-width', 1);
376
-
377
- tooltip.style('opacity', 0);
378
- });
379
- } else {
380
- // Draw only the Latin American features
381
- svg.selectAll('.country')
382
- .data(latinAmericanFeatures)
383
- .enter()
384
- .append('path')
385
- .attr('class', 'country')
386
- .attr('d', path)
387
- .attr('fill', function(d) {
388
- if (d.id && countryData[d.id]) {
389
- return colorScale(countryData[d.id].percent);
390
- } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
391
- return colorScale(countryData[d.properties.iso_a3].percent);
392
- } else {
393
- return colorScale(50); // Default to mid-range if no match
394
- }
395
- })
396
- .attr('stroke', '#0f1218')
397
- .attr('stroke-width', 1)
398
- .on('mouseover', function(event, d) {
399
- if (d.id && countryData[d.id]) {
400
- const iso = d.id;
401
- showTooltip(event, iso);
402
- } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
403
- const iso = d.properties.iso_a3;
404
- showTooltip(event, iso);
405
- }
406
- })
407
- .on('mousemove', function(event) {
408
- tooltip.style('left', (event.pageX + 15) + 'px')
409
- .style('top', (event.pageY + 15) + 'px');
410
- })
411
- .on('mouseout', function() {
412
- d3.select(this)
413
- .attr('stroke', '#0f1218')
414
- .attr('stroke-width', 1);
415
-
416
- tooltip.style('opacity', 0);
417
- });
418
- }
419
- } else {
420
- // Draw only our target countries
421
- svg.selectAll('.country')
422
- .data(relevantFeatures)
423
- .enter()
424
- .append('path')
425
- .attr('class', 'country')
426
- .attr('d', path)
427
- .attr('fill', function(d) {
428
- const iso = d.id;
429
- return colorScale(countryData[iso].percent);
430
- })
431
- .attr('stroke', '#0f1218')
432
- .attr('stroke-width', 1)
433
- .on('mouseover', function(event, d) {
434
- const iso = d.id;
435
- showTooltip(event, iso);
436
- })
437
- .on('mousemove', function(event) {
438
- tooltip.style('left', (event.pageX + 15) + 'px')
439
- .style('top', (event.pageY + 15) + 'px');
440
- })
441
- .on('mouseout', function() {
442
- d3.select(this)
443
- .attr('stroke', '#0f1218')
444
- .attr('stroke-width', 1);
445
-
446
- tooltip.style('opacity', 0);
447
- });
448
- }
449
-
450
- // Function to show tooltip
451
- function showTooltip(event, iso) {
452
- d3.select(event.currentTarget)
453
- .attr('stroke', '#fff')
454
- .attr('stroke-width', 1.5);
455
-
456
- tooltip.style('opacity', 1)
457
- .style('left', (event.pageX + 15) + 'px')
458
- .style('top', (event.pageY + 15) + 'px')
459
- .html('<strong>' + countryData[iso].name + '</strong><br/>' +
460
- 'Progress: ' + countryData[iso].percent + '%');
461
- }
462
-
463
- // Add a legend on the right side of the map
464
- const legendWidth = 200;
465
- const legendHeight = 15;
466
- const legendX = width - legendWidth - 20;
467
- const legendY = 20;
468
-
469
- // Create legend group
470
- const legend = svg.append('g')
471
- .attr('transform', 'translate(' + legendX + ',' + legendY + ')');
472
-
473
- // Legend title
474
- legend.append('text')
475
- .attr('x', legendWidth / 2)
476
- .attr('y', -5)
477
- .attr('text-anchor', 'middle')
478
- .style('fill', '#fff')
479
- .style('font-size', '12px')
480
- .text('Porcentaje de Datos Recolectado');
481
-
482
- // Create gradient for legend
483
- const defs = svg.append('defs');
484
- const gradient = defs.append('linearGradient')
485
- .attr('id', 'legendGradient')
486
- .attr('x1', '0%')
487
- .attr('x2', '100%')
488
- .attr('y1', '0%')
489
- .attr('y2', '0%');
490
-
491
- gradient.append('stop')
492
- .attr('offset', '0%')
493
- .attr('stop-color', '#4a1942');
494
-
495
- gradient.append('stop')
496
- .attr('offset', '100%')
497
- .attr('stop-color', '#f32b7b');
498
-
499
- // Add legend rectangle
500
- legend.append('rect')
501
- .attr('width', legendWidth)
502
- .attr('height', legendHeight)
503
- .style('fill', 'url(#legendGradient)')
504
- .style('stroke', 'none');
505
-
506
- // Add min and max labels
507
- legend.append('text')
508
- .attr('x', 0)
509
- .attr('y', legendHeight + 15)
510
- .attr('text-anchor', 'start')
511
- .style('fill', '#fff')
512
- .style('font-size', '12px')
513
- .text('0%');
514
-
515
- legend.append('text')
516
- .attr('x', legendWidth / 2)
517
- .attr('y', legendHeight + 15)
518
- .attr('text-anchor', 'middle')
519
- .style('fill', '#fff')
520
- .style('font-size', '12px')
521
- .text('50%');
522
-
523
- legend.append('text')
524
- .attr('x', legendWidth)
525
- .attr('y', legendHeight + 15)
526
- .attr('text-anchor', 'end')
527
- .style('fill', '#fff')
528
- .style('font-size', '12px')
529
- .text('100%');
530
-
531
- // Update statistics
532
- updateStatistics();
533
- })
534
- .catch(function(error) {
535
- console.error('Error loading or rendering the map:', error);
536
- container.innerHTML = '<div style="color: white; text-align: center; padding: 20px;">Error loading map: ' + error.message + '</div>';
537
- });
538
-
539
- // Function to update statistics
540
- function updateStatistics() {
541
- console.log('Updating statistics');
542
-
543
- // Add random document counts to countries that don't have them
544
- Object.keys(countryData).forEach(code => {
545
- if (!countryData[code].documents) {
546
- countryData[code].documents = Math.floor(Math.random() * 300000) + 300000;
547
- }
548
- });
549
-
550
- // Calculate total documents
551
- const totalDocs = Object.values(countryData).reduce((sum, country) => {
552
- return sum + (country.documents || 0);
553
- }, 0);
554
-
555
- // Calculate average percentage
556
- const avgPercent = Object.values(countryData).reduce((sum, country) => {
557
- return sum + country.percent;
558
- }, 0) / Object.values(countryData).length;
559
-
560
- // Update the stats
561
- document.getElementById('total-docs').textContent = totalDocs.toLocaleString();
562
- document.getElementById('avg-percent').textContent = avgPercent.toFixed(1) + '%';
563
-
564
- // Create an array of countries with document counts
565
- const countriesWithDocs = Object.keys(countryData).map(code => {
566
- return {
567
- code: code,
568
- name: countryData[code].name,
569
- percent: countryData[code].percent,
570
- documents: countryData[code].documents
571
- };
572
- });
573
-
574
- // Sort by document count descending
575
- countriesWithDocs.sort((a, b) => b.documents - a.documents);
576
-
577
- // Take the top 5
578
- const topCountries = countriesWithDocs.slice(0, 5);
579
-
580
- // Update the top countries list
581
- const topCountriesList = document.getElementById('top-countries-list');
582
- topCountriesList.innerHTML = '';
583
-
584
- topCountries.forEach(country => {
585
- const countryDiv = document.createElement('div');
586
- countryDiv.className = 'country-stat';
587
-
588
- countryDiv.innerHTML = `
589
- <span>${country.name}</span>
590
- <div class="country-bar">
591
- <div class="country-bar-fill" style="width: ${country.percent}%;"></div>
592
- </div>
593
- <span class="country-value">${country.documents.toLocaleString()}</span>
594
- `;
595
-
596
- topCountriesList.appendChild(countryDiv);
597
- });
598
-
599
- console.log('Statistics updated');
600
- }
601
-
602
- // Handle window resize
603
- window.addEventListener('resize', function() {
604
- console.log('Window resized');
605
-
606
- const width = container.clientWidth;
607
- const height = container.clientHeight;
608
-
609
- // Update SVG dimensions
610
- d3.select('svg')
611
- .attr('width', width)
612
- .attr('height', height);
613
-
614
- // Update projection
615
- projection.scale(width / 3)
616
- .translate([width / 2, height / 2]);
617
-
618
- // Update paths
619
- d3.selectAll('path').attr('d', path);
620
-
621
- // Update legend position
622
- const legendX = width - 220;
623
- d3.select('.legend')
624
- .attr('transform', 'translate(' + legendX + ',20)');
625
- });
626
- });
627
- </script>
628
- </body>
629
- </html>
630
- """
631
 
632
  # Route to serve the map visualization
633
  @app.get("/d3-map")
634
  async def serve_map():
635
- # Generate random data for each country with document counts
636
- country_data = generate_data()
637
-
638
- # Add random document counts
639
- for code in country_data:
640
- country_data[code]["documents"] = random.randint(300000, 700000)
641
 
642
  # Convert to JSON for JavaScript
643
  country_data_json = json.dumps(country_data)
644
 
645
  # Replace the placeholder with actual data
646
- html_content = HTML_TEMPLATE.replace("COUNTRY_DATA_PLACEHOLDER", country_data_json)
 
 
 
647
 
648
  return fastapi.responses.HTMLResponse(content=html_content)
649
 
@@ -663,7 +132,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="pink", secondary_hue="purple"))
663
  def refresh():
664
  return create_iframe()
665
 
666
- gr.Button("Generate New Data").click(fn=refresh, outputs=iframe_output)
667
 
668
  # Mount the Gradio app to the FastAPI app
669
  gr.mount_gradio_app(app, demo, path="/")
 
3
  import json
4
  import fastapi
5
  from fastapi import FastAPI
6
+ import os
7
+ import argilla as rg
8
 
9
+ # Initialize Argilla client
10
+ client = rg.Argilla(
11
+ api_url=os.getenv("ARGILLA_API_URL", ""),
12
+ api_key=os.getenv("ARGILLA_API_KEY", "")
13
+ )
14
 
15
+ # List of countries to check
16
+ COUNTRIES = [
17
+ "MEX", "ARG", "COL", "CHL", "PER", "ESP", "BRA",
18
+ "VEN", "ECU", "BOL", "PRY", "URY", "CRI", "PAN",
19
+ "DOM", "GTM", "HND", "SLV", "NIC", "CUB"
20
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ def count_answers_per_space(country: str):
23
+ """
24
+ Count the number of answers for a specific country's answering space.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ Args:
27
+ country: Country code (e.g., "CHL")
28
+
29
+ Returns:
30
+ Dictionary with statistics about answers in the space
31
+ """
32
+ # Convert country code to full country name
33
+ country_mapping = {
34
+ "MEX": "Mexico", "ARG": "Argentina", "COL": "Colombia",
35
+ "CHL": "Chile", "PER": "Peru", "ESP": "Spain",
36
+ "BRA": "Brazil", "VEN": "Venezuela", "ECU": "Ecuador",
37
+ "BOL": "Bolivia", "PRY": "Paraguay", "URY": "Uruguay",
38
+ "CRI": "Costa Rica", "PAN": "Panama", "DOM": "Dominican Republic",
39
+ "GTM": "Guatemala", "HND": "Honduras", "SLV": "El Salvador",
40
+ "NIC": "Nicaragua", "CUB": "Cuba"
41
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ full_country_name = country_mapping.get(country, country)
44
+ dataset_name = f"{full_country_name}_responder_preguntas"
45
 
46
+ try:
47
+ dataset = client.datasets(dataset_name)
 
48
 
49
+ # Get all records with their responses
50
+ records = dataset.records(with_responses=True)
51
+
52
+ # Count total questions
53
+ total_questions = len(list(records))
54
+
55
+ # Count answered questions
56
+ answered_questions = 0
57
+ total_answers = 0
58
+ answers_per_user = {}
59
+
60
+ for record in records:
61
+ responses = record.responses.get("text", [])
62
+ if responses:
63
+ answered_questions += 1
64
+ total_answers += len(responses)
65
+
66
+ # Count per user
67
+ for response in responses:
68
+ user_id = str(response.user_id)
69
+ if user_id in answers_per_user:
70
+ answers_per_user[user_id] += 1
71
+ else:
72
+ answers_per_user[user_id] = 1
73
+
74
+ percentage_complete = (answered_questions / total_questions * 100) if total_questions > 0 else 0
75
+
76
+ return {
77
+ "name": full_country_name,
78
+ "total_questions": total_questions,
79
+ "answered_questions": answered_questions,
80
+ "total_answers": total_answers,
81
+ "percent": round(percentage_complete, 2),
82
+ "documents": total_answers * 10 # Approximation of document count
83
+ }
84
+
85
+ except Exception as e:
86
+ # If space doesn't exist, return zero values
87
+ print(f"No dataset found for {dataset_name}: {e}")
88
+ return {
89
+ "name": full_country_name,
90
+ "total_questions": 0,
91
+ "answered_questions": 0,
92
+ "total_answers": 0,
93
+ "percent": 0,
94
+ "documents": 0
95
+ }
96
+
97
+ # Create a FastAPI app
98
+ app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  # Route to serve the map visualization
101
  @app.get("/d3-map")
102
  async def serve_map():
103
+ # Generate data for each country by querying Argilla
104
+ country_data = {}
105
+ for country_code in COUNTRIES:
106
+ country_data[country_code] = count_answers_per_space(country_code)
 
 
107
 
108
  # Convert to JSON for JavaScript
109
  country_data_json = json.dumps(country_data)
110
 
111
  # Replace the placeholder with actual data
112
+ with open('template.txt', 'r') as f:
113
+ html_template = f.read()
114
+
115
+ html_content = html_template.replace("COUNTRY_DATA_PLACEHOLDER", country_data_json)
116
 
117
  return fastapi.responses.HTMLResponse(content=html_content)
118
 
 
132
  def refresh():
133
  return create_iframe()
134
 
135
+ gr.Button("Actualizar Datos").click(fn=refresh, outputs=iframe_output)
136
 
137
  # Mount the Gradio app to the FastAPI app
138
  gr.mount_gradio_app(app, demo, path="/")