soury commited on
Commit
0662adb
·
1 Parent(s): beee3d3

better handling of dynamic sections to keep in memory fields that are already filled

Browse files
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
- return message, file_path, json_output
 
 
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
- algorithms_list = []
42
- algorithm_fields = {"trainingType": trainingType, "algorithmType": algorithmType, "algorithmName": algorithmName, "algorithmUri": algorithmUri, "foundationModelName": foundationModelName, "foundationModelUri": foundationModelUri,
43
- "parametersNumber": parametersNumber, "framework": framework, "frameworkVersion": frameworkVersion, "classPath": classPath, "layersNumber": layersNumber, "epochsNumber": epochsNumber, "optimizer": optimizer, "quantization": quantization}
44
- nb_algo = 0
45
- # ça ça marche pas
46
- for f in algorithm_fields:
47
- nb_algo = max(nb_algo, len(algorithm_fields[f]))
48
- for i in range(nb_algo):
49
- algortithm = {}
50
- for f in algorithm_fields:
51
- if i < len(algorithm_fields[f]) and algorithm_fields[f][i]:
52
- algortithm[f] = algorithm_fields[f][i]
53
- algorithms_list.append(algortithm)
54
-
55
- # Process dataset
56
- dataset_list = []
57
- dataset_fields = {"dataUsage": dataUsage, "dataType": dataType, "dataFormat": dataFormat, "dataSize": dataSize,
58
- "dataQuantity": dataQuantity, "shape": shape, "source": source, "sourceUri": sourceUri, "owner": owner}
59
- nb_data = 0
60
- for f in dataset_fields:
61
- nb_data = max(nb_data, len(dataset_fields[f]))
62
- for i in range(nb_data):
63
- data = {}
64
- for f in dataset_fields:
65
- if i < len(dataset_fields[f]) and dataset_fields[f][i]:
66
- data[f] = dataset_fields[f][i]
67
- dataset_list.append(data)
68
-
69
- # Process measures
70
- measures_list = []
71
- measure_fields = {"measurementMethod": measurementMethod, "manufacturer": manufacturer, "version": version, "cpuTrackingMode": cpuTrackingMode,
72
- "gpuTrackingMode": gpuTrackingMode, "averageUtilizationCpu": averageUtilizationCpu, "averageUtilizationGpu": averageUtilizationGpu,
73
- "powerCalibrationMeasurement": powerCalibrationMeasurement, "durationCalibrationMeasurement": durationCalibrationMeasurement,
74
- "powerConsumption": powerConsumption, "measurementDuration": measurementDuration, "measurementDateTime": measurementDateTime}
75
- nb_measures = 0
76
- for f in measure_fields:
77
- nb_measures = max(nb_measures, len(measure_fields[f]))
78
- for i in range(nb_measures):
79
- measure = {}
80
- for f in measure_fields:
81
- if i < len(measure_fields[f]) and measure_fields[f][i]:
82
- measure[f] = measure_fields[f][i]
83
- measures_list.append(measure)
84
-
85
- # Process components
86
- components_list = []
87
- component_fields = {"componentName": componentName, "componentType": componentType, "nbComponent": nbComponent,
88
- "memorySize": memorySize, "manufacturer_infra": manufacturer_infra, "family": family,
89
- "series": series, "share": share}
90
- nb_components = 0
91
- for f in component_fields:
92
- nb_components = max(nb_components, len(component_fields[f]))
93
- for i in range(nb_components):
94
- component = {}
95
- for f in component_fields:
96
- if i < len(component_fields[f]) and component_fields[f][i]:
97
- component[f] = component_fields[f][i]
98
- components_list.append(component)
 
 
 
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 in a Gradio interface where users can add or remove rows of input fields.
 
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 in the section.
19
- Each dictionary should include:
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
- # State management
36
- # Tracks the number of rows in the section.
37
- count_state = gr.State(value=initial_count+1)
38
- # Stores the values for each field across all rows.
39
  field_states = [gr.State([]) for _ in fields_config]
40
- # A list to store all dynamically generated components.
 
 
 
 
 
41
  all_components = []
42
 
43
- def update_fields(*states_and_values):
44
- """
45
- Updates the state of the fields when a value changes.
46
-
47
- Args:
48
- *states_and_values: A combination of the current states and the new values for the fields.
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 = [] # Components for the current row.
88
- field_refs = [] # References to the current row's components.
89
 
90
  for field_idx, config in enumerate(fields_config):
91
- # Create a component for the field using its configuration.
92
  component = config["type"](
93
- label=f"{config['label']} ({section_name}{i + 1})",
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
- # Create a change event to update the field states when the value changes.
 
 
 
 
 
103
  component.change(
104
- fn=update_fields,
105
- inputs=[*field_states, *field_refs, gr.State(i)],
106
- outputs=field_states
107
  )
108
 
109
- # Add a "Remove" button to delete the current row.
110
- remove_btn = gr.Button("❌", variant="secondary")
111
- remove_btn.click(
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
- # Add the row's components to the list of all components.
123
- all_components.extend(row_components)
124
- return all_components
125
 
126
- # Initialize the section with the initial count of rows.
127
- render_dynamic_section(count=initial_count)
128
 
129
- # Create an "Add" button to add new rows to the section.
130
  add_btn = gr.Button(f"Add {section_name}")
131
- add_btn.click(lambda x: x + 1, count_state, count_state)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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=0,
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=0,
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=0,
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=0,
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