Spaces:
Sleeping
Sleeping
better handling of dynamic sections to keep in memory fields that are already filled
Browse files- app.py +3 -1
- src/services/huggingface.py +0 -4
- src/services/json_generator.py +93 -59
- src/ui/form_components.py +112 -93
app.py
CHANGED
@@ -23,7 +23,9 @@ def handle_submit(*inputs):
|
|
23 |
|
24 |
# Check if the message indicates validation failure
|
25 |
if message.startswith("The following fields are required"):
|
26 |
-
|
|
|
|
|
27 |
|
28 |
publish_button = gr.Button(
|
29 |
"Share your data to the public repository", interactive=True, elem_classes="pubbutton")
|
|
|
23 |
|
24 |
# Check if the message indicates validation failure
|
25 |
if message.startswith("The following fields are required"):
|
26 |
+
publish_button = gr.Button(
|
27 |
+
"Share your data to the public repository", interactive=False, elem_classes="pubbutton")
|
28 |
+
return message, file_path, json_output, publish_button
|
29 |
|
30 |
publish_button = gr.Button(
|
31 |
"Share your data to the public repository", interactive=True, elem_classes="pubbutton")
|
src/services/huggingface.py
CHANGED
@@ -1,10 +1,6 @@
|
|
1 |
from huggingface_hub import HfApi, login
|
2 |
-
import json
|
3 |
from src.services.util import HF_TOKEN, DATASET_NAME
|
4 |
-
import tempfile
|
5 |
import os
|
6 |
-
import json
|
7 |
-
import re
|
8 |
|
9 |
|
10 |
def init_huggingface():
|
|
|
1 |
from huggingface_hub import HfApi, login
|
|
|
2 |
from src.services.util import HF_TOKEN, DATASET_NAME
|
|
|
3 |
import os
|
|
|
|
|
4 |
|
5 |
|
6 |
def init_huggingface():
|
src/services/json_generator.py
CHANGED
@@ -7,6 +7,37 @@ import tempfile
|
|
7 |
import os
|
8 |
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
def generate_json(
|
11 |
# Header
|
12 |
licensing, formatVersion, formatVersionSpecificationUri, reportId,
|
@@ -37,65 +68,68 @@ def generate_json(
|
|
37 |
quality
|
38 |
):
|
39 |
"""Generate JSON data from form inputs."""
|
40 |
-
# Process algorithms
|
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 |
# process report
|
101 |
report = {}
|
|
|
7 |
import os
|
8 |
|
9 |
|
10 |
+
def process_component_list(fields_dict):
|
11 |
+
"""
|
12 |
+
Fonction générique pour traiter une liste de composants à partir d'un dictionnaire de champs.
|
13 |
+
|
14 |
+
Args:
|
15 |
+
fields_dict (dict): Dictionnaire où les clés sont les noms des champs
|
16 |
+
et les valeurs sont des listes de valeurs.
|
17 |
+
|
18 |
+
Returns:
|
19 |
+
list: Liste de dictionnaires représentant les composants.
|
20 |
+
"""
|
21 |
+
component_list = []
|
22 |
+
|
23 |
+
# Trouver le nombre maximum d'éléments parmi tous les champs
|
24 |
+
max_items = 0
|
25 |
+
for field_values in fields_dict.values():
|
26 |
+
if field_values:
|
27 |
+
max_items = max(max_items, len(field_values))
|
28 |
+
|
29 |
+
# Créer les composants
|
30 |
+
for i in range(max_items):
|
31 |
+
component = {}
|
32 |
+
for field_name, field_values in fields_dict.items():
|
33 |
+
if i < len(field_values) and field_values[i]:
|
34 |
+
component[field_name] = field_values[i]
|
35 |
+
if component: # Ajouter seulement si le composant n'est pas vide
|
36 |
+
component_list.append(component)
|
37 |
+
|
38 |
+
return component_list
|
39 |
+
|
40 |
+
|
41 |
def generate_json(
|
42 |
# Header
|
43 |
licensing, formatVersion, formatVersionSpecificationUri, reportId,
|
|
|
68 |
quality
|
69 |
):
|
70 |
"""Generate JSON data from form inputs."""
|
71 |
+
# Process algorithms using the generic function
|
72 |
+
algorithm_fields = {
|
73 |
+
"trainingType": trainingType,
|
74 |
+
"algorithmType": algorithmType,
|
75 |
+
"algorithmName": algorithmName,
|
76 |
+
"algorithmUri": algorithmUri,
|
77 |
+
"foundationModelName": foundationModelName,
|
78 |
+
"foundationModelUri": foundationModelUri,
|
79 |
+
"parametersNumber": parametersNumber,
|
80 |
+
"framework": framework,
|
81 |
+
"frameworkVersion": frameworkVersion,
|
82 |
+
"classPath": classPath,
|
83 |
+
"layersNumber": layersNumber,
|
84 |
+
"epochsNumber": epochsNumber,
|
85 |
+
"optimizer": optimizer,
|
86 |
+
"quantization": quantization
|
87 |
+
}
|
88 |
+
algorithms_list = process_component_list(algorithm_fields)
|
89 |
+
|
90 |
+
# Process dataset using the generic function
|
91 |
+
dataset_fields = {
|
92 |
+
"dataUsage": dataUsage,
|
93 |
+
"dataType": dataType,
|
94 |
+
"dataFormat": dataFormat,
|
95 |
+
"dataSize": dataSize,
|
96 |
+
"dataQuantity": dataQuantity,
|
97 |
+
"shape": shape,
|
98 |
+
"source": source,
|
99 |
+
"sourceUri": sourceUri,
|
100 |
+
"owner": owner
|
101 |
+
}
|
102 |
+
dataset_list = process_component_list(dataset_fields)
|
103 |
+
|
104 |
+
# Process measures using the generic function
|
105 |
+
measure_fields = {
|
106 |
+
"measurementMethod": measurementMethod,
|
107 |
+
"manufacturer": manufacturer,
|
108 |
+
"version": version,
|
109 |
+
"cpuTrackingMode": cpuTrackingMode,
|
110 |
+
"gpuTrackingMode": gpuTrackingMode,
|
111 |
+
"averageUtilizationCpu": averageUtilizationCpu,
|
112 |
+
"averageUtilizationGpu": averageUtilizationGpu,
|
113 |
+
"powerCalibrationMeasurement": powerCalibrationMeasurement,
|
114 |
+
"durationCalibrationMeasurement": durationCalibrationMeasurement,
|
115 |
+
"powerConsumption": powerConsumption,
|
116 |
+
"measurementDuration": measurementDuration,
|
117 |
+
"measurementDateTime": measurementDateTime
|
118 |
+
}
|
119 |
+
measures_list = process_component_list(measure_fields)
|
120 |
+
|
121 |
+
# Process components using the generic function
|
122 |
+
component_fields = {
|
123 |
+
"componentName": componentName,
|
124 |
+
"componentType": componentType,
|
125 |
+
"nbComponent": nbComponent,
|
126 |
+
"memorySize": memorySize,
|
127 |
+
"manufacturer": manufacturer_infra,
|
128 |
+
"family": family,
|
129 |
+
"series": series,
|
130 |
+
"share": share
|
131 |
+
}
|
132 |
+
components_list = process_component_list(component_fields)
|
133 |
|
134 |
# process report
|
135 |
report = {}
|
src/ui/form_components.py
CHANGED
@@ -11,124 +11,143 @@ from src.services.util import (
|
|
11 |
|
12 |
def create_dynamic_section(section_name, fields_config, initial_count=1, layout="row"):
|
13 |
"""
|
14 |
-
Creates a dynamic section
|
|
|
15 |
|
16 |
Args:
|
17 |
section_name (str): The name of the section (e.g., "Algorithms", "Components").
|
18 |
-
fields_config (list): A list of dictionaries defining the configuration for each field
|
19 |
-
|
20 |
-
- "type": The Gradio component type (e.g., gr.Textbox, gr.Number).
|
21 |
-
- "label": The label for the field.
|
22 |
-
- "info": Additional information or tooltip for the field.
|
23 |
-
- "value" (optional): The default value for the field.
|
24 |
-
- "kwargs" (optional): Additional keyword arguments for the component.
|
25 |
-
- "elem_classes" (optional): CSS classes for styling the field.
|
26 |
-
initial_count (int): The initial number of rows to render in the section.
|
27 |
layout (str): The layout of the fields in each row ("row" or "column").
|
28 |
|
29 |
Returns:
|
30 |
-
tuple: A tuple containing
|
31 |
-
- count_state: A Gradio state object tracking the number of rows.
|
32 |
-
- field_states: A list of Gradio state objects, one for each field, to store the values of the fields.
|
33 |
-
- add_btn: The "Add" button component for adding new rows.
|
34 |
"""
|
35 |
-
#
|
36 |
-
|
37 |
-
|
38 |
-
#
|
39 |
field_states = [gr.State([]) for _ in fields_config]
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
41 |
all_components = []
|
42 |
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
Returns:
|
51 |
-
tuple: Updated states for all fields.
|
52 |
-
"""
|
53 |
-
# Split states and current values
|
54 |
-
# Extract the current states for each field.
|
55 |
-
states = list(states_and_values[:len(fields_config)])
|
56 |
-
# Extract the new values for the fields.
|
57 |
-
current_values = states_and_values[len(fields_config):-1]
|
58 |
-
index = states_and_values[-1] # The index of the row being updated.
|
59 |
-
|
60 |
-
# Update each field's state
|
61 |
-
for field_idx, (state, value) in enumerate(zip(states, current_values)):
|
62 |
-
# Ensure the state list is long enough to accommodate the current index.
|
63 |
-
while len(state) <= index:
|
64 |
-
state.append("")
|
65 |
-
# Update the value at the specified index.
|
66 |
-
state[index] = value if value is not None else ""
|
67 |
-
|
68 |
-
return tuple(states)
|
69 |
-
|
70 |
-
@gr.render(inputs=count_state)
|
71 |
-
def render_dynamic_section(count):
|
72 |
-
"""
|
73 |
-
Renders the dynamic section with the current number of rows and their states.
|
74 |
-
|
75 |
-
Args:
|
76 |
-
count (int): The number of rows to render.
|
77 |
-
|
78 |
-
Returns:
|
79 |
-
list: A list of dynamically generated components for the section.
|
80 |
-
"""
|
81 |
-
nonlocal all_components
|
82 |
-
all_components = [] # Reset the list of components for re-rendering.
|
83 |
-
|
84 |
-
for i in range(count):
|
85 |
-
# Create a row or column layout for the current row of fields.
|
86 |
with (gr.Row() if layout == "row" else gr.Column()):
|
87 |
-
row_components = []
|
88 |
-
field_refs = [] # References to the current row's components.
|
89 |
|
90 |
for field_idx, config in enumerate(fields_config):
|
91 |
-
# Create
|
92 |
component = config["type"](
|
93 |
-
label=f"{config['label']} ({section_name}{
|
94 |
info=config.get("info", ""),
|
95 |
value=config.get("value", ""),
|
96 |
**config.get("kwargs", {}),
|
97 |
elem_classes=config.get("elem_classes", "")
|
98 |
)
|
99 |
row_components.append(component)
|
100 |
-
field_refs.append(component)
|
101 |
|
102 |
-
#
|
|
|
|
|
|
|
|
|
|
|
103 |
component.change(
|
104 |
-
fn=
|
105 |
-
inputs=[
|
106 |
-
outputs=
|
107 |
)
|
108 |
|
109 |
-
# Add
|
110 |
-
remove_btn = gr.Button(
|
111 |
-
|
112 |
-
lambda x, idx=i, fs=field_states: (
|
113 |
-
max(0, x - 1), # Decrease the count of rows.
|
114 |
-
# Remove the row's values.
|
115 |
-
*[fs[i].value[:idx] + fs[i].value[idx + 1:] for i in range(len(fs))]
|
116 |
-
),
|
117 |
-
inputs=count_state,
|
118 |
-
outputs=[count_state, *field_states]
|
119 |
-
)
|
120 |
row_components.append(remove_btn)
|
121 |
|
122 |
-
|
123 |
-
all_components.extend(row_components)
|
124 |
-
return all_components
|
125 |
|
126 |
-
#
|
127 |
-
|
128 |
|
129 |
-
#
|
130 |
add_btn = gr.Button(f"Add {section_name}")
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
|
133 |
return (count_state, *field_states, add_btn)
|
134 |
|
@@ -265,7 +284,7 @@ def create_task_tab():
|
|
265 |
"info": "(the type of quantization used : fp32, fp16, b16, int8 ...)",
|
266 |
}
|
267 |
],
|
268 |
-
initial_count=
|
269 |
layout="column"
|
270 |
)
|
271 |
|
@@ -329,7 +348,7 @@ def create_task_tab():
|
|
329 |
"info": "(the owner of the dataset if available)",
|
330 |
}
|
331 |
],
|
332 |
-
initial_count=
|
333 |
layout="column"
|
334 |
)
|
335 |
|
@@ -427,7 +446,7 @@ def create_measures_tab():
|
|
427 |
"info": "(the date when the measurement began, in format YYYY-MM-DD HH:MM:SS)",
|
428 |
}
|
429 |
],
|
430 |
-
initial_count=
|
431 |
layout="column"
|
432 |
)
|
433 |
|
@@ -526,7 +545,7 @@ def create_infrastructure_tab():
|
|
526 |
"info": "(the percentage of the physical equipment used by the task, this sharing property should be set to 1 by default (if no share) and otherwise to the correct percentage, e.g. 0.5 if you share half-time.)",
|
527 |
}
|
528 |
],
|
529 |
-
initial_count=
|
530 |
layout="column"
|
531 |
)
|
532 |
|
|
|
11 |
|
12 |
def create_dynamic_section(section_name, fields_config, initial_count=1, layout="row"):
|
13 |
"""
|
14 |
+
Creates a simplified dynamic section with a fixed number of pre-created rows.
|
15 |
+
This approach prioritizes data preservation over true dynamic functionality.
|
16 |
|
17 |
Args:
|
18 |
section_name (str): The name of the section (e.g., "Algorithms", "Components").
|
19 |
+
fields_config (list): A list of dictionaries defining the configuration for each field.
|
20 |
+
initial_count (int): The initial number of rows to show (up to MAX_ROWS).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
layout (str): The layout of the fields in each row ("row" or "column").
|
22 |
|
23 |
Returns:
|
24 |
+
tuple: A tuple containing states and the add button, compatible with existing code.
|
|
|
|
|
|
|
25 |
"""
|
26 |
+
# Fixed number of rows - simple but reliable approach
|
27 |
+
MAX_ROWS = 5
|
28 |
+
|
29 |
+
# Create field states for compatibility with existing code
|
30 |
field_states = [gr.State([]) for _ in fields_config]
|
31 |
+
|
32 |
+
# Initialize field states with empty values for all possible rows
|
33 |
+
for field_state in field_states:
|
34 |
+
field_state.value = [""] * MAX_ROWS
|
35 |
+
|
36 |
+
# Create all rows upfront (some hidden initially)
|
37 |
all_components = []
|
38 |
|
39 |
+
for row_idx in range(MAX_ROWS):
|
40 |
+
# Use accordion instead of Group for better visibility control
|
41 |
+
# Show only initial_count rows at the beginning
|
42 |
+
is_visible = row_idx < initial_count
|
43 |
+
|
44 |
+
# Use accordion that's open for visible rows
|
45 |
+
with gr.Accordion(f"{section_name} {row_idx + 1}", open=is_visible, visible=is_visible) as group:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
with (gr.Row() if layout == "row" else gr.Column()):
|
47 |
+
row_components = []
|
|
|
48 |
|
49 |
for field_idx, config in enumerate(fields_config):
|
50 |
+
# Create component
|
51 |
component = config["type"](
|
52 |
+
label=f"{config['label']} ({section_name} {row_idx + 1})",
|
53 |
info=config.get("info", ""),
|
54 |
value=config.get("value", ""),
|
55 |
**config.get("kwargs", {}),
|
56 |
elem_classes=config.get("elem_classes", "")
|
57 |
)
|
58 |
row_components.append(component)
|
|
|
59 |
|
60 |
+
# Simple change handler that updates field state
|
61 |
+
def make_change_handler(field_idx, row_idx):
|
62 |
+
def handle_change(value):
|
63 |
+
field_states[field_idx].value[row_idx] = value if value is not None else ""
|
64 |
+
return handle_change
|
65 |
+
|
66 |
component.change(
|
67 |
+
fn=make_change_handler(field_idx, row_idx),
|
68 |
+
inputs=[component],
|
69 |
+
outputs=[]
|
70 |
)
|
71 |
|
72 |
+
# Add remove button for this row
|
73 |
+
remove_btn = gr.Button(
|
74 |
+
"❌ Remove", variant="secondary", size="sm", visible=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
row_components.append(remove_btn)
|
76 |
|
77 |
+
all_components.append((group, row_components))
|
|
|
|
|
78 |
|
79 |
+
# Visibility state
|
80 |
+
visible_count = gr.State(initial_count)
|
81 |
|
82 |
+
# Add button
|
83 |
add_btn = gr.Button(f"Add {section_name}")
|
84 |
+
|
85 |
+
def handle_add(current_count):
|
86 |
+
"""Show one more row if available"""
|
87 |
+
new_count = min(current_count + 1, MAX_ROWS)
|
88 |
+
|
89 |
+
# Update visibility for all groups
|
90 |
+
visibility_updates = []
|
91 |
+
for i in range(MAX_ROWS):
|
92 |
+
# For accordion, we need to update both visible and open states
|
93 |
+
visibility_updates.append(
|
94 |
+
gr.update(visible=(i < new_count), open=(i < new_count)))
|
95 |
+
|
96 |
+
return new_count, *visibility_updates
|
97 |
+
|
98 |
+
def handle_remove(current_count):
|
99 |
+
"""Hide the last visible row"""
|
100 |
+
new_count = max(current_count - 1,
|
101 |
+
1) # Always keep at least 1 row visible
|
102 |
+
|
103 |
+
# Update visibility for all groups
|
104 |
+
visibility_updates = []
|
105 |
+
for i in range(MAX_ROWS):
|
106 |
+
# For accordion, we need to update both visible and open states
|
107 |
+
visibility_updates.append(
|
108 |
+
gr.update(visible=(i < new_count), open=(i < new_count)))
|
109 |
+
|
110 |
+
return new_count, *visibility_updates
|
111 |
+
|
112 |
+
# Connect add button
|
113 |
+
group_outputs = [group for group, _ in all_components]
|
114 |
+
add_btn.click(
|
115 |
+
fn=handle_add,
|
116 |
+
inputs=[visible_count],
|
117 |
+
outputs=[visible_count] + group_outputs
|
118 |
+
)
|
119 |
+
|
120 |
+
# Connect remove buttons for each row
|
121 |
+
for row_idx, (group, row_components) in enumerate(all_components):
|
122 |
+
remove_btn = row_components[-1] # Remove button is the last component
|
123 |
+
remove_btn.click(
|
124 |
+
fn=handle_remove,
|
125 |
+
inputs=[visible_count],
|
126 |
+
outputs=[visible_count] + group_outputs
|
127 |
+
)
|
128 |
+
|
129 |
+
# Force initial visibility on interface load
|
130 |
+
def force_initial_visibility():
|
131 |
+
"""Force initial visibility when the interface loads"""
|
132 |
+
visibility_updates = []
|
133 |
+
for i in range(MAX_ROWS):
|
134 |
+
visibility_updates.append(
|
135 |
+
gr.update(visible=(i < initial_count), open=(i < initial_count)))
|
136 |
+
return visibility_updates
|
137 |
+
|
138 |
+
# Create a simple info display
|
139 |
+
info_display = gr.Markdown(f"**{section_name}** (Max {MAX_ROWS} items)")
|
140 |
+
|
141 |
+
# Dummy count state for compatibility
|
142 |
+
count_state = gr.State(initial_count)
|
143 |
+
|
144 |
+
# Apply initial visibility immediately after component creation
|
145 |
+
if initial_count > 0:
|
146 |
+
# Use app load event to ensure visibility
|
147 |
+
for i, (group, _) in enumerate(all_components):
|
148 |
+
if i < initial_count:
|
149 |
+
group.visible = True
|
150 |
+
group.open = True
|
151 |
|
152 |
return (count_state, *field_states, add_btn)
|
153 |
|
|
|
284 |
"info": "(the type of quantization used : fp32, fp16, b16, int8 ...)",
|
285 |
}
|
286 |
],
|
287 |
+
initial_count=1,
|
288 |
layout="column"
|
289 |
)
|
290 |
|
|
|
348 |
"info": "(the owner of the dataset if available)",
|
349 |
}
|
350 |
],
|
351 |
+
initial_count=1,
|
352 |
layout="column"
|
353 |
)
|
354 |
|
|
|
446 |
"info": "(the date when the measurement began, in format YYYY-MM-DD HH:MM:SS)",
|
447 |
}
|
448 |
],
|
449 |
+
initial_count=1,
|
450 |
layout="column"
|
451 |
)
|
452 |
|
|
|
545 |
"info": "(the percentage of the physical equipment used by the task, this sharing property should be set to 1 by default (if no share) and otherwise to the correct percentage, e.g. 0.5 if you share half-time.)",
|
546 |
}
|
547 |
],
|
548 |
+
initial_count=1,
|
549 |
layout="column"
|
550 |
)
|
551 |
|