scilens 0.1.3__py3-none-any.whl → 0.2.1__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/cli/main.py CHANGED
@@ -1,3 +1,4 @@
1
+ _B='green'
1
2
  _A=True
2
3
  import logging,os,coloredlogs,rich_click as click
3
4
  from scilens.app import pkg_name,pkg_version
@@ -11,9 +12,9 @@ from scilens.cli.info import echo_info,echo_system_info,echo_draw
11
12
  from scilens.config.cli_run_options import get_vars
12
13
  from scilens.helpers.search_and_index import SearchAndIndex
13
14
  def echo_separator(msg=None):
14
- B='green';A=msg
15
- if A:A=f" {A}";click.echo(click.style(A.rjust(80,'='),fg=B))
16
- else:click.echo(click.style('='*80,fg=B))
15
+ A=msg
16
+ if A:A=f" {A}";click.echo(click.style(A.rjust(80,'='),fg=_B))
17
+ else:click.echo(click.style('='*80,fg=_B))
17
18
  def echo_task_error():click.echo(click.style('='*80,fg='red'))
18
19
  @click.group()
19
20
  @click.option('--log-level',default='INFO',help='Log level. Default is INFO. Available levels are DEBUG, INFO, WARNING, ERROR, CRITICAL.')
@@ -27,8 +28,9 @@ def info():echo_draw();echo_info()
27
28
  @cli.command(short_help='Show system informations. (Useful for dynamic context configuration)')
28
29
  def sysinfo():echo_system_info()
29
30
  @cli.command(short_help='Show the reader plugins')
30
- def reader():
31
- for A in ReaderManager()._get_plugin_names():click.echo(click.style(A,fg='yellow'))
31
+ def readers():
32
+ J='configuration_type_code';I='class';D='yellow';A=' ';C=ReaderManager()._get_plugin_info();E='Class';F='Configuration Type Code';B=max([len(A[I])for A in C]+[len(E)]);G=max([len(A[J])for A in C]+[len(F)]);click.echo(click.style((E+A).ljust(B,A),fg=_B)+A+click.style(F,fg=D));click.echo(click.style(('-'*B).ljust(B,A),fg=_B)+A+click.style('-'*G,fg=D))
33
+ for H in C:click.echo(click.style(H[I].ljust(B,A),fg=_B)+A+click.style(H[J].ljust(G,A),fg=D))
32
34
  @cli.group()
33
35
  def config():0
34
36
  @config.command(name='default',short_help='Example of default config')
File without changes
@@ -1,39 +1,50 @@
1
- _C='amplitude'
2
- _B=False
1
+ _B='amplitude'
3
2
  _A=None
4
3
  from scilens.components.compare_errors import CompareErrors,SEVERITY_ERROR,SEVERITY_WARNING,CompareErrFloats
5
4
  from scilens.config.models import CompareFloatThresholdsConfig
6
- def vector_get_amplitude(vector):A=vector;B=min(A);C=max(A);return{'min':B,'max':C,_C:abs(C-B)}
5
+ try:from scilens_compare import vectors as CheckVectors
6
+ except ModuleNotFoundError:pass
7
+ def vector_get_amplitude(vector):min_val=min(vector);max_val=max(vector);return{'min':min_val,'max':max_val,_B:abs(max_val-min_val)}
7
8
  class CompareFloats:
8
- def __init__(A,compare_errors,config):A.compare_errors=compare_errors;A.thresholds=config
9
- def compare_vectors(B,test_vector,reference_vector,group_idx=_A,info_vector=_A):
10
- H=info_vector;E=reference_vector;C=test_vector;I=0;J={SEVERITY_ERROR:0,SEVERITY_WARNING:0};K=_B;F=_A
11
- if B.thresholds.vectors.amplitude_moderation:P=vector_get_amplitude(C)[_C];F=P*B.thresholds.vectors.amplitude_moderation.multiplier;L=B.thresholds.vectors.amplitude_moderation.method
12
- Q=len(C)
13
- for A in range(Q):
14
- M=C[A]-E[A]
15
- if M==0:continue
16
- else:I+=1
17
- if K:continue
18
- if F is not _A and abs(M)<F:
19
- if L=='ignore':continue
20
- elif L=='soften':D,N,G=B.compare_2_values(C[A],E[A]);D=SEVERITY_WARNING
21
- else:D,N,G=B.compare_2_values(C[A],E[A])
22
- if G:
23
- J[D]+=1;O={'index':A}
24
- if H:O['info']=H[A]
25
- K=B.compare_errors.add(D,N,G,group_idx=group_idx,info=O)
26
- return _B,I,J
27
- def compare_2_values(G,test,reference):
28
- D=test;B=reference;A=G.thresholds;F=-1 if D-B<0 else 1
29
- if abs(D)>A.relative_vs_absolute_min and B!=0:
30
- C=abs(D-B)/abs(B);E=CompareErrFloats(is_relative=True,value=F*C,test=D,reference=B)
31
- if C<A.relative_error_max:
32
- if C>A.relative_error_min:return SEVERITY_WARNING,f"Rel. err. > {A.relative_error_min} and < {A.relative_error_max}",E
33
- else:return SEVERITY_ERROR,f"Rel. err. > {A.relative_error_max}",E
9
+ def __init__(self,compare_errors,config):self.compare_errors=compare_errors;self.thresholds=config
10
+ def compare_vectors(self,test_vector,reference_vector,group_idx=_A,info_vector=_A):
11
+ A='ignore';diffs_count=0;counts={SEVERITY_ERROR:0,SEVERITY_WARNING:0};err_limit_reached=False;amplitude_compare=_A
12
+ if self.thresholds.vectors and self.thresholds.vectors.ponderation_method=='amplitude_moderation':amplitude=vector_get_amplitude(test_vector)[_B];amplitude_compare=amplitude*self.thresholds.vectors.amplitude_moderation_multiplier;reduction_method=self.thresholds.vectors.reduction_method
13
+ RIAE_force_severity=_A
14
+ if self.thresholds.vectors and self.thresholds.vectors.ponderation_method in['RIAE','RIAE_trapezoid','RIAE_midpoint']:
15
+ RIAE_force_severity=self.thresholds.vectors.reduction_method
16
+ if'CheckVectors'not in globals():raise Exception('scilens_compare not found. Please install scilens-compare package with `pip install scilens-compare`.')
17
+ riae_error=CheckVectors.relative_integral_absolute_error_trapezoid(reference_vector,test_vector,range(len(test_vector)));riae_error=CheckVectors.relative_integral_absolute_error_midpoint(reference_vector,test_vector,range(len(test_vector)));print('riae_error');print(riae_error)
18
+ if riae_error<self.thresholds.vectors.riae_threshold:RIAE_force_severity=self.thresholds.vectors.reduction_method
19
+ else:0
20
+ nb=len(test_vector)
21
+ for idx in range(nb):
22
+ diff=test_vector[idx]-reference_vector[idx]
23
+ if diff==0:continue
24
+ else:diffs_count+=1
25
+ if err_limit_reached:continue
26
+ if RIAE_force_severity==A:continue
27
+ if amplitude_compare is not _A and abs(diff)<amplitude_compare:
28
+ if reduction_method==A:continue
29
+ elif reduction_method=='soften':severity,message,comp_err=self.compare_2_values(test_vector[idx],reference_vector[idx]);severity=SEVERITY_WARNING
30
+ else:
31
+ severity,message,comp_err=self.compare_2_values(test_vector[idx],reference_vector[idx])
32
+ if RIAE_force_severity:severity=SEVERITY_WARNING
33
+ if comp_err:
34
+ counts[severity]+=1;info={'index':idx}
35
+ if info_vector:info['info']=info_vector[idx]
36
+ err_limit_reached=self.compare_errors.add(severity,message,comp_err,group_idx=group_idx,info=info)
37
+ return err_limit_reached,diffs_count,counts
38
+ def compare_2_values(self,test,reference):
39
+ thr=self.thresholds;sign=-1 if test-reference<0 else 1
40
+ if abs(test)>thr.relative_vs_absolute_min and reference!=0:
41
+ err=abs(test-reference)/abs(reference);comp_err=CompareErrFloats(is_relative=True,value=sign*err,test=test,reference=reference)
42
+ if err<thr.relative_error_max:
43
+ if err>thr.relative_error_min:return SEVERITY_WARNING,f"Rel. err. > {thr.relative_error_min} and < {thr.relative_error_max}",comp_err
44
+ else:return SEVERITY_ERROR,f"Rel. err. > {thr.relative_error_max}",comp_err
34
45
  else:
35
- C=abs(D-B);E=CompareErrFloats(is_relative=_B,value=F*C,test=D,reference=B)
36
- if C<A.absolute_error_max:
37
- if C>A.absolute_error_min:return SEVERITY_WARNING,f"Abs. err. > {A.absolute_error_min} and < {A.absolute_error_max}",E
38
- else:return SEVERITY_ERROR,f"Abs. err. > {A.absolute_error_max}",E
46
+ err=abs(test-reference);comp_err=CompareErrFloats(is_relative=False,value=sign*err,test=test,reference=reference)
47
+ if err<thr.absolute_error_max:
48
+ if err>thr.absolute_error_min:return SEVERITY_WARNING,f"Abs. err. > {thr.absolute_error_min} and < {thr.absolute_error_max}",comp_err
49
+ else:return SEVERITY_ERROR,f"Abs. err. > {thr.absolute_error_max}",comp_err
39
50
  return _A,_A,_A
@@ -15,16 +15,11 @@ class FileReader:
15
15
  if not B:raise Exception(f"Custom curve parser not found: {A}")
16
16
  J=C.split('/')[-1].replace('.py','');F=importlib.util.spec_from_file_location(J,B);G=importlib.util.module_from_spec(F);F.loader.exec_module(G);return getattr(G,H)
17
17
  def read(A,path):
18
- logging.info(f"Reading file: {path}");E=_A;D=A.config.custom_curve_parser
19
- if D:
20
- if isinstance(D,str):E=A._get_custom_parser(D)
21
- try:
22
- B=A.reader_mgmr.get_reader_from_file(path,encoding=A.config.encoding,curve_parser=E,extension_mapping=A.config.extension_mapping,extension_fallback=A.config.extension_fallback);C=_A
23
- if B.__class__.__name__=='ReaderTxt':C=A.readers_config.txt
24
- elif B.__class__.__name__=='ReaderCsv':C=A.readers_config.csv
25
- elif B.__class__.__name__=='ReaderNetcdf':C=A.readers_config.netcdf
26
- B.read(C)
18
+ logging.info(f"Reading file: {path}");D=_A;B=A.config.custom_curve_parser
19
+ if B:
20
+ if isinstance(B,str):D=A._get_custom_parser(B)
21
+ try:C,E=A.reader_mgmr.get_reader_from_file(path,config=A.config,readers_config=A.readers_config,curve_parser=D);C.read(E)
27
22
  except NoReaderFound:
28
- if A.config.extension_unknown_ignore:0
23
+ if A.config.extension_unknown_ignore:C=_A
29
24
  else:raise Exception(f"No reader found")
30
- return B
25
+ return C
@@ -5,6 +5,7 @@ from.execute_and_compare import ExecuteAndCompareConfig
5
5
  from.execute import ExecuteConfig
6
6
  from.reader_format_txt import ReaderTxtIgnoreConfig,ReaderTxtConfig
7
7
  from.reader_format_csv import ReaderCsvConfig
8
+ from.reader_format_txt_fixed_cols import ReaderTxtFixedColsConfig
8
9
  from.readers import ReadersConfig
9
10
  from.file_reader import FileReaderConfig
10
11
  from.report_html import ReportHtmlConfig,ReportHtmlCurvesConfig,ReportParameterOpenFileInConfig,ReportParameterPageModeConfig
@@ -1,4 +1,4 @@
1
+ _A=None
1
2
  from pydantic import BaseModel,Field
2
- class CompareFloatVectorsAmplitudeConfig(BaseModel):method:str=Field(default='soften',description="Si `soften`, réduit la sévérité de l'erreur. Si `ignore`, ne lève pas d'erreur.");multiplier:float=Field(description='Processe la méthode si `|Test-Reference|` (Erreur Absolue) est inférieure à `multiplier * |Max(Vector)-Min(Vector)|` (Amplitude pondérée). Ex: `0.1`.')
3
- class CompareFloatVectorsConfig(BaseModel):amplitude_moderation:CompareFloatVectorsAmplitudeConfig|None=Field(default=None,description="Pondére la gestion d'erreur avec l'amplitude.")
4
- 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=Field(default=CompareFloatVectorsConfig(),description='Paramètres pour la comparaison de vecteurs de flottants (csv, nc, ...).')
3
+ class CompareFloatVectorsConfig(BaseModel):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.")
4
+ 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, ...).')
@@ -1,3 +1,3 @@
1
1
  _A=None
2
2
  from pydantic import BaseModel,Field
3
- class FileReaderConfig(BaseModel):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.");encoding:str=Field(default='utf-8',description='Encodage utilisé pour lire les fichiers.');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):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`")
@@ -0,0 +1,16 @@
1
+ _A='(Valide seulement si la première ligne est les en-têtes)'
2
+ from pydantic import BaseModel,Field,model_validator
3
+ from enum import Enum
4
+ class ReaderCurveParserNameConfig(str,Enum):COL_X='col_x';COLS_COUPLE='cols_couple'
5
+ 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."+_A+"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."+_A)
6
+ class ReaderCurveParserColsCoupleConfig(BaseModel):x_index:int=Field(description='NOT IMPLEMENTED - Index Col x');y_index:int=Field(description='NOT IMPLEMENTED - Index Col y')
7
+ class ReaderColsCurveParserConfig(BaseModel):
8
+ 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')
9
+ @model_validator(mode='after')
10
+ def validate_model(cls,model):
11
+ A=model
12
+ if isinstance(A.parameters,ReaderCurveParserColXConfig)and A.name!=ReaderCurveParserNameConfig.COL_X or isinstance(A.parameters,ReaderCurveParserColsCoupleConfig)and A.name!=ReaderCurveParserNameConfig.COLS_COUPLE:raise ValueError(f"Curve Parser {A.name} Parameters are not correct")
13
+ if isinstance(A.parameters,dict):
14
+ if A.name==ReaderCurveParserNameConfig.COL_X:A.parameters=ReaderCurveParserColXConfig(**A.parameters)
15
+ elif A.name==ReaderCurveParserNameConfig.COLS_COUPLE:A.parameters=ReaderCurveParserColsCoupleConfig(**A.parameters)
16
+ return A
@@ -1,18 +1,4 @@
1
- _B='(Valide seulement si la première ligne est les en-têtes)'
2
1
  _A=None
3
- from pydantic import BaseModel,Field,model_validator
4
- from enum import Enum
5
- class ReaderCsvCurveParserNameConfig(str,Enum):COL_X='col_x';COLS_COUPLE='cols_couple'
6
- class ReaderCsvCurveParserColXConfig(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."+_B+"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."+_B)
7
- class ReaderCsvCurveParserColsCoupleConfig(BaseModel):x_index:int=Field(description='NOT IMPLEMENTED - Index Col x');y_index:int=Field(description='NOT IMPLEMENTED - Index Col y')
8
- class ReaderCsvCurveParserConfig(BaseModel):
9
- name:ReaderCsvCurveParserNameConfig=Field(description='Le type de `paramètres` dépend de cette valeur');parameters:ReaderCsvCurveParserColXConfig|ReaderCsvCurveParserColsCoupleConfig=Field(description='Paramètres du parser de courbes')
10
- @model_validator(mode='after')
11
- def validate_model(cls,model):
12
- A=model
13
- if isinstance(A.parameters,ReaderCsvCurveParserColXConfig)and A.name!=ReaderCsvCurveParserNameConfig.COL_X or isinstance(A.parameters,ReaderCsvCurveParserColsCoupleConfig)and A.name!=ReaderCsvCurveParserNameConfig.COLS_COUPLE:raise ValueError(f"Curve Parser {A.name} Parameters are not correct")
14
- if isinstance(A.parameters,dict):
15
- if A.name==ReaderCsvCurveParserNameConfig.COL_X:A.parameters=ReaderCsvCurveParserColXConfig(**A.parameters)
16
- elif A.name==ReaderCsvCurveParserNameConfig.COLS_COUPLE:A.parameters=ReaderCsvCurveParserColsCoupleConfig(**A.parameters)
17
- return A
18
- class ReaderCsvConfig(BaseModel):ignore_colunmns:list[str]|_A=Field(default=_A,description='Liste des noms des colonnes à ignorer. (Valide seulement si la première ligne est les en-têtes)');curve_parser:ReaderCsvCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
2
+ from pydantic import BaseModel,Field
3
+ from scilens.config.models.reader_format_cols_curve import ReaderColsCurveParserConfig
4
+ class ReaderCsvConfig(BaseModel):ignore_columns:list[str]|_A=Field(default=_A,description='Liste des noms des colonnes à ignorer. (Valide seulement si la première ligne est les en-têtes)');delimiter:str|_A=Field(default=',',description='Délimiteur de colonnes.');quotechar:str|_A=Field(default='"',description='Délimiteur de texte.');curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
@@ -0,0 +1,4 @@
1
+ _A=None
2
+ from pydantic import BaseModel,Field
3
+ from scilens.config.models.reader_format_cols_curve import ReaderColsCurveParserConfig
4
+ class ReaderTxtFixedColsConfig(BaseModel):ignore_lines_patterns:list[str]|_A=Field(default=_A,description='Liste des patterns de lignes à ignorer. Ex: ["^#", "^//"].');has_header:bool=Field(default=False,description="Indique si le dataset contient une ligne d'entête.");has_header_repetition:bool=Field(default=False,description="Indique si l'entête est répétée (pagination).");has_header_ignore:list[str]|_A=Field(default=_A,description="Chaînes de caractères à ignorer dans la ligne d'entête. eex: ['##', '|'].");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[list[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`.');curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.')
@@ -1,5 +1,17 @@
1
- from pydantic import BaseModel,Field
2
- from scilens.config.models.reader_format_txt import ReaderTxtIgnoreConfig,ReaderTxtConfig
1
+ _B='netcdf'
2
+ _A='txt_fixed_cols'
3
+ from typing import Literal
4
+ from pydantic import BaseModel,Field,model_validator
5
+ from scilens.config.models.reader_format_txt import ReaderTxtConfig
3
6
  from scilens.config.models.reader_format_csv import ReaderCsvConfig
7
+ from scilens.config.models.reader_format_txt_fixed_cols import ReaderTxtFixedColsConfig
4
8
  from scilens.config.models.reader_format_netcdf import ReaderNetcdfConfig
5
- class ReadersConfig(BaseModel):txt:ReaderTxtConfig=Field(default=ReaderTxtConfig(),description='Configuration des readers txt.');csv:ReaderCsvConfig=Field(default=ReaderCsvConfig(),description='Configuration des readers csv.');netcdf:ReaderNetcdfConfig=Field(default=ReaderNetcdfConfig(),description='Configuration des readers NetCDF.')
9
+ TYPE_PARAMETERS_CLASS={'txt':ReaderTxtConfig,'csv':ReaderCsvConfig,_A:ReaderTxtFixedColsConfig,_B:ReaderNetcdfConfig}
10
+ class ReaderConfig(BaseModel):
11
+ type:Literal['txt','csv',_A,_B]=Field(description='Type du reader. Les paramètres `parameters` dépendent de ce type.');parameters:ReaderTxtConfig|ReaderCsvConfig|ReaderTxtFixedColsConfig|ReaderNetcdfConfig=Field(description='Paramètres du reader')
12
+ @model_validator(mode='after')
13
+ def validate_model(cls,model):
14
+ A=model
15
+ if not isinstance(A.parameters,TYPE_PARAMETERS_CLASS[A.type]):raise ValueError(f"Reader Type {A.type} Parameters are not correct")
16
+ return A
17
+ 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,ReaderConfig]|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': ';'}}}`")
@@ -0,0 +1,45 @@
1
+ _E='charts'
2
+ _D='x_index'
3
+ _C='csv_col_index'
4
+ _B='curves'
5
+ _A=None
6
+ from dataclasses import dataclass,field
7
+ from scilens.components.compare_floats import CompareFloats
8
+ from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
9
+ @dataclass
10
+ class ColsDataset: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:[])
11
+ @dataclass
12
+ class ColsCurves:type:str;info:dict;curves:dict
13
+ def cols_dataset_get_curves_col_x(cols_dataset,col_x):
14
+ I='title';B=col_x;A=cols_dataset;E={}
15
+ if isinstance(B,int):
16
+ C=B-1
17
+ if C<0 or C>=A.cols_count:raise Exception('curve parser col_x: col_index is out of range.')
18
+ if isinstance(B,str):B=[B]
19
+ if isinstance(B,list):
20
+ G=[A for(A,C)in enumerate(A.names)if C in B]
21
+ if len(G)==0:return _A,E
22
+ C=G[0]
23
+ E[_D]=C;J=[B for(A,B)in enumerate(A.numeric_col_indexes)if A!=C];F=[];H=[]
24
+ for D in J:B=A.data[C];K=A.data[D];L={I:A.names[D],'short_title':A.names[D],'series':[[B[A],K[A]]for A in range(A.rows_count)],_C:D};F+=[L];M={I:A.names[D],'type':'simple','xaxis':A.names[C],'yaxis':A.names[D],_B:[len(F)-1]};H+=[M]
25
+ return{_B:F,_E:H},E
26
+ def compare(compare_floats,reader_test,reader_ref,cols_curve):
27
+ Q='error';J=compare_floats;I='Errors limit reached';E=reader_ref;C=cols_curve;A=reader_test
28
+ if len(A.numeric_col_indexes)!=len(E.numeric_col_indexes):R=f"Number Float columns indexes are different: {len(A.numeric_col_indexes)} != {len(E.numeric_col_indexes)}";return R,_A
29
+ K=0;D=[''for A in range(A.cols_count)];L=_A;F=_A
30
+ if C and C.type==ReaderCurveParserNameConfig.COL_X:M=C.info[_D];L=A.data[M];F=A.names[M]
31
+ G=False
32
+ for B in range(A.cols_count):
33
+ if B not in A.numeric_col_indexes:continue
34
+ if G:D[B]=I;continue
35
+ S=A.data[B];T=E.data[B];U=J.compare_errors.add_group(A.names[B],data={'info_prefix':F}if F else _A);V,W,N=J.compare_vectors(S,T,group_idx=U,info_vector=L);K+=W
36
+ if V:G=True;D[B]=I;continue
37
+ if N[Q]>0:D[B]=f"{N[Q]} comparison errors"
38
+ if C:
39
+ for O in C.curves[_E]:
40
+ P=0
41
+ for X in O[_B]:
42
+ H=C.curves[_B][X]
43
+ if D[H[_C]]:H['comparison_error']=D[H[_C]];P+=1
44
+ O['comparison']={'curves_nb_with_error':P}
45
+ Y=I if G else _A;return Y,{'type':'vectors','total_diffs':K,'cols_has_error':D}
@@ -0,0 +1,74 @@
1
+ _E='charts'
2
+ _D='x_index'
3
+ _C='csv_col_index'
4
+ _B='curves'
5
+ _A=None
6
+ import logging,csv
7
+ from scilens.readers.reader_interface import ReaderInterface
8
+ from scilens.config.models import ReaderCsvConfig
9
+ from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
10
+ from scilens.components.compare_floats import CompareFloats
11
+ def is_num(x):
12
+ try:return float(x)
13
+ except ValueError:return
14
+ def csv_row_detect_header(first_row):
15
+ A=first_row
16
+ if all(not A.isdigit()for A in A):return True,A
17
+ else:return False,[f"Column {A}"for(A,B)in enumerate(A)]
18
+ def csv_row_detect_cols_num(row):return[A for(A,B)in enumerate(row)if is_num(B)!=_A]
19
+ def csv_detect(path,delimiter,quotechar):
20
+ with open(path,'r')as B:A=csv.reader(B,delimiter=delimiter,quotechar=quotechar);C=next(A);D,E=csv_row_detect_header(C);F=next(A);G=csv_row_detect_cols_num(F);return D,E,G
21
+ class ReaderCsv(ReaderInterface):
22
+ category='datalines';extensions=['CSV']
23
+ def read(A,reader_options):
24
+ B=reader_options;A.reader_options=B;C,G,H=csv_detect(A.origin.path,A.reader_options.delimiter,A.reader_options.quotechar);A.has_header=C;A.cols=G;A.numeric_col_indexes=H
25
+ if B.ignore_columns:
26
+ if not C:raise Exception('Ignore columns is not supported without header.')
27
+ A.numeric_col_indexes=[C for C in A.numeric_col_indexes if A.cols[C]not in B.ignore_columns]
28
+ E=open(A.origin.path,'r',encoding=A.encoding);I=csv.reader(E,delimiter=A.reader_options.delimiter,quotechar=A.reader_options.quotechar);D=[]
29
+ for J in I:D+=[J]
30
+ if C:D.pop(0)
31
+ E.close();A.data_rows=D;A.raw_lines_number=len(A.data_rows)+(1 if C else 0);A.curves=_A
32
+ if B.curve_parser:
33
+ if B.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
34
+ A.curves,K=A._get_curves_col_x(B.curve_parser.parameters.x)
35
+ if A.curves:A.curves_parser_type=B.curve_parser.name;A.curves_info=K
36
+ elif B.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
37
+ else:raise Exception('Curve parser not supported.')
38
+ A.cols_data=[_A]*len(A.cols)
39
+ for F in A.numeric_col_indexes:A.cols_data[F]=[float(A[F])for A in D]
40
+ def compare(A,compare_floats,param_reader,param_is_ref=True):
41
+ U='error';M=param_is_ref;L=param_reader;K=compare_floats;J='Errors limit reached';C=A if M else L;E=A if not M else L
42
+ if len(C.numeric_col_indexes)!=len(E.numeric_col_indexes):V=f"Number Float columns indexes are different: {len(C.numeric_col_indexes)} != {len(E.numeric_col_indexes)}";return V,_A
43
+ N=0;D=[''for A in C.cols];O=_A;F=_A
44
+ if A.curves and A.curves_parser_type==ReaderCurveParserNameConfig.COL_X:P=A.curves_info[_D];O=C.cols_data[P];F=A.cols[P]
45
+ G=False
46
+ for B in range(len(C.cols_data)):
47
+ if C.cols_data[B]==_A:continue
48
+ logging.debug(f"Comparing {A.cols[B]}")
49
+ if G:D[B]=J;continue
50
+ W=C.cols_data[B];X=E.cols_data[B];Y=K.compare_errors.add_group(A.cols[B],data={'info_prefix':F}if F else _A);Q,R,H=K.compare_vectors(W,X,group_idx=Y,info_vector=O);N+=R;logging.debug(f"limit_reached, diffs_count, counts: {Q}, {R}, {H}")
51
+ if Q:G=True;D[B]=J;continue
52
+ if H[U]>0:D[B]=f"{H[U]} comparison errors"
53
+ if A.curves:
54
+ for S in A.curves[_E]:
55
+ T=0
56
+ for Z in S[_B]:
57
+ I=A.curves[_B][Z]
58
+ if D[I[_C]]:I['comparison_error']=D[I[_C]];T+=1
59
+ S['comparison']={'curves_nb_with_error':T}
60
+ a=J if G else _A;return a,{'type':'vectors','total_diffs':N,'cols_has_error':D}
61
+ def class_info(A):return{'cols':A.cols,'raw_lines_number':A.raw_lines_number,_B:A.curves}
62
+ def _get_curves_col_x(E,col_x):
63
+ J='title';A=col_x;F={};B=E.cols
64
+ if isinstance(A,int):
65
+ C=A-1
66
+ if C<0 or C>=len(B):raise Exception('curve parser col_x: col_index is out of range.')
67
+ if isinstance(A,str):A=[A]
68
+ if isinstance(A,list):
69
+ H=[B for(B,C)in enumerate(B)if C in A]
70
+ if len(H)==0:return _A,F
71
+ C=H[0]
72
+ F[_D]=C;K=[B for(A,B)in enumerate(E.numeric_col_indexes)if A!=C];G=[];I=[]
73
+ for D in K:L={J:B[D],'short_title':B[D],'series':[[float(A[C]),float(A[D])]for A in E.data_rows],_C:D};G+=[L];M={J:B[D],'type':'simple','xaxis':B[C],'yaxis':B[D],_B:[len(G)-1]};I+=[M]
74
+ return{_B:G,_E:I},F
@@ -1,12 +1,8 @@
1
- _E='charts'
2
- _D='x_index'
3
- _C='csv_col_index'
4
- _B='curves'
5
- _A=None
6
- import csv
1
+ import logging,csv
7
2
  from scilens.readers.reader_interface import ReaderInterface
3
+ from scilens.readers.cols_dataset import ColsDataset,ColsCurves,cols_dataset_get_curves_col_x,compare
8
4
  from scilens.config.models import ReaderCsvConfig
9
- from scilens.config.models.reader_format_csv import ReaderCsvCurveParserNameConfig
5
+ from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
10
6
  from scilens.components.compare_floats import CompareFloats
11
7
  def is_num(x):
12
8
  try:return float(x)
@@ -15,59 +11,30 @@ def csv_row_detect_header(first_row):
15
11
  A=first_row
16
12
  if all(not A.isdigit()for A in A):return True,A
17
13
  else:return False,[f"Column {A}"for(A,B)in enumerate(A)]
18
- def csv_row_detect_cols_num(row):return[A for(A,B)in enumerate(row)if is_num(B)!=_A]
19
- def csv_detect(path):
20
- with open(path,'r')as B:A=csv.reader(B);C=next(A);D,E=csv_row_detect_header(C);F=next(A);G=csv_row_detect_cols_num(F);return D,E,G
14
+ def csv_row_detect_cols_num(row):return[A for(A,B)in enumerate(row)if is_num(B)!=None]
15
+ def csv_detect(path,delimiter,quotechar):
16
+ with open(path,'r')as B:A=csv.reader(B,delimiter=delimiter,quotechar=quotechar);C=next(A);D,E=csv_row_detect_header(C);F=next(A);G=csv_row_detect_cols_num(F);return D,E,G
21
17
  class ReaderCsv(ReaderInterface):
22
- category='datalines';extensions=['CSV']
18
+ configuration_type_code='csv';category='datalines';extensions=['CSV']
23
19
  def read(A,reader_options):
24
- B=reader_options;A.reader_options=B;C,G,H=csv_detect(A.origin.path);A.has_header=C;A.cols=G;A.numeric_col_indexes=H
25
- if B.ignore_colunmns:
26
- if not C:raise Exception('Ignore columns is not supported without header.')
27
- A.numeric_col_indexes=[C for C in A.numeric_col_indexes if A.cols[C]not in B.ignore_colunmns]
28
- E=open(A.origin.path,'r',encoding=A.encoding);I=csv.reader(E);D=[]
29
- for J in I:D+=[J]
30
- if C:D.pop(0)
31
- E.close();A.data_rows=D;A.raw_lines_number=len(A.data_rows)+(1 if C else 0);A.curves=_A
32
- if B.curve_parser:
33
- if B.curve_parser.name==ReaderCsvCurveParserNameConfig.COL_X:
34
- A.curves,K=A._get_curves_col_x(B.curve_parser.parameters.x)
35
- if A.curves:A.curves_parser_type=B.curve_parser.name;A.curves_info=K
36
- elif B.curve_parser.name==ReaderCsvCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
20
+ C=reader_options;A.reader_options=C;D,E,K=csv_detect(A.origin.path,A.reader_options.delimiter,A.reader_options.quotechar);A.has_header=D;A.cols=E;A.numeric_col_indexes=K
21
+ if C.ignore_columns:
22
+ if not D:raise Exception('Ignore columns is not supported without header.')
23
+ A.numeric_col_indexes=[B for B in A.numeric_col_indexes if A.cols[B]not in C.ignore_columns]
24
+ H=len(E);B=ColsDataset(cols_count=H,names=E,numeric_col_indexes=A.numeric_col_indexes,data=[[]for A in range(H)]);I=open(A.origin.path,'r',encoding=A.encoding);L=csv.reader(I,delimiter=A.reader_options.delimiter,quotechar=A.reader_options.quotechar);F=0
25
+ for M in L:
26
+ F+=1
27
+ if D and F==1:continue
28
+ for(J,G)in enumerate(M):
29
+ if J in B.numeric_col_indexes:G=float(G)
30
+ B.data[J].append(G)
31
+ B.origin_line_nb.append(F)
32
+ B.rows_count=len(B.origin_line_nb);I.close();A.cols_dataset=B;A.raw_lines_number=B.rows_count+(1 if D else 0);A.curves=None
33
+ if C.curve_parser:
34
+ if C.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
35
+ A.curves,N=cols_dataset_get_curves_col_x(B,C.curve_parser.parameters.x)
36
+ if A.curves:A.cols_curve=ColsCurves(type=ReaderCurveParserNameConfig.COL_X,info=N,curves=A.curves)
37
+ elif C.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
37
38
  else:raise Exception('Curve parser not supported.')
38
- A.cols_data=[_A]*len(A.cols)
39
- for F in A.numeric_col_indexes:A.cols_data[F]=[float(A[F])for A in D]
40
- def compare(A,compare_floats,param_reader,param_is_ref=True):
41
- S='error';R='Errors limit reached';J=param_is_ref;I=param_reader;H=compare_floats;B=A if J else I;E=A if not J else I
42
- if len(B.numeric_col_indexes)!=len(E.numeric_col_indexes):T=f"Number Float columns indexes are different: {len(B.numeric_col_indexes)} != {len(E.numeric_col_indexes)}";return T,_A
43
- K=0;D=[''for A in B.cols];L=_A;F=_A
44
- if A.curves and A.curves_parser_type==ReaderCsvCurveParserNameConfig.COL_X:M=A.curves_info[_D];L=B.cols_data[M];F=A.cols[M]
45
- N=False
46
- for C in range(len(B.cols_data)):
47
- if B.cols_data[C]==_A:continue
48
- if N:D[C]=R;continue
49
- U=B.cols_data[C];V=E.cols_data[C];W=H.compare_errors.add_group(A.cols[C],data={'info_prefix':F}if F else _A);X,Y,O=H.compare_vectors(U,V,group_idx=W,info_vector=L);K+=Y
50
- if X:N=True;D[C]=R;continue
51
- if O[S]>0:D[C]=f"{O[S]} comparison errors"
52
- if A.curves:
53
- for P in A.curves[_E]:
54
- Q=0
55
- for Z in P[_B]:
56
- G=A.curves[_B][Z]
57
- if D[G[_C]]:G['comparison_error']=D[G[_C]];Q+=1
58
- P['comparison']={'curves_nb_with_error':Q}
59
- return _A,{'type':'vectors','total_diffs':K,'cols_has_error':D}
60
- def class_info(A):return{'cols':A.cols,'raw_lines_number':A.raw_lines_number,_B:A.curves}
61
- def _get_curves_col_x(E,col_x):
62
- J='title';A=col_x;F={};B=E.cols
63
- if isinstance(A,int):
64
- C=A-1
65
- if C<0 or C>=len(B):raise Exception('curve parser col_x: col_index is out of range.')
66
- if isinstance(A,str):A=[A]
67
- if isinstance(A,list):
68
- H=[B for(B,C)in enumerate(B)if C in A]
69
- if len(H)==0:return _A,F
70
- C=H[0]
71
- F[_D]=C;K=[B for(A,B)in enumerate(E.numeric_col_indexes)if A!=C];G=[];I=[]
72
- for D in K:L={J:B[D],'short_title':B[D],'series':[[float(A[C]),float(A[D])]for A in E.data_rows],_C:D};G+=[L];M={J:B[D],'type':'simple','xaxis':B[C],'yaxis':B[D],_B:[len(G)-1]};I+=[M]
73
- return{_B:G,_E:I},F
39
+ def compare(A,compare_floats,param_reader,param_is_ref=True):C=param_is_ref;B=param_reader;D=A.cols_dataset if C else B.cols_dataset;E=A.cols_dataset if not C else B.cols_dataset;F=A.cols_curve;return compare(compare_floats,D,E,F)
40
+ def class_info(A):return{'cols':A.cols,'raw_lines_number':A.raw_lines_number,'curves':A.curves}
@@ -1,30 +1,50 @@
1
+ _A=None
1
2
  import os,sys
2
3
  from importlib.metadata import entry_points
3
4
  from scilens.readers.exceptions import NoReaderFound
4
5
  from scilens.readers.reader_interface import ReaderOrigin
6
+ from scilens.config.models import FileReaderConfig
7
+ from scilens.config.models.readers import ReadersConfig
5
8
  def extension_format(extension):
6
9
  A=extension
7
10
  if A.startswith('.'):A=A[1:]
8
11
  return A.upper()
9
12
  from scilens.readers.reader_txt import ReaderTxt
10
13
  from scilens.readers.reader_csv import ReaderCsv
11
- BUILTIN_PLUGINS=[ReaderTxt,ReaderCsv]
14
+ from scilens.readers.reader_txt_fixed_cols import ReaderTxtFixedCols
15
+ BUILTIN_PLUGINS=[ReaderTxt,ReaderCsv,ReaderTxtFixedCols]
12
16
  LIB_PLUGINS_ENTRY_POINT='scilens.reader_plugins'
13
17
  class ReaderManager:
14
18
  def __init__(A):
15
19
  A.plugins=[]+BUILTIN_PLUGINS;B=entry_points(group=LIB_PLUGINS_ENTRY_POINT)if sys.version_info.minor>=12 else entry_points().get(LIB_PLUGINS_ENTRY_POINT,[])
16
20
  for C in B:A.plugins+=C.load()()
17
21
  def _get_plugin_names(A):return[A.__name__ for A in A.plugins]
22
+ def _get_plugin_info(A):return[{'class':A.__name__,'configuration_type_code':A.configuration_type_code}for A in A.plugins]
18
23
  def __str__(A):return f"plugins: {A._get_plugin_names()}"
19
24
  def _get_reader_from_extension(B,extension):
20
25
  for A in B.plugins:
21
26
  if extension_format(extension)in A.extensions:return A
22
- def get_reader_from_file(D,path,name='',encoding='',curve_parser=None,extension_mapping=None,extension_fallback=None):
23
- F=extension_fallback;E=extension_mapping;C=path;J,A=os.path.splitext(C);A=extension_format(A)
24
- if E:
25
- for(G,H)in E.items():
26
- if extension_format(G)==A:A=extension_format(H);break
27
- B=D._get_reader_from_extension(A)
28
- if not B and F:B=D._get_reader_from_extension(F)
27
+ def _get_reader_from_configuration_type_code(B,code):
28
+ for A in B.plugins:
29
+ if code==A.configuration_type_code:return A
30
+ def get_reader_from_file(F,path,name='',config=_A,readers_config=_A,curve_parser=_A):
31
+ I=curve_parser;G=path;C=readers_config;A=config;J=ReaderOrigin(type='file',path=G,short_name=os.path.basename(G));K=A.encoding if A else'utf-8';Q,D=os.path.splitext(G);D=extension_format(D)
32
+ if A and A.extension_readers_catalog:
33
+ for(M,L)in A.extension_readers_catalog.items():
34
+ if extension_format(M)==D:
35
+ if not C.catalog or L not in C.catalog.keys():raise NoReaderFound(f"Reader config not found for {D}")
36
+ H=C.catalog[L];B=F._get_reader_from_configuration_type_code(H.type)
37
+ if not B:raise Exception(f"Reader not found for contiguration type code {H.type}")
38
+ N=H.parameters;return B(J,name=name,encoding=K,curve_parser=I),N
39
+ if A and A.extension_mapping:
40
+ for(O,P)in A.extension_mapping.items():
41
+ if extension_format(O)==D:D=extension_format(P);break
42
+ B=F._get_reader_from_extension(D)
43
+ if not B and A and A.extension_fallback:B=F._get_reader_from_extension(A.extension_fallback)
29
44
  if not B:raise NoReaderFound(f"Reader cound not be derived")
30
- I=ReaderOrigin(type='file',path=C,short_name=os.path.basename(C));return B(I,name=name,encoding=encoding,curve_parser=curve_parser)
45
+ E=_A
46
+ if B.__name__=='ReaderTxt':E=C.txt
47
+ elif B.__name__=='ReaderCsv':E=C.csv
48
+ elif B.__name__=='ReaderTxtFixedCols':E=C.txt_fixed_cols
49
+ elif B.__name__=='ReaderNetcdf':E=C.netcdf
50
+ return B(J,name=name,encoding=K,curve_parser=I),E
@@ -10,7 +10,7 @@ from scilens.config.models import ReaderTxtConfig
10
10
  from scilens.components.compare_errors import SEVERITY_ERROR
11
11
  from scilens.components.compare_floats import CompareFloats
12
12
  class ReaderTxt(ReaderInterface):
13
- category='datalines';extensions=['TXT']
13
+ configuration_type_code='txt';category='datalines';extensions=['TXT']
14
14
  def read(A,config):
15
15
  F='_';B=config;A.reader_options=B;A.get_lines_pre=1;A.get_lines_post=1
16
16
  if B.report_lines:
@@ -0,0 +1,38 @@
1
+ import logging,re
2
+ from scilens.readers.transform import string_2_float
3
+ from scilens.readers.reader_interface import ReaderInterface
4
+ from scilens.readers.cols_dataset import ColsDataset,ColsCurves,cols_dataset_get_curves_col_x,compare
5
+ from scilens.config.models import ReaderTxtFixedColsConfig
6
+ from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
7
+ from scilens.components.compare_floats import CompareFloats
8
+ class ReaderTxtFixedCols(ReaderInterface):
9
+ configuration_type_code='txt_fixed_cols';category='datalines';extensions=[]
10
+ def read(B,reader_options):
11
+ A=reader_options;B.reader_options=A;J=open(B.origin.path,'r',encoding=B.encoding);K=A.column_widths;L=A.ignore_lines_patterns;E=len(K);C=ColsDataset(cols_count=E,names=[f"Column {A+1}"for A in range(E)],numeric_col_indexes=[A for A in range(E)],data=[[]for A in range(E)]);F=None;G=0
12
+ for D in J:
13
+ G+=1
14
+ if L:
15
+ M=False
16
+ for P in L:
17
+ if bool(re.match(P,D)):M=True;break
18
+ if M:continue
19
+ if A.has_header:
20
+ if not F:
21
+ F=D.strip();H=F
22
+ if A.has_header_ignore:
23
+ for Q in A.has_header_ignore:H=H.replace(Q,'')
24
+ C.names=H.split();continue
25
+ elif A.has_header_repetition and F==D.strip():continue
26
+ if not D.strip():continue
27
+ I=0;N=0
28
+ for O in K:R=D[I:I+O].strip();S=string_2_float(R);C.data[N].append(S);I+=O;N+=1
29
+ C.origin_line_nb.append(G)
30
+ C.rows_count=len(C.origin_line_nb);J.close();B.cols_dataset=C;B.raw_lines_number=G;B.curves=None
31
+ if A.curve_parser:
32
+ if A.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
33
+ B.curves,T=cols_dataset_get_curves_col_x(C,A.curve_parser.parameters.x)
34
+ if B.curves:B.cols_curve=ColsCurves(type=ReaderCurveParserNameConfig.COL_X,info=T,curves=B.curves)
35
+ elif A.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
36
+ else:raise Exception('Curve parser not supported.')
37
+ def compare(A,compare_floats,param_reader,param_is_ref=True):C=param_is_ref;B=param_reader;D=A.cols_dataset if C else B.cols_dataset;E=A.cols_dataset if not C else B.cols_dataset;F=A.cols_curve;return compare(compare_floats,D,E,F)
38
+ def class_info(A):return{'cols':A.cols_dataset.names,'raw_lines_number':A.raw_lines_number,'curves':A.curves}
@@ -1,3 +1,8 @@
1
+ def string_2_float(text):
2
+ try:
3
+ A=float(text)
4
+ if A!=float('inf')and A!=float('-inf'):return A
5
+ except ValueError:pass
1
6
  def string_2_floats(text):
2
7
  B=[];C=text.split()
3
8
  for D in range(len(C)):
@@ -49,9 +49,19 @@
49
49
  <td class="{{ 'ERROR' if file.error else '' }}">
50
50
  {% if file.error %}<span>{{ file.error }}</span>{% endif %}
51
51
  </td>
52
+ {% if file.comparison_errors and file.comparison %}
52
53
  <td class="number {{ 'ERROR' if file.comparison_errors.error_nb else '' }}">{{ file.comparison_errors.error_nb }}</td>
53
54
  <td class="number {{ 'WARNING' if file.comparison_errors.warning_nb else '' }}">{{ file.comparison_errors.warning_nb }}</td>
54
55
  <td class="number">{{ file.comparison.total_diffs }}</td>
56
+ {% elif not file.skipped %}
57
+ <td class="ERROR">ERROR</td>
58
+ <td class="ERROR">ERROR</td>
59
+ <td class="ERROR">ERROR</td>
60
+ {% else %}
61
+ <td></td>
62
+ <td></td>
63
+ <td></td>
64
+ {% endif %}
55
65
  <td class="number test-bg-20">{{ file.test.raw_lines_number }}</td>
56
66
  <td class="number refe-bg-20">{{ file.ref.raw_lines_number }}</td>
57
67
  <td class="number test-bg-20">{{ file.test.ignore_lines_number }}</td>
@@ -131,7 +131,7 @@
131
131
  {% endif %}
132
132
 
133
133
  <!--COMPARISONS ERRORS AND WARNINGS-->
134
- {% if file.comparison_errors.error_nb or file.comparison_errors.warning_nb %}
134
+ {% if file.comparison_errors and (file.comparison_errors.error_nb or file.comparison_errors.warning_nb) %}
135
135
  <h3>Number Comparison Errors or Warnings <action data-command="toogle" data-args="numbers_errors_{{ loop.index|string }}"></action></h3>
136
136
  <div id="numbers_errors_{{ loop.index }}" >
137
137
  {% with file=file %}
@@ -1,3 +1,4 @@
1
+ {% if file.comparison_errors %}
1
2
  {% set errs = file.comparison_errors %}
2
3
  {% set c_type = file.comparison.type %}
3
4
  <table>
@@ -89,3 +90,4 @@
89
90
  {% endfor %}
90
91
 
91
92
  </table>
93
+ {% endif %}
@@ -10,7 +10,7 @@
10
10
  {% endmacro %}
11
11
  {% set ns = namespace(statuses=['']) %}
12
12
  {% for file in data.files %}
13
- {% set ns.statuses = ns.statuses + ['SKIPPED' if file.skipped else ('ERROR' if file.error else ('WARNING' if file.comparison_errors.error_nb > 0 else 'SUCCESS'))] %}
13
+ {% set ns.statuses = ns.statuses + ['SKIPPED' if file.skipped else ('ERROR' if file.error else ('WARNING' if file.comparison_errors and file.comparison_errors.error_nb > 0 else 'SUCCESS'))] %}
14
14
  {% endfor %}
15
15
  <html>
16
16
  <head>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scilens
3
- Version: 0.1.3
3
+ Version: 0.2.1
4
4
  Summary: A CesGensLaB framework for data collecting and deep analysis
5
5
  Home-page: https://scilens.dev
6
6
  License: Proprietary
@@ -5,29 +5,32 @@ scilens/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  scilens/cli/cglb.py,sha256=R0377H5H193DJ_h4hj32m6qvrZrMnSFOxSGVl2FItiU,443
6
6
  scilens/cli/config.py,sha256=XVY6q5E9fb6MZzPct4-X7m6e45erpt5MCIuzKTBKPAI,965
7
7
  scilens/cli/info.py,sha256=xE7q9epjrCQRL6Agi3nhKsG6Mr3B8HSUFMti-epMoXA,1929
8
- scilens/cli/main.py,sha256=ozGeSEANZx1FKpq9v55GvCZkXk3cazgLa0gtpjbMImg,5637
8
+ scilens/cli/main.py,sha256=LljcS0s2E35y4YZpV01GhMhMK9HyRRHYmxc_q_kSurI,6004
9
+ scilens/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
10
  scilens/components/analyse_folder.py,sha256=yqc-dscKaHLZJCYeXGak2v0c3F2aeX0E11AFPfya6r0,208
10
11
  scilens/components/compare_2_files.py,sha256=oQ5p9JwB1r_HhfCb29mv1kkkrUd0EdrVug9T-1fQshY,1286
11
12
  scilens/components/compare_errors.py,sha256=_n9uWwLVZyPn99mwsAF8WBfcYppVQCsMwj0xuOrlhkU,1241
12
- scilens/components/compare_floats.py,sha256=T461VNiwVXtYWqrIr5LMiUwBkHsPmhv6LjzIVfkQOI8,1978
13
+ scilens/components/compare_floats.py,sha256=UGUJF-WEZJSCpHZUAvVhkWY-mWiQZ8RqYnkTjFdRp1Y,3617
13
14
  scilens/components/compare_folders.py,sha256=LZ1AuYxLVHMNbtXWXQrdms4vZgOQthvDy-8NFD_EFjc,2617
14
15
  scilens/components/executor.py,sha256=8ZZq9wwoiMr7ys9LXv1pEg5Zc06QatT9PGIigMsDAB8,3620
15
- scilens/components/file_reader.py,sha256=tfRbmPu4pB7lNLUHNl8XVryx_Gy_huxe9ZrJ2OkR3b4,1623
16
+ scilens/components/file_reader.py,sha256=2xvBoiJSb0KUihLzRK_o-DdGSRqEBu0-QwT1i9CCWRM,1346
16
17
  scilens/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
18
  scilens/config/cli_run_options.py,sha256=Ls7yK5QDUPFbk73nbjGuPvuRbBRYw4Miag5ISpu3prg,281
18
19
  scilens/config/env_var.py,sha256=NqNBoIfngJEXaGEm7jGqre5pmkJ9eUjiWzbDrTVfi2c,292
19
20
  scilens/config/load.py,sha256=4U51o4cJfqhSuRIHKUDIsDQA0C4wv6SzTkVmInGDJdI,1647
20
- scilens/config/models/__init__.py,sha256=B3D5sAUfms8urbWPKQ-OgsU1Hm9p6hRHU1dAGAcJbOo,607
21
+ scilens/config/models/__init__.py,sha256=eLCW1OLVINewGFy5GXSrOk8Rab_QsgKAuoErBjphHRs,673
21
22
  scilens/config/models/app.py,sha256=JltWRjjqXYkH6rg3OHYhMfkBqHFhZEZdthqES3LxvzY,1453
22
23
  scilens/config/models/compare.py,sha256=_SLMxf81fpuod8izZm72Jn12euRPxB0wHJSfUuIANbE,1639
23
- scilens/config/models/compare_float_thresholds.py,sha256=wvXJ_ewmjp8ro_aiq_9AS2eo4wnW26GI6swN3zw5drs,1570
24
+ scilens/config/models/compare_float_thresholds.py,sha256=J5XBK1dAnmU-i2uA2bsaHnTM_m07_i17wsO8UiCw46o,1844
24
25
  scilens/config/models/execute.py,sha256=pFY-gZuBvLbcjTEcoNhPPO7FMFmKa6_TU5IXyKaf81A,1706
25
26
  scilens/config/models/execute_and_compare.py,sha256=TWL6yXGvQSaaV6nhHqWLvtr3v396APIoDNt0U1TbMro,582
26
- scilens/config/models/file_reader.py,sha256=sDIh1z_q4B5c7PhNC1WvQWLNGR4NMbXr-WhT6zDyLB4,1000
27
- scilens/config/models/reader_format_csv.py,sha256=r2ERmC_dWoESr9sCJ_T3dF2F00ypttZfSH8ZFG-PKnw,2208
27
+ scilens/config/models/file_reader.py,sha256=c18vKhVcBX4ufpbnCBJyVyAsQtlxpwx0lGVuf1i8EGA,1176
28
+ scilens/config/models/reader_format_cols_curve.py,sha256=eKLvifq1xuN8sPds9ijfru3vgMZ3Odv5tGfeiK4Sfk4,1860
29
+ scilens/config/models/reader_format_csv.py,sha256=XWZTr6s0PPfcOMRrsNeOZGtExC1uyXb67A158MMWzC4,577
28
30
  scilens/config/models/reader_format_netcdf.py,sha256=nbfTB3avO0DidbNa1dCZGFZmmQvzTYhpe6mqfAanaOA,1025
29
31
  scilens/config/models/reader_format_txt.py,sha256=eHg90gwEI_VpqwqEjMRhwlS8dHcl5G4ow-37HjQq_zY,1168
30
- scilens/config/models/readers.py,sha256=dK_d02jqsrYTe42HO3SHQ6Fyw3SC_jleMafJpelAhdY,605
32
+ scilens/config/models/reader_format_txt_fixed_cols.py,sha256=xHD1_JOoRZow8lSNaDSYFeNckojctkT4C61mbBcjeKg,1079
33
+ scilens/config/models/readers.py,sha256=Pq5kOGW3b6g1x5cp_BbwUF7LUB_P3m9bHDYLSTVXNBY,1769
31
34
  scilens/config/models/report.py,sha256=nTmP2nIwL2Ku5IH9QMwYLPKmfsK2ttu9UK0GnzPUHeM,870
32
35
  scilens/config/models/report_html.py,sha256=9I9iKRDOoLMZRBY0lQV4UFtg5-D-VDfYiFGF1VFAnQ8,1389
33
36
  scilens/config/models/report_output.py,sha256=XoqUe-t-y8GRbUR3_bDwwaWf6hif-rZ-5pKDGdCMugw,875
@@ -43,13 +46,16 @@ scilens/processors/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
43
46
  scilens/processors/models/results.py,sha256=KoWxh13Zgi7PuPql8hkf4VjCis42ZxAuzIgJxBWVaX8,119
44
47
  scilens/processors/processor_interface.py,sha256=jzMp1529JXnMGTJijVy6b_1zmARAMNv70f2lgys7vn4,452
45
48
  scilens/readers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ scilens/readers/cols_dataset.py,sha256=suEttRHfj9UhHndchAKazLD2z_YY9dFEVLjlodRJS44,2363
46
50
  scilens/readers/exceptions.py,sha256=JzmxcjnR5sH-IOWVeCC5A1bSwxv-jCAtIJvDjzx1CTI,32
47
51
  scilens/readers/reader_com_txt_lines.py,sha256=zsCumTD0sv06OQNvRKPMuv94De_6KGMvN904-E1Uqeg,91
48
- scilens/readers/reader_csv.py,sha256=6im4FdkCFQXz2nVQlrKzwViQUh6yBZ0Vw8zpKKwzesw,3753
52
+ scilens/readers/reader_csv copy.py,sha256=eFWQvMt4Ot7KgUIDsed1QymMbPblgboUclvQj6viVGs,4069
53
+ scilens/readers/reader_csv.py,sha256=oLbA4wU6rlzL1cIrW-AzWifP-RvmG3yHKWQQqITYQ1U,2732
49
54
  scilens/readers/reader_interface.py,sha256=nnttHL7wt4MOXpi-SBkk8DYxVWscOPG8JFl_z12mIAo,922
50
- scilens/readers/reader_manager.py,sha256=1ULW0cPOtgep2JhS-KmvvW1bSfrjGHkpwSUfIi41vyg,1519
51
- scilens/readers/reader_txt.py,sha256=IpIA_qfdL70wrDnOotUtFjt7Yt0lEenYv93E63mQwZw,3744
52
- scilens/readers/transform.py,sha256=5OAcexHf2vj4obKsltWpwc_iNdsN7qgZNZEQJ18v3D0,185
55
+ scilens/readers/reader_manager.py,sha256=DFinxIk3IIIcB6JxybGcv-mXt3jhXgCwUtzR0TqhB2Q,2684
56
+ scilens/readers/reader_txt.py,sha256=xeiQNrWSr-jRnb0KoYya8Y_gdjT-0L_iXS4TW_bfqQc,3774
57
+ scilens/readers/reader_txt_fixed_cols.py,sha256=KuZd62_cmV9vHRlar2K-acFVVLf28bAqRSbpRRNjblI,2258
58
+ scilens/readers/transform.py,sha256=kppfgPkXymF0qtquFivuosLVfF66L9bE-wGx-3bMHv8,307
53
59
  scilens/report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
60
  scilens/report/assets/logo.svg,sha256=W-1OVqcvdBjf-1AHHcV6WciIUqBoVFUh52Tc3o_jqtA,4519
55
61
  scilens/report/assets/logo_cglb.svg,sha256=tEpkSr2h-jjQMecqiHef98Mxod4GD5j5nCQaFloTYso,2411
@@ -59,11 +65,11 @@ scilens/report/report.py,sha256=aS7ktJ2u0IAMMk-HwqqSsRkr77ZBQyYT4wXJ7djQvAk,1811
59
65
  scilens/report/template.py,sha256=cPs5gd3uEwb-6JgitGQD_i4IiUxigBTlZLNRS9KVuos,581
60
66
  scilens/report/templates/body_01_title.html,sha256=59BmETKHqRO1T_xYp0XLKx3Vha9hU9bu7yaUTVt2p9Y,2146
61
67
  scilens/report/templates/body_99_footer.html,sha256=8cWebeWfZwZ-9bYAMZkZj8rbCWq3BLIMjKQThWQxoQM,362
62
- scilens/report/templates/compare_11_summary.html,sha256=wskX861ISVQKFyEf3cF5mD53XNP1DhsCt6gQ-ebL9Cw,3476
63
- scilens/report/templates/compare_12_sections.html,sha256=m6HK-BL4Sb8VKNbCl-48Kmu9rh-IFkdp-Hckdedp7Gg,5923
68
+ scilens/report/templates/compare_11_summary.html,sha256=4rxBlOxTcn59ztYtqDbi6SRQXlaz30HkVl7dJpzCmZE,3776
69
+ scilens/report/templates/compare_12_sections.html,sha256=HWsfCmfdleyRK6IHJeMEheenOuyA0mLzOZ-0qLcuzJU,5952
64
70
  scilens/report/templates/compare_13_section_numbers copy.html,sha256=0PWK_I2kNX3LjPLkkY4eSYIeB7YFkA28nk-PPLDhnaY,1753
65
- scilens/report/templates/compare_13_section_numbers.html,sha256=ocPnazj2YsoroZUaBMNLqluejr7s8Kp71egceDxjzOI,2776
66
- scilens/report/templates/index.html,sha256=1kN-omesa_ns7uJz25KIFHrOONvNcoBg0lhXDmAesDA,3643
71
+ scilens/report/templates/compare_13_section_numbers.html,sha256=JjD20F6X2RjzJIgAAF431JOdmUyNNOHJOufdH21iUsM,2820
72
+ scilens/report/templates/index.html,sha256=mNTu-CAzEJ2rhz81cHAdmT_KcQeOQ3b_FSC73NPEi0U,3670
67
73
  scilens/report/templates/js_chartlibs_echarts.js,sha256=6YicVhTNIBmmBpV31XCVN5oBeiD0t29JIosJZRUv01M,907
68
74
  scilens/report/templates/js_chartlibs_plotly.js,sha256=uVAOKUB5XE33-r04phR-LlRkFoaHEIXyQ3jXT5r97Rc,1521
69
75
  scilens/report/templates/js_com_dom.js,sha256=YAJNGmgq2YbF9D3TqB7YSms67-xB88Molp9cKhWlqsI,1228
@@ -90,7 +96,7 @@ scilens/utils/template.py,sha256=9dlXX3nmfzDRUwzPJOkoxk15UXivZ2SW-McdCwokFa4,443
90
96
  scilens/utils/time_tracker.py,sha256=DdVBoMpVLXrX0qZZXyLm4g38EwDVLlRcBqcpNex1mYY,545
91
97
  scilens/utils/vectors.py,sha256=4N2BZSC5n3HgZqPujDGF5NdjVmSL1rOHb_qw4OoABQY,103
92
98
  scilens/utils/web.py,sha256=E4T8Fra65u9g_BpcFANPk4ORvsYavAeiSgWA3vRca2E,804
93
- scilens-0.1.3.dist-info/METADATA,sha256=jqg_M9TD7uwLyZRKSnDjmHKe3aNfJOsxqjyy42gD8A8,1367
94
- scilens-0.1.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
95
- scilens-0.1.3.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
96
- scilens-0.1.3.dist-info/RECORD,,
99
+ scilens-0.2.1.dist-info/METADATA,sha256=QYXvNkOg3U3JDqKslGVMo1OCq3bCM6yT7eCzAS4onX4,1367
100
+ scilens-0.2.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
101
+ scilens-0.2.1.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
102
+ scilens-0.2.1.dist-info/RECORD,,