scilens 0.2.0__py3-none-any.whl → 0.3.0__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')
@@ -1,22 +1,26 @@
1
1
  import os
2
2
  from scilens.run.task_context import TaskContext
3
+ from scilens.readers.reader_interface import ReaderInterface
3
4
  from scilens.components.file_reader import FileReader
4
- from scilens.components.compare_errors import CompareErrors,SEVERITY_ERROR
5
+ from scilens.components.compare_models import SEVERITY_ERROR
6
+ from scilens.components.compare_errors import CompareErrors
5
7
  from scilens.components.compare_floats import CompareFloats
6
8
  class Compare2Files:
7
9
  def __init__(A,context):A.context=context
8
10
  def compare(B,path_test,path_ref):
9
- Q='comparison_errors';P='comparison';M='reader';J='error';I='ref';H='test';E='path';A={H:{},I:{},P:None,Q:None};D={H:{E:path_test},I:{E:path_ref}}
10
- for(K,F)in D.items():
11
- if not F.get(E)or not os.path.exists(F[E]):A[J]=f"file {K} does not exist";return A
12
- R=FileReader(B.context.working_dir,B.context.config.file_reader,B.context.config.readers,config_alternate_path=B.context.origin_working_dir)
13
- for(K,F)in D.items():D[K][M]=R.read(F[E])
14
- C=D[H][M];G=D[I][M]
15
- if not C or not G:A['skipped']=True;return A
16
- A[H]=C.info();A[I]=G.info()
17
- if C.read_error:A[J]=C.read_error;return A
18
- L=CompareErrors(B.context.config.compare.errors_limit,B.context.config.compare.ignore_warnings);N,S=C.compare(CompareFloats(L,B.context.config.compare.float_thresholds),G,param_is_ref=True);A[P]=S;A[Q]=L.get_data()
19
- if N:A[J]=N;return A
20
- C.close();G.close();O=len(L.errors[SEVERITY_ERROR])
21
- if O>0:T=f"{O} comparison errors";A[J]=T
11
+ R='comparison_errors';Q='comparison';N='reader';L='error';K='ref';J='test';F='path';A={J:{},K:{},Q:None,R:None};D={J:{F:path_test},K:{F:path_ref}}
12
+ for(M,G)in D.items():
13
+ if not G.get(F)or not os.path.exists(G[F]):A[L]=f"file {M} does not exist";return A
14
+ S=FileReader(B.context.working_dir,B.context.config.file_reader,B.context.config.readers,config_alternate_path=B.context.origin_working_dir)
15
+ for(M,G)in D.items():D[M][N]=S.read(G[F])
16
+ C=D[J][N];H=D[K][N]
17
+ if not C or not H:A['skipped']=True;return A
18
+ A[J]=C.info();A[K]=H.info()
19
+ if C.read_error:A[L]=C.read_error;return A
20
+ I=CompareErrors(B.context.config.compare.errors_limit,B.context.config.compare.ignore_warnings);T=CompareFloats(I,B.context.config.compare.float_thresholds);C.compare(T,H,param_is_ref=True);E=I.root_group;O={'total_diffs':E.total_diffs}
21
+ if E.info:O.update(E.info)
22
+ A[Q]=O;A[R]=I.get_data()
23
+ if E.error:A[L]=E.error;return A
24
+ C.close();H.close();P=len(I.errors[SEVERITY_ERROR])
25
+ if P>0:U=f"{P} comparison errors";A[L]=U
22
26
  return A
@@ -1,22 +1,23 @@
1
1
  _B=False
2
2
  _A=None
3
- from pydantic import BaseModel
4
- class CompareGroup(BaseModel):name:str;data:dict|_A
5
- class CompareErrFloats(BaseModel):is_relative:bool;value:float;test:float;reference:float
6
- class CompareErr(BaseModel):err:CompareErrFloats|_A;msg:int;group:int|_A=_A;info:dict|_A=_A
7
- SEVERITY_ERROR='error'
8
- SEVERITY_WARNING='warning'
3
+ from dataclasses import asdict
4
+ from.compare_models import SEVERITY_ERROR,SEVERITY_WARNING,COMPARE_GROUP_TYPE,CompareGroup,CompareFloatsErr,CompareErr,Compare2ValuesResults
9
5
  class CompareErrors:
10
- def __init__(A,nb_max,ignore_warnings=_B):A.nb_max=nb_max;A.ignore_warnings=ignore_warnings;A.errors={SEVERITY_ERROR:[],SEVERITY_WARNING:[]};A.count=0;A.limit_reached=_B;A.messages=[];A.messages_map={};A.groups=[]
11
- def add_group(A,name,data=_A):B=len(A.groups);A.groups.append(CompareGroup(name=name,data=data));return B
12
- def add(A,severity,message,comp_err,group_idx=_A,info=_A):
13
- D=severity;C=message
6
+ def __init__(A,nb_max,ignore_warnings=_B):A.nb_max=nb_max;A.ignore_warnings=ignore_warnings;A.errors={SEVERITY_ERROR:[],SEVERITY_WARNING:[]};A.count=0;A.limit_reached=_B;A.root_group=_A;A.messages=[];A._messages_map={};A.groups=[]
7
+ def add_group(A,type,name,parent=_A,data=_A):
8
+ B=parent
9
+ if not A.root_group and B:raise Exception('No root group defined')
10
+ id=len(A.groups);C=CompareGroup(id=id,type=type,name=name,parent=B,data=data)
11
+ if not B:A.root_group=C
12
+ A.groups.append(C);return id,C
13
+ def add(A,group,comp_res,info=_A):
14
+ F=group;C=comp_res;D=C.severity;E=C.message;G=C.comp_err
14
15
  if A.ignore_warnings and D==SEVERITY_WARNING:return _B
15
- A.count+=1;B=A.messages_map.get(C)
16
- if B is _A:B=len(A.messages);A.messages.append(C);A.messages_map[C]=B
17
- A.errors[D].append(CompareErr(err=comp_err,msg=B,group=group_idx,info=info))
16
+ A.count+=1;B=A._messages_map.get(E)
17
+ if B is _A:B=len(A.messages);A.messages.append(E);A._messages_map[E]=B
18
+ A.errors[D].append(CompareErr(err=G,msg=B,group=F.id,info=info));F.incr(D)
18
19
  if A.count>=A.nb_max:A.limit_reached=True;return True
19
20
  def get_data(B):
20
- A={'messages':B.messages,'groups':[A.model_dump()for A in B.groups]}
21
- for(C,D)in B.errors.items():A[C]=[A.model_dump()for A in D];A[C+'_nb']=len(A[C])
21
+ D=[{'id':A.id,'type':A.type,'name':A.name,'error':A.error,'total_diffs':A.total_diffs,'total_warnings':A.total_warnings,'total_errors':A.total_errors,'data':A.data,'info':A.info,'parent_id':A.parent.id if A.parent else _A}for A in B.groups];A={'messages':B.messages,'groups':D}
22
+ for(C,E)in B.errors.items():A[C]=[A.model_dump()for A in E];A[C+'_nb']=len(A[C])
22
23
  return A
@@ -1,47 +1,72 @@
1
- _B='amplitude'
1
+ _C='amplitude'
2
+ _B=False
2
3
  _A=None
3
- from scilens.components.compare_errors import CompareErrors,SEVERITY_ERROR,SEVERITY_WARNING,CompareErrFloats
4
+ from scilens.components.compare_models import SEVERITY_ERROR,SEVERITY_WARNING,CompareFloatsErr,Compare2ValuesResults
5
+ from scilens.components.compare_errors import CompareErrors
4
6
  from scilens.config.models import CompareFloatThresholdsConfig
5
- try:from scilens_check_vectors import CheckVectors
7
+ try:from scilens_compare import vectors as CheckVectors
6
8
  except ModuleNotFoundError:pass
7
- def vector_get_amplitude(vector):A=vector;B=min(A);C=max(A);return{'min':B,'max':C,_B:abs(C-B)}
9
+ def vector_get_amplitude(vector):min_val=min(vector);max_val=max(vector);return{'min':min_val,'max':max_val,_C:abs(max_val-min_val)}
8
10
  class CompareFloats:
9
- def __init__(A,compare_errors,config):A.compare_errors=compare_errors;A.thresholds=config
10
- def compare_vectors(A,test_vector,reference_vector,group_idx=_A,info_vector=_A):
11
- Q='ignore';J=info_vector;F=reference_vector;C=test_vector;K=0;L={SEVERITY_ERROR:0,SEVERITY_WARNING:0};G=False;H=_A
12
- if A.thresholds.vectors and A.thresholds.vectors.ponderation_method=='amplitude_moderation':R=vector_get_amplitude(C)[_B];H=R*A.thresholds.vectors.amplitude_moderation_multiplier;M=A.thresholds.vectors.reduction_method
13
- E=_A
14
- if A.thresholds.vectors and A.thresholds.vectors.ponderation_method in['RIAE','RIAE_trapezoid','RIAE_midpoint']:
15
- E=A.thresholds.vectors.reduction_method;S=.01
16
- if S>=A.thresholds.vectors.riae_threshold:E=A.thresholds.vectors.reduction_method
17
- T=len(C)
18
- for B in range(T):
19
- N=C[B]-F[B]
20
- if N==0:continue
21
- else:K+=1
22
- if G:continue
23
- if E==Q:continue
24
- if H is not _A and abs(N)<H:
25
- if M==Q:continue
26
- elif M=='soften':D,O,I=A.compare_2_values(C[B],F[B]);D=SEVERITY_WARNING
27
- else:
28
- D,O,I=A.compare_2_values(C[B],F[B])
29
- if E:D=SEVERITY_WARNING
30
- if I:
31
- L[D]+=1;P={'index':B}
32
- if J:P['info']=J[B]
33
- G=A.compare_errors.add(D,O,I,group_idx=group_idx,info=P)
34
- return G,K,L
35
- def compare_2_values(G,test,reference):
36
- D=test;B=reference;A=G.thresholds;F=-1 if D-B<0 else 1
37
- if abs(D)>A.relative_vs_absolute_min and B!=0:
38
- C=abs(D-B)/abs(B);E=CompareErrFloats(is_relative=True,value=F*C,test=D,reference=B)
39
- if C<A.relative_error_max:
40
- if C>A.relative_error_min:return SEVERITY_WARNING,f"Rel. err. > {A.relative_error_min} and < {A.relative_error_max}",E
41
- else:return SEVERITY_ERROR,f"Rel. err. > {A.relative_error_max}",E
11
+ def __init__(self,compare_errors,config):self.compare_errors=compare_errors;self.thresholds=config
12
+ def compare_2_values(self,test,reference):
13
+ thr=self.thresholds;sign=-1 if test-reference<0 else 1
14
+ if abs(test)>thr.relative_vs_absolute_min and reference!=0:
15
+ err=abs(test-reference)/abs(reference);comp_err=CompareFloatsErr(is_relative=True,value=sign*err,test=test,reference=reference)
16
+ if err<thr.relative_error_max:
17
+ if err>thr.relative_error_min:return Compare2ValuesResults(SEVERITY_WARNING,f"Rel. err. > {thr.relative_error_min} and < {thr.relative_error_max}",comp_err)
18
+ else:return Compare2ValuesResults(SEVERITY_ERROR,f"Rel. err. > {thr.relative_error_max}",comp_err)
42
19
  else:
43
- C=abs(D-B);E=CompareErrFloats(is_relative=False,value=F*C,test=D,reference=B)
44
- if C<A.absolute_error_max:
45
- if C>A.absolute_error_min:return SEVERITY_WARNING,f"Abs. err. > {A.absolute_error_min} and < {A.absolute_error_max}",E
46
- else:return SEVERITY_ERROR,f"Abs. err. > {A.absolute_error_max}",E
47
- return _A,_A,_A
20
+ err=abs(test-reference);comp_err=CompareFloatsErr(is_relative=_B,value=sign*err,test=test,reference=reference)
21
+ if err<thr.absolute_error_max:
22
+ if err>thr.absolute_error_min:return Compare2ValuesResults(SEVERITY_WARNING,f"Abs. err. > {thr.absolute_error_min} and < {thr.absolute_error_max}",comp_err)
23
+ else:return Compare2ValuesResults(SEVERITY_ERROR,f"Abs. err. > {thr.absolute_error_max}",comp_err)
24
+ def compare_vectors(self,test_vector,reference_vector,group_id,info_vector=_A):
25
+ B='ignore';A='RIAE_trapezoid';group=self.compare_errors.groups[group_id]
26
+ if len(test_vector)!=len(reference_vector):raise Exception('Vectors have different lengths')
27
+ diffs_count=0;err_limit_reached=_B;ponderation_method=self.thresholds.vectors.ponderation_method
28
+ if ponderation_method=='RIAE':ponderation_method=A
29
+ amplitude_compare=_A
30
+ if self.thresholds.vectors and ponderation_method=='amplitude_moderation':amplitude=vector_get_amplitude(test_vector)[_C];amplitude_compare=amplitude*self.thresholds.vectors.amplitude_moderation_multiplier;reduction_method=self.thresholds.vectors.reduction_method
31
+ RIAE_force_severity=_A
32
+ if self.thresholds.vectors and ponderation_method in[A,'RIAE_midpoint']:
33
+ RIAE_force_severity=self.thresholds.vectors.reduction_method
34
+ if'CheckVectors'not in globals():raise Exception('scilens_compare not found. Please install scilens-compare package with `pip install scilens-compare`.')
35
+ riae_error=CheckVectors.relative_integral_absolute_error_trapezoid(reference_vector,test_vector,range(len(test_vector)))if ponderation_method==A else CheckVectors.relative_integral_absolute_error_midpoint(reference_vector,test_vector,range(len(test_vector)))
36
+ if riae_error>self.thresholds.vectors.riae_threshold:ee=CompareFloatsErr(is_relative=_B,value=riae_error);res_compare=Compare2ValuesResults(SEVERITY_ERROR,f"RIAE ({ponderation_method}) > {self.thresholds.vectors.riae_threshold}",ee);err_limit_reached=self.compare_errors.add(group,res_compare)
37
+ nb=len(test_vector)
38
+ for idx in range(nb):
39
+ diff=test_vector[idx]-reference_vector[idx]
40
+ if diff==0:continue
41
+ else:diffs_count+=1;group.incr('diff')
42
+ if err_limit_reached:continue
43
+ if RIAE_force_severity==B:continue
44
+ if amplitude_compare is not _A and abs(diff)<amplitude_compare:
45
+ if reduction_method==B:continue
46
+ elif reduction_method=='soften':
47
+ res_compare=self.compare_2_values(test_vector[idx],reference_vector[idx])
48
+ if res_compare:res_compare.severity=SEVERITY_WARNING
49
+ else:
50
+ res_compare=self.compare_2_values(test_vector[idx],reference_vector[idx])
51
+ if res_compare and RIAE_force_severity:res_compare.severity=SEVERITY_WARNING
52
+ if res_compare:
53
+ info={'index':idx}
54
+ if info_vector:info['info']=info_vector[idx]
55
+ err_limit_reached=self.compare_errors.add(group,res_compare,info=info)
56
+ return err_limit_reached,diffs_count
57
+ def compare_matrices(self,test_mat,ref_mat,group_id,x_vector=_A,y_vector=_A):
58
+ group=self.compare_errors.groups[group_id];info={};diffs_count=0;err_limit_reached=_B;test_nb_lines=len(test_mat);test_nb_columns=len(test_mat[0])if test_nb_lines>0 else 0;ref_nb_lines=len(ref_mat);ref_nb_columns=len(ref_mat[0])if ref_nb_lines>0 else 0
59
+ if test_nb_lines!=ref_nb_lines or test_nb_columns!=ref_nb_columns:raise Exception('Matrices have different dimensions')
60
+ for i in range(test_nb_lines):
61
+ for j in range(test_nb_columns):
62
+ diff=test_mat[i][j]-ref_mat[i][j]
63
+ if diff==0:continue
64
+ else:diffs_count+=1
65
+ if err_limit_reached:continue
66
+ res_compare=self.compare_2_values(test_mat[i][j],ref_mat[i][j])
67
+ if res_compare:
68
+ info={'i':i+1,'j':j+1}
69
+ if x_vector:info['x']=x_vector[j]
70
+ if y_vector:info['y']=y_vector[i]
71
+ err_limit_reached=self.compare_errors.add(group,res_compare,info=info)
72
+ return err_limit_reached,diffs_count
@@ -0,0 +1,20 @@
1
+ _A=None
2
+ from dataclasses import dataclass
3
+ from typing import Literal,Optional
4
+ from pydantic import BaseModel
5
+ SEVERITY_ERROR='error'
6
+ SEVERITY_WARNING='warning'
7
+ @dataclass
8
+ class CompareFloatsErr:is_relative:bool;value:float;test:float|_A=_A;reference:float|_A=_A
9
+ @dataclass
10
+ class Compare2ValuesResults:severity:str;message:str;comp_err:CompareFloatsErr|_A=_A
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']
13
+ @dataclass
14
+ class CompareGroup:
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
16
+ def incr(A,type):
17
+ if type==SEVERITY_ERROR:A.total_errors+=1
18
+ elif type==SEVERITY_WARNING:A.total_warnings+=1
19
+ elif type=='diff':A.total_diffs+=1
20
+ if A.parent is not _A:A.parent.incr(type)
@@ -3,6 +3,7 @@ import logging,importlib,os
3
3
  from scilens.config.models import FileReaderConfig
4
4
  from scilens.config.models.readers import ReadersConfig
5
5
  from scilens.readers.reader_manager import ReaderManager
6
+ from scilens.readers.reader_interface import ReaderInterface
6
7
  from scilens.readers.exceptions import NoReaderFound
7
8
  class FileReader:
8
9
  def __init__(A,absolute_working_dir,config,readers_config,config_alternate_path=_A):A.path=absolute_working_dir;A.config_alternate_path=config_alternate_path;A.reader_mgmr=ReaderManager();A.config=config;A.readers_config=readers_config
@@ -15,17 +16,11 @@ class FileReader:
15
16
  if not B:raise Exception(f"Custom curve parser not found: {A}")
16
17
  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
18
  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__=='ReaderTxtFixedCols':C=A.readers_config.txt_fixed_cols
26
- elif B.__class__.__name__=='ReaderNetcdf':C=A.readers_config.netcdf
27
- B.read(C)
19
+ logging.info(f"Reading file: {path}");D=_A;B=A.config.custom_curve_parser
20
+ if B:
21
+ if isinstance(B,str):D=A._get_custom_parser(B)
22
+ 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)
28
23
  except NoReaderFound:
29
- if A.config.extension_unknown_ignore:0
24
+ if A.config.extension_unknown_ignore:C=_A
30
25
  else:raise Exception(f"No reader found")
31
- return B
26
+ return C
@@ -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`")
@@ -1,4 +1,5 @@
1
1
  _A=None
2
2
  from pydantic import BaseModel,Field
3
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.')
4
+ 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=False,description='Indique si la première colonne est ma colonne des valeurs y.')
5
+ 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=False,description='Indique si le fichier est une matrice.');ignore_columns:list[str]|_A=Field(default=_A,description='Liste des noms des colonnes à ignorer. (Valide seulement si la première ligne est une ligne en-têtes et non matrice)');curve_parser:ReaderColsCurveParserConfig|_A=Field(default=_A,description='Parseur de courbe à utiliser.');matrix:ReaderCsvMatrixConfig|_A=Field(default=_A,description='Configuration de la matrice. (Valide seulement si `is_matrix` est vrai)')
@@ -1,6 +1,17 @@
1
- from pydantic import BaseModel,Field
1
+ _B='netcdf'
2
+ _A='txt_fixed_cols'
3
+ from typing import Literal
4
+ from pydantic import BaseModel,Field,model_validator
2
5
  from scilens.config.models.reader_format_txt import ReaderTxtConfig
3
6
  from scilens.config.models.reader_format_csv import ReaderCsvConfig
4
7
  from scilens.config.models.reader_format_txt_fixed_cols import ReaderTxtFixedColsConfig
5
8
  from scilens.config.models.reader_format_netcdf import ReaderNetcdfConfig
6
- 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.')
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': ';'}}}`")
@@ -3,7 +3,9 @@ _D='x_index'
3
3
  _C='csv_col_index'
4
4
  _B='curves'
5
5
  _A=None
6
+ import logging
6
7
  from dataclasses import dataclass,field
8
+ from scilens.components.compare_models import CompareGroup
7
9
  from scilens.components.compare_floats import CompareFloats
8
10
  from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
9
11
  @dataclass
@@ -23,23 +25,23 @@ def cols_dataset_get_curves_col_x(cols_dataset,col_x):
23
25
  E[_D]=C;J=[B for(A,B)in enumerate(A.numeric_col_indexes)if A!=C];F=[];H=[]
24
26
  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
27
  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
28
+ def compare(group,compare_floats,reader_test,reader_ref,cols_curve):
29
+ L=compare_floats;K='Errors limit reached';G=reader_ref;E=group;C=cols_curve;A=reader_test;logging.debug(f"compare cols: {E.name}")
30
+ if len(A.numeric_col_indexes)!=len(G.numeric_col_indexes):E.error=f"Number Float columns indexes are different: {len(A.numeric_col_indexes)} != {len(G.numeric_col_indexes)}";return
31
+ D=[''for A in range(A.cols_count)];M=_A;H=_A
32
+ if C and C.type==ReaderCurveParserNameConfig.COL_X:N=C.info[_D];M=A.data[N];H=A.names[N]
33
+ I=False
32
34
  for B in range(A.cols_count):
33
35
  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"
36
+ if I:D[B]=K;continue
37
+ Q=A.data[B];R=G.data[B];U,F=L.compare_errors.add_group('vectors',A.names[B],parent=E,data={'info_prefix':H}if H else _A);logging.debug(f"compare cols: {F.name}");S,V=L.compare_vectors(Q,R,group_id=F.id,info_vector=M)
38
+ if S:I=True;D[B]=K;continue
39
+ if F.total_errors>0:D[B]=f"{F.total_errors} comparison errors"
38
40
  if C:
39
41
  for O in C.curves[_E]:
40
42
  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
43
+ for T in O[_B]:
44
+ J=C.curves[_B][T]
45
+ if D[J[_C]]:J['comparison_error']=D[J[_C]];P+=1
44
46
  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}
47
+ E.error=K if I else _A;E.info={'cols_has_error':D}
@@ -0,0 +1,22 @@
1
+ _A=None
2
+ import csv
3
+ from collections.abc import Iterator
4
+ from dataclasses import dataclass,field
5
+ from scilens.components.compare_models import CompareGroup
6
+ from scilens.components.compare_floats import CompareFloats
7
+ @dataclass
8
+ class MatDataset:nb_lines:int=0;nb_columns:int=0;data:list[list[float]]=field(default_factory=lambda:[]);x_values:list[float]|_A=_A;y_values:list[float]|_A=_A
9
+ def from_reader(reader,x_value_line=_A,has_header=False,has_y=False):
10
+ E=has_y;D=x_value_line;B=reader;H=_A;I=[]if E else _A;A=[];F=0
11
+ if D:
12
+ J=1 if E else 0
13
+ for M in range(0,D):
14
+ K=next(B);F+=1
15
+ if F==D:H=[float(A)for A in K[J:]]
16
+ if F==0 and has_header:next(B)
17
+ if E:
18
+ for C in B:L=float(C[0]);G=[float(A)for A in C[1:]];I.append(L);A.append(G)
19
+ else:
20
+ for C in B:G=[float(A)for A in C];A.append(G)
21
+ return MatDataset(nb_lines=len(A),nb_columns=len(A[0])if len(A)>0 else 0,data=A,x_values=H,y_values=I)
22
+ def compare(parent_group,compare_floats,test,ref,group_name=''):C=compare_floats;B=parent_group;A=test;F,D=C.compare_errors.add_group('matrix',group_name,parent=B);E,G=C.compare_matrices(A.data,ref.data,D.id,x_vector=A.x_values,y_vector=A.y_values);B.error='Errors limit reached'if E else _A
@@ -1,8 +1,11 @@
1
+ _A=None
1
2
  import logging,csv
2
3
  from scilens.readers.reader_interface import ReaderInterface
3
- from scilens.readers.cols_dataset import ColsDataset,ColsCurves,cols_dataset_get_curves_col_x,compare
4
- from scilens.config.models import ReaderCsvConfig
4
+ from scilens.readers.cols_dataset import ColsDataset,ColsCurves,cols_dataset_get_curves_col_x,compare as cols_compare
5
+ from scilens.readers.mat_dataset import MatDataset,from_reader as mat_from_reader,compare as mat_compare
6
+ from scilens.config.models.reader_format_csv import ReaderCsvConfig,ReaderCsvMatrixConfig
5
7
  from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
8
+ from scilens.components.compare_models import CompareGroup
6
9
  from scilens.components.compare_floats import CompareFloats
7
10
  def is_num(x):
8
11
  try:return float(x)
@@ -11,30 +14,36 @@ def csv_row_detect_header(first_row):
11
14
  A=first_row
12
15
  if all(not A.isdigit()for A in A):return True,A
13
16
  else:return False,[f"Column {A}"for(A,B)in enumerate(A)]
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
17
+ def csv_row_detect_cols_num(row):return[A for(A,B)in enumerate(row)if is_num(B)!=_A]
18
+ def csv_detect(path,delimiter,quotechar,encoding):
19
+ with open(path,'r',encoding=encoding)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
17
20
  class ReaderCsv(ReaderInterface):
18
- category='datalines';extensions=['CSV']
21
+ configuration_type_code='csv';category='datalines';extensions=['CSV']
19
22
  def read(A,reader_options):
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')
38
- else:raise Exception('Curve parser not supported.')
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)
23
+ B=reader_options;A.reader_options=B;A.raw_lines_number=_A;A.curves=_A;D,E,M=csv_detect(A.origin.path,A.reader_options.delimiter,A.reader_options.quotechar,encoding=A.encoding);A.has_header=D;A.cols=E;A.numeric_col_indexes=M;H=open(A.origin.path,'r',encoding=A.encoding);I=csv.reader(H,delimiter=A.reader_options.delimiter,quotechar=A.reader_options.quotechar)
24
+ if B.is_matrix:J=B.matrix or ReaderCsvMatrixConfig();N=mat_from_reader(I,has_header=D,x_value_line=J.x_value_line,has_y=J.has_y);A.mat_dataset=N
25
+ else:
26
+ if B.ignore_columns:
27
+ if not D:raise Exception('Ignore columns is not supported without header.')
28
+ A.numeric_col_indexes=[C for C in A.numeric_col_indexes if A.cols[C]not in B.ignore_columns]
29
+ K=len(E);C=ColsDataset(cols_count=K,names=E,numeric_col_indexes=A.numeric_col_indexes,data=[[]for A in range(K)]);F=0
30
+ for O in I:
31
+ F+=1
32
+ if D and F==1:continue
33
+ for(L,G)in enumerate(O):
34
+ if L in C.numeric_col_indexes:G=float(G)
35
+ C.data[L].append(G)
36
+ C.origin_line_nb.append(F)
37
+ C.rows_count=len(C.origin_line_nb);A.cols_dataset=C;A.raw_lines_number=C.rows_count+(1 if D else 0)
38
+ if B.curve_parser:
39
+ if B.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
40
+ A.curves,P=cols_dataset_get_curves_col_x(C,B.curve_parser.parameters.x)
41
+ if A.curves:A.cols_curve=ColsCurves(type=ReaderCurveParserNameConfig.COL_X,info=P,curves=A.curves)
42
+ elif B.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
43
+ else:raise Exception('Curve parser not supported.')
44
+ H.close()
45
+ def compare(A,compare_floats,param_reader,param_is_ref=True):
46
+ H='node';D=param_is_ref;C=param_reader;B=compare_floats;I=A.reader_options
47
+ if I.is_matrix:E=A.mat_dataset if D else C.mat_dataset;F=A.mat_dataset if not D else C.mat_dataset;J,G=B.compare_errors.add_group(H,'csv matrix');mat_compare(G,B,E,F)
48
+ else:E=A.cols_dataset if D else C.cols_dataset;F=A.cols_dataset if not D else C.cols_dataset;K=A.cols_curve if hasattr(A,'cols_curve')else _A;J,G=B.compare_errors.add_group(H,'csv cols');cols_compare(G,B,E,F,K)
40
49
  def class_info(A):return{'cols':A.cols,'raw_lines_number':A.raw_lines_number,'curves':A.curves}
@@ -1,7 +1,10 @@
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:]
@@ -16,16 +19,32 @@ class ReaderManager:
16
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,[])
17
20
  for C in B:A.plugins+=C.load()()
18
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]
19
23
  def __str__(A):return f"plugins: {A._get_plugin_names()}"
20
24
  def _get_reader_from_extension(B,extension):
21
25
  for A in B.plugins:
22
26
  if extension_format(extension)in A.extensions:return A
23
- def get_reader_from_file(D,path,name='',encoding='',curve_parser=None,extension_mapping=None,extension_fallback=None):
24
- F=extension_fallback;E=extension_mapping;C=path;J,A=os.path.splitext(C);A=extension_format(A)
25
- if E:
26
- for(G,H)in E.items():
27
- if extension_format(G)==A:A=extension_format(H);break
28
- B=D._get_reader_from_extension(A)
29
- 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)
30
44
  if not B:raise NoReaderFound(f"Reader cound not be derived")
31
- 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
@@ -7,10 +7,10 @@ _A=None
7
7
  from scilens.readers.reader_interface import ReaderInterface
8
8
  from scilens.readers.transform import string_2_floats
9
9
  from scilens.config.models import ReaderTxtConfig
10
- from scilens.components.compare_errors import SEVERITY_ERROR
10
+ from scilens.components.compare_models import SEVERITY_ERROR,Compare2ValuesResults
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:
@@ -42,23 +42,22 @@ class ReaderTxt(ReaderInterface):
42
42
  C,S=A.find_patterns_lines_nb(B.error_rule_patterns);A.read_data['error_rule_patterns']={'found':C,'data':S}
43
43
  if C:A.read_error='String error pattern found'
44
44
  def compare(I,compare_floats,param_reader,param_is_ref=_D):
45
- K=param_is_ref;J=param_reader;D=compare_floats;A=I if K else J;B=I if not K else J;E=0;C=_C;Y=[]
46
- if A.floats_lines_number!=B.floats_lines_number:T=f"Nb number lines 1: {A.floats_lines_number} 2: {B.floats_lines_number} different";return T,_A
47
- L=A.floats_lines;U=B.floats_lines
48
- for M in range(len(L)):
49
- F=L[M];G=U[M];H=F[_F];N=G[_F];O={'line_nb_1':F[_B],'line_nb_2':G[_B],'line_1':A.get_raw_lines(F[_B]),'line_2':B.get_raw_lines(G[_B])}
50
- if len(H)!=len(N):
51
- if not C:E+=1;C=D.compare_errors.add(SEVERITY_ERROR,'Not same numbers number in the lines',_A,info=O)
45
+ U='diff';K=param_is_ref;J=param_reader;A=compare_floats;V,L=A.compare_errors.add_group('node','txt');V,B=A.compare_errors.add_group(_E,_E,parent=L);C=I if K else J;D=I if not K else J;E=_C
46
+ if C.floats_lines_number!=D.floats_lines_number:L.error=f"Nb number lines 1: {C.floats_lines_number} 2: {D.floats_lines_number} different"
47
+ M=C.floats_lines;W=D.floats_lines
48
+ for N in range(len(M)):
49
+ F=M[N];G=W[N];H=F[_F];O=G[_F];P={'line_nb_1':F[_B],'line_nb_2':G[_B],'line_1':C.get_raw_lines(F[_B]),'line_2':D.get_raw_lines(G[_B])}
50
+ if len(H)!=len(O):
51
+ if not E:B.incr(U);E=A.compare_errors.add(B,Compare2ValuesResults(SEVERITY_ERROR,'Not same numbers number in the lines'),info=P)
52
52
  continue
53
- for P in range(len(H)):
54
- Q=H[P];R=N[P];V=Q-R
55
- if V==0:continue
53
+ for Q in range(len(H)):
54
+ R=H[Q];S=O[Q];X=R-S
55
+ if X==0:continue
56
56
  else:
57
- E+=1
58
- if not C:
59
- W,X,S=D.compare_2_values(Q,R)
60
- if S:C=D.compare_errors.add(W,X,S,info=O)
61
- return _A,{'type':_E,'total_diffs':E}
57
+ B.incr(U)
58
+ if not E:
59
+ T=A.compare_2_values(R,S)
60
+ if T:E=A.compare_errors.add(B,T,info=P)
62
61
  def find_patterns_lines_nb(D,patterns):
63
62
  A=patterns;B=_C;map={A:[]for A in A}
64
63
  for(E,F)in enumerate(D.raw_lines):
@@ -6,32 +6,33 @@ from scilens.config.models import ReaderTxtFixedColsConfig
6
6
  from scilens.config.models.reader_format_cols_curve import ReaderCurveParserNameConfig
7
7
  from scilens.components.compare_floats import CompareFloats
8
8
  class ReaderTxtFixedCols(ReaderInterface):
9
- category='datalines';extensions=['DAT']
9
+ configuration_type_code='txt_fixed_cols';category='datalines';extensions=[]
10
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;D=len(K);C=ColsDataset(cols_count=D,names=[f"Column {A+1}"for A in range(D)],numeric_col_indexes=[A for A in range(D)],data=[[]for A in range(D)]);E=None;G=0
12
- for F in J:
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
13
  G+=1
14
14
  if L:
15
15
  M=False
16
16
  for P in L:
17
- if bool(re.match(P,F)):M=True;break
17
+ if bool(re.match(P,D)):M=True;break
18
18
  if M:continue
19
19
  if A.has_header:
20
- if not E:
21
- E=F.strip();H=E
20
+ if not F:
21
+ F=D.strip();H=F
22
22
  if A.has_header_ignore:
23
23
  for Q in A.has_header_ignore:H=H.replace(Q,'')
24
24
  C.names=H.split();continue
25
- elif A.has_header_repetition and E==F.strip():continue
25
+ elif A.has_header_repetition and F==D.strip():continue
26
+ if not D.strip():continue
26
27
  I=0;N=0
27
- for O in K:R=F[I:I+O].strip();S=string_2_float(R);C.data[N].append(S);I+=O;N+=1
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
28
29
  C.origin_line_nb.append(G)
29
- C.rows_count=len(C.origin_line_nb);J.close();B.cols_dataset=C;B.raw_lines_number=G;print(C);B.curves=None
30
+ C.rows_count=len(C.origin_line_nb);J.close();B.cols_dataset=C;B.raw_lines_number=G;B.curves=None
30
31
  if A.curve_parser:
31
32
  if A.curve_parser.name==ReaderCurveParserNameConfig.COL_X:
32
33
  B.curves,T=cols_dataset_get_curves_col_x(C,A.curve_parser.parameters.x)
33
34
  if B.curves:B.cols_curve=ColsCurves(type=ReaderCurveParserNameConfig.COL_X,info=T,curves=B.curves)
34
35
  elif A.curve_parser.name==ReaderCurveParserNameConfig.COLS_COUPLE:raise NotImplementedError('cols_couple not implemented')
35
36
  else:raise Exception('Curve parser not supported.')
36
- 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)
37
+ def compare(A,compare_floats,param_reader,param_is_ref=True):D=param_is_ref;C=param_reader;B=compare_floats;E=A.cols_dataset if D else C.cols_dataset;F=A.cols_dataset if not D else C.cols_dataset;G=A.cols_curve;I,H=B.compare_errors.add_group('node','txt cols');return compare(H,B,E,F,G)
37
38
  def class_info(A):return{'cols':A.cols_dataset.names,'raw_lines_number':A.raw_lines_number,'curves':A.curves}
@@ -53,10 +53,14 @@
53
53
  <td class="number {{ 'ERROR' if file.comparison_errors.error_nb else '' }}">{{ file.comparison_errors.error_nb }}</td>
54
54
  <td class="number {{ 'WARNING' if file.comparison_errors.warning_nb else '' }}">{{ file.comparison_errors.warning_nb }}</td>
55
55
  <td class="number">{{ file.comparison.total_diffs }}</td>
56
- {% else %}
56
+ {% elif not file.skipped %}
57
57
  <td class="ERROR">ERROR</td>
58
58
  <td class="ERROR">ERROR</td>
59
59
  <td class="ERROR">ERROR</td>
60
+ {% else %}
61
+ <td></td>
62
+ <td></td>
63
+ <td></td>
60
64
  {% endif %}
61
65
  <td class="number test-bg-20">{{ file.test.raw_lines_number }}</td>
62
66
  <td class="number refe-bg-20">{{ file.ref.raw_lines_number }}</td>
@@ -1,93 +1,12 @@
1
1
  {% if file.comparison_errors %}
2
- {% set errs = file.comparison_errors %}
3
- {% set c_type = file.comparison.type %}
4
- <table>
5
- <tr>
6
- <th class="p-2" colspan="6">Error</th>
7
- <th class="p-2" colspan="99">Source</th>
8
- </tr>
9
- <tr>
10
- <th class="p-2" rowspan="2">Severity</th>
11
- <th class="p-2" rowspan="2">Message</th>
12
- <th class="p-2" colspan="2">Value</th>
13
- <th class="p-2" rowspan="2">Error</th>
14
- <th class="p-2" rowspan="2">Visu</th>
2
+ {% for group in file.comparison_errors.groups if group.type != 'node' %}
15
3
 
16
- {% if c_type == "lines" %}
17
- <th class="p-2" colspan="2">Line Number</th>
18
- <th class="p-2" colspan="2">Line(s)</th>
19
- {% endif %}
4
+ {% if group.total_warnings or group.total_errors %}
20
5
 
21
- {% if c_type == "vectors" %}
22
- <th class="p-2" rowspan="2">Column</th>
23
- <th class="p-2" rowspan="2">Index</th>
24
- <th class="p-2" rowspan="2">Info</th>
25
- {% endif %}
6
+ <h4>{{ group.name }}</h4>
7
+ {% include 'compare_13_section_numbers_table.html' with context %}
26
8
 
27
- </tr>
28
- <tr>
29
- <th class="p-2 test-bg-50">Test</th>
30
- <th class="p-2 refe-bg-50">Ref.</th>
31
-
32
- {% if c_type == "lines" %}
33
- <th class="p-2 test-bg-50">Test</th>
34
- <th class="p-2 refe-bg-50">Ref.</th>
35
- <th class="p-2 test-bg-50">Test</th>
36
- <th class="p-2 refe-bg-50">Ref.</th>
37
- {% endif %}
38
-
39
- </tr>
40
-
41
-
42
- {% for error_type in [{"data_key": "error", "class": "ERROR"}, {"data_key": "warning", "class": "WARNING"}] %}
43
- {% for item in errs[error_type.data_key] %}
44
- <tr>
45
-
46
- <td class="{{error_type.class}}">{{error_type.class}}</td>
47
- <td>{{ errs.messages[item.msg] }}</td>
48
-
49
- <!-- COMPARISON -->
50
-
51
- <td class="number test-bg-20">{{ item.err.test }}</td>
52
- <td class="number refe-bg-20">{{ item.err.reference }}</td>
53
- <td class="number">
54
- {% if item.err.is_relative %}
55
- {{ '%0.2f' % (item.err.value*100)|float}}%
56
- {% else %}
57
- {{ item.err.value }}
58
- {% endif %}
59
- </td>
60
- <td>
61
- {% if item.err.test and item.err.reference %}
62
- <div style="width:200px;height:20px">
63
- <div class="test-bg-80" style="width:{{100*(item.err.test|abs)/([(item.err.test|abs), (item.err.reference|abs)]|max)}}%;height:10px"></div>
64
- <div class="refe-bg-80" style="width:{{100*(item.err.reference|abs)/([(item.err.test|abs), (item.err.reference|abs)]|max)}}%; height:10px"></div>
65
- </div>
66
- {% endif %}
67
- </td>
68
-
69
-
70
- {% if c_type == "vectors" %}
71
- {% set group = errs.groups[item.group] %}
72
- <td>{{ group.name }}</td>
73
- <td>{{ item.info.index }}</td>
74
- <td>
75
- {% if group.data %}
76
- {{ group.data.info_prefix + ": " + item.info.info|string }}
77
- {% endif %}
78
- </td>
79
- {% endif %}
80
-
81
- {% if c_type == "lines" %}
82
- <td class="test-bg-20 number">{{ item.info.line_nb_1 }}</td>
83
- <td class="refe-bg-20 number">{{ item.info.line_nb_2 }}</td>
84
- <td class="test-bg-20"><pre>{{ item.info.line_1 }}</pre></td>
85
- <td class="refe-bg-20"><pre>{{ item.info.line_2 }}</pre></td>
86
- {% endif %}
9
+ {% endif %}
87
10
 
88
- </tr>
89
- {% endfor %}
90
- {% endfor %}
91
-
92
- </table>
11
+ {% endfor %}
93
12
  {% endif %}
@@ -0,0 +1,99 @@
1
+ {% set comp_errs = file.comparison_errors %}
2
+ <table>
3
+ <!-- HEADERS -->
4
+ <tr>
5
+ <th class="p-2" colspan="6">Error</th>
6
+ <th class="p-2" colspan="99">Source</th>
7
+ </tr>
8
+ <tr>
9
+ <th class="p-2" rowspan="2">Severity</th>
10
+ <th class="p-2" rowspan="2">Message</th>
11
+ <th class="p-2" colspan="2">Value</th>
12
+ <th class="p-2" rowspan="2">Error</th>
13
+ <th class="p-2" rowspan="2">Visu</th>
14
+ {% if group.type == "lines" %}
15
+ <th class="p-2" colspan="2">Line Number</th>
16
+ <th class="p-2" colspan="2">Line(s)</th>
17
+ {% endif %}
18
+ {% if group.type == "vectors" %}
19
+ <th class="p-2" rowspan="2">Column</th>
20
+ <th class="p-2" rowspan="2">Index</th>
21
+ <th class="p-2" rowspan="2">Info</th>
22
+ {% endif %}
23
+ {% if group.type == "matrix" %}
24
+ <th class="p-2" rowspan="2">i</th>
25
+ <th class="p-2" rowspan="2">j</th>
26
+ <th class="p-2" rowspan="2">x</th>
27
+ <th class="p-2" rowspan="2">y</th>
28
+ {% endif %}
29
+ </tr>
30
+ <tr>
31
+ <th class="p-2 test-bg-50">Test</th>
32
+ <th class="p-2 refe-bg-50">Ref.</th>
33
+ {% if group.type == "lines" %}
34
+ <th class="p-2 test-bg-50">Test</th>
35
+ <th class="p-2 refe-bg-50">Ref.</th>
36
+ <th class="p-2 test-bg-50">Test</th>
37
+ <th class="p-2 refe-bg-50">Ref.</th>
38
+ {% endif %}
39
+ </tr>
40
+ <!-- BODY -->
41
+ {% for error_type in [{"data_key": "error", "class": "ERROR"}, {"data_key": "warning", "class": "WARNING"}] %}
42
+ {% for item in comp_errs[error_type.data_key] if item.group == group.id %}
43
+ <tr>
44
+
45
+ <td class="{{error_type.class}}">{{error_type.class}}</td>
46
+ <td>{{ comp_errs.messages[item.msg] }}</td>
47
+
48
+ <!-- COMPARISON -->
49
+
50
+ <td class="number test-bg-20">{{ item.err.test }}</td>
51
+ <td class="number refe-bg-20">{{ item.err.reference }}</td>
52
+ <td class="number">
53
+ {% if item.err.is_relative %}
54
+ {{ '%0.2f' % (item.err.value*100)|float}}%
55
+ {% else %}
56
+ {{ item.err.value }}
57
+ {% endif %}
58
+ </td>
59
+ <td>
60
+ {% if item.err.test and item.err.reference %}
61
+ <div style="width:200px;height:20px">
62
+ <div class="test-bg-80" style="width:{{100*(item.err.test|abs)/([(item.err.test|abs), (item.err.reference|abs)]|max)}}%;height:10px"></div>
63
+ <div class="refe-bg-80" style="width:{{100*(item.err.reference|abs)/([(item.err.test|abs), (item.err.reference|abs)]|max)}}%; height:10px"></div>
64
+ </div>
65
+ {% endif %}
66
+ </td>
67
+
68
+
69
+ {% if group.type == "vectors" %}
70
+ {% set group = comp_errs.groups[item.group] %}
71
+ <td>{{ group.name }}</td>
72
+ <td>{{ item.info.index }}</td>
73
+ <td>
74
+ {% if group.data %}
75
+ {{ group.data.info_prefix + ": " + item.info.info|string }}
76
+ {% endif %}
77
+ </td>
78
+ {% endif %}
79
+
80
+ {% if group.type == "lines" %}
81
+ <td class="test-bg-20 number">{{ item.info.line_nb_1 }}</td>
82
+ <td class="refe-bg-20 number">{{ item.info.line_nb_2 }}</td>
83
+ <td class="test-bg-20"><pre>{{ item.info.line_1 }}</pre></td>
84
+ <td class="refe-bg-20"><pre>{{ item.info.line_2 }}</pre></td>
85
+ {% endif %}
86
+
87
+ {% if group.type == "matrix" %}
88
+ <td class="number">{{ item.info.i }}</td>
89
+ <td class="number">{{ item.info.j }}</td>
90
+ <td class="number">{{ item.info.x }}</td>
91
+ <td class="number">{{ item.info.y }}</td>
92
+ {% endif %}
93
+
94
+ </tr>
95
+ {% endfor %}
96
+ {% endfor %}
97
+
98
+ </table>
99
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scilens
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A CesGensLaB framework for data collecting and deep analysis
5
5
  Home-page: https://scilens.dev
6
6
  License: Proprietary
@@ -5,15 +5,16 @@ 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
9
  scilens/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  scilens/components/analyse_folder.py,sha256=yqc-dscKaHLZJCYeXGak2v0c3F2aeX0E11AFPfya6r0,208
11
- scilens/components/compare_2_files.py,sha256=oQ5p9JwB1r_HhfCb29mv1kkkrUd0EdrVug9T-1fQshY,1286
12
- scilens/components/compare_errors.py,sha256=_n9uWwLVZyPn99mwsAF8WBfcYppVQCsMwj0xuOrlhkU,1241
13
- scilens/components/compare_floats.py,sha256=FKEppQFv1AxeaLcLI9UpgBlh2ZScbAHNxmrMZih8luc,2406
11
+ scilens/components/compare_2_files.py,sha256=U4xumE28ijFbnrTPH8FgRyR_b5f04jOjaCmegJvCvSE,1483
12
+ scilens/components/compare_errors.py,sha256=Kw_zpVmA3Fb7yVDXog2poLaTsV_K81eLqv-z-b73Nlw,1495
13
+ scilens/components/compare_floats.py,sha256=GF4kKvkSKqCLLrx-BnmGi7yWTEBkYZjlCim7B9NBWxc,5028
14
14
  scilens/components/compare_folders.py,sha256=LZ1AuYxLVHMNbtXWXQrdms4vZgOQthvDy-8NFD_EFjc,2617
15
+ scilens/components/compare_models.py,sha256=SCPd747h_nd4ewZsqLB6CFr27v6q99NELJb-gpkdj0o,918
15
16
  scilens/components/executor.py,sha256=8ZZq9wwoiMr7ys9LXv1pEg5Zc06QatT9PGIigMsDAB8,3620
16
- scilens/components/file_reader.py,sha256=XaGQDTWP8k5GokLegA8-dp4DqM4A1Srqr6nCMiixPRE,1708
17
+ scilens/components/file_reader.py,sha256=7SbKCqb4Co_pqAKX3wweYhqAcVkU7BDlT903sLd76Kc,1407
17
18
  scilens/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  scilens/config/cli_run_options.py,sha256=Ls7yK5QDUPFbk73nbjGuPvuRbBRYw4Miag5ISpu3prg,281
19
20
  scilens/config/env_var.py,sha256=NqNBoIfngJEXaGEm7jGqre5pmkJ9eUjiWzbDrTVfi2c,292
@@ -24,13 +25,13 @@ scilens/config/models/compare.py,sha256=_SLMxf81fpuod8izZm72Jn12euRPxB0wHJSfUuIA
24
25
  scilens/config/models/compare_float_thresholds.py,sha256=J5XBK1dAnmU-i2uA2bsaHnTM_m07_i17wsO8UiCw46o,1844
25
26
  scilens/config/models/execute.py,sha256=pFY-gZuBvLbcjTEcoNhPPO7FMFmKa6_TU5IXyKaf81A,1706
26
27
  scilens/config/models/execute_and_compare.py,sha256=TWL6yXGvQSaaV6nhHqWLvtr3v396APIoDNt0U1TbMro,582
27
- scilens/config/models/file_reader.py,sha256=sDIh1z_q4B5c7PhNC1WvQWLNGR4NMbXr-WhT6zDyLB4,1000
28
+ scilens/config/models/file_reader.py,sha256=c18vKhVcBX4ufpbnCBJyVyAsQtlxpwx0lGVuf1i8EGA,1176
28
29
  scilens/config/models/reader_format_cols_curve.py,sha256=eKLvifq1xuN8sPds9ijfru3vgMZ3Odv5tGfeiK4Sfk4,1860
29
- scilens/config/models/reader_format_csv.py,sha256=XWZTr6s0PPfcOMRrsNeOZGtExC1uyXb67A158MMWzC4,577
30
+ scilens/config/models/reader_format_csv.py,sha256=cxvkVbBF5DtrbRCIppjDUC2EIkb-GIe9qou3TGrihxU,1265
30
31
  scilens/config/models/reader_format_netcdf.py,sha256=nbfTB3avO0DidbNa1dCZGFZmmQvzTYhpe6mqfAanaOA,1025
31
32
  scilens/config/models/reader_format_txt.py,sha256=eHg90gwEI_VpqwqEjMRhwlS8dHcl5G4ow-37HjQq_zY,1168
32
33
  scilens/config/models/reader_format_txt_fixed_cols.py,sha256=xHD1_JOoRZow8lSNaDSYFeNckojctkT4C61mbBcjeKg,1079
33
- scilens/config/models/readers.py,sha256=RDuf2uL9I9N5XJI0J3AKSSNwwdwLW936RTY-tpuAIDU,818
34
+ scilens/config/models/readers.py,sha256=Pq5kOGW3b6g1x5cp_BbwUF7LUB_P3m9bHDYLSTVXNBY,1769
34
35
  scilens/config/models/report.py,sha256=nTmP2nIwL2Ku5IH9QMwYLPKmfsK2ttu9UK0GnzPUHeM,870
35
36
  scilens/config/models/report_html.py,sha256=9I9iKRDOoLMZRBY0lQV4UFtg5-D-VDfYiFGF1VFAnQ8,1389
36
37
  scilens/config/models/report_output.py,sha256=XoqUe-t-y8GRbUR3_bDwwaWf6hif-rZ-5pKDGdCMugw,875
@@ -46,15 +47,14 @@ scilens/processors/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
46
47
  scilens/processors/models/results.py,sha256=KoWxh13Zgi7PuPql8hkf4VjCis42ZxAuzIgJxBWVaX8,119
47
48
  scilens/processors/processor_interface.py,sha256=jzMp1529JXnMGTJijVy6b_1zmARAMNv70f2lgys7vn4,452
48
49
  scilens/readers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- scilens/readers/cols_dataset.py,sha256=suEttRHfj9UhHndchAKazLD2z_YY9dFEVLjlodRJS44,2363
50
+ scilens/readers/cols_dataset.py,sha256=JIfPBzQZml6VlykbrMcCanR12eEnjY1A-ECgXs8wtZ4,2527
50
51
  scilens/readers/exceptions.py,sha256=JzmxcjnR5sH-IOWVeCC5A1bSwxv-jCAtIJvDjzx1CTI,32
51
- scilens/readers/reader_com_txt_lines.py,sha256=zsCumTD0sv06OQNvRKPMuv94De_6KGMvN904-E1Uqeg,91
52
- scilens/readers/reader_csv copy.py,sha256=eFWQvMt4Ot7KgUIDsed1QymMbPblgboUclvQj6viVGs,4069
53
- scilens/readers/reader_csv.py,sha256=VslJZ5sYAay_OgmhdaMeO2hzCWwFQB6_2pkJ9RrBcP8,2702
52
+ scilens/readers/mat_dataset.py,sha256=vUEjAK6nuY7miLJvjSkiHAU64KaNa1VCx7EhGPazp-U,1190
53
+ scilens/readers/reader_csv.py,sha256=aPd2IxFiUXYmH_EpNZkCHV1oMWctZW7YSPwC8q75SIo,3490
54
54
  scilens/readers/reader_interface.py,sha256=nnttHL7wt4MOXpi-SBkk8DYxVWscOPG8JFl_z12mIAo,922
55
- scilens/readers/reader_manager.py,sha256=vkCLtFkfs315SW71KJVbVBY5SEUSiT9qW9UDCxRYjFo,1607
56
- scilens/readers/reader_txt.py,sha256=IpIA_qfdL70wrDnOotUtFjt7Yt0lEenYv93E63mQwZw,3744
57
- scilens/readers/reader_txt_fixed_cols.py,sha256=6y5VlDNAK3nKuQbs5ZDu0SGnT7N433SJAHNMkmg3mKo,2202
55
+ scilens/readers/reader_manager.py,sha256=DFinxIk3IIIcB6JxybGcv-mXt3jhXgCwUtzR0TqhB2Q,2684
56
+ scilens/readers/reader_txt.py,sha256=WPsFunEA_idzAKkD3UJQbLnaOzG2U03P3gY4gphuIw0,3868
57
+ scilens/readers/reader_txt_fixed_cols.py,sha256=vIA6e38_3nkFeCT835eSyCufCXQNGav8Iy7VgHlT_EE,2314
58
58
  scilens/readers/transform.py,sha256=kppfgPkXymF0qtquFivuosLVfF66L9bE-wGx-3bMHv8,307
59
59
  scilens/report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  scilens/report/assets/logo.svg,sha256=W-1OVqcvdBjf-1AHHcV6WciIUqBoVFUh52Tc3o_jqtA,4519
@@ -65,10 +65,11 @@ scilens/report/report.py,sha256=aS7ktJ2u0IAMMk-HwqqSsRkr77ZBQyYT4wXJ7djQvAk,1811
65
65
  scilens/report/template.py,sha256=cPs5gd3uEwb-6JgitGQD_i4IiUxigBTlZLNRS9KVuos,581
66
66
  scilens/report/templates/body_01_title.html,sha256=59BmETKHqRO1T_xYp0XLKx3Vha9hU9bu7yaUTVt2p9Y,2146
67
67
  scilens/report/templates/body_99_footer.html,sha256=8cWebeWfZwZ-9bYAMZkZj8rbCWq3BLIMjKQThWQxoQM,362
68
- scilens/report/templates/compare_11_summary.html,sha256=KbOEvK060WJmPEx1SwQtpVBxg7oJLZCH5SFhpIoNt74,3686
68
+ scilens/report/templates/compare_11_summary.html,sha256=4rxBlOxTcn59ztYtqDbi6SRQXlaz30HkVl7dJpzCmZE,3776
69
69
  scilens/report/templates/compare_12_sections.html,sha256=HWsfCmfdleyRK6IHJeMEheenOuyA0mLzOZ-0qLcuzJU,5952
70
70
  scilens/report/templates/compare_13_section_numbers copy.html,sha256=0PWK_I2kNX3LjPLkkY4eSYIeB7YFkA28nk-PPLDhnaY,1753
71
- scilens/report/templates/compare_13_section_numbers.html,sha256=JjD20F6X2RjzJIgAAF431JOdmUyNNOHJOufdH21iUsM,2820
71
+ scilens/report/templates/compare_13_section_numbers.html,sha256=FpIYAVXRiWf2zrJtjy2fWPdit_RNbHP2zQLQYScIrtY,291
72
+ scilens/report/templates/compare_13_section_numbers_table.html,sha256=sJy6ZYtjl80vM1b3oqZSXawZWp7KNIwLI_wCnvBwYPE,3270
72
73
  scilens/report/templates/index.html,sha256=mNTu-CAzEJ2rhz81cHAdmT_KcQeOQ3b_FSC73NPEi0U,3670
73
74
  scilens/report/templates/js_chartlibs_echarts.js,sha256=6YicVhTNIBmmBpV31XCVN5oBeiD0t29JIosJZRUv01M,907
74
75
  scilens/report/templates/js_chartlibs_plotly.js,sha256=uVAOKUB5XE33-r04phR-LlRkFoaHEIXyQ3jXT5r97Rc,1521
@@ -96,7 +97,7 @@ scilens/utils/template.py,sha256=9dlXX3nmfzDRUwzPJOkoxk15UXivZ2SW-McdCwokFa4,443
96
97
  scilens/utils/time_tracker.py,sha256=DdVBoMpVLXrX0qZZXyLm4g38EwDVLlRcBqcpNex1mYY,545
97
98
  scilens/utils/vectors.py,sha256=4N2BZSC5n3HgZqPujDGF5NdjVmSL1rOHb_qw4OoABQY,103
98
99
  scilens/utils/web.py,sha256=E4T8Fra65u9g_BpcFANPk4ORvsYavAeiSgWA3vRca2E,804
99
- scilens-0.2.0.dist-info/METADATA,sha256=V12MlFtIhmET6Ht6GmZEwLX2QPCh-k55YD-6cGS5QHw,1367
100
- scilens-0.2.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
101
- scilens-0.2.0.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
102
- scilens-0.2.0.dist-info/RECORD,,
100
+ scilens-0.3.0.dist-info/METADATA,sha256=zvMTkfew0QXwM9EzvFc4bu1oeo4XZCAo1GonbGZ4LtI,1367
101
+ scilens-0.3.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
102
+ scilens-0.3.0.dist-info/entry_points.txt,sha256=DaKGgxUEUv34GJAoXtta6ecL37ercejep9sCSSRQK2s,48
103
+ scilens-0.3.0.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- class ReaderComTxtLines:
2
- def __init__(A,raw_lines):A.raw_lines=raw_lines;A.ignore_lines=[]
@@ -1,74 +0,0 @@
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