3232
3333from ckanext .recombinant .errors import (
3434 RecombinantException ,
35+ RecombinantFieldError ,
3536 BadExcelData ,
3637 format_trigger_error
3738)
5960from ckanapi import NotFound , LocalCKAN
6061
6162
63+ KEY_ERROR_MATCH = re .compile ('"([^"]*)"' )
64+
6265log = getLogger (__name__ )
6366recombinant = Blueprint ('recombinant' , __name__ )
6467
6568
66- @recombinant .route ('/recombinant/upload/<id>' , methods = ['POST' ])
67- def upload (id : str ) -> Response :
69+ @recombinant .route ('/recombinant/upload/<id>' , methods = ['GET' , ' POST' ])
70+ def upload (id : str ) -> Union [ Response , str ] :
6871 package_type = _get_package_type (id )
6972 geno = get_geno (package_type )
7073 lc = LocalCKAN (username = g .user )
7174 dataset = lc .action .package_show (id = id )
7275 org = lc .action .organization_show (id = dataset ['owner_org' ])
76+ if request .method != 'POST' :
77+ # handle page refreshes
78+ h .flash_notice (_ ('Form not submitted, please try again.' ))
79+ return h .redirect_to ('recombinant.preview_table' ,
80+ resource_name = package_type ,
81+ owner_org = org ['name' ])
7382 dry_run = 'validate' in request .form
7483 # resource_name is only required for redirect,
7584 # so do not need to heavily validate that it exists in the geno.
@@ -98,6 +107,15 @@ def upload(id: str) -> Response:
98107 return h .redirect_to ('recombinant.preview_table' ,
99108 resource_name = resource_name ,
100109 owner_org = org ['name' ])
110+ except RecombinantFieldError as e :
111+ h .flash_error (render ('recombinant/snippets/outdated_error.html' ,
112+ extra_vars = {'key_errors' : str (e ).replace ("'" , '' ),
113+ 'dataset_id' : dataset ['id' ],
114+ 'res_name' : resource_name ,
115+ 'owner_org' : dataset ['owner_org' ]}))
116+ return h .redirect_to ('recombinant.preview_table' ,
117+ resource_name = resource_name ,
118+ owner_org = org ['name' ])
101119 except BadExcelData as e :
102120 h .flash_error (_ (e .message ))
103121 return h .redirect_to ('recombinant.preview_table' ,
@@ -120,6 +138,13 @@ def delete_records(id: str, resource_id: str) -> Union[str, Response]:
120138 res = lc .action .resource_show (id = resource_id )
121139 org = lc .action .organization_show (id = pkg ['owner_org' ])
122140
141+ if request .method != 'POST' :
142+ # handle page refreshes
143+ h .flash_notice (_ ('Form not submitted, please try again.' ))
144+ return h .redirect_to ('recombinant.preview_table' ,
145+ resource_name = res ['name' ],
146+ owner_org = org ['name' ])
147+
123148 dataset = lc .action .recombinant_show (
124149 dataset_type = pkg ['type' ], owner_org = org ['name' ])
125150
@@ -190,7 +215,8 @@ def record_fail(err: str) -> str:
190215 'recombinant.preview_table' ,
191216 resource_name = res ['name' ],
192217 owner_org = org ['name' ])
193- if 'confirm' not in request .form or request .method == 'GET' :
218+ # type_ignore_reason: incomplete typing
219+ if 'confirm' not in request .form or request .method == 'GET' : # type: ignore
194220 return render ('recombinant/confirm_delete.html' ,
195221 extra_vars = {'dataset' : dataset ,
196222 'resource' : res ,
@@ -249,7 +275,7 @@ def _xlsx_response_headers() -> Tuple[str, str]:
249275
250276@recombinant .route ('/recombinant-template/<dataset_type>_<lang>_<owner_org>.xlsx' ,
251277 methods = ['GET' , 'POST' ])
252- def template (dataset_type : str , lang : str , owner_org : str ) -> Response :
278+ def template (dataset_type : str , lang : str , owner_org : str ) -> Union [ Response , str ] :
253279 """
254280 POST requests to this endpoint contain primary keys of
255281 records that are to be included in the excel file
@@ -307,7 +333,17 @@ def template(dataset_type: str, lang: str, owner_org: str) -> Response:
307333 resource ['id' ])
308334 record_data += result ['records' ]
309335
310- append_data (book , record_data , chromo )
336+ try :
337+ append_data (book , record_data , chromo )
338+ except RecombinantFieldError as e :
339+ h .flash_error (render ('recombinant/snippets/outdated_error.html' ,
340+ extra_vars = {'key_errors' : str (e ).replace ("'" , '' ),
341+ 'dataset_id' : dataset ['id' ],
342+ 'res_name' : resource_name ,
343+ 'owner_org' : dataset ['owner_org' ]}))
344+ return h .redirect_to ('recombinant.preview_table' ,
345+ resource_name = resource_name ,
346+ owner_org = org ['name' ])
311347
312348 resource_names = dict ((r ['id' ], r ['name' ]) for r in dataset ['resources' ])
313349 ds_info = lc .action .datastore_info (id = resource ['id' ])
@@ -576,7 +612,8 @@ def preview_table(resource_name: str,
576612 # check that the resource has errors
577613 for _r in dataset ['resources' ]:
578614 if _r ['name' ] == resource_name and ('error' in _r or
579- not _r ['datastore_correct' ]):
615+ not _r ['datastore_correct' ] or
616+ not _r ['schema_correct' ]):
580617 raise NotFound
581618 except NotFound :
582619 try :
@@ -632,6 +669,50 @@ def preview_table(resource_name: str,
632669 })
633670
634671
672+ @recombinant .route ('/recombinant/refresh/<resource_name>/<owner_org>' ,
673+ methods = ['GET' , 'POST' ])
674+ def refresh_dataset (resource_name : str , owner_org : str ):
675+ if not is_sysadmin (g .user ):
676+ return abort (403 )
677+ if request .method != 'POST' :
678+ # handle page refreshes
679+ h .flash_notice (_ ('Form not submitted, please try again.' ))
680+ return h .redirect_to ('recombinant.preview_table' ,
681+ resource_name = resource_name ,
682+ owner_org = owner_org )
683+ if 'refresh' in request .form :
684+ dataset_id = request .form ['dataset_id' ]
685+ if not dataset_id :
686+ h .flash_error (_ ('Could not determine dataset to update.' ))
687+ else :
688+ lc = LocalCKAN ()
689+ dataset_dict = lc .action .package_show (id = dataset_id )
690+ for res in dataset_dict ['resources' ]:
691+ if not h .check_access ('datastore_delete' ,
692+ {'resource_id' : res ['id' ],
693+ 'filters' : {}}):
694+ return abort (403 , _ ('User {0} not authorized '
695+ 'to update resource {1}' .format (
696+ str (g .user ), res ['id' ])))
697+ try :
698+ lc .action .recombinant_update (
699+ owner_org = dataset_dict ['organization' ]['name' ],
700+ dataset_type = dataset_dict ['type' ],
701+ dataset_id = dataset_id ,
702+ force_update = True )
703+ h .flash_success (_ ('Resources successfully refreshed.' ))
704+ except Exception :
705+ h .flash_error (_ (
706+ 'Unable to regenerate the resources. Please contact '
707+ '<a href="mailto:open-ouvert@tbs-sct.gc.ca">'
708+ 'open-ouvert@tbs-sct.gc.ca</a> for assistance.' ),
709+ allow_html = True )
710+ return h .redirect_to (
711+ 'recombinant.preview_table' ,
712+ resource_name = resource_name ,
713+ owner_org = owner_org )
714+
715+
635716def _process_upload_file (lc : LocalCKAN ,
636717 dataset : Dict [str , Any ],
637718 upload_file : Union [str , FlaskFileStorage , FieldStorage ],
@@ -752,6 +833,15 @@ def _process_upload_file(lc: LocalCKAN,
752833 # type_ignore_reason: incomplete typing
753834 pgerror = e .error_dict ['info' ]['orig' ][0 ].decode ( # type: ignore
754835 'utf-8' )
836+ elif 'fields' in e .error_dict :
837+ # type_ignore_reason: incomplete typing
838+ pgerror = e .error_dict ['fields' ][0 ] # type: ignore
839+ key = re .search (KEY_ERROR_MATCH , str (pgerror ))
840+ if key :
841+ key = key .group (1 )
842+ else :
843+ key = _ ('unknown' )
844+ raise RecombinantFieldError (key )
755845 else :
756846 # type_ignore_reason: incomplete typing
757847 pgerror = e .error_dict ['records' ][0 ] # type: ignore
0 commit comments