scilens 0.3.9__py3-none-any.whl → 0.3.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,38 +1,42 @@
1
1
  _E='ref'
2
- _D='test'
3
- _C='name'
4
- _B=None
2
+ _D='name'
3
+ _C=None
4
+ _B='test'
5
5
  _A='path'
6
6
  import logging,os
7
7
  from scilens.run.task_context import TaskContext
8
8
  from scilens.components.compare_2_files import Compare2Files
9
- def list_dir(path,filename_match_ignore,recursive,exclude_filepaths=_B):
10
- I=exclude_filepaths;H=filename_match_ignore;G='rel_path';F='filename_clean';B='';A=path
9
+ def list_dir(path,filename_match_ignore,recursive,exclude_filepaths=_C):
10
+ H=exclude_filepaths;G=filename_match_ignore;F='rel_path';E='filename_clean';A=path
11
11
  if recursive:
12
- C=[]
13
- for(D,L,K)in os.walk(A):
14
- for J in K:C.append({_A:os.path.join(D,J),F:J.replace(str(H),B),G:D.replace(A+os.path.sep,B)if D!=A else B})
15
- E={os.path.join(A[G],A[F]):A for A in C}
16
- else:C={C.replace(str(H),B):C for C in os.listdir(A)if os.path.isfile(os.path.join(A,C))};E={C:{_A:os.path.join(A,D),F:C,G:B}for(C,D)in C.items()}
17
- return{B:A for(B,A)in E.items()if A[_A]not in I}if I else E
12
+ B=[]
13
+ for(C,K,J)in os.walk(A):
14
+ for I in J:B.append({_A:os.path.join(C,I),E:I.replace(str(G),''),F:C.replace(A+os.path.sep,'')if C!=A else''})
15
+ D={os.path.join(A[F],A[E]):A for A in B}
16
+ else:B={B.replace(str(G),''):B for B in os.listdir(A)if os.path.isfile(os.path.join(A,B))};D={B:{_A:os.path.join(A,C),E:B,F:''}for(B,C)in B.items()}
17
+ return{B:A for(B,A)in D.items()if A[_A]not in H}if H else D
18
18
  class CompareFolders:
19
- def __init__(A,context):
20
- C=context;A.context=C;B=C.config.compare.sources;A.cfg=B;A.test_base=os.path.join(C.working_dir,B.test_folder_relative_path);A.ref_base=os.path.join(C.working_dir,B.reference_folder_relative_path)
21
- if B.additional_path_suffix:A.test=os.path.join(A.test_base,B.additional_path_suffix);A.ref=os.path.join(A.ref_base,B.additional_path_suffix)
22
- else:A.test=A.test_base;A.ref=A.ref_base
19
+ def __init__(A,context):B=context;A.context=B;C=B.config.compare.sources;A.cfg=C;A.test=os.path.join(B.working_dir,C.test_folder_relative_path);A.ref=os.path.join(B.working_dir,C.reference_folder_relative_path)
23
20
  def compute_list_filenames(A):
24
- logging.info(f"Comparing folders content: test vs reference");logging.debug(f"Comparing folders content: {A.test} vs {A.ref}");E=[A.context.config_file]if A.context.config_file else _B;F=[]
25
- if A.test!=A.ref:
26
- logging.info(f"Listing files in test folder");logging.debug(f"-- test folder: {A.test}");C=list_dir(A.test,A.cfg.test_filename_match_ignore,A.cfg.recursive,exclude_filepaths=E);logging.info(f"Listing files in reference folder");logging.debug(f"-- test reference: {A.ref}");D=list_dir(A.ref,A.cfg.reference_filename_match_ignore,A.cfg.recursive,exclude_filepaths=E);G=sorted(list(set(C.keys())|set(D.keys())))
27
- for B in G:F.append({_C:B,_D:C[B][_A]if C.get(B)else _B,_E:D[B][_A]if D.get(B)else _B})
28
- return F
21
+ N='reference';I='filename_match_ignore';C='dict_files';logging.info(f"Comparing folders content: test vs reference");logging.debug(f"Comparing folders content: {A.test} vs {A.ref}")
22
+ if A.test==A.ref:logging.warning(f"Test and reference folders are the same: {A.test}. No comparison will be done.");return[]
23
+ O=[A.context.config_file]if A.context.config_file else _C;J=[];K=A.context.config.compare.sources.additional_path_suffixes or[''];E={_B:{C:{},_A:A.test,I:A.cfg.test_filename_match_ignore},N:{C:{},_A:A.ref,I:A.cfg.reference_filename_match_ignore}}
24
+ for(L,D)in E.items():
25
+ logging.info(f"Listing files in {L} folder");logging.debug(f"-- {L} folder: {D[_A]}")
26
+ for M in K:
27
+ F=list_dir(os.path.join(D[_A],M),D[I],A.cfg.recursive,exclude_filepaths=O)
28
+ if len(K)>1:F={os.path.join(M,A):B for(A,B)in F.items()}
29
+ D[C].update(F)
30
+ G=E[_B][C];H=E[N][C];P=sorted(list(set(G.keys())|set(H.keys())))
31
+ for B in P:J.append({_D:B,_B:G[B][_A]if G.get(B)else _C,_E:H[B][_A]if H.get(B)else _C})
32
+ return J
29
33
  def compute_comparison(E,items):
30
34
  C='error';D=[]
31
35
  for B in items:
32
- logging.info(f"Comparing file: {B[_C]}")
33
- try:A=Compare2Files(E.context).compare(B[_D],B[_E])
34
- except Exception as F:A={C:str(F),_D:{},_E:{}}
35
- A[_C]=B[_C];D.append(A)
36
+ logging.info(f"Comparing file: {B[_D]}")
37
+ try:A=Compare2Files(E.context).compare(B[_B],B[_E])
38
+ except Exception as F:A={C:str(F),_B:{},_E:{}}
39
+ A[_D]=B[_D];D.append(A)
36
40
  if A.get(C):
37
41
  logging.warning(f"Error found in comparison: {A[C]}")
38
42
  if A[C]=='No reader found':logging.warning(f"Maybe Config Options could used to derive the correct reader or skip the file");logging.warning(f" - file_reader.extension_unknown_ignore to skip");logging.warning(f" - file_reader.extension_fallback to use a default reader");logging.warning(f" - file_reader.extension_mapping to map extensions")
scilens/config/load.py CHANGED
@@ -2,7 +2,7 @@ import logging,os,json,yaml
2
2
  from pydantic import BaseModel
3
3
  from scilens.config.models import AppConfig
4
4
  from scilens.config.env_var import get_vars
5
- from scilens.utils.dict import dict_path_set,dict_path_get
5
+ from scilens.utils.dict import dict_path_set,dict_path_get,dict_update_rec
6
6
  PATH_ENV_VARS_VALUES={}
7
7
  def env_vars_load():
8
8
  B=get_vars()
@@ -25,7 +25,7 @@ def config_load(config,options_path_value=None,config_override=None):
25
25
  try:I=yaml.safe_load(G)
26
26
  except yaml.YAMLError as D:raise Exception(f"Error in configuration file {B}: {D}")
27
27
  elif isinstance(A,dict):I=B
28
- C.update(I)
28
+ dict_update_rec(C,I)
29
29
  for(E,H)in PATH_ENV_VARS_VALUES.items():
30
30
  if not dict_path_get(C,E):dict_path_set(C,E,H)
31
31
  if F:
@@ -0,0 +1,11 @@
1
+ import types,pydantic
2
+ class StrOrPath(str):
3
+ @classmethod
4
+ def __get_pydantic_core_schema__(A,source_type,handler):return handler(str)
5
+ def __repr__(A):return f"{A.__class__.__name__}({super().__repr__()})"
6
+ def is_StrOrPath_and_path(value,field_info):
7
+ D=False;B=field_info;A=value;C='file://'
8
+ if not isinstance(A,str):return D,''
9
+ if B.annotation==StrOrPath or isinstance(B.annotation,types.UnionType)and StrOrPath in B.annotation.__args__:
10
+ if A.startswith(C):return True,A[len(C):]
11
+ return D,''
@@ -1,5 +1,5 @@
1
1
  _A=None
2
2
  from pydantic import BaseModel,Field
3
3
  from scilens.config.models.compare_float_thresholds import CompareFloatThresholdsConfig
4
- class CompareSourceFoldersConfig(BaseModel):not_matching_source_ignore_pattern:str|_A=Field(default=_A,description="Ignore les fichiers de test et de référence qui ne correspondent pas selon l'expression régulière défini (si = `*` ignore tout).");recursive:bool=Field(default=False,description='Recherche récursive dans les répertoires de test et de référence.');test_folder_relative_path:str=Field(default='test',description='Relative path to the working directory for the test folder.');reference_folder_relative_path:str=Field(default='reference',description='Relative path to the working directory for the reference folder.');additional_path_suffix:str=Field(default='',description='Additional path to add after test and reference folders.');test_filename_match_ignore:str|_A=Field(default=_A,description='Chaîne spécifique dans le nom de fichier test à ignorer pour matcher les noms. Ex : `.test` or `test_`');reference_filename_match_ignore:str|_A=Field(default=_A,description='Chaîne spécifique dans le nom de fichier référence à ignorer pour matcher les noms. Ex : `.ref` or `ref_`')
4
+ class CompareSourceFoldersConfig(BaseModel):not_matching_source_ignore_pattern:str|_A=Field(default=_A,description="Ignore les fichiers de test et de référence qui ne correspondent pas selon l'expression régulière défini (si = `*` ignore tout).");recursive:bool=Field(default=False,description='Recherche récursive dans les répertoires de test et de référence.');additional_path_suffixes:list[str]|_A=Field(default=_A,description='Additionals paths to add after test and reference folders.');test_folder_relative_path:str=Field(default='test',description='Relative path to the working directory for the test folder.');reference_folder_relative_path:str=Field(default='reference',description='Relative path to the working directory for the reference folder.');test_filename_match_ignore:str|_A=Field(default=_A,description='Chaîne spécifique dans le nom de fichier test à ignorer pour matcher les noms. Ex : `.test` or `test_`');reference_filename_match_ignore:str|_A=Field(default=_A,description='Chaîne spécifique dans le nom de fichier référence à ignorer pour matcher les noms. Ex : `.ref` or `ref_`')
5
5
  class CompareConfig(BaseModel):sources:CompareSourceFoldersConfig=Field(default=CompareSourceFoldersConfig(),description='Configuration pour les sources des données de test et de référence.');float_thresholds:CompareFloatThresholdsConfig=Field(default=CompareFloatThresholdsConfig(),description='Seuil de comparaison pour les valeurs flottantes.');ignore_warnings:bool=Field(default=False,description='Si `true`, ne lève pas les erreurs de sévérité `warning`.');errors_limit:int=Field(default=1000,description="Limite d'erreurs à atteindre avant de s'arrêter (Sévérité `warning` et `error`).")
@@ -3,9 +3,11 @@ _C=False
3
3
  _B=True
4
4
  _A=None
5
5
  from pydantic import BaseModel,Field
6
+ from scilens.config.models.base import StrOrPath
6
7
  class ReportParameterPageModeConfig(BaseModel):is_user_preference:bool=Field(default=_B,description=_D);default_value:str=Field(default='onepage',description='`tabs` ou `onepage`')
7
8
  class ReportParameterOpenFileInConfig(BaseModel):is_user_preference:bool=Field(default=_B,description=_D);default_value:str=Field(default='browser',description='`browser` ou `vscode`')
8
9
  class ReportHtmlCurvesConfig(BaseModel):display_on_load:bool=Field(default=_C,description="Si `true`, affiche tous les graphiques courbes à l'ouverture du rapport.");init_width:int=Field(default=600,description='Largeur initiale des graphiques courbes.');init_height:int=Field(default=400,description='Hauteur initiale des graphiques courbes.');compare_vs_values:bool=Field(default=_B,description='Dans le chart de comparaison, affiche les valeurs de référence et de test.');compare_vs_difference:bool=Field(default=_C,description='Dans le chart de comparaison, affiche la différence entre les valeurs de référence et de test.')
9
- class ReportHtmlSpectrogramsConfig(BaseModel):test_ref:bool=Field(default=_B,description='Indique si les spectrogrammes de test doivent être affichés.');differences:bool=Field(default=_B,description='Indique si les spectrogrammes de différence doivent être affichés.');init_width:int=Field(default=300,description='Largeur initiale des spectrogrammes.');init_height:int=Field(default=300,description='Hauteur initiale des spectrogrammes.')
10
- class ReportHtmlMatrixConfig(BaseModel):spectrograms:ReportHtmlSpectrogramsConfig|_A=_A;frameseries:bool=Field(default=_C,description='Indique si les matrices doivent être affichées en mode frameseries.')
11
- class ReportHtmlConfig(BaseModel):custom_style:str|_A=Field(default=_A,description='CSS personnalisé.');custom_script_head:str|_A=Field(default=_A,description='Script personnalisé dans le `<head>`.');custom_script_body:str|_A=Field(default=_A,description='Script personnalisé en fin de `<body>`.');extra_html_start:str|_A=Field(default=_A,description='HTML personnalisé en début de rapport.');extra_html_summary:str|_A=Field(default=_A,description='HTML personnalisé dans la section `Summary`.');extra_html_end:str|_A=Field(default=_A,description='HTML personnalisé en fin de rapport.');logo_height:int=Field(default=40,description='Hauteur du logo dans le titre.');compare_color_test:str=Field(default='1982c4',description='Couleur pour les données de test.');compare_color_reference:str=Field(default='6a4c93',description='Couleur pour les données de référence.');collapse_if_successful:bool=Field(default=_B,description="N'affiche par défaut les sections de comparaison sans erreur.");parameter_page_mode:ReportParameterPageModeConfig=ReportParameterPageModeConfig();parameter_open_file_in:ReportParameterOpenFileInConfig=ReportParameterOpenFileInConfig();curves:ReportHtmlCurvesConfig=ReportHtmlCurvesConfig();matrix:ReportHtmlMatrixConfig=ReportHtmlMatrixConfig()
10
+ class ReportHtmlSpectrogramsConfig(BaseModel):test_ref:bool=Field(default=_B,description='Indique si les spectrogrammes de test doivent être affichés.');differences:bool=Field(default=_B,description='Indique si les spectrogrammes de différence doivent être affichés.');init_width:int=Field(default=300,description='Largeur initiale des spectrogrammes.');init_height:int=Field(default=300,description='Hauteur initiale des spectrogrammes.');is_3D:bool=Field(default=_C,description='Indique si les spectrogrammes doivent être affichés en 3D.')
11
+ class ReportHtmlFrameseriesConfig(BaseModel):width:int=Field(default=300,description='Largeur initiale des graphiques frameseries.');height:int=Field(default=300,description='Hauteur initiale des graphiques frameseries.')
12
+ class ReportHtmlMatrixConfig(BaseModel):spectrograms:ReportHtmlSpectrogramsConfig|_A=_A;frameseries:ReportHtmlFrameseriesConfig|_A=_A
13
+ class ReportHtmlConfig(BaseModel):custom_style:StrOrPath|_A=Field(default=_A,description='CSS personnalisé.');custom_script_head:StrOrPath|_A=Field(default=_A,description="Script personnalisé dans le `<head>`. Chaine de caractères ou chemin d'un fichier sous la forme `file://`.");custom_script_body:StrOrPath|_A=Field(default=_A,description="Script personnalisé en fin de `<body>`. Chaine de caractères ou chemin d'un fichier sous la forme `file://`.");extra_html_start:StrOrPath|_A=Field(default=_A,description="HTML personnalisé en début de rapport. Chaine de caractères ou chemin d'un fichier sous la forme `file://`.");extra_html_summary:StrOrPath|_A=Field(default=_A,description="HTML personnalisé dans la section `Summary`. Chaine de caractères ou chemin d'un fichier sous la forme `file://`.");extra_html_end:StrOrPath|_A=Field(default=_A,description="HTML personnalisé en fin de rapport. Chaine de caractères ou chemin d'un fichier sous la forme `file://`.");logo_height:int=Field(default=40,description='Hauteur du logo dans le titre.');compare_color_test:str=Field(default='1982c4',description='Couleur pour les données de test.');compare_color_reference:str=Field(default='6a4c93',description='Couleur pour les données de référence.');collapse_if_successful:bool=Field(default=_B,description="N'affiche par défaut les sections de comparaison sans erreur.");parameter_page_mode:ReportParameterPageModeConfig=ReportParameterPageModeConfig();parameter_open_file_in:ReportParameterOpenFileInConfig=ReportParameterOpenFileInConfig();curves:ReportHtmlCurvesConfig=ReportHtmlCurvesConfig();matrix:ReportHtmlMatrixConfig=ReportHtmlMatrixConfig()
@@ -19,7 +19,7 @@
19
19
  </h3>
20
20
 
21
21
  <div id="section_summary_datasets">
22
- <table>
22
+ <table class="shadow-lg">
23
23
  <tr>
24
24
  <th class="p-2" rowspan="2">#</th>
25
25
  <th class="p-2" rowspan="2">Status</th>
@@ -147,7 +147,7 @@
147
147
 
148
148
  <!--COMPARISONS ERRORS AND WARNINGS-->
149
149
  {% if file.comparison_errors and (file.comparison_errors.error_nb or file.comparison_errors.warning_nb) %}
150
- <h3>Number Comparison Errors or Warnings <action data-command="toogle" data-args="numbers_errors_{{ file_index|string }}"></action></h3>
150
+ <h3>Number Comparison<action data-command="toogle" data-args="numbers_errors_{{ file_index|string }}"></action></h3>
151
151
  <div id="numbers_errors_{{ file_index }}" >
152
152
  {% with file=file %}
153
153
  {% include 'compare_13_section_numbers.html' %}
@@ -1,5 +1,5 @@
1
1
  {% set comp_errs = file.comparison_errors %}
2
- <table>
2
+ <table class="shadow-lg">
3
3
  <!-- HEADERS -->
4
4
  <tr>
5
5
  <th class="p-2" colspan="6">Error</th>
@@ -28,10 +28,12 @@
28
28
  <script>
29
29
 
30
30
  ((w_1, undef) => { w_1.None = undef; w_1.True = true; ; w_1.False = false; })(window); // for Python None = undef when templating
31
+ {% include 'models.js' %}
31
32
  {% include 'js_dom.js' %}
32
33
  {% include 'js_palette.js' %}
33
34
 
34
35
  {% include 'js_com_page.js' with context %}
36
+ {% include 'uti_widget.js' with context %}
35
37
  {% include 'js_chartlibs_plotly.js' %}
36
38
  {% include 'js_chartlibs_echarts.js' %}
37
39
  {% include 'js_curvemgr.js' %}
@@ -45,8 +47,12 @@ Spectrograms.config({
45
47
  "DISPLAY_DIFF": {{ meta.config.matrix.spectrograms.differences }},
46
48
  "WIDTH": {{ meta.config.matrix.spectrograms.init_width }},
47
49
  "HEIGHT": {{ meta.config.matrix.spectrograms.init_height }},
50
+ "IS_3D": {{ meta.config.matrix.spectrograms.is_3D }},
48
51
  });
49
52
  {% endif %}
53
+ {% if meta.config.matrix.frameseries %}
54
+ {% include 'uti_frameseries.js' %}
55
+ {% endif %}
50
56
  {% if ns.has_matrices %}{% include 'uti_matrix.js' %}{% endif %}
51
57
 
52
58
  </script>
@@ -1,4 +1,4 @@
1
- ((window1, undefined) => {
1
+ ((w_1, undefined) => {
2
2
  //
3
3
  const plotly = {
4
4
  add: function(elt, meta, items) {
@@ -55,6 +55,7 @@
55
55
  if (yaxis2) {layout.yaxis2 = yaxis2;}
56
56
 
57
57
  Plotly.newPlot(elt,series,layout);
58
+ WFull.add(elt)
58
59
  },
59
60
  add_ys: function(elt, meta, x_data, y_items) {
60
61
  this.add(elt, meta, y_items.map((item) => {return {name: item.name, color: item.color, x_data: x_data, y_data: item.data} } ))
@@ -67,7 +68,24 @@
67
68
  },
68
69
  };
69
70
  // global
70
- if(window1.chartlibs === undefined) window1.chartlibs = {};
71
- window1.chartlibs.plotly = plotly;
71
+ if(w_1.chartlibs === undefined) w_1.chartlibs = {};
72
+ w_1.chartlibs.plotly = plotly;
72
73
  //
74
+
75
+ function resize22(obj, container, dir) { // ASSUMPTIONS : obj.size [int, int], "js-plotly-plot"
76
+ // size
77
+ const w = obj.size[0] + (dir * obj.size[0] * 0.1);
78
+ const h = obj.size[0] + (dir * obj.size[0] * 0.1);
79
+ obj.size = [w, h];
80
+ // update widgets
81
+ const update = { width: w, height: h };
82
+ const nodes = dom.q(container, ".js-plotly-plot");
83
+ nodes.forEach((node) => {
84
+ node.style.width = w + "px";
85
+ node.style.height = h + "px";
86
+ Plotly.relayout(node, update); // update plotly
87
+ });
88
+ }
89
+ w_1.resize22 = resize22;
90
+
73
91
  })(window);
@@ -6,6 +6,7 @@ elt(tag,style,attrs,html) { const e = document.createElement(tag); e.style.cssTe
6
6
  elt_p(parent,tag,style,attrs,html) { const e = this.elt(tag,style,attrs,html); if (parent) parent.appendChild(e); return e; },
7
7
  div(style,attrs,html) { return this.elt('div',style,attrs,html); },
8
8
  div_p(parent,style,attrs,html) { return this.elt_p(parent,'div',style,attrs,html); },
9
+ but_p(parent,style,attrs,html,fn) { const e=this.elt_p(parent,'button',style,attrs,html);e.addEventListener("click", fn);return e; },
9
10
  tree(parent, data, out) {
10
11
  if (!data) return;
11
12
  const e = this.elt_p(parent, data.tag, data.style, data.attrs, data.html);
@@ -0,0 +1,24 @@
1
+ ((w_1, undef) => {
2
+ function dataclass(fields, defaults = {}, validate = () => {}, methods = {}) {
3
+ const Cls = class {
4
+ constructor(...args) {
5
+ fields.forEach((field, index) => {
6
+ this[field] = args[index] !== undefined ? args[index] : defaults[field];
7
+ });
8
+ validate(this);
9
+ // Object.freeze(this); // immutable
10
+ }
11
+ // toString() {
12
+ // return `${this.constructor.name}(${fields.map(f => `${f}=${this[f]}`).join(', ')})`;
13
+ // }
14
+ // toJSON() {
15
+ // return Object.fromEntries(fields.map(f => [f, this[f]]));
16
+ // }
17
+ };
18
+ Object.entries(methods).forEach(([name, fn]) => {
19
+ Cls.prototype[name] = fn;
20
+ });
21
+ return Cls;
22
+ }
23
+ w_1.dataclass = dataclass;
24
+ })(window);
@@ -150,4 +150,3 @@ button {border: 0;user-select: none; /* Standard syntax */ cursor: pointer;}
150
150
  .shadow-2xl { box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); }
151
151
  .shadow-inner { box-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); }
152
152
  .shadow-none { box-shadow: 0 0 #0000; }
153
-
@@ -0,0 +1,187 @@
1
+ ((w_1, undef) => {
2
+ //
3
+ // FramesData
4
+ //
5
+ const FramesData = dataclass(
6
+ ['length', 'data_vector', 'name', 'unit', 'steps_starts'],
7
+ {},
8
+ (self) => {
9
+ if (self.data_vector && self.data_vector.length != self.length) {
10
+ throw new Error("FramesData: data_vector length does not match length");
11
+ }
12
+ if (self.data_vector && self.steps_starts) {
13
+ const steps = [];
14
+ const start = self.data_vector[0];
15
+ const end = self.data_vector[self.length-1];
16
+ self.steps_starts.forEach((vstart,i) => {
17
+ if (vstart >= end) return; // start outside right data_vector
18
+ const sstart = (vstart < start) ? start : vstart; // start outside left data_vector
19
+ const vend = (i+1 < self.steps_starts.length) ? self.steps_starts[i+1] : end;
20
+ if (vend < start) return; // end outside left data_vector
21
+ const send = (vend > end) ? end : vend; // end outside right data_vector
22
+ steps.push({
23
+ index: i,
24
+ value: vstart,
25
+ start: sstart,
26
+ end: send,
27
+ color: palette.get(i),
28
+ disp_per_margin_left: (100*(sstart-start)/(end-start))+"%", // rendering
29
+ disp_per_width: (100*(send-sstart)/(end-start))+"%", // rendering
30
+ })
31
+ });
32
+ self.steps = steps;
33
+ self.steps[self.steps.length-1].last = true
34
+ }
35
+ },
36
+ {
37
+ label(idx) {
38
+ const f = `(Frames: ${1+parseInt(idx)}/${this.length})`;
39
+ const l = (this.data_vector)?`${this.data_vector[idx]} ${this.unit || ""}`:"";
40
+ return `${this.name || ""} ${l} ${f}`;
41
+ },
42
+ step(idx) {
43
+ const v = this.data_vector[idx];
44
+ return this.steps.find((s) => s.value >= v || (s.last && v<= s.end));
45
+ },
46
+ }
47
+ );
48
+ //
49
+ // Config
50
+ //
51
+ const PREFIX = "frameseries_"; // section prefix
52
+ const CFG = {
53
+ "WIDTH": 300, // default width
54
+ "HEIGHT": 300, // default height
55
+ }
56
+ //
57
+ //
58
+ /**
59
+ * @param {FramesData} frames_data
60
+ * @returns {string}
61
+ */
62
+ class Frameseries {
63
+ constructor(index, arr_mat, frames_data, invert = false) {
64
+ // size
65
+ this.size = [CFG.WIDTH, CFG.HEIGHT]; // default size
66
+ //
67
+ this.arr_mat = arr_mat;
68
+ this.elt = dom.get(PREFIX+(1+index));
69
+ // frames
70
+ this.frames_data = frames_data;
71
+ //
72
+ this.finvert = true; // invert frames
73
+ // Steps
74
+ this.steps = null;
75
+ // init
76
+ this.init();
77
+ }
78
+ init() {
79
+ //
80
+ const that = this;
81
+ //
82
+ let dom_steps = null;
83
+ if (this.frames_data.steps) {
84
+ const divs = [];
85
+ this.frames_data.steps.forEach((s,i) => {
86
+ const m = (i==0) ? "margin-left:"+s.disp_per_margin_left+";" : "";
87
+ divs.push({tag:"div", style:"display: inline-block;"+m+"width: "+s.disp_per_width+"; height: 20px; background-color: "+s.color+";"});
88
+ });
89
+ dom_steps = {tag:"div", children: [
90
+ {tag:"div", style:"height:15px;background-color:#eee;", children: divs},
91
+ ]};
92
+ }
93
+ //
94
+ dom.tree(
95
+ this.elt,
96
+ {out:"root", tag:"div", children: [
97
+ {out: "tools", tag:"div"},
98
+ dom_steps,
99
+ {out:"sli", tag:"input", style:"width: 100%;", attrs: {"type":"range","value":0,"min":0,"max":this.frames_data.length-1}},
100
+ {out:"lab", tag:"div", style:"text-align: center;"},
101
+ {out:"tbuttons", tag:"div"},
102
+ {out:"graphs", tag:"div", style:"display: flex; flex-wrap: wrap;"},
103
+ ]},
104
+ this
105
+ );
106
+ this.sli.addEventListener('input', function(){that.frame_change();}, true);
107
+ // tools
108
+ Buttons.add(this.tools, [
109
+ {label: "Increase", fn: function() { resize22(that, that.elt, 1); }},
110
+ {label: "Decrease", fn: function() { resize22(that, that.elt, -1); }},
111
+ ]);
112
+ // frame change initialization (only label)
113
+ this.frame_change();
114
+ // add graphs
115
+ this.arr_mat.forEach((m, i) => { this.var_add(i); });
116
+ // toogle vars buttons
117
+ Buttons.add(that.tbuttons, this.arr_mat.map((mat, i) => ({label: mat.name, state:true, fn: function() { return that.var_toogle(i); }})));
118
+ }
119
+ frame_change() {
120
+ const that = this;
121
+ const idx = this.sli.value;
122
+ // steps
123
+ let steps_label = "" ;
124
+ if (this.frames_data.steps) {
125
+ const step = this.frames_data.step(idx);
126
+ steps_label = (step) ? ` <span style="padding:3px 5px;background-color:${step.color};">Step: ${step.index+1}/${this.frames_data.steps.length}</span>` : "";
127
+ }
128
+ // label
129
+ this.lab.innerHTML = this.frames_data.label(idx) + steps_label ;
130
+ // curves
131
+ this.arr_mat.forEach((m, i) => {
132
+ if (that["var_"+i]) {
133
+ if (this.finvert) {
134
+ chartlibs.plotly.upd_y_all(that["var_"+i], [m.data.map((x) => x[idx]), m.ref.map((x) => x[idx])]);
135
+ } else {
136
+ chartlibs.plotly.upd_y_all(that["var_"+i], [m.data[idx], m.ref[idx]]);
137
+ }
138
+ }
139
+ });
140
+ }
141
+ var_toogle(var_i) {
142
+ if (this["var_"+var_i]) { this.var_rmv(var_i); return false; } else { this.var_add(var_i); return true; }
143
+ }
144
+ var_add(var_i) {
145
+ dom.tree(
146
+ this.graphs,
147
+ {out:"var_"+var_i, tag:"div", style:"width:"+this.size[0]+"px;height:"+this.size[1]+"px;", attrs: { class: "m-1 shadow-lg" }},
148
+ this
149
+ );
150
+ this.add_widget(var_i);
151
+ }
152
+ var_rmv(var_i) {
153
+ this["var_"+var_i].remove();
154
+ delete this["var_"+var_i];
155
+ }
156
+ add_widget(var_i) {
157
+ const idx = this.sli.value;
158
+ const mat = this.arr_mat[var_i];
159
+ console.log("mat.y");
160
+ console.log(mat.y);
161
+ if (this.finvert) {
162
+ chartlibs.plotly.add_ys(
163
+ this["var_"+var_i],
164
+ { title: mat.name, xaxis: mat.y_name , yaxis: "Values" },
165
+ (mat.y) ? mat.y : mat.data.map((x,i) => parseInt(i)+1 ),
166
+ [
167
+ { name: "Test" , data: mat.data.map((x) => x[idx]) },
168
+ { name: "Reference" , data: mat.ref.map((x) => x[idx]) },
169
+ ]
170
+ );
171
+ } else {
172
+ chartlibs.plotly.add_ys(
173
+ this["var_"+var_i],
174
+ { title: mat.name, xaxis: "Values" , yaxis: mat.y_name },
175
+ (mat.x) ? mat.x : mat.data[0].map((x,i) => parseInt(i)+1 ),
176
+ [
177
+ { name: "Test" , data: mat.data[idx] },
178
+ { name: "Reference" , data: mat.ref[idx] },
179
+ ]
180
+ );
181
+ }
182
+ }
183
+ }
184
+ Frameseries.config = function(cfg) {for (k in cfg) {CFG[k] = cfg[k];}};
185
+ w_1.FramesData = FramesData;
186
+ w_1.Frameseries = Frameseries;
187
+ })(window);
@@ -56,9 +56,10 @@ class Matrix {
56
56
  absolueMatrice
57
57
  }
58
58
  class MatrixGroup {
59
- constructor(arr, is_spectro, is_frameseries, has_ref) {
59
+ constructor(arr, has_ref, frames_steps) {
60
60
  this.arr = arr; // array of Matrix objects
61
61
  this.has_ref = has_ref;
62
+ this.frames_steps = frames_steps;
62
63
  }
63
64
  }
64
65
  const MatrixMgmt = {
@@ -70,7 +71,7 @@ const MatrixMgmt = {
70
71
  const group_data = [];
71
72
  const ref = g.reference[i];
72
73
  data.datasets.forEach((dataset, j) => {
73
- console.log(dataset);
74
+ // console.log(dataset);
74
75
  const mat = new Matrix(
75
76
  dataset.data,
76
77
  dataset.x_values,
@@ -82,19 +83,30 @@ const MatrixMgmt = {
82
83
  );
83
84
  group_data.push(mat);
84
85
  });
85
- const group = new MatrixGroup(group_data, true);
86
+ // frameseries
87
+ const group = new MatrixGroup(group_data, true, data.frames_steps);
86
88
  this.groups.push(group);
87
89
  });
88
90
  // IMPORTANT
89
91
  // Release Global
90
92
  delete GLOBAL_VARS.matrices;
91
- // Init
93
+ // Init Frameseries / Spectrograms
92
94
  this.groups.forEach((group, i) => {
93
- // Init Spectrograms
94
- if (has_spectrograms) {
95
- const spec = new Spectrograms(i, group.arr);
95
+ if (has_spectrograms) { new Spectrograms(i, group.arr); }
96
+ if (has_frameseries) {
97
+ const mat = group.arr[0];
98
+ const invert = true ;
99
+ const frames_data = new FramesData(
100
+ invert?mat.data[0].length:mat.data.length,
101
+ invert?(mat.x):(mat.y),
102
+ invert?(mat.x_name):(mat.y_name),
103
+ null, // unit
104
+ group.frames_steps, // steps
105
+ ) ;
106
+ console.log("frames_data.steps")
107
+ console.log(frames_data.steps)
108
+ new Frameseries(i, group.arr, frames_data, invert);
96
109
  }
97
- // Init Frameseries
98
110
  }
99
111
  );
100
112
  },
@@ -1,40 +1,27 @@
1
1
  ((w_1, undef) => {
2
2
  //
3
- // tmp
4
- //
5
- function resize22(obj, parent, dir) { // ASSUMPTIONS : obj.size int, "js-plotly-plot"
6
- // size
7
- const w = obj.size[0] + (dir * obj.size[0] * 0.1);
8
- const h = obj.size[0] + (dir * obj.size[0] * 0.1);
9
- obj.size = [w, h];
10
- // update widgets
11
- const update = { width: w, height: h };
12
- const nodes = dom.q(parent, ".js-plotly-plot");
13
- nodes.forEach((node) => {
14
- node.style.width = w + "px";
15
- node.style.height = h + "px";
16
- Plotly.relayout(node, update); // update plotly
17
- });
18
- }
19
- //
20
- //
3
+ // Config
21
4
  //
22
5
  const PREFIX = "spectrograms_"; // section prefix
23
6
  const COLORSCALE = "Viridis"; // plotly
24
7
  const COLORSCALE_DIFF = "RdBu"; // plotly
25
8
  const CFG = {
26
- "DISPLAY_TESTREF": true, // display test reference
27
- "DISPLAY_DIFF": true, // display diff
28
9
  "WIDTH": 300, // default width
29
10
  "HEIGHT": 300, // default height
11
+ "DISPLAY_TESTREF": true, // display test reference
12
+ "DISPLAY_DIFF": true, // display diff
13
+ "IS_3D": false, // use 3D surface plot
30
14
  }
31
15
 
32
16
  //
33
17
  class Spectrograms {
34
18
  constructor(index, arr_mat) {
35
- //console.log("index data", index);
36
19
  // data
20
+ this.disp_values = CFG.DISPLAY_TESTREF;
21
+ this.disp_diff = CFG.DISPLAY_DIFF;
22
+ this.is_3D = CFG.IS_3D; // 3D or 2D
37
23
  this.size = [CFG.WIDTH, CFG.HEIGHT]; // default size
24
+ //
38
25
  this.arr_mat = arr_mat;
39
26
  this.elt = dom.get(PREFIX+(1+index));
40
27
  // init
@@ -43,42 +30,47 @@ class Spectrograms {
43
30
  init() {
44
31
  // resize
45
32
  const that = this;
46
- //
47
- const tmpls = []
48
- this.arr_mat.forEach((mat, i) => { tmpls.push({tag:"button", html: mat.name, click: function() { that.var_toogle(i); } }) });
49
- //
33
+ const o = {};
50
34
  dom.tree(
51
35
  this.elt,
52
36
  {tag:"div", children: [
53
- {tag:"div", attrs:{class:"py-2"}, children: [
54
- {tag:"button", html: '+ Increase', click: function() { resize22(that, that.elt, 1); }},
55
- {tag:"button", html: '- Decrease', click: function() { resize22(that, that.elt, -1); }},
56
- ]},
57
- {tag:"div", attrs:{class:"py-2"}, children: tmpls},
37
+ {out: "tools", tag:"div", attrs:{class:"py-2"}},
38
+ {out: "vars", tag:"div", attrs:{class:"py-2"}},
58
39
  ]},
40
+ o
59
41
  );
60
- // toogle
42
+ Buttons.add(o.tools, [
43
+ {label: "Increase", fn: function() { resize22(that, that.elt, 1); }},
44
+ {label: "Decrease", fn: function() { resize22(that, that.elt, -1); }},
45
+ {label: "Values", fn: function() { that.disp_values = !that.disp_values; that.rerender(); return that.disp_values; }, state: this.disp_values },
46
+ {label: "Diffs", fn: function() { that.disp_diff = !that.disp_diff; that.rerender(); return that.disp_diff; }, state: this.disp_diff },
47
+ {label: "3D", fn: function() { that.is_3D = !that.is_3D; that.rerender(); return that.is_3D; }, state: this.is_3D },
48
+ ]);
61
49
  // Show all
62
50
  this.arr_mat.forEach((mat, i) => { this.var_add(i); });
51
+ // toogle buttons
52
+ Buttons.add(o.vars, this.arr_mat.map((mat, i) => ({label: mat.name, state:true, fn: function() { return that.var_toogle(i); }})));
63
53
  }
64
- var_get_id(idx) {
65
- return PREFIX+"block_"+idx;
54
+ var_get_id(var_i) {
55
+ return this.elt.id+"_"+var_i;
66
56
  }
67
- var_toogle(idx) {
68
- if (dom.get(this.var_get_id(idx))) { this.var_rmv(idx); }
69
- else { this.var_add(idx); }
57
+ var_toogle(var_i) {
58
+ if (dom.get(this.var_get_id(var_i))) { this.var_rmv(var_i); return false; }
59
+ else { this.var_add(var_i); return true; }
70
60
  }
71
61
  var_add(idx) {
72
62
  const mat = this.arr_mat[idx];
63
+ const loading = dom.div_p(this.elt, null, null, "Creating ... "+mat.name);
64
+ this.elt.offsetHeight; // force reflow
73
65
  //
74
66
  const style = "width:"+this.size[0]+"px;height:"+this.size[1]+"px;";
75
67
  const attrs = { class: "m-1 shadow-lg" };
76
68
  //
77
69
  const templs = [];
78
- if (CFG.DISPLAY_TESTREF) templs.push({out:"var", tag:"div", attrs: attrs, style:style});
70
+ if (this.disp_values) templs.push({out:"var", tag:"div", attrs: attrs, style:style});
79
71
  if (mat.ref) {
80
- if (CFG.DISPLAY_TESTREF) templs.push({out:"ref", tag:"div", attrs: attrs, style:style});
81
- if (CFG.DISPLAY_DIFF) templs.push({out:"dif", tag:"div", attrs: attrs, style:style});
72
+ if (this.disp_values) templs.push({out:"ref", tag:"div", attrs: attrs, style:style});
73
+ if (this.disp_diff) templs.push({out:"dif", tag:"div", attrs: attrs, style:style});
82
74
  }
83
75
  const e = {};
84
76
  dom.tree(
@@ -89,30 +81,47 @@ class Spectrograms {
89
81
  e
90
82
  );
91
83
 
92
- if (CFG.DISPLAY_TESTREF) this.add_widget(e["var"], mat, mat.data);
84
+ if (this.disp_values) this.add_widget(e["var"], mat, mat.data);
93
85
  if (mat.ref) {
94
- if (CFG.DISPLAY_TESTREF) this.add_widget(e["ref"], mat, mat.ref, " - Ref.");
95
- if (CFG.DISPLAY_DIFF) this.add_widget(e["dif"], mat, mat.ref_diff_abs_data(), " - Diff.", COLORSCALE_DIFF);
86
+ if (this.disp_values) this.add_widget(e["ref"], mat, mat.ref, " - Ref.");
87
+ if (this.disp_diff) this.add_widget(e["dif"], mat, mat.ref_diff_abs_data(), " - Diff.", COLORSCALE_DIFF);
88
+ // copy22(e["dif"], e["dif2"]);
96
89
  }
90
+ loading.remove();
97
91
  }
98
92
  var_rmv(idx) {
99
93
  dom.get(this.var_get_id(idx)).remove();
100
94
  }
95
+ rerender() {
96
+ this.arr_mat.forEach((m, i) => {
97
+ if (dom.get(this.var_get_id(i))) {
98
+ this.var_rmv(i);
99
+ this.var_add(i);
100
+ }
101
+ });
102
+ }
101
103
  add_widget(elt, mat, matdata, suffix="", colorscale=COLORSCALE) {
102
104
  const data = [{
103
105
  z: matdata,
104
- type: 'heatmap',
106
+ type: this.is_3D ? 'surface' : 'heatmap',
105
107
  colorscale: colorscale,
108
+ // contours: { z: { show:true, usecolormap: true, highlightcolor:"#42f462", project:{z: true} } },
106
109
  }];
107
-
108
110
  const layout = {
109
111
  title: mat.name + suffix,
110
112
  xaxis: { title: mat.x_name || 'X' },
111
113
  yaxis: { title: mat.y_name || 'Y' },
114
+ scene: {
115
+ xaxis: { title: mat.x_name || 'X' },
116
+ yaxis: { title: mat.y_name || 'Y' },
117
+ zaxis: { title: mat.z_name || 'Z' },
118
+ },
112
119
  };
113
120
 
114
- Plotly.newPlot(elt, data, layout);
121
+ Plotly.newPlot(elt, data, layout);
122
+ WFull.add(elt)
115
123
  }
124
+
116
125
  }
117
126
  Spectrograms.config = function(cfg) {for (k in cfg) {CFG[k] = cfg[k];}};
118
127
  w_1.Spectrograms = Spectrograms;
@@ -0,0 +1,57 @@
1
+ ((w_1, undef) => {
2
+ const CLASSES = "text-xs rounded-lg mx-1 my-1";
3
+ const CLASSES_ACTIVE = "text-xs rounded-lg mx-1 my-1 bg-slate-500 text-white";
4
+ const Buttons = {
5
+ add: function(elt, specs) {
6
+ const that = this;
7
+ specs.forEach((spec) => {
8
+ dom.but_p(elt,"",{"class":(spec.state===true)?CLASSES_ACTIVE:CLASSES},spec.label,that.click).fn = spec.fn;
9
+ });
10
+ },
11
+ click: function(e) {
12
+ const b = e.target;
13
+ const r = b.fn();
14
+ if (r === true || r=== false) { b.className = (r)?CLASSES_ACTIVE:CLASSES; } // toggle
15
+ },
16
+ };
17
+ w_1.Buttons = Buttons;
18
+ })(window);
19
+
20
+
21
+
22
+
23
+
24
+ ((w_1, undef) => {
25
+
26
+ function plotlycopy(e, new_e) {
27
+ const layout = JSON.parse(JSON.stringify(e.layout))
28
+ layout.width = null; layout.height = null; // force to null to apply responsive
29
+ // layout.margin = {l: 40, r: 40, t: 40, b: 40}; // remove margins
30
+ Plotly.newPlot(
31
+ new_e,
32
+ JSON.parse(JSON.stringify(e.data)), // data
33
+ layout, // layout
34
+ {responsive: true, displayModeBar: true},
35
+ );
36
+ // fixed bug with modebar position
37
+ dom.q(new_e, ".modebar-container")[0].style.position = "fixed"; // fix position of modebar
38
+ }
39
+ const WFull = {
40
+ add: function(elt) {
41
+ elt.addEventListener("dblclick", function(){WFull.show(elt);}, true);
42
+ },
43
+ show: function(elt) {
44
+ document.body.style.overflow = 'hidden';
45
+ this.fe = dom.div_p(document.body,"position:fixed;top:0;left:0;width: 100%;height: 100vh;display:flex;z-index: 9998;");
46
+ this.fb = dom.but_p(document.body,"position:fixed;top:0;left:0;z-index: 9999;padding:10px;", null, "Close", WFull.hide);
47
+ plotlycopy(elt, this.fe);
48
+ },
49
+ hide: function() {
50
+ document.body.style.overflow = 'auto';
51
+ WFull.fe.remove();
52
+ WFull.fb.remove();
53
+ },
54
+ };
55
+ w_1.WFull = WFull;
56
+ })(window);
57
+
scilens/run/run_task.py CHANGED
@@ -11,24 +11,31 @@ from scilens.report.report import Report
11
11
  from scilens.utils.system import info as system_info
12
12
  from scilens.utils.time_tracker import TimeTracker
13
13
  from scilens.utils.template import template_render_string
14
+ from scilens.config.models.base import is_StrOrPath_and_path
14
15
  def var_render(value,runtime):return template_render_string(value,runtime.model_dump())
15
16
  def runtime_process_vars(config):
16
17
  A=TaskRuntime(sys=system_info(),env=os.environ.copy(),vars={})
17
18
  for(B,C)in config.variables.items():A.vars[B]=var_render(C,A)
18
19
  return A
19
- def runtime_apply_to_config(runtime,config_model):
20
- C=runtime;B=config_model
21
- for(D,J)in B.__class__.__pydantic_fields__.items():
22
- A=getattr(B,D);E=issubclass(A.__class__,BaseModel);F=isinstance(A.__class__,type)and issubclass(A.__class__,Enum);G=isinstance(A,str);H=isinstance(A,list)and all(isinstance(A,str)for A in A);I=isinstance(A,dict)and all(isinstance(A,str)and isinstance(B,str)for(A,B)in A.items())
23
- if E:runtime_apply_to_config(C,A)
24
- elif G and not F:setattr(B,D,var_render(A,C))
25
- elif H:setattr(B,D,[var_render(A,C)for A in A])
26
- elif I:setattr(B,D,{A:var_render(B,C)for(A,B)in A.items()})
20
+ def runtime_apply_to_config(runtime,config_model,working_dir):
21
+ G=working_dir;D=runtime;B=config_model
22
+ for(C,H)in B.__class__.__pydantic_fields__.items():
23
+ A=getattr(B,C);I,E=is_StrOrPath_and_path(A,H)
24
+ if I:
25
+ F=os.path.join(G,E)if not os.path.isabs(E)else E
26
+ if not os.path.exists(F):raise Exception(f"Config {C}: {A} Path '{F}' does not exist.")
27
+ else:
28
+ with open(F,'r')as J:A=J.read();setattr(B,C,A)
29
+ K=issubclass(A.__class__,BaseModel);L=isinstance(A.__class__,type)and issubclass(A.__class__,Enum);M=isinstance(A,str);N=isinstance(A,list)and all(isinstance(A,str)for A in A);O=isinstance(A,dict)and all(isinstance(A,str)and isinstance(B,str)for(A,B)in A.items())
30
+ if K:runtime_apply_to_config(D,A,G)
31
+ elif M and not L:setattr(B,C,var_render(A,D))
32
+ elif N:setattr(B,C,[var_render(A,D)for A in A])
33
+ elif O:setattr(B,C,{A:var_render(B,D)for(A,B)in A.items()})
27
34
  class RunTask:
28
35
  def __init__(A,context):A.context=context
29
36
  def _get_processors(A):return{A.__name__:A for A in[Analyse,Compare,ExecuteAndCompare]}
30
37
  def process(A):
31
- logging.info(f"Running task");logging.info(f"Prepare runtime variables");H=runtime_process_vars(A.context.config);logging.info(f"Apply runtime variables to config");runtime_apply_to_config(H,A.context.config);logging.debug(f"on working_dir '{A.context.working_dir}'");logging.debug(f"with origin_working_dir '{A.context.origin_working_dir}'");logging.debug(f"with config {A.context.config.model_dump_json(indent=4)}");C=A.context.config.processor
38
+ logging.info(f"Running task");logging.info(f"Prepare runtime variables");H=runtime_process_vars(A.context.config);logging.info(f"Apply runtime variables to config");runtime_apply_to_config(H,A.context.config,A.context.working_dir);logging.debug(f"on working_dir '{A.context.working_dir}'");logging.debug(f"with origin_working_dir '{A.context.origin_working_dir}'");logging.debug(f"with config {A.context.config.model_dump_json(indent=4)}");C=A.context.config.processor
32
39
  if not C:raise Exception('Processor not defined in config.')
33
40
  D=A._get_processors().get(C)
34
41
  if not D:raise Exception('Processor not found.')
@@ -5,5 +5,5 @@ from scilens.run.run_task import RunTask
5
5
  from scilens.config.models import AppConfig
6
6
  class StandaloneTaskRunner:
7
7
  config:AppConfig;config_path=None
8
- def __init__(A,config):A.config=config_load(config)
8
+ def __init__(A,config,config_override=None):A.config=config_load(config,config_override=config_override)
9
9
  def process(A,working_dir,origin_working_dir=None):B=TaskContext(config=A.config,config_file=A.config_path,working_dir=working_dir,origin_working_dir=origin_working_dir);C=RunTask(B);D=C.process();return D
scilens/utils/dict.py CHANGED
@@ -3,4 +3,9 @@ def dict_path_set(obj,path,value):
3
3
  A=obj;B=path.split('.')
4
4
  for C in B[:-1]:A=A.setdefault(C,{})
5
5
  A[B[-1]]=value
6
- def dict_path_get(obj,path):return reduce(dict.get,path.split('.'),obj)
6
+ def dict_path_get(obj,path):return reduce(dict.get,path.split('.'),obj)
7
+ def dict_update_rec(base,updates):
8
+ A=base
9
+ for(B,C)in updates.items():
10
+ if B in A and isinstance(A[B],dict)and isinstance(C,dict):dict_update_rec(A[B],C)
11
+ else:A[B]=C
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scilens
3
- Version: 0.3.9
3
+ Version: 0.3.11
4
4
  Summary: A CesGensLaB framework for data collecting and deep analysis
5
5
  Home-page: https://scilens.dev
6
6
  License: Proprietary
@@ -11,17 +11,18 @@ scilens/components/analyse_folder.py,sha256=yqc-dscKaHLZJCYeXGak2v0c3F2aeX0E11AF
11
11
  scilens/components/compare_2_files.py,sha256=jbZNLbRj3x8TxUADM5QC2u_TGl4I7u5OdaEMh-gMBUk,1713
12
12
  scilens/components/compare_errors.py,sha256=vGb4DWP89HMIeBm0dZU2nt-ksppAs_37xtCHaPd0w5Y,1640
13
13
  scilens/components/compare_floats.py,sha256=yYJZ_QpAZUhP1uwNtGshXQLIzKtki9ifCN3hd9AFj2U,5916
14
- scilens/components/compare_folders.py,sha256=LZ1AuYxLVHMNbtXWXQrdms4vZgOQthvDy-8NFD_EFjc,2617
14
+ scilens/components/compare_folders.py,sha256=bCpFBnWTpu-i-SvAWgg-T2qwRJ7QErtgShIiklclfMM,2705
15
15
  scilens/components/compare_models.py,sha256=SCPd747h_nd4ewZsqLB6CFr27v6q99NELJb-gpkdj0o,918
16
16
  scilens/components/executor.py,sha256=PLeKolzPd4wPX8e6DdfOb0uHGky3DxTcKN9QtB6oe3Q,3712
17
17
  scilens/components/file_reader.py,sha256=7SbKCqb4Co_pqAKX3wweYhqAcVkU7BDlT903sLd76Kc,1407
18
18
  scilens/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  scilens/config/cli_run_options.py,sha256=Ls7yK5QDUPFbk73nbjGuPvuRbBRYw4Miag5ISpu3prg,281
20
20
  scilens/config/env_var.py,sha256=NqNBoIfngJEXaGEm7jGqre5pmkJ9eUjiWzbDrTVfi2c,292
21
- scilens/config/load.py,sha256=4U51o4cJfqhSuRIHKUDIsDQA0C4wv6SzTkVmInGDJdI,1647
21
+ scilens/config/load.py,sha256=ltcv90GlsMJR2FE2ZL_jDscL7k5aGoHoMatWw61lrTw,1672
22
22
  scilens/config/models/__init__.py,sha256=eLCW1OLVINewGFy5GXSrOk8Rab_QsgKAuoErBjphHRs,673
23
23
  scilens/config/models/app.py,sha256=JltWRjjqXYkH6rg3OHYhMfkBqHFhZEZdthqES3LxvzY,1453
24
- scilens/config/models/compare.py,sha256=esRqW3PNVOqkA_mt4_qbS7dVCLulUrZTBUhANQOHoqE,1847
24
+ scilens/config/models/base.py,sha256=k92CR8TA5L8dZJtg28c8albk16AK9-3umdosA7aYsxw,499
25
+ scilens/config/models/compare.py,sha256=veI5yFZ422i7NNBf6yH_3mk17jk1WNNzhcxb4XuIJBI,1860
25
26
  scilens/config/models/compare_float_thresholds.py,sha256=mHu48-70i3o_qUw6x-1A7XeRwFUNAO6WP6qNPGwAew0,2097
26
27
  scilens/config/models/execute.py,sha256=Hx3DoDJ0fq7bZruqwtPtSKL6PVOF_LpsaDCLrn5klLk,2325
27
28
  scilens/config/models/execute_and_compare.py,sha256=TWL6yXGvQSaaV6nhHqWLvtr3v396APIoDNt0U1TbMro,582
@@ -33,7 +34,7 @@ scilens/config/models/reader_format_txt.py,sha256=eHg90gwEI_VpqwqEjMRhwlS8dHcl5G
33
34
  scilens/config/models/reader_format_txt_fixed_cols.py,sha256=SQ84OW9BLc5mr_TC6gQuYzzHJvrU-sVz223WOtAQMc0,1133
34
35
  scilens/config/models/readers.py,sha256=oWdE4AkckvwN6boln55orq3hUeAt6S9IdQAZkGROR6E,1657
35
36
  scilens/config/models/report.py,sha256=6mzqZMJnS_z5Rs01ISo8L8HRcWvmiQrK0dYqu8a58vM,993
36
- scilens/config/models/report_html.py,sha256=EqP42x6dupXzHzP4Ar7sP7mmZp1TqpUzgOYV-8FSoOk,3040
37
+ scilens/config/models/report_html.py,sha256=OU_df-IwopOILbWgNHImTtZf7Yhj_fsD0lpRQB8-bP8,3728
37
38
  scilens/config/models/report_output.py,sha256=XoqUe-t-y8GRbUR3_bDwwaWf6hif-rZ-5pKDGdCMugw,875
38
39
  scilens/helpers/assets.py,sha256=XphDA3-yE1PPKw4XFZhDrlLQjMZfGMlpOBXa8uy_xX0,1552
39
40
  scilens/helpers/search_and_index.py,sha256=kXZ7124ra_SGAdKUZ7msy55UOWQ9dCSuPuNoU-NdUyM,1522
@@ -66,21 +67,24 @@ scilens/report/template.py,sha256=cPs5gd3uEwb-6JgitGQD_i4IiUxigBTlZLNRS9KVuos,58
66
67
  scilens/report/templates/body_01_title.html,sha256=LJOWO6ImNPW9cg3tCDlgdllVTwnFGWcb2qvA3yv6wNk,1758
67
68
  scilens/report/templates/body_02_tabs.html,sha256=oOFslWcfNsWpCX12GECZDzdR5uAHE54HPP6IYUf7y0U,412
68
69
  scilens/report/templates/body_99_footer.html,sha256=8cWebeWfZwZ-9bYAMZkZj8rbCWq3BLIMjKQThWQxoQM,362
69
- scilens/report/templates/compare_11_summary.html,sha256=qVvFydtAvAYyVOOTqjTN3LUj5Lh_Cl680EYL5SCZkd4,4086
70
- scilens/report/templates/compare_12_sections.html,sha256=EYYMqF2HeRXnWRke2vn9O2JCm8oKbx-r1NL7FJvYFDg,6431
70
+ scilens/report/templates/compare_11_summary.html,sha256=xpqii8f2sWqa3weQ6BRuLaD1loCJsoQ5MnDTUObRbQo,4104
71
+ scilens/report/templates/compare_12_sections.html,sha256=mHO8efJ2YOV-dC4B4E0VlcAnLbdIvgx5D-RlYRk8idY,6411
71
72
  scilens/report/templates/compare_13_section_numbers copy.html,sha256=0PWK_I2kNX3LjPLkkY4eSYIeB7YFkA28nk-PPLDhnaY,1753
72
73
  scilens/report/templates/compare_13_section_numbers.html,sha256=9etEMSqwrDyJIn_nMbKEVaDgnFL_hBxSjPR-hU2wgDI,851
73
- scilens/report/templates/compare_13_section_numbers_table.html,sha256=sJy6ZYtjl80vM1b3oqZSXawZWp7KNIwLI_wCnvBwYPE,3270
74
- scilens/report/templates/index.html,sha256=fcC1jrG2nhmCSzXUV6fe14pRlNQ6CAgGRHMHhlx6nNg,5757
74
+ scilens/report/templates/compare_13_section_numbers_table.html,sha256=Bbcv7311WhhNXNIjPPS9ho-CHzNpikjCs4la6G4274M,3288
75
+ scilens/report/templates/index.html,sha256=ilvsUfuINb7bDP8X1UgjhxPqtQJ4pq6pgPXWnkLaaFU,5969
75
76
  scilens/report/templates/js_chartlibs_echarts.js,sha256=6YicVhTNIBmmBpV31XCVN5oBeiD0t29JIosJZRUv01M,907
76
- scilens/report/templates/js_chartlibs_plotly.js,sha256=3uiQfbd95NMN-3N2NX3c4CC7zFb0JRtH-ZzezDVGeO8,2111
77
+ scilens/report/templates/js_chartlibs_plotly.js,sha256=MQGpYtBc1gKtu98BcfVOx_W0TktABqanxDjK9YaVqNE,2649
77
78
  scilens/report/templates/js_com_page.js,sha256=Q-_Smn77IYIAdlrS1zJtsVIYBOL1t-J1AYYJKji4eL0,6864
78
79
  scilens/report/templates/js_curvemgr.js,sha256=gnRLO6HbZOMLIBQKjVhV2PciqXtuNKxpGx4boijV2Qc,12175
79
- scilens/report/templates/js_dom.js,sha256=XnxgdB0x-Xtt0eQFrwjcFO1cb_KPsTImpJBB6m_y8FI,1229
80
+ scilens/report/templates/js_dom.js,sha256=r5fNSEdeHopMUjSaYtuAIBNRhvSIPoQ9HzAtxvLZcuk,1363
80
81
  scilens/report/templates/js_palette.js,sha256=HeewAmkR67QiqXSanJS3cCgp6IPKomlULUTKt55F6es,218
81
- scilens/report/templates/style.css,sha256=SOKxdCqoj0yBt2zt3g1RkYx4ZV0_9PhGtO-TDWjmSHE,4217
82
- scilens/report/templates/uti_matrix.js,sha256=N2fNH_E3Km6H60IOW-BjL0LIKTuJ2eGIHnJV_O354es,2936
83
- scilens/report/templates/uti_spectrograms.js,sha256=WYnNt5d8aGIeUCXy8q3MWRtpJzr4htMiH-mlxXpBeug,3586
82
+ scilens/report/templates/models.js,sha256=MrgnVxPnoimLbR_Eb7Rq3OoNlT89m4PYk6vqt-D7KyY,715
83
+ scilens/report/templates/style.css,sha256=WN8Fi3s6PiuExuDiQHHXCB6sFWwjVnGO5VSk0u-E1sI,4216
84
+ scilens/report/templates/uti_frameseries.js,sha256=2edOgmgEfD9F7wYvcB1ZAELnLRq43tcxYMhERnNC5NY,6282
85
+ scilens/report/templates/uti_matrix.js,sha256=wD9vZ9aMq897XupXDmGYkiUQl8behMAYDjcfDvxGwQ0,3441
86
+ scilens/report/templates/uti_spectrograms.js,sha256=qE2wdit6Zq8AxShtV9FdA2pRTk4KY9zZUZ58ccYj_Ow,4311
87
+ scilens/report/templates/uti_widget.js,sha256=r8pM3yDxIPKuxIqVcFrTOIhWCSjplg2eGX1Y-sJUSW8,1742
84
88
  scilens/report/templates/utils_compare_report_anim.js,sha256=6Yg1nQDrmAQSJzHtsmA0CO2Ng1ddiLIhrbKXSZAB-Ms,2971
85
89
  scilens/report/templates/utils_compare_report_framesseries.js,sha256=kg5NDVJYL0dF2cwdunD4WpnckvfGOsxXuO1ttmEV8hw,5766
86
90
  scilens/report/vendors/tailwindcss_3_4_15.js,sha256=niHw6Rf0uS2pE5_8h-E18NI24o1urZL5Ac31_n9htKE,407279
@@ -88,11 +92,11 @@ scilens/report/vendors/tailwindcss_min_4.0.0-beta.4.js,sha256=fy2LOvMX7m4b1V9Wdt
88
92
  scilens/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
93
  scilens/run/models/task_results.py,sha256=hdr_QEwMnjdfdawpfuBRMGqCHWQvsF61G39CVEMXKl8,284
90
94
  scilens/run/models/task_runtime.py,sha256=VNbMPS1ocl6WUDG5ipUxp3RSAST2OZ5DqGcfJWFEed8,114
91
- scilens/run/run_task.py,sha256=suCnACK2RmcwGdmOUAxnb0UD3LC_VT8RH9S525rsr14,2828
92
- scilens/run/standalone_task_runner.py,sha256=D3SUgVzoYtFvfDRacX286gXswuyN4z37et_qxfly4Rs,515
95
+ scilens/run/run_task.py,sha256=EDi9Lk69DC5ik9ArWF47hZ5j9Z_TTAES7liTXzuUZEg,3183
96
+ scilens/run/standalone_task_runner.py,sha256=QYUZ22YPV8hlUFANRcfxy_RXAmwKSfb7glPqPdcgdMA,568
93
97
  scilens/run/task_context.py,sha256=NnujvpwnxY-YEzivYPYWaX-YChcZlEXt9y0_DXLqZkk,659
94
98
  scilens/run/tasks_collector.py,sha256=m_FQaJdQRi4fCLW17ryJxU0TvGNJN54JTw2Mg6XPojY,3174
95
- scilens/utils/dict.py,sha256=1MVQc8vZCs8_gQJMBkBSXO828wMe2eIWFiraLVmcjqk,214
99
+ scilens/utils/dict.py,sha256=ORdZ_521Em4YjV5S8EqzESi9eM2Dh5CR4JpLbd8JASk,384
96
100
  scilens/utils/file.py,sha256=ljtTHCvT7vfDSbHA-5aKDl9885SVce3TBXWRIA-aRx0,1664
97
101
  scilens/utils/load_model_from_file.py,sha256=k5I-B6s5nVZu90MgzKSM0_IRj9oNL-4oJJRTwEvOyw8,619
98
102
  scilens/utils/php.py,sha256=VBJxpzwwRPNcr3379f6ViwhpTzjGc4BKlSXHv4lnor8,444
@@ -101,7 +105,7 @@ scilens/utils/template.py,sha256=9dlXX3nmfzDRUwzPJOkoxk15UXivZ2SW-McdCwokFa4,443
101
105
  scilens/utils/time_tracker.py,sha256=DdVBoMpVLXrX0qZZXyLm4g38EwDVLlRcBqcpNex1mYY,545
102
106
  scilens/utils/vectors.py,sha256=4N2BZSC5n3HgZqPujDGF5NdjVmSL1rOHb_qw4OoABQY,103
103
107
  scilens/utils/web.py,sha256=MAFWpIFOKz7QhqDoFh-Qwstvc76KpcxstSgHFT8FOL4,901
104
- scilens-0.3.9.dist-info/METADATA,sha256=ylBI_UDX3OeXxMTpfxODknkBUEFLGyE-_YU88jb6mn8,1367
105
- scilens-0.3.9.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
106
- scilens-0.3.9.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
107
- scilens-0.3.9.dist-info/RECORD,,
108
+ scilens-0.3.11.dist-info/METADATA,sha256=3qe3gjJdQmtWCcZECpHkXLS6wnyVDtmws8ikK7y-NwQ,1368
109
+ scilens-0.3.11.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
110
+ scilens-0.3.11.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
111
+ scilens-0.3.11.dist-info/RECORD,,