33import csv
44import sys
55import json
6+ import re
7+ from openpyxl .formula import Tokenizer
68
79from typing import Dict , List , Any , Optional , TextIO
810
2022from ckanext .recombinant .read_csv import csv_data_batch
2123from ckanext .recombinant .write_excel import excel_template
2224from ckanext .recombinant .logic import _update_triggers
25+ from ckanext .recombinant .errors import RecombinantFieldError
2326
2427
2528DATASTORE_PAGINATE = 10000 # max records for single datastore query
@@ -36,6 +39,106 @@ def recombinant():
3639 pass
3740
3841
42+ def _check_matching_brackets (formula : str ):
43+ """
44+ Checks for balanced brackets.
45+ """
46+ lefts = [(t .start (), 1 ) for t in
47+ re .finditer (r"\[" , formula )]
48+ rights = [(t .start (), - 1 ) for t in
49+ re .finditer (r"\]" , formula )]
50+ if len (lefts ) < len (rights ):
51+ raise RecombinantFieldError (
52+ 'Encountered missing opening bracket [' )
53+ if len (lefts ) > len (rights ):
54+ raise RecombinantFieldError (
55+ 'Encountered missing closing bracket ]' )
56+
57+ lefts = [(t .start (), 1 ) for t in
58+ re .finditer (r"\{" , formula )]
59+ rights = [(t .start (), - 1 ) for t in
60+ re .finditer (r"\}" , formula )]
61+ if len (lefts ) < len (rights ):
62+ raise RecombinantFieldError (
63+ 'Encountered missing opening bracket {' )
64+ if len (lefts ) > len (rights ):
65+ raise RecombinantFieldError (
66+ 'Encountered missing closing bracket }' )
67+
68+ lefts = [(t .start (), 1 ) for t in
69+ re .finditer (r"\(" , formula )]
70+ rights = [(t .start (), - 1 ) for t in
71+ re .finditer (r"\)" , formula )]
72+ if len (lefts ) < len (rights ):
73+ raise RecombinantFieldError (
74+ 'Encountered missing opening bracket (' )
75+ if len (lefts ) > len (rights ):
76+ raise RecombinantFieldError (
77+ 'Encountered missing closing bracket )' )
78+
79+
80+ @recombinant .command (
81+ short_help = "Checks Excel formulae for basic syntax errors." )
82+ @click .argument ("dataset_type" , required = False )
83+ @click .argument ("datastore_id" , required = False )
84+ @click .option ('-v' , '--verbose' , is_flag = True ,
85+ type = click .BOOL , help = 'Increase verbosity.' )
86+ def check_excel_syntax (dataset_type : Optional [str ] = None ,
87+ datastore_id : Optional [str ] = None ,
88+ verbose : bool = False ):
89+ """
90+ Check Excel formulae for basic syntax errors.
91+ Checks excel_error_formula and excel_required_formula.
92+
93+ Full Usage:\n
94+ recombinant check-excel-syntax [DATASET_TYPE [DATASTORE_ID]]
95+ """
96+ types = [dataset_type ] if dataset_type else get_dataset_types ()
97+ for dtype in types :
98+ geno = get_geno (dtype )
99+ if verbose :
100+ click .echo ('Checking Excel formulae for type: %s' % dtype )
101+ for resource in geno .get ('resources' , []):
102+ if verbose :
103+ click .echo ('Checking Excel formulae for resource: %s' %
104+ resource ['resource_name' ])
105+ for field in resource .get ('fields' , []):
106+ if datastore_id and field ['datastore_id' ] != datastore_id :
107+ continue
108+ if 'excel_error_formula' in field :
109+ formula = Tokenizer (field ['excel_error_formula' ])
110+ errors = False
111+ for f in formula .items :
112+ try :
113+ _check_matching_brackets (f .value )
114+ except RecombinantFieldError as e :
115+ errors = True
116+ click .echo ('%s:%s.%s - syntax BAD' %
117+ (dtype , resource ['resource_name' ],
118+ field ['datastore_id' ]))
119+ click .echo ('\t %s' % e )
120+ if not errors and verbose :
121+ click .echo ('%s:%s.%s - excel_error_formula syntax OK' %
122+ (dtype , resource ['resource_name' ],
123+ field ['datastore_id' ]))
124+ if 'excel_required_formula' in field :
125+ formula = Tokenizer (field ['excel_required_formula' ])
126+ errors = False
127+ for f in formula .items :
128+ try :
129+ _check_matching_brackets (f .value )
130+ except RecombinantFieldError as e :
131+ errors = True
132+ click .echo ('%s:%s.%s - syntax BAD' %
133+ (dtype , resource ['resource_name' ],
134+ field ['datastore_id' ]))
135+ click .echo ('\t %s' % e )
136+ if not errors and verbose :
137+ click .echo ('%s:%s.%s - excel_required_formula syntax OK' %
138+ (dtype , resource ['resource_name' ],
139+ field ['datastore_id' ]))
140+
141+
39142@recombinant .command (
40143 short_help = "Display some information about the "
41144 "status of recombinant datasets." )
0 commit comments