scilens 0.4.3__py3-none-any.whl → 0.4.5__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.
- scilens/components/compare_2_files.py +35 -21
- scilens/components/compare_floats.py +14 -5
- scilens/components/compare_models.py +1 -1
- scilens/config/models/app.py +1 -1
- scilens/config/models/compare.py +3 -2
- scilens/config/models/compare_float_thresholds.py +3 -2
- scilens/config/models/execute.py +1 -1
- scilens/config/models/execute_and_compare.py +1 -1
- scilens/config/models/file_reader.py +1 -1
- scilens/config/models/reader_format_cols.py +8 -7
- scilens/config/models/reader_format_csv.py +3 -2
- scilens/config/models/reader_format_netcdf.py +1 -1
- scilens/config/models/reader_format_txt.py +6 -3
- scilens/config/models/reader_format_txt_fixed_cols.py +1 -1
- scilens/config/models/reader_metrics.py +4 -1
- scilens/config/models/readers.py +7 -6
- scilens/config/models/report.py +1 -1
- scilens/config/models/report_html.py +11 -10
- scilens/config/models/report_output.py +1 -1
- scilens/readers/cols_dataset.py +14 -13
- scilens/readers/reader_csv.py +33 -34
- scilens/readers/reader_interface.py +2 -2
- scilens/readers/reader_txt.py +38 -21
- scilens/report/templates/body_02_tabs.html +2 -2
- scilens/report/templates/compare_11_summary.html +5 -69
- scilens/report/templates/compare_11_summary_datasets.html +71 -0
- scilens/report/templates/compare_11_summary_metrics copy.html +20 -0
- scilens/report/templates/compare_11_summary_metrics.html +25 -0
- scilens/report/templates/compare_12_sections.html +9 -8
- scilens/report/templates/compare_13_section_numbers_table.html +2 -7
- scilens/report/templates/html_macros.html +33 -0
- scilens/report/templates/html_widget_err.html +5 -0
- scilens/report/templates/index.html +1 -0
- scilens/report/templates/js_com_page.js +4 -2
- scilens/report/templates/style.css +82 -1
- {scilens-0.4.3.dist-info → scilens-0.4.5.dist-info}/METADATA +1 -1
- {scilens-0.4.3.dist-info → scilens-0.4.5.dist-info}/RECORD +39 -34
- {scilens-0.4.3.dist-info → scilens-0.4.5.dist-info}/WHEEL +0 -0
- {scilens-0.4.3.dist-info → scilens-0.4.5.dist-info}/entry_points.txt +0 -0
|
@@ -2,31 +2,45 @@ import os,re
|
|
|
2
2
|
from scilens.run.task_context import TaskContext
|
|
3
3
|
from scilens.readers.reader_interface import ReaderInterface
|
|
4
4
|
from scilens.components.file_reader import FileReader
|
|
5
|
-
from scilens.components.compare_models import SEVERITY_ERROR
|
|
5
|
+
from scilens.components.compare_models import SEVERITY_ERROR,SEVERITY_WARNING
|
|
6
6
|
from scilens.components.compare_errors import CompareErrors
|
|
7
7
|
from scilens.components.compare_floats import CompareFloats
|
|
8
8
|
class Compare2Files:
|
|
9
9
|
def __init__(A,context):A.context=context
|
|
10
10
|
def compare(B,path_test,path_ref):
|
|
11
|
-
|
|
12
|
-
for(
|
|
13
|
-
if not
|
|
14
|
-
if
|
|
15
|
-
if
|
|
11
|
+
i='status';h='severity';g='comparison_errors';f='comparison';Y=path_ref;X=path_test;W='err_index';V='reader';U='skipped';T=None;R='metrics';Q='error';P=True;O='ref';K='path';J='test';A={J:{},O:{},f:T,g:T};H={J:{K:X},O:{K:Y}};S=B.context.config.compare.sources.not_matching_source_ignore_pattern
|
|
12
|
+
for(C,L)in H.items():
|
|
13
|
+
if not L.get(K)or not os.path.exists(L[K]):
|
|
14
|
+
if S:
|
|
15
|
+
if S=='*':A[U]=P;return A
|
|
16
16
|
else:
|
|
17
|
-
|
|
18
|
-
if
|
|
19
|
-
A[
|
|
20
|
-
|
|
21
|
-
for(
|
|
22
|
-
|
|
23
|
-
if not
|
|
24
|
-
A[
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
if
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
j=os.path.basename(Y if C==J else X);k=re.search(S,j)
|
|
18
|
+
if k:A[U]=P;return A
|
|
19
|
+
A[Q]=f"file {C} does not exist";return A
|
|
20
|
+
l=FileReader(B.context.working_dir,B.context.config.file_reader,B.context.config.readers,config_alternate_path=B.context.origin_working_dir)
|
|
21
|
+
for(C,L)in H.items():H[C][V]=l.read(L[K])
|
|
22
|
+
D=H[J][V];F=H[O][V]
|
|
23
|
+
if not D or not F:A[U]=P;return A
|
|
24
|
+
A[J]=D.info();A[O]=F.info()
|
|
25
|
+
if D.read_error:A[Q]=D.read_error;return A
|
|
26
|
+
E=CompareErrors(B.context.config.compare.errors_limit,B.context.config.compare.ignore_warnings);Z=CompareFloats(E,B.context.config.compare.float_thresholds);a=D.compare(Z,F,param_is_ref=P);G=E.root_group;M=T
|
|
27
|
+
if B.context.config.compare.metrics_compare and(D.metrics or F.metrics):
|
|
28
|
+
o,M=E.add_group(R,R,parent=G)
|
|
29
|
+
if B.context.config.compare.metrics_thresholds:b=CompareFloats(E,B.context.config.compare.metrics_thresholds)
|
|
30
|
+
else:b=Z
|
|
31
|
+
b.compare_dicts(D.metrics,F.metrics,M)
|
|
32
|
+
I={'total_diffs':G.total_diffs}
|
|
33
|
+
if G.info:I.update(G.info)
|
|
34
|
+
if a:I.update(a)
|
|
35
|
+
if M:
|
|
36
|
+
N={}
|
|
37
|
+
for c in[SEVERITY_ERROR,SEVERITY_WARNING]:
|
|
38
|
+
for(m,d)in enumerate(E.errors[c]):
|
|
39
|
+
if d.group==M.id:N[d.info['key']]={h:c,W:m}
|
|
40
|
+
I[R]={}
|
|
41
|
+
for C in D.metrics.keys():I[R][C]={i:N[C][h],W:N[C][W]}if C in N else{i:'success'}
|
|
42
|
+
A[f]=I;A[g]=E.get_data()
|
|
43
|
+
if G.error:A[Q]=G.error;return A
|
|
44
|
+
D.close();F.close();e=len(E.errors[SEVERITY_ERROR])
|
|
45
|
+
if e>0:n=f"{e} comparison errors";A[Q]=n
|
|
32
46
|
return A
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
_D='amplitude'
|
|
2
|
+
_C='diff'
|
|
2
3
|
_B=False
|
|
3
4
|
_A=None
|
|
4
5
|
import logging
|
|
@@ -7,7 +8,7 @@ from scilens.components.compare_errors import CompareErrors
|
|
|
7
8
|
from scilens.config.models import CompareFloatThresholdsConfig
|
|
8
9
|
try:from scilens_compare import vectors as CheckVectors
|
|
9
10
|
except ModuleNotFoundError:pass
|
|
10
|
-
def vector_get_amplitude(vector):min_val=min(vector);max_val=max(vector);return{'min':min_val,'max':max_val,
|
|
11
|
+
def vector_get_amplitude(vector):min_val=min(vector);max_val=max(vector);return{'min':min_val,'max':max_val,_D:abs(max_val-min_val)}
|
|
11
12
|
class CompareFloats:
|
|
12
13
|
def __init__(self,compare_errors,config):self.compare_errors=compare_errors;self.thresholds=config
|
|
13
14
|
def compare_2_values(self,test,reference):
|
|
@@ -22,6 +23,14 @@ class CompareFloats:
|
|
|
22
23
|
if err<thr.absolute_error_max:
|
|
23
24
|
if err>thr.absolute_error_min:return Compare2ValuesResults(SEVERITY_WARNING,f"Abs. err. > {thr.absolute_error_min} and < {thr.absolute_error_max}",comp_err)
|
|
24
25
|
else:return Compare2ValuesResults(SEVERITY_ERROR,f"Abs. err. > {thr.absolute_error_max}",comp_err)
|
|
26
|
+
def compare_dicts(self,test_dict,reference_dict,group):
|
|
27
|
+
diffs_count=0;err_limit_reached=_B
|
|
28
|
+
if set(test_dict.keys())!=set(reference_dict.keys()):raise Exception('Dictionaries have different keys')
|
|
29
|
+
for key in test_dict:
|
|
30
|
+
test_value=test_dict[key];reference_value=reference_dict[key];res_compare=self.compare_2_values(test_value,reference_value)
|
|
31
|
+
if res_compare:err_limit_reached=self.compare_errors.add(group,res_compare,info={'key':key});diffs_count+=1;group.incr(_C)
|
|
32
|
+
if err_limit_reached:break
|
|
33
|
+
return err_limit_reached,diffs_count
|
|
25
34
|
def compare_vectors(self,test_vector,reference_vector,group,info_vector=_A):
|
|
26
35
|
B='ignore';A='RIAE_trapezoid'
|
|
27
36
|
if len(test_vector)!=len(reference_vector):raise Exception('Vectors have different lengths')
|
|
@@ -29,7 +38,7 @@ class CompareFloats:
|
|
|
29
38
|
if ponderation_method=='RIAE':ponderation_method=A
|
|
30
39
|
if ponderation_method:logging.debug(f"Using ponderation method: {ponderation_method} with reduction_method {self.thresholds.vectors.reduction_method}")
|
|
31
40
|
amplitude_compare=_A
|
|
32
|
-
if self.thresholds.vectors and ponderation_method=='amplitude_moderation':amplitude=vector_get_amplitude(test_vector)[
|
|
41
|
+
if self.thresholds.vectors and ponderation_method=='amplitude_moderation':amplitude=vector_get_amplitude(test_vector)[_D];amplitude_compare=amplitude*self.thresholds.vectors.amplitude_moderation_multiplier;reduction_method=self.thresholds.vectors.reduction_method
|
|
33
42
|
RIAE_force_severity=_A
|
|
34
43
|
if self.thresholds.vectors and ponderation_method in[A,'RIAE_midpoint']:
|
|
35
44
|
if'CheckVectors'not in globals():raise Exception('scilens_compare not found. Please install scilens-compare package with `pip install scilens-compare`.')
|
|
@@ -42,7 +51,7 @@ class CompareFloats:
|
|
|
42
51
|
for idx in range(nb):
|
|
43
52
|
diff=test_vector[idx]-reference_vector[idx]
|
|
44
53
|
if diff==0:continue
|
|
45
|
-
else:diffs_count+=1;group.incr(
|
|
54
|
+
else:diffs_count+=1;group.incr(_C)
|
|
46
55
|
if err_limit_reached:continue
|
|
47
56
|
if RIAE_force_severity==B:continue
|
|
48
57
|
if amplitude_compare is not _A and abs(diff)<amplitude_compare:
|
|
@@ -66,7 +75,7 @@ class CompareFloats:
|
|
|
66
75
|
for j in range(test_nb_columns):
|
|
67
76
|
diff=test_mat[i][j]-ref_mat[i][j]
|
|
68
77
|
if diff==0:continue
|
|
69
|
-
else:diffs_count+=1;group.incr(
|
|
78
|
+
else:diffs_count+=1;group.incr(_C)
|
|
70
79
|
if err_limit_reached:continue
|
|
71
80
|
res_compare=self.compare_2_values(test_mat[i][j],ref_mat[i][j])
|
|
72
81
|
if res_compare:
|
|
@@ -9,7 +9,7 @@ class CompareFloatsErr:is_relative:bool;value:float;test:float|_A=_A;reference:f
|
|
|
9
9
|
@dataclass
|
|
10
10
|
class Compare2ValuesResults:severity:str;message:str;comp_err:CompareFloatsErr|_A=_A
|
|
11
11
|
class CompareErr(BaseModel):err:CompareFloatsErr|_A;msg:int;group:int|_A=_A;info:dict|_A=_A
|
|
12
|
-
COMPARE_GROUP_TYPE=Literal['node','lines','vectors',' matrix']
|
|
12
|
+
COMPARE_GROUP_TYPE=Literal['node','lines','vectors',' matrix','metrics','pixels']
|
|
13
13
|
@dataclass
|
|
14
14
|
class CompareGroup:
|
|
15
15
|
id:int;type:COMPARE_GROUP_TYPE;name:str;parent:Optional['CompareGroup']=_A;error:str|_A=_A;total_diffs:int=0;total_warnings:int=0;total_errors:int=0;data:dict|_A=_A;info:dict|_A=_A
|
scilens/config/models/app.py
CHANGED
|
@@ -6,4 +6,4 @@ from scilens.config.models.execute_and_compare import ExecuteAndCompareConfig
|
|
|
6
6
|
from scilens.config.models.file_reader import FileReaderConfig
|
|
7
7
|
from scilens.config.models.readers import ReadersConfig
|
|
8
8
|
from scilens.config.models.report import ReportConfig
|
|
9
|
-
class AppConfig(BaseModel):processor:str=Field(description='Nom du processeur à utiliser. `Compare` ou `ExecuteAndCompare`.');variables:dict[str,str]=Field(default={},description='Variables Utilisateur.');tags:list[str]|None=Field(default=None,description="Utilisé dans un contexte ligne de commande, utilisé en conjonction avec l'option `--discover` pour filtrer les cas à éxécuter.");execute:ExecuteConfig=Field(default=ExecuteConfig(),description=_A);execute_and_compare:ExecuteAndCompareConfig=Field(default=ExecuteAndCompareConfig(),description=_A);file_reader:FileReaderConfig=Field(default=FileReaderConfig(),description='Configuration des readers fichiers.');readers:ReadersConfig=Field(default=ReadersConfig(),description='Configuration des readers.');compare:CompareConfig=Field(default=CompareConfig(),description='Configuration utile aux processeurs `Compare` et `ExecuteAndCompare`');report:ReportConfig=Field(default=ReportConfig(),description='Configuration des Reports.')
|
|
9
|
+
class AppConfig(BaseModel,extra='forbid'):processor:str=Field(description='Nom du processeur à utiliser. `Compare` ou `ExecuteAndCompare`.');variables:dict[str,str]=Field(default={},description='Variables Utilisateur.');tags:list[str]|None=Field(default=None,description="Utilisé dans un contexte ligne de commande, utilisé en conjonction avec l'option `--discover` pour filtrer les cas à éxécuter.");execute:ExecuteConfig=Field(default=ExecuteConfig(),description=_A);execute_and_compare:ExecuteAndCompareConfig=Field(default=ExecuteAndCompareConfig(),description=_A);file_reader:FileReaderConfig=Field(default=FileReaderConfig(),description='Configuration des readers fichiers.');readers:ReadersConfig=Field(default=ReadersConfig(),description='Configuration des readers.');compare:CompareConfig=Field(default=CompareConfig(),description='Configuration utile aux processeurs `Compare` et `ExecuteAndCompare`');report:ReportConfig=Field(default=ReportConfig(),description='Configuration des Reports.')
|
scilens/config/models/compare.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
_B='forbid'
|
|
1
2
|
_A=None
|
|
2
3
|
from pydantic import BaseModel,Field
|
|
3
4
|
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.');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
|
-
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='
|
|
5
|
+
class CompareSourceFoldersConfig(BaseModel,extra=_B):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_`')
|
|
6
|
+
class CompareConfig(BaseModel,extra=_B):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='Seuils 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`).");metrics_compare:bool=Field(default=True,description='Si `true`, compare les métriques entre les données de test et de référence.');metrics_thresholds:CompareFloatThresholdsConfig|_A=Field(default=_A,description='Seuils de comparaison pour les métriques.')
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
_B='forbid'
|
|
1
2
|
_A=None
|
|
2
3
|
from pydantic import BaseModel,Field,model_validator
|
|
3
|
-
class CompareFloatVectorsConfig(BaseModel):
|
|
4
|
+
class CompareFloatVectorsConfig(BaseModel,extra=_B):
|
|
4
5
|
reduction_method:str=Field(default='soften',description="Méthode de réduction de sévérité des erreurs. Peut être `soften` ou `ignore`. Si `soften`, réduit la sévérité de l'erreur. Si `ignore`, ne lève pas d'erreur.");ponderation_method:str=Field(description='Méthode de calcul de reduction de sévérité. Peut être`amplitude_moderation`, `RIAE`, `RIAE_trapezoid` ou `RIAE_midpoint`.');amplitude_moderation_multiplier:float|_A=Field(default=_A,description="Multipler utilisé dans l'amplitude pondérée `multiplier * |Max(Vector)-Min(Vector)|` qui sera comparée à l'erreur absolue `|Test-Reference|`.");riae_threshold:float|_A=Field(default=_A,description="Seuil de l'erreur relative intégrale absolue. Si l'erreur est supérieure à ce seuil, on appliquera la reduction_method globalement.")
|
|
5
6
|
@model_validator(mode='after')
|
|
6
7
|
def check_value_required(self):
|
|
7
8
|
A=self
|
|
8
9
|
if A.ponderation_method.startswith('RIAE')and not A.riae_threshold:raise ValueError('riae_threshold is required when ponderation_method is "RIAE"')
|
|
9
10
|
return A
|
|
10
|
-
class CompareFloatThresholdsConfig(BaseModel):relative_vs_absolute_min:float=Field(default=1e-12,description="Si la valeur de test est inférieure à ce seuil, calcul de l'erreur absolue.");relative_error_min:float=Field(default=.001,description="Si l'erreur relative est supérieure à ce seuil, génère une erreur de sévérité `warning`.");relative_error_max:float=Field(default=.01,description="Si l'erreur relative est supérieure à ce seuil, génère une erreur de sévérité `error`.");absolute_error_min:float=Field(default=1e-07,description="Si l'erreur absolue est supérieure à ce seuil, génère une erreur de sévérité `warning`.");absolute_error_max:float=Field(default=1e-06,description="Si l'erreur absolue est supérieure à ce seuil, génère une erreur de sévérité `error`.");vectors:CompareFloatVectorsConfig|_A=Field(default=_A,description='Paramètres pour la comparaison de vecteurs de flottants (csv, nc, ...).')
|
|
11
|
+
class CompareFloatThresholdsConfig(BaseModel,extra=_B):relative_vs_absolute_min:float=Field(default=1e-12,description="Si la valeur de test est inférieure à ce seuil, calcul de l'erreur absolue.");relative_error_min:float=Field(default=.001,description="Si l'erreur relative est supérieure à ce seuil, génère une erreur de sévérité `warning`.");relative_error_max:float=Field(default=.01,description="Si l'erreur relative est supérieure à ce seuil, génère une erreur de sévérité `error`.");absolute_error_min:float=Field(default=1e-07,description="Si l'erreur absolue est supérieure à ce seuil, génère une erreur de sévérité `warning`.");absolute_error_max:float=Field(default=1e-06,description="Si l'erreur absolue est supérieure à ce seuil, génère une erreur de sévérité `error`.");vectors:CompareFloatVectorsConfig|_A=Field(default=_A,description='Paramètres pour la comparaison de vecteurs de flottants (csv, nc, ...).')
|
scilens/config/models/execute.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
_A=None
|
|
2
2
|
from pydantic import BaseModel,Field
|
|
3
|
-
class ExecuteConfig(BaseModel):pre_files_delete:bool=Field(default=False,description="Si `true`, supprime les fichiers avant l'éxecution. (le fichier de configuration et les fichiers . ne seront pas supprimés)");pre_folder_delete:list[str]|_A=Field(default=_A,description='Liste de répertoire à supprimer avant l\'éxecution. Ex: `["DEBUG", "LOG", "OUT"]`');pre_folder_creation:list[str]|_A=Field(default=_A,description='Liste de répertoire à créeravant l\'éxecution. Ex : `["INI", "DAT"]`');exe_path:str|_A=Field(default=_A,description="Chemin de l'exécutable. Ex : `/absolute/path/executable` or `relative/path/executable`. Dans le cas, d'un chemin relatif, il testera le `working directory` et le `origin working directory` (exclusif avec `exe_url` et `exe_command`).");exe_url:str|_A=Field(default=_A,description="Url de l'exécutable à télécharger avant de l'exécuter. Ex : `https://github.com/org/app/releases/download/v3.31.5/app-2.12.2-linux-x86-64` (exclusif avec `exe_path` et `exe_command`).");exe_command:str|_A=Field(default=_A,description='Commande à exécuter sans résolution de chemin. Ex : `./app-2.12.2-linux-x86-64` ou `python3 app.py` (exclusif avec `exe_path` et `exe_url`).');exe_url_headers:dict[str,str]|_A=Field(default=_A,description='Si `exe_url` les en-têtes à ajouter à la requête de téléchargement de l\'exécutable. Ex : `{"Authorization": "Bearer token"}`');exe_unzip_and_use:str|_A=Field(default=_A,description="Si l'exécutable (`exe_path` ou `exe_url`) est zippé, alors décompresse et utilise le fichier spécifié. Ex : `app-2.12.2-linux-x86-64`");exe_guess_os_extension:bool=Field(default=False,description="Si l'exécutable (`exe_path` ou `exe_url`) n'est pas trouvée, cherche avec des extensions en fonction de l'OS. Ex : `.exe`, `.bat` pour windows");command_suffix:str|_A=Field(default=_A,description='Suffixe à ajouter à la commande. Typiquemnt des secrets. Ex : ` --token DGDFGDFGDH`');working_dir:str|_A=Field(default=_A,description="Chemin relatif du répertoire de travail pour l'éxecution de la commande. Dans le contexte processeur `ExecuteAndCompare` si non défini, récupère respectivement la valeur de `compare.sources.test_folder_relative_path` ou `compare.sources.reference_folder_relative_path`")
|
|
3
|
+
class ExecuteConfig(BaseModel,extra='forbid'):pre_files_delete:bool=Field(default=False,description="Si `true`, supprime les fichiers avant l'éxecution. (le fichier de configuration et les fichiers . ne seront pas supprimés)");pre_folder_delete:list[str]|_A=Field(default=_A,description='Liste de répertoire à supprimer avant l\'éxecution. Ex: `["DEBUG", "LOG", "OUT"]`');pre_folder_creation:list[str]|_A=Field(default=_A,description='Liste de répertoire à créeravant l\'éxecution. Ex : `["INI", "DAT"]`');exe_path:str|_A=Field(default=_A,description="Chemin de l'exécutable. Ex : `/absolute/path/executable` or `relative/path/executable`. Dans le cas, d'un chemin relatif, il testera le `working directory` et le `origin working directory` (exclusif avec `exe_url` et `exe_command`).");exe_url:str|_A=Field(default=_A,description="Url de l'exécutable à télécharger avant de l'exécuter. Ex : `https://github.com/org/app/releases/download/v3.31.5/app-2.12.2-linux-x86-64` (exclusif avec `exe_path` et `exe_command`).");exe_command:str|_A=Field(default=_A,description='Commande à exécuter sans résolution de chemin. Ex : `./app-2.12.2-linux-x86-64` ou `python3 app.py` (exclusif avec `exe_path` et `exe_url`).');exe_url_headers:dict[str,str]|_A=Field(default=_A,description='Si `exe_url` les en-têtes à ajouter à la requête de téléchargement de l\'exécutable. Ex : `{"Authorization": "Bearer token"}`');exe_unzip_and_use:str|_A=Field(default=_A,description="Si l'exécutable (`exe_path` ou `exe_url`) est zippé, alors décompresse et utilise le fichier spécifié. Ex : `app-2.12.2-linux-x86-64`");exe_guess_os_extension:bool=Field(default=False,description="Si l'exécutable (`exe_path` ou `exe_url`) n'est pas trouvée, cherche avec des extensions en fonction de l'OS. Ex : `.exe`, `.bat` pour windows");command_suffix:str|_A=Field(default=_A,description='Suffixe à ajouter à la commande. Typiquemnt des secrets. Ex : ` --token DGDFGDFGDH`');working_dir:str|_A=Field(default=_A,description="Chemin relatif du répertoire de travail pour l'éxecution de la commande. Dans le contexte processeur `ExecuteAndCompare` si non défini, récupère respectivement la valeur de `compare.sources.test_folder_relative_path` ou `compare.sources.reference_folder_relative_path`")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
_A=None
|
|
2
2
|
from pydantic import BaseModel,Field
|
|
3
3
|
from scilens.config.models.execute import ExecuteConfig
|
|
4
|
-
class ExecuteAndCompareConfig(BaseModel):test:ExecuteConfig|_A=Field(default=_A,description='Surcharge les paramètres de la section `execute` pour le contexte de test.');test_only:bool=Field(default=False,description='Si `true`, aucune éxécution faite pour la référence (les sorties de réferences pour comparaison existent déjà).');reference:ExecuteConfig|_A=Field(default=_A,description='Surcharge les paramètres de la section `execute` pour le contexte de référence.')
|
|
4
|
+
class ExecuteAndCompareConfig(BaseModel,extra='forbid'):test:ExecuteConfig|_A=Field(default=_A,description='Surcharge les paramètres de la section `execute` pour le contexte de test.');test_only:bool=Field(default=False,description='Si `true`, aucune éxécution faite pour la référence (les sorties de réferences pour comparaison existent déjà).');reference:ExecuteConfig|_A=Field(default=_A,description='Surcharge les paramètres de la section `execute` pour le contexte de référence.')
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
_A=None
|
|
2
2
|
from pydantic import BaseModel,Field
|
|
3
|
-
class FileReaderConfig(BaseModel):encoding:str=Field(default='utf-8',description='Encodage utilisé pour lire les fichiers.');extension_unknown_ignore:bool=Field(default=False,description="Si aucun reader n'est trouvé pour une extension, passe au suivant sans générer d'erreur.");extension_mapping:dict|_A=Field(default=_A,description='Mapping d\'extensions source - cible. Ex `{"txt": "csv"}`.');extension_fallback:str|_A=Field(default=_A,description="Si aucun reader n'est trouvé pour une extension, utilise cette extension pour déterminer le reader.");extension_readers_catalog:dict[str,str]|_A=Field(default=_A,description="Mapping extension clé du catalogue de readers. Ex: `{'dat1': 'csv_comma', 'dat2': 'csv_semicolon'}`");custom_curve_parser:str|dict[str,str]|_A=Field(default=_A,description="Représente le chemin d'un fichier/module python suivi par :: suivi par le nom d'une fonction à appeller pour parser les courbes d'un fichier. Le chemin peut être absolu ou relatif. Dans le cas, d'un chemin relatif, il testera le `working directory` et le `origin working directory`. Ex: `custom_parser.py::parse_curves`")
|
|
3
|
+
class FileReaderConfig(BaseModel,extra='forbid'):encoding:str=Field(default='utf-8',description='Encodage utilisé pour lire les fichiers.');extension_unknown_ignore:bool=Field(default=False,description="Si aucun reader n'est trouvé pour une extension, passe au suivant sans générer d'erreur.");extension_mapping:dict|_A=Field(default=_A,description='Mapping d\'extensions source - cible. Ex `{"txt": "csv"}`.');extension_fallback:str|_A=Field(default=_A,description="Si aucun reader n'est trouvé pour une extension, utilise cette extension pour déterminer le reader.");extension_readers_catalog:dict[str,str]|_A=Field(default=_A,description="Mapping extension clé du catalogue de readers. Ex: `{'dat1': 'csv_comma', 'dat2': 'csv_semicolon'}`");custom_curve_parser:str|dict[str,str]|_A=Field(default=_A,description="Représente le chemin d'un fichier/module python suivi par :: suivi par le nom d'une fonction à appeller pour parser les courbes d'un fichier. Le chemin peut être absolu ou relatif. Dans le cas, d'un chemin relatif, il testera le `working directory` et le `origin working directory`. Ex: `custom_parser.py::parse_curves`")
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
_C='(Valide seulement si la première ligne est les en-têtes)'
|
|
2
|
+
_B='forbid'
|
|
2
3
|
_A=None
|
|
3
4
|
from pydantic import BaseModel,Field,model_validator
|
|
4
5
|
from enum import Enum
|
|
5
6
|
class ReaderCurveParserNameConfig(str,Enum):COL_X='col_x';COLS_COUPLE='cols_couple'
|
|
6
|
-
class ReaderCurveParserColXConfig(BaseModel):x:int|str|list[str]=Field(default=1,description="Si `integer`, index de la colonne pour l'axe x (index `1` pour la première colonne)."+"Si `String`, nom de la colonne pour l'axe x. Si la colonne n'existe pas, il n'y aura pas de parsing."+
|
|
7
|
-
class ReaderCurveParserColsCoupleConfig(BaseModel):x_index:int=Field(description='NOT IMPLEMENTED - Index Col x');y_index:int=Field(description='NOT IMPLEMENTED - Index Col y')
|
|
8
|
-
class ReaderColsCurveParserConfig(BaseModel):
|
|
9
|
-
name:ReaderCurveParserNameConfig=Field(description='Le type de `paramètres` dépend de cette valeur');parameters:ReaderCurveParserColXConfig|ReaderCurveParserColsCoupleConfig=Field(description='Paramètres du parser de courbes')
|
|
7
|
+
class ReaderCurveParserColXConfig(BaseModel,extra=_B):x:int|str|list[str]=Field(default=1,description="Si `integer`, index de la colonne pour l'axe x (index `1` pour la première colonne)."+"Si `String`, nom de la colonne pour l'axe x. Si la colonne n'existe pas, il n'y aura pas de parsing."+_C+"Si `List[String] (utiles dans un contexte multi dataset), noms de la colonne pour l'axe x, noms des colonnes pour l'axe x , si plusieurs colonnes correspondent, la première trouvée sera utilisée. Si aucune colonne ne correspond, il n'y aura pas de parsing."+_C)
|
|
8
|
+
class ReaderCurveParserColsCoupleConfig(BaseModel,extra=_B):x_index:int=Field(description='NOT IMPLEMENTED - Index Col x');y_index:int=Field(description='NOT IMPLEMENTED - Index Col y')
|
|
9
|
+
class ReaderColsCurveParserConfig(BaseModel,extra=_B):
|
|
10
|
+
name:ReaderCurveParserNameConfig=Field(description='Le type de `paramètres` dépend de cette valeur');parameters:ReaderCurveParserColXConfig|ReaderCurveParserColsCoupleConfig|_A=Field(default=_A,description='Paramètres du parser de courbes')
|
|
10
11
|
@model_validator(mode='after')
|
|
11
12
|
def validate_model(cls,model):
|
|
12
13
|
A=model
|
|
@@ -15,5 +16,5 @@ class ReaderColsCurveParserConfig(BaseModel):
|
|
|
15
16
|
if A.name==ReaderCurveParserNameConfig.COL_X:A.parameters=ReaderCurveParserColXConfig(**A.parameters)
|
|
16
17
|
elif A.name==ReaderCurveParserNameConfig.COLS_COUPLE:A.parameters=ReaderCurveParserColsCoupleConfig(**A.parameters)
|
|
17
18
|
return A
|
|
18
|
-
class ReaderColsRowsConfig(BaseModel):ignore_patterns:list[str]|_A=Field(default=_A,description='Liste des patterns de lignes à ignorer. Ex: ["^#", "^//"]. Non applicable si `is_matrix` est vrai.');line_start:int|_A=Field(default=_A,description='Ligne de début pour lire le fichier (les en-têtes sont comptées comme une ligne). Si non défini, commence à la première ligne.');line_end:int|_A=Field(default=_A,description="Ligne de fin pour lire le fichier (les en-têtes sont comptées comme une ligne). Si non défini, lit jusqu'à la dernière ligne.");index_min_value:float|_A=Field(default=_A,description="Valeur minimale de l'index pour les lignes à lire (valeur incluses).");index_max_value:float|_A=Field(default=_A,description="Valeur maximale de l'index pour les lignes à lire (valeur incluses).")
|
|
19
|
-
class ReaderColsConfig(BaseModel):ignore_columns:list[str]|list[int]|_A=Field(default=_A,description="Liste des colonnes à ignorer (nom de l'en tête ou numéro de colonne).");index_col:int|str|_A=Field(default=_A,description='Colonne à utiliser comme index (date/heure, itération, étape, ...) (Utilisé dans différentes règles). Numéro de colonne ou nom de colonne. (Valide seulement si non matrice)');rows:ReaderColsRowsConfig|_A=Field(default=_A,description='Configuration des lignes à lire.');curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
|
|
19
|
+
class ReaderColsRowsConfig(BaseModel,extra=_B):ignore_patterns:list[str]|_A=Field(default=_A,description='Liste des patterns de lignes à ignorer. Ex: ["^#", "^//"]. Non applicable si `is_matrix` est vrai.');line_start:int|_A=Field(default=_A,description='Ligne de début pour lire le fichier (les en-têtes sont comptées comme une ligne). Si non défini, commence à la première ligne.');line_end:int|_A=Field(default=_A,description="Ligne de fin pour lire le fichier (les en-têtes sont comptées comme une ligne). Si non défini, lit jusqu'à la dernière ligne.");index_min_value:float|_A=Field(default=_A,description="Valeur minimale de l'index pour les lignes à lire (valeur incluses).");index_max_value:float|_A=Field(default=_A,description="Valeur maximale de l'index pour les lignes à lire (valeur incluses).")
|
|
20
|
+
class ReaderColsConfig(BaseModel,extra=_B):ignore_columns:list[str]|list[int]|_A=Field(default=_A,description="Liste des colonnes à ignorer (nom de l'en tête ou numéro de colonne).");index_col:int|str|list[str]|_A=Field(default=_A,description='Colonne(s) à utiliser comme index (date/heure, itération, étape, ...) (Utilisé dans différentes règles). Numéro de colonne ou nom(s) de colonne. (Valide seulement si non matrice)');rows:ReaderColsRowsConfig|_A=Field(default=_A,description='Configuration des lignes à lire.');curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
_C='forbid'
|
|
1
2
|
_B=False
|
|
2
3
|
_A=None
|
|
3
4
|
from pydantic import BaseModel,Field
|
|
4
5
|
from scilens.config.models.reader_format_cols import ReaderColsConfig
|
|
5
6
|
from scilens.config.models.reader_metrics import ReaderColsMetricsConfig
|
|
6
|
-
class ReaderCsvMatrixConfig(BaseModel):x_value_line:int|_A=Field(default=_A,description="Indique la ligne des valeurs x si applicable. Peut être la ligne d'en-tête (=1).");has_y:bool=Field(default=_B,description='Indique si la première colonne est la colonne des valeurs y.');x_name:str=Field(default='X',description='Nom de la colonne des valeurs x.');y_name:str=Field(default='Y',description='Nom de la colonne des valeurs y.');export_report:bool=Field(default=_B,description='Indique si les matrices doivent être exportées dans les rapports.')
|
|
7
|
-
class ReaderCsvConfig(BaseModel):delimiter:str|_A=Field(default=',',description='Délimiteur de colonnes.');quotechar:str|_A=Field(default='"',description='Délimiteur de texte.');has_header:bool|_A=Field(default=_A,description="Indique si la 1ère ligne est une ligne d'en-tête. Si non spécifié, il y aura une détection automatique.");is_matrix:bool=Field(default=_B,description='Indique si le fichier est une matrice.');matrix:ReaderCsvMatrixConfig|_A=Field(default=_A,description='Configuration de la matrice. (Valide seulement si `is_matrix` est vrai)');cols:ReaderColsConfig|_A=Field(default=_A,description='Configuration des colonnes. (Valide seulement si non matrice)');metrics:list['ReaderColsMetricsConfig']|_A=Field(default=_A,description='Liste des métriques à calculer pour les colonnes. (Valide seulement si non matrice)')
|
|
7
|
+
class ReaderCsvMatrixConfig(BaseModel,extra=_C):x_value_line:int|_A=Field(default=_A,description="Indique la ligne des valeurs x si applicable. Peut être la ligne d'en-tête (=1).");has_y:bool=Field(default=_B,description='Indique si la première colonne est la colonne des valeurs y.');x_name:str=Field(default='X',description='Nom de la colonne des valeurs x.');y_name:str=Field(default='Y',description='Nom de la colonne des valeurs y.');export_report:bool=Field(default=_B,description='Indique si les matrices doivent être exportées dans les rapports.')
|
|
8
|
+
class ReaderCsvConfig(BaseModel,extra=_C):delimiter:str|_A=Field(default=',',description='Délimiteur de colonnes.');quotechar:str|_A=Field(default='"',description='Délimiteur de texte.');has_header:bool|_A=Field(default=_A,description="Indique si la 1ère ligne est une ligne d'en-tête. Si non spécifié, il y aura une détection automatique.");is_matrix:bool=Field(default=_B,description='Indique si le fichier est une matrice.');matrix:ReaderCsvMatrixConfig|_A=Field(default=_A,description='Configuration de la matrice. (Valide seulement si `is_matrix` est vrai)');cols:ReaderColsConfig|_A=Field(default=_A,description='Configuration des colonnes. (Valide seulement si non matrice)');metrics:list['ReaderColsMetricsConfig']|_A=Field(default=_A,description='Liste des métriques à calculer pour les colonnes. (Valide seulement si non matrice)')
|
|
@@ -3,4 +3,4 @@ _A=None
|
|
|
3
3
|
from typing import Literal
|
|
4
4
|
from pydantic import BaseModel,Field
|
|
5
5
|
CurveParser=Literal['simple_1D','frameseries_2D']
|
|
6
|
-
class ReaderNetcdfConfig(BaseModel):groups_depth:int|_A=Field(default=_A,description='Définit la profondeur de recherche des groupes. Si vide prendra tous les groupes.');units_attributes:list[str]|_A=Field(default=_A,description='Noms des attributs de variable qui spécifient les unités.');curve_parser:CurveParser|_A=Field(default=_A,description='Parser de courbes: `simple_1D` or `frameseries_2D`.');matrices_display_spectrogram:bool=Field(default=_B,description='Parseur de courbes les variables matricielles (`2D`).');curve_x_variable:str|_A=Field(default=_A,description='Nom de variable (`1D`) pour les axes X (tout parseur).');curve_y_variable:str|_A=Field(default=_A,description='Nom de variable (`1D`) pour les axes Y (`curve_parser_frameseries`).');curve_step_variable:str|_A=Field(default=_A,description='Nom de variable `Step` (`1D`) (`curve_parser_frameseries`).');compare_1D:bool=Field(default=_B,description='Pour les processeurs qui comparent, compare les variables vecteurs (`1D`).');compare_2D:bool=Field(default=_B,description='Pour les processeurs qui comparent, compare les variables matrices (`2D`).')
|
|
6
|
+
class ReaderNetcdfConfig(BaseModel,extra='forbid'):groups_depth:int|_A=Field(default=_A,description='Définit la profondeur de recherche des groupes. Si vide prendra tous les groupes.');units_attributes:list[str]|_A=Field(default=_A,description='Noms des attributs de variable qui spécifient les unités.');curve_parser:CurveParser|_A=Field(default=_A,description='Parser de courbes: `simple_1D` or `frameseries_2D`.');matrices_display_spectrogram:bool=Field(default=_B,description='Parseur de courbes les variables matricielles (`2D`).');curve_x_variable:str|_A=Field(default=_A,description='Nom de variable (`1D`) pour les axes X (tout parseur).');curve_y_variable:str|_A=Field(default=_A,description='Nom de variable (`1D`) pour les axes Y (`curve_parser_frameseries`).');curve_step_variable:str|_A=Field(default=_A,description='Nom de variable `Step` (`1D`) (`curve_parser_frameseries`).');compare_1D:bool=Field(default=_B,description='Pour les processeurs qui comparent, compare les variables vecteurs (`1D`).');compare_2D:bool=Field(default=_B,description='Pour les processeurs qui comparent, compare les variables matrices (`2D`).')
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
_B='forbid'
|
|
1
2
|
_A=None
|
|
2
3
|
from pydantic import BaseModel,Field
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class
|
|
4
|
+
from scilens.config.models.reader_metrics import ReaderTxtMetricsConfig
|
|
5
|
+
from scilens.config.models.compare_float_thresholds import CompareFloatThresholdsConfig
|
|
6
|
+
class ReaderTxtReportLines(BaseModel,extra=_B):pre:int=Field(default=0,description="Nombre de ligne avant l'erreur à présenter avec le message d'erreur.");post:int=Field(default=0,description="Nombre de ligne après l'erreur à présenter avec le message d'erreur.")
|
|
7
|
+
class ReaderTxtIgnoreConfig(BaseModel,extra=_B):pattern:str=Field(default='',description='Motif de chaîne à ignorer.');pre:int=Field(default=0,description='Nombre de ligne supplémentaires à ignorer avant.');post:int=Field(default=0,description='Nombre de ligne supplémentaires à ignorer après.')
|
|
8
|
+
class ReaderTxtConfig(BaseModel,extra=_B):error_rule_patterns:list[str]|_A=Field(default=_A,description='Liste de motifs de chaîne à vérifier. Si un motif est trouvé, génère une erreur. Ex: `["NaN", "infinity"]`');ignore:dict[str,list[ReaderTxtIgnoreConfig]]|_A=Field(default=_A,description='Ignore les lignes d\'un fichier selon un motif. Ex: `{"path": [{"pattern": "NaN", "pre": 0, "post": 0}]}`');report_lines:dict[str,ReaderTxtReportLines]|_A=Field(default=_A,description='Nombre de lignes à reporter avant et après une erreur. Ex: `{"path": {"pre": 0, "post": 0}`');metrics:list['ReaderTxtMetricsConfig']|_A=Field(default=_A,description='Liste des métriques')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
_A=None
|
|
2
2
|
from pydantic import BaseModel,Field
|
|
3
3
|
from scilens.config.models.reader_format_cols import ReaderColsCurveParserConfig,ReaderColsConfig
|
|
4
|
-
class ReaderTxtFixedColsConfig(BaseModel):column_widths:list[int]|_A=Field(default=_A,description='Liste des largeurs de colonnes. Ex: [9, 13, 12]. Exclusif avec `column_indexes`.');column_indexes:list[tuple[int,int]]|_A=Field(default=_A,description='Liste des index de début et fin de colonnes. Ex: [[0, 8], [9, 12], [13, 28]]. Exclusif avec `column_widths`.');cols:ReaderColsConfig|_A=Field(default=_A,description='Configuration des colonnes. (Valide seulement si non matrice)');has_header:bool=Field(default=False,description="Indique si le dataset contient une ligne d'entête.");has_header_line:int|_A=Field(default=_A,description="Numéro de la ligne d'entête. Si None, la première ligne non ignorée est considérée comme l'entête.");has_header_ignore:list[str]|_A=Field(default=_A,description="Chaînes de caractères à ignorer dans la ligne d'entête. eex: ['##', '|'].");curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
|
|
4
|
+
class ReaderTxtFixedColsConfig(BaseModel,extra='forbid'):column_widths:list[int]|_A=Field(default=_A,description='Liste des largeurs de colonnes. Ex: [9, 13, 12]. Exclusif avec `column_indexes`.');column_indexes:list[tuple[int,int]]|_A=Field(default=_A,description='Liste des index de début et fin de colonnes. Ex: [[0, 8], [9, 12], [13, 28]]. Exclusif avec `column_widths`.');cols:ReaderColsConfig|_A=Field(default=_A,description='Configuration des colonnes. (Valide seulement si non matrice)');has_header:bool=Field(default=False,description="Indique si le dataset contient une ligne d'entête.");has_header_line:int|_A=Field(default=_A,description="Numéro de la ligne d'entête. Si None, la première ligne non ignorée est considérée comme l'entête.");has_header_ignore:list[str]|_A=Field(default=_A,description="Chaînes de caractères à ignorer dans la ligne d'entête. eex: ['##', '|'].");curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
_C='Nom de la métrique.'
|
|
2
|
+
_B='forbid'
|
|
1
3
|
_A=None
|
|
2
4
|
from pydantic import BaseModel,Field
|
|
3
|
-
class
|
|
5
|
+
class ReaderTxtMetricsConfig(BaseModel,extra=_B):name:str|_A=Field(default=_A,description=_C);pattern:str=Field(default=_A,description='Expression régulière pour identifier la métrique.');number_position:int=Field(default=1,description='Position du nombre dans le tableau de nombres de la ligne.')
|
|
6
|
+
class ReaderColsMetricsConfig(BaseModel,extra=_B):name:str|_A=Field(default=_A,description=_C);col:int|str|_A=Field(default=_A,description="Index de la colonne pour la métrique (index `1` pour la première colonne). Si `String`, nom de la colonne pour la métrique. Si la colonne n'existe pas, il n'y aura pas de parsing. (Valide seulement si la première ligne est les en-têtes)");aggregation:str=Field(default='sum',description="Méthode d'agrégation pour la métrique. Peut être `mean`, `sum`, `min`, `max`.")
|
scilens/config/models/readers.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
_A='forbid'
|
|
1
2
|
from typing import Literal
|
|
2
3
|
from pydantic import BaseModel,Field
|
|
3
4
|
from scilens.config.models.reader_format_txt import ReaderTxtConfig
|
|
4
5
|
from scilens.config.models.reader_format_csv import ReaderCsvConfig
|
|
5
6
|
from scilens.config.models.reader_format_txt_fixed_cols import ReaderTxtFixedColsConfig
|
|
6
7
|
from scilens.config.models.reader_format_netcdf import ReaderNetcdfConfig
|
|
7
|
-
class BaseCatalogItem(BaseModel):type:str
|
|
8
|
-
class ReaderTxtConfigItem(BaseCatalogItem):type:Literal['txt'];parameters:ReaderTxtConfig
|
|
9
|
-
class ReaderCsvConfigItem(BaseCatalogItem):type:Literal['csv'];parameters:ReaderCsvConfig
|
|
10
|
-
class ReaderTxtFixedColsConfigItem(BaseCatalogItem):type:Literal['txt_fixed_cols'];parameters:ReaderTxtFixedColsConfig
|
|
11
|
-
class ReaderNetcdfConfigItem(BaseCatalogItem):type:Literal['netcdf'];parameters:ReaderNetcdfConfig
|
|
8
|
+
class BaseCatalogItem(BaseModel,extra=_A):type:str
|
|
9
|
+
class ReaderTxtConfigItem(BaseCatalogItem,extra=_A):type:Literal['txt'];parameters:ReaderTxtConfig
|
|
10
|
+
class ReaderCsvConfigItem(BaseCatalogItem,extra=_A):type:Literal['csv'];parameters:ReaderCsvConfig
|
|
11
|
+
class ReaderTxtFixedColsConfigItem(BaseCatalogItem,extra=_A):type:Literal['txt_fixed_cols'];parameters:ReaderTxtFixedColsConfig
|
|
12
|
+
class ReaderNetcdfConfigItem(BaseCatalogItem,extra=_A):type:Literal['netcdf'];parameters:ReaderNetcdfConfig
|
|
12
13
|
CATALOG_ITEM_TYPE=ReaderTxtConfigItem|ReaderCsvConfigItem|ReaderTxtFixedColsConfigItem|ReaderNetcdfConfigItem
|
|
13
|
-
class ReadersConfig(BaseModel):txt:ReaderTxtConfig=Field(default=ReaderTxtConfig(),description='Configuration des readers txt.');csv:ReaderCsvConfig=Field(default=ReaderCsvConfig(),description='Configuration des readers csv.');txt_fixed_cols:ReaderTxtFixedColsConfig=Field(default=ReaderTxtFixedColsConfig(),description='Configuration des readers txt avec colonnes fixes.');netcdf:ReaderNetcdfConfig=Field(default=ReaderNetcdfConfig(),description='Configuration des readers NetCDF.');catalog:dict[str,CATALOG_ITEM_TYPE]|None=Field(default=None,description="Catalogue de configuration de readers par clé. Ex: `{'csv_comma': {'type': 'csv', 'parameters': {'delimiter': ','}}, 'csv_semicolon': {'type': 'csv', 'parameters': {'delimiter': ';'}}}`")
|
|
14
|
+
class ReadersConfig(BaseModel,extra=_A):txt:ReaderTxtConfig=Field(default=ReaderTxtConfig(),description='Configuration des readers txt.');csv:ReaderCsvConfig=Field(default=ReaderCsvConfig(),description='Configuration des readers csv.');txt_fixed_cols:ReaderTxtFixedColsConfig=Field(default=ReaderTxtFixedColsConfig(),description='Configuration des readers txt avec colonnes fixes.');netcdf:ReaderNetcdfConfig=Field(default=ReaderNetcdfConfig(),description='Configuration des readers NetCDF.');catalog:dict[str,CATALOG_ITEM_TYPE]|None=Field(default=None,description="Catalogue de configuration de readers par clé. Ex: `{'csv_comma': {'type': 'csv', 'parameters': {'delimiter': ','}}, 'csv_semicolon': {'type': 'csv', 'parameters': {'delimiter': ';'}}}`")
|
scilens/config/models/report.py
CHANGED
|
@@ -3,4 +3,4 @@ from pydantic import BaseModel,Field
|
|
|
3
3
|
from scilens.app import product_name
|
|
4
4
|
from scilens.config.models.report_html import ReportHtmlConfig
|
|
5
5
|
from scilens.config.models.report_output import ReportOutputConfig
|
|
6
|
-
class ReportConfig(BaseModel):debug:bool=Field(default=False,description='If `true` the report will be more verbose and displays template input data.');title:str=Field(default=_A,description='Titre du rapport');title_prefix:str=Field(default=f"{product_name} Report",description='Préfixe au Titre du rapport');description:str|_A=Field(default=_A,description='Description inclu dans une section spécifique description des rapports');logo:str|_A=Field(default=_A,description="Source d'une image logo. Peut être une url ou une url data image encodée base64 (exclusif avec `logo_file`).");logo_file:str|_A=Field(default=_A,description="Source fichier d'une image logo (exclusif avec `logo`).");output:ReportOutputConfig=ReportOutputConfig();html:ReportHtmlConfig=ReportHtmlConfig()
|
|
6
|
+
class ReportConfig(BaseModel,extra='forbid'):debug:bool=Field(default=False,description='If `true` the report will be more verbose and displays template input data.');title:str=Field(default=_A,description='Titre du rapport');title_prefix:str=Field(default=f"{product_name} Report",description='Préfixe au Titre du rapport');description:str|_A=Field(default=_A,description='Description inclu dans une section spécifique description des rapports');logo:str|_A=Field(default=_A,description="Source d'une image logo. Peut être une url ou une url data image encodée base64 (exclusif avec `logo_file`).");logo_file:str|_A=Field(default=_A,description="Source fichier d'une image logo (exclusif avec `logo`).");output:ReportOutputConfig=ReportOutputConfig();html:ReportHtmlConfig=ReportHtmlConfig()
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
_E="Paramètre modifiable par l'utilisateur"
|
|
2
|
+
_D=False
|
|
3
|
+
_C=True
|
|
4
|
+
_B='forbid'
|
|
4
5
|
_A=None
|
|
5
6
|
from pydantic import BaseModel,Field
|
|
6
7
|
from scilens.config.models.base import StrOrPath
|
|
7
|
-
class ReportParameterPageModeConfig(BaseModel):is_user_preference:bool=Field(default=
|
|
8
|
-
class ReportParameterOpenFileInConfig(BaseModel):is_user_preference:bool=Field(default=
|
|
9
|
-
class ReportHtmlCurvesConfig(BaseModel):display_on_load:bool=Field(default=
|
|
10
|
-
class ReportHtmlSpectrogramsConfig(BaseModel):test_ref:bool=Field(default=
|
|
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=
|
|
8
|
+
class ReportParameterPageModeConfig(BaseModel,extra=_B):is_user_preference:bool=Field(default=_C,description=_E);default_value:str=Field(default='onepage',description='`tabs` ou `onepage`')
|
|
9
|
+
class ReportParameterOpenFileInConfig(BaseModel,extra=_B):is_user_preference:bool=Field(default=_C,description=_E);default_value:str=Field(default='browser',description='`browser` ou `vscode`')
|
|
10
|
+
class ReportHtmlCurvesConfig(BaseModel,extra=_B):display_on_load:bool=Field(default=_D,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=_C,description='Dans le chart de comparaison, affiche les valeurs de référence et de test.');compare_vs_difference:bool=Field(default=_D,description='Dans le chart de comparaison, affiche la différence entre les valeurs de référence et de test.')
|
|
11
|
+
class ReportHtmlSpectrogramsConfig(BaseModel,extra=_B):test_ref:bool=Field(default=_C,description='Indique si les spectrogrammes de test doivent être affichés.');differences:bool=Field(default=_C,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=_D,description='Indique si les spectrogrammes doivent être affichés en 3D.')
|
|
12
|
+
class ReportHtmlFrameseriesConfig(BaseModel,extra=_B):width:int=Field(default=300,description='Largeur initiale des graphiques frameseries.');height:int=Field(default=300,description='Hauteur initiale des graphiques frameseries.')
|
|
13
|
+
class ReportHtmlMatrixConfig(BaseModel,extra=_B):spectrograms:ReportHtmlSpectrogramsConfig|_A=_A;frameseries:ReportHtmlFrameseriesConfig|_A=_A
|
|
14
|
+
class ReportHtmlConfig(BaseModel,extra=_B):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=_C,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()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
_A=False
|
|
2
2
|
from pydantic import BaseModel,Field
|
|
3
3
|
from scilens.app import pkg_name
|
|
4
|
-
class ReportOutputConfig(BaseModel):filename:str=Field(default=f"{pkg_name}_report",description="Nom de fichier (sans l'extension) des rapports générés.");export_html:bool=Field(default=_A,description='Génère un rapport HTML');export_txt:bool=Field(default=_A,description='Génère un rapport TXT');export_json:bool=Field(default=_A,description='Génère un rapport JSON');export_yaml:bool=Field(default=_A,description='Génère un rapport YAML');export_py:bool=Field(default=_A,description='Génère un rapport Python (Variable)');export_js:bool=Field(default=_A,description='Génère un rapport Javascript (Variable)');export_ts:bool=Field(default=_A,description='Génère un rapport Typecript (Variable)');export_php:bool=Field(default=_A,description='Génère un rapport Php (Variable)')
|
|
4
|
+
class ReportOutputConfig(BaseModel,extra='forbid'):filename:str=Field(default=f"{pkg_name}_report",description="Nom de fichier (sans l'extension) des rapports générés.");export_html:bool=Field(default=_A,description='Génère un rapport HTML');export_txt:bool=Field(default=_A,description='Génère un rapport TXT');export_json:bool=Field(default=_A,description='Génère un rapport JSON');export_yaml:bool=Field(default=_A,description='Génère un rapport YAML');export_py:bool=Field(default=_A,description='Génère un rapport Python (Variable)');export_js:bool=Field(default=_A,description='Génère un rapport Javascript (Variable)');export_ts:bool=Field(default=_A,description='Génère un rapport Typecript (Variable)');export_php:bool=Field(default=_A,description='Génère un rapport Php (Variable)')
|
scilens/readers/cols_dataset.py
CHANGED
|
@@ -9,22 +9,23 @@ from scilens.components.compare_models import CompareGroup
|
|
|
9
9
|
from scilens.components.compare_floats import CompareFloats
|
|
10
10
|
from scilens.config.models.reader_format_cols import ReaderCurveParserNameConfig
|
|
11
11
|
from scilens.config.models.reader_metrics import ReaderColsMetricsConfig
|
|
12
|
+
def get_index_col_index(index_col,numeric_col_indexes,names):
|
|
13
|
+
C=names;A=index_col;B=_A
|
|
14
|
+
if isinstance(A,int):B=A-1
|
|
15
|
+
if isinstance(A,str):A=[A]
|
|
16
|
+
if isinstance(A,list):
|
|
17
|
+
for D in A:
|
|
18
|
+
if D in C:B=C.index(D);break
|
|
19
|
+
if B:
|
|
20
|
+
if B not in numeric_col_indexes:raise ValueError(f"Index column index {A} is not a numeric column.")
|
|
21
|
+
return B
|
|
12
22
|
@dataclass
|
|
13
23
|
class ColsDataset:
|
|
14
24
|
cols_count:int=0;rows_count:int=0;names:list[str]=field(default_factory=lambda:[]);numeric_col_indexes:list[int]=field(default_factory=lambda:[]);data:list[list[float]]=field(default_factory=lambda:[]);origin_line_nb:list[int]=field(default_factory=lambda:[])
|
|
15
|
-
def get_curves_col_x(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if C<0 or C>=A.cols_count:raise Exception('curve parser col_x: col_index is out of range.')
|
|
20
|
-
if isinstance(B,str):B=[B]
|
|
21
|
-
if isinstance(B,list):
|
|
22
|
-
G=[A for(A,C)in enumerate(A.names)if C in B]
|
|
23
|
-
if len(G)==0:return _A,E
|
|
24
|
-
C=G[0]
|
|
25
|
-
E[_D]=C;K=[B for(A,B)in enumerate(A.numeric_col_indexes)if A!=C];F=[];H=[]
|
|
26
|
-
for D in K:B=A.data[C];L=A.data[D];M={I:A.names[D],'short_title':A.names[D],'series':[[B[A],L[A]]for A in range(A.rows_count)],_C:D};F+=[M];N={I:A.names[D],'type':'simple','xaxis':A.names[C],'yaxis':A.names[D],_B:[len(F)-1]};H+=[N]
|
|
27
|
-
return{_B:F,_E:H},E
|
|
25
|
+
def get_curves_col_x(I,col_x):
|
|
26
|
+
H='title';D=col_x;A=I;F={};C=get_index_col_index(D,A.numeric_col_indexes,A.names);F[_D]=C;J=[B for(A,B)in enumerate(A.numeric_col_indexes)if A!=C];E=[];G=[]
|
|
27
|
+
for B in J:D=A.data[C];K=A.data[B];L={H:A.names[B],'short_title':A.names[B],'series':[[D[A],K[A]]for A in range(A.rows_count)],_C:B};E+=[L];M={H:A.names[B],'type':'simple','xaxis':A.names[C],'yaxis':A.names[B],_B:[len(E)-1]};G+=[M]
|
|
28
|
+
return{_B:E,_E:G},F
|
|
28
29
|
def compute_metrics(I,config):
|
|
29
30
|
D=I;H={}
|
|
30
31
|
for A in config:
|
scilens/readers/reader_csv.py
CHANGED
|
@@ -2,7 +2,7 @@ _B=True
|
|
|
2
2
|
_A=None
|
|
3
3
|
import logging,csv,re
|
|
4
4
|
from scilens.readers.reader_interface import ReaderInterface
|
|
5
|
-
from scilens.readers.cols_dataset import ColsDataset,ColsCurves,compare as cols_compare
|
|
5
|
+
from scilens.readers.cols_dataset import ColsDataset,ColsCurves,compare as cols_compare,get_index_col_index
|
|
6
6
|
from scilens.readers.mat_dataset import MatDataset,from_iterator as mat_from_iterator,compare as mat_compare,get_data
|
|
7
7
|
from scilens.config.models.reader_format_csv import ReaderCsvConfig,ReaderCsvMatrixConfig
|
|
8
8
|
from scilens.config.models.reader_format_cols import ReaderCurveParserNameConfig
|
|
@@ -27,61 +27,60 @@ class ReaderCsv(ReaderInterface):
|
|
|
27
27
|
if bool(re.match(B,line)):return _B
|
|
28
28
|
return False
|
|
29
29
|
def read(A,reader_options):
|
|
30
|
-
C=reader_options;A.reader_options=C;F,
|
|
30
|
+
C=reader_options;A.reader_options=C;F,L,U=csv_detect(A.origin.path,A.reader_options.delimiter,A.reader_options.quotechar,encoding=A.encoding);A.has_header=F;A.cols=L;A.numeric_col_indexes=U;A.index_col_index=_A;A.ignore_lines_patterns=_A;G=_A;H=_A;I=_A;J=_A;B=C.cols
|
|
31
31
|
if B:
|
|
32
32
|
if B.index_col:
|
|
33
|
-
if B.index_col:
|
|
34
|
-
if isinstance(B.index_col,int):
|
|
35
|
-
G=B.index_col-1
|
|
36
|
-
if G not in A.numeric_col_indexes:raise ValueError(f"Index column index {B.index_col} is not a numeric column.")
|
|
37
|
-
A.index_col_index=G
|
|
38
|
-
elif isinstance(B.index_col,str):
|
|
39
|
-
try:A.index_col_index=I.index(B.index_col)
|
|
40
|
-
except ValueError:raise ValueError(f"Index column '{B.index_col}' not found in CSV headers.")
|
|
33
|
+
if B.index_col:A.index_col_index=get_index_col_index(B.index_col,A.numeric_col_indexes,L)
|
|
41
34
|
if B.rows:
|
|
42
|
-
A.ignore_lines_patterns=B.rows.ignore_patterns;
|
|
35
|
+
A.ignore_lines_patterns=B.rows.ignore_patterns;G=B.rows.line_start;H=B.rows.line_end
|
|
36
|
+
if G and H and H<G:raise ValueError(f"Line end {H} cannot be before line start {G}.")
|
|
43
37
|
if B.rows.index_min_value or B.rows.index_max_value:
|
|
44
38
|
if A.index_col_index is _A:raise ValueError('Index column must be defined to use index min/max values.')
|
|
45
|
-
|
|
39
|
+
I=B.rows.index_min_value;J=B.rows.index_max_value
|
|
40
|
+
if I and J and I>J:raise ValueError(f"Index min value {I} cannot be greater than index max value {J}.")
|
|
46
41
|
A.raw_lines_number=_A;A.curves=_A;A.report_matrices=_A
|
|
47
|
-
with open(A.origin.path,'r',encoding=A.encoding)as
|
|
48
|
-
R=
|
|
42
|
+
with open(A.origin.path,'r',encoding=A.encoding)as V:
|
|
43
|
+
R=V.readlines();M=csv.reader(R,delimiter=A.reader_options.delimiter,quotechar=A.reader_options.quotechar)
|
|
49
44
|
if C.is_matrix:
|
|
50
|
-
|
|
51
|
-
if
|
|
45
|
+
K=C.matrix or ReaderCsvMatrixConfig();P=mat_from_iterator(x_name=K.x_name,y_name=K.y_name,reader=M,has_header=F,x_value_line=K.x_value_line,has_y=K.has_y)
|
|
46
|
+
if K.export_report:A.report_matrices=get_data([P],['csv'])
|
|
52
47
|
A.mat_dataset=P;A.raw_lines_number=P.nb_lines+(1 if F else 0)
|
|
53
48
|
else:
|
|
54
49
|
if C.cols and C.cols.ignore_columns:
|
|
55
50
|
if not F:raise Exception('Ignore columns is not supported without header.')
|
|
56
51
|
if isinstance(C.cols.ignore_columns[0],str):A.numeric_col_indexes=[B for B in A.numeric_col_indexes if A.cols[B]not in C.cols.ignore_columns]
|
|
57
|
-
if isinstance(C.cols.ignore_columns[0],int):
|
|
58
|
-
S=len(
|
|
59
|
-
if F and E==0:next(
|
|
60
|
-
if
|
|
52
|
+
if isinstance(C.cols.ignore_columns[0],int):W=[A-1 for A in B.ignore_columns];A.numeric_col_indexes=[A for A in A.numeric_col_indexes if A not in W]
|
|
53
|
+
S=len(L);D=ColsDataset(cols_count=S,names=L,numeric_col_indexes=A.numeric_col_indexes,data=[[]for A in range(S)]);E=0
|
|
54
|
+
if F and E==0:next(M);E+=1
|
|
55
|
+
if G:
|
|
61
56
|
try:
|
|
62
57
|
while _B:
|
|
63
|
-
if
|
|
64
|
-
|
|
58
|
+
if G<=E+1:break
|
|
59
|
+
N=next(M);E+=1
|
|
65
60
|
except StopIteration:pass
|
|
66
61
|
try:
|
|
67
62
|
while _B:
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
-
if
|
|
63
|
+
N=next(M);E+=1
|
|
64
|
+
if H and E>H:break
|
|
65
|
+
if J is not _A and float(N[A.index_col_index])>J:break
|
|
71
66
|
if A.ignore_lines_patterns and A._ignore_line(R[E-1].rstrip('\n')):continue
|
|
72
|
-
if
|
|
73
|
-
for(
|
|
74
|
-
if
|
|
75
|
-
D.data[
|
|
67
|
+
if I is not _A and float(N[A.index_col_index])<I:continue
|
|
68
|
+
for(T,Q)in enumerate(N):
|
|
69
|
+
if T in D.numeric_col_indexes:Q=float(Q)
|
|
70
|
+
D.data[T].append(Q)
|
|
76
71
|
D.origin_line_nb.append(E)
|
|
77
72
|
except StopIteration:pass
|
|
78
73
|
D.rows_count=len(D.origin_line_nb);A.cols_dataset=D;A.raw_lines_number=D.rows_count+(1 if F else 0);A.metrics=_A
|
|
79
74
|
if C.metrics:A.metrics=D.compute_metrics(C.metrics)
|
|
80
|
-
if
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
if
|
|
84
|
-
|
|
75
|
+
if B and B.curve_parser:
|
|
76
|
+
if B.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
|
|
77
|
+
O=_A
|
|
78
|
+
if B.curve_parser.parameters:O=B.curve_parser.parameters.x
|
|
79
|
+
elif B.index_col:O=B.index_col
|
|
80
|
+
if not O:raise ValueError('Curve parser COL_X requires a parameter x, or index_col to be defined.')
|
|
81
|
+
A.curves,X=D.get_curves_col_x(O)
|
|
82
|
+
if A.curves:A.cols_curve=ColsCurves(type=ReaderCurveParserNameConfig.COL_X,info=X,curves=A.curves)
|
|
83
|
+
elif B.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
|
|
85
84
|
else:raise Exception('Curve parser not supported.')
|
|
86
85
|
def compare(A,compare_floats,param_reader,param_is_ref=_B):
|
|
87
86
|
H='node';D=param_is_ref;C=param_reader;B=compare_floats;I=A.reader_options
|
|
@@ -6,10 +6,10 @@ class ReaderInterface(ABC):
|
|
|
6
6
|
@property
|
|
7
7
|
@abstractmethod
|
|
8
8
|
def category(self):0
|
|
9
|
-
def __init__(A,origin,name='',encoding='',curve_parser=None):A.origin=origin;A.name=name;A.encoding=encoding or'utf-8';A.curve_parser=curve_parser;A.read_error=None;A.read_data={}
|
|
9
|
+
def __init__(A,origin,name='',encoding='',curve_parser=None):A.origin=origin;A.name=name;A.encoding=encoding or'utf-8';A.curve_parser=curve_parser;A.read_error=None;A.read_data={};A.reader_info={}
|
|
10
10
|
def read(A,reader_options=None):raise NotImplementedError(_A)
|
|
11
11
|
def compare(A,reader,param_is_ref=True):raise NotImplementedError(_A)
|
|
12
12
|
def get_raw_lines(A,line_nb,pre=0,post=0):raise NotImplementedError(_A)
|
|
13
13
|
def class_info(A):raise NotImplementedError(_A)
|
|
14
14
|
def close(A):0
|
|
15
|
-
def info(A):B={'reader':A.__class__.__name__,'
|
|
15
|
+
def info(A):B={'reader':A.__class__.__name__,'reader_info':A.reader_info,'name':A.name,'origin':A.origin.dict(),'encoding':A.encoding,'read_error':A.read_error,'read_data':A.read_data};B.update(A.class_info());return B
|