pumpwood_communication.microservice_abc.simple
Facilitate communication with Pumpowood backend.
This packages facilitates the communication with end-points with Pumpwood pattern and helps with authentication.
Source-code at Github:
https://github.com/Murabei-OpenSource-Codes/pumpwood-communication
1""" 2Facilitate communication with Pumpowood backend. 3 4This packages facilitates the communication with end-points with Pumpwood 5pattern and helps with authentication. 6 7Source-code at Github:<br> 8https://github.com/Murabei-OpenSource-Codes/pumpwood-communication 9""" 10 11__docformat__ = "google" 12 13 14from .batch import ABCSimpleBatchMicroservice 15from .retrieve import ABCSimpleRetriveMicroservice 16from .delete import ABCSimpleDeleteMicroservice 17from .save import ABCSimpleSaveMicroservice 18from .list import ABCSimpleListMicroservice 19from .dimensions import ABCSimpleDimensionMicroservice 20from .action import ABCSimpleActionMicroservice 21from .info import ABCSimpleInfoMicroservice 22 23 24__all__ = [ 25 ABCSimpleBatchMicroservice, ABCSimpleRetriveMicroservice, 26 ABCSimpleDeleteMicroservice, ABCSimpleSaveMicroservice, 27 ABCSimpleListMicroservice, ABCSimpleDimensionMicroservice, 28 ABCSimpleActionMicroservice, ABCSimpleInfoMicroservice]
21class ABCSimpleBatchMicroservice(ABC, PumpWoodMicroServiceBase): 22 """Abstract class for batch end-points.""" 23 24 @staticmethod 25 def _build_aggregate_url(model_class: str): 26 return "rest/%s/aggregate/" % (model_class.lower(),) 27 28 def aggregate(self, model_class: str, group_by: List[str] | str, agg: dict, 29 filter_dict: None | dict = None, exclude_dict: dict = None, 30 order_by: List[str] = None, auth_header: dict = None, 31 limit: int = None, as_dataframe: bool = False 32 ) -> pd.DataFrame: 33 """Save a list of objects with one request. 34 35 Avaiable aggregate functions: 36 - **sum:** Sum. 37 - **mean:** Mean. 38 - **count:** Count. 39 - **min:** Minimum. 40 - **max:** Maximum. 41 - **stddev_pop:** Population standard deviation. 42 - **stddev_samp:** Sample standard deviation. 43 - **var_pop:** Population variance. 44 - **var_samp:** Sample variance. 45 - **std:** Sample standard deviation. 46 - **var:** Sample variance. 47 48 Args: 49 model_class (str): 50 Model class of the end-point that will be aggregated. 51 group_by (List[str] | str): 52 List of the fields that will be used on aggregation as 53 group by. If a string is passed as argument, it will be 54 considered the sigle group_by column. 55 agg (dict): 56 A dictionary with dictionary items as `field` and `function` 57 specifing the field that will be aggregated using a function. 58 59 The dictinary keys will be used to return the results as 60 columns. 61 filter_dict (dict): 62 Filter that will be applied before the aggregation. 63 exclude_dict (dict): 64 Exclude clause that will be applied before the aggregation. 65 order_by (list): 66 Ordenation acording to grouping elements. It can be used 67 fields created as keys of agg dictinary. 68 auth_header (dict): 69 Authentication header used to impersonation of user. 70 limit (int): 71 Limit number of returned row at aggregation query. 72 as_dataframe (bool): 73 If result should be returned as a dataframe. The columns 74 will be set to match the group_by and agg arguments, this 75 way empty dataframe with respect the columns. 76 77 Returns: 78 Return a DataFrame with aggregation results. 79 80 Example: 81 ``` 82 microservice.aggregate( 83 model_class="ToLoadCalendar", 84 group_by=["calendar_id"], 85 agg={ 86 "n": {"field": "id", "function": "count"}, 87 "mean": {"field": "value", "function": "mean" 88 }}) 89 ``` 90 """ 91 filter_dict = {} if filter_dict is None else filter_dict 92 exclude_dict = {} if exclude_dict is None else exclude_dict 93 order_by = [] if order_by is None else order_by 94 95 # If group_by is a string, convert to a list with this string 96 group_by = [group_by] if isinstance(group_by, str) else group_by 97 if not isinstance(group_by, (list, tuple, set)): 98 error_msg = "Argument `group_by` must be list, tuple, set or str." 99 raise TypeError(error_msg) 100 if not isinstance(agg, (dict)): 101 error_msg = "Argument `agg` must be dict." 102 raise TypeError(error_msg) 103 104 url_str = self._build_aggregate_url(model_class=model_class) 105 data = { 106 'agg': agg, 'group_by': group_by, 'filter_dict': filter_dict, 107 'exclude_dict': exclude_dict, 'order_by': order_by, 108 'limit': limit} 109 return_data = self.request_post( 110 url=url_str, data=data, auth_header=auth_header) 111 if not as_dataframe: 112 return return_data 113 else: 114 # Return the results as a dataframe using the group_by columns 115 # and the columns created at aggregation to ensure that 116 # even empty results would have the correct columns 117 return_columns = group_by + list(agg.keys()) 118 return pd.DataFrame(return_data, columns=return_columns) 119 120 @staticmethod 121 def _build_pivot_url(model_class): 122 return "rest/%s/pivot/" % (model_class.lower(), ) 123 124 def pivot(self, model_class: str, columns: None | List[str] = None, 125 format: str = 'list', filter_dict: dict = None, 126 exclude_dict: dict = None, order_by: List[str] = None, 127 variables: List[str] = None, fields: List[str] = None, 128 show_deleted: bool = False, 129 add_pk_column: bool = False, auth_header: dict = None, 130 as_dataframe: bool = False 131 ) -> Union[List[dict], Dict[str, list], pd.DataFrame]: 132 """Pivot object data acording to columns specified. 133 134 Pivoting per-se is not usually used, beeing the name of the function 135 a legacy. Normality data transformation is done at the client level. 136 137 Args: 138 model_class (str): 139 Model class to check search parameters. 140 columns (List[str]): 141 List of fields to be used as columns when pivoting the data. 142 format (str): 143 Format to be used to convert pandas.DataFrame to 144 dictionary, must be in ['dict','list','series', 145 'split', 'records','index']. 146 filter_dict (dict): 147 Same as list function. 148 exclude_dict (dict): 149 Same as list function. 150 order_by (List[str]): 151 Same as list function. 152 fields (List[str]): 153 List of the fields to be returned, if None, the default 154 variables will be returned. Same as fields on list functions. 155 variables (List[str]): 156 List of the fields to be returned, if None, the default 157 variables will be returned. Same as fields on list functions. 158 **DEPRECTED** use fields. 159 show_deleted (bool): 160 Fields with deleted column will have objects with deleted=True 161 omited from results. show_deleted=True will return this 162 information. 163 add_pk_column (bool): 164 If add pk values of the objects at pivot results. Adding 165 pk key on pivot end-points won't be possible to pivot since 166 pk is unique for each entry. 167 auth_header (dict): 168 Auth header to substitute the microservice original 169 at the request (user impersonation). 170 as_dataframe (bool): 171 If results should be returned as a dataframe. 172 173 Returns: 174 Return a list or a dictinary depending on the format set on 175 format parameter. 176 177 Raises: 178 PumpWoodException: 179 'Columns must be a list of elements.'. Indicates that the list 180 argument was not a list. 181 PumpWoodException: 182 'Column chosen as pivot is not at model variables'. Indicates 183 that columns that were set to pivot are not present on model 184 variables. 185 PumpWoodException: 186 "Format must be in ['dict','list','series','split', 187 'records','index']". Indicates that format set as paramenter 188 is not implemented. 189 PumpWoodException: 190 "Can not add pk column and pivot information". If 191 add_pk_column is True (results will have the pk column), it is 192 not possible to pivot the information (pk is an unique value 193 for each object, there is no reason to pivot it). 194 PumpWoodException: 195 "'value' column not at melted data, it is not possible 196 to pivot dataframe.". Indicates that data does not have a value 197 column, it must have it to populate pivoted table. 198 """ 199 # Deprect variables argument 200 if variables is not None: 201 msg = ( 202 "Deprecation Warning: pivot variable is deprected, " 203 "you should use fields instead") 204 logger.warning(msg) 205 fields = fields if fields is not None else variables 206 207 filter_dict = {} if filter_dict is None else filter_dict 208 exclude_dict = {} if exclude_dict is None else exclude_dict 209 order_by = [] if order_by is None else order_by 210 columns = [] if columns is None else columns 211 212 url_str = self._build_pivot_url(model_class) 213 post_data = { 214 'columns': columns, 'format': format, 215 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 216 'order_by': order_by, "variables": variables, 217 "show_deleted": show_deleted, "add_pk_column": add_pk_column} 218 pivot_results = self.request_post( 219 url=url_str, data=post_data, auth_header=auth_header) 220 221 if not add_pk_column: 222 if as_dataframe: 223 # If passed, set the fields as columns to ensure empty 224 # dataframes to have the correct fields 225 return pd.DataFrame(pivot_results, columns=fields) 226 else: 227 return pivot_results 228 else: 229 # If passed, set the fields as columns to ensure empty 230 # dataframes to have the correct fields 231 pd_pivot_results = pd.DataFrame(pivot_results, columns=fields) 232 if len(pd_pivot_results) != 0: 233 fill_options = self.fill_options( 234 model_class=model_class, auth_header=auth_header) 235 primary_keys_data = fill_options["pk"] 236 # Allow legacy pk definition and actual that uses extra_info 237 primary_keys = primary_keys_data\ 238 .get('extra_info', {})\ 239 .get('columns', primary_keys_data['column']) 240 pd_pivot_results["pk"] = pd_pivot_results[primary_keys].apply( 241 CompositePkBase64Converter.dump, 242 primary_keys=primary_keys, axis=1) 243 if as_dataframe: 244 return pd_pivot_results 245 else: 246 return pd_pivot_results.to_dict(format) 247 248 def _flat_list_by_chunks_helper(self, args): 249 try: 250 # Unpacking arguments 251 model_class = args["model_class"] 252 filter_dict = args["filter_dict"] 253 exclude_dict = args["exclude_dict"] 254 fields = args["fields"] 255 show_deleted = args["show_deleted"] 256 auth_header = args["auth_header"] 257 chunk_size = args["chunk_size"] 258 259 temp_filter_dict = copy.deepcopy(filter_dict) 260 url_str = self._build_pivot_url(model_class) 261 max_pk = 0 262 263 # Fetch data until an empty result is returned 264 list_dataframes = [] 265 while True: 266 sys.stdout.write(".") 267 sys.stdout.flush() 268 temp_filter_dict["id__gt"] = max_pk 269 post_data = { 270 'format': 'list', 271 'filter_dict': temp_filter_dict, 272 'exclude_dict': exclude_dict, 273 'order_by': ["id"], "variables": fields, 274 "show_deleted": show_deleted, 275 "limit": chunk_size, 276 "add_pk_column": True} 277 temp_dateframe = pd.DataFrame(self.request_post( 278 url=url_str, data=post_data, auth_header=auth_header)) 279 280 # Break if results are less than chunk size, so no more results 281 # are avaiable 282 if len(temp_dateframe) < chunk_size: 283 list_dataframes.append(temp_dateframe) 284 break 285 286 max_pk = int(temp_dateframe["id"].max()) 287 list_dataframes.append(temp_dateframe) 288 289 if len(list_dataframes) == 0: 290 return pd.DataFrame() 291 else: 292 return pd.concat(list_dataframes) 293 except Exception as e: 294 raise Exception("Exception at flat_list_by_chunks:", str(e)) 295 296 def flat_list_by_chunks(self, model_class: str, filter_dict: dict = None, 297 exclude_dict: dict = None, 298 fields: List[str] = None, 299 show_deleted: bool = False, 300 auth_header: dict = None, 301 chunk_size: int = 1000000, 302 n_parallel: int = None, 303 create_composite_pk: bool = False, 304 start_date: str = None, 305 end_date: str = None, 306 time_column: str = 'time') -> pd.DataFrame: 307 """Incrementally fetch data from pivot end-point. 308 309 Fetch data from pivot end-point paginating by id of chunk_size length. 310 311 If table is partitioned it will split the query acording to partition 312 to facilitate query at the database. 313 314 If start_date and end_date are set, also breaks the query by month 315 retrieving each month data in parallel. 316 317 Args: 318 model_class (str): 319 Model class to be pivoted. 320 filter_dict (dict): 321 Dictionary to to be used in objects.filter argument 322 (Same as list end-point). 323 exclude_dict (dict): 324 Dictionary to to be used in objects.exclude argument 325 (Same as list end-point). 326 fields (List[str] | None): 327 List of the variables to be returned, 328 if None, the default variables will be returned. 329 If fields is set, dataframe will return that columns 330 even if data is empty. 331 start_date (datetime | str): 332 Set a begin date for the query. If begin and end date are 333 set, query will be splited with chucks by month that will be 334 requested in parallel. 335 end_date (datetime | str): 336 Set a end date for the query. If begin and end date are 337 set, query will be splited with chucks by month that will be 338 requested in parallel. 339 show_deleted (bool): 340 If deleted data should be returned. 341 auth_header (dict): 342 Auth header to substitute the microservice original 343 at the request (user impersonation). 344 chunk_size (int): 345 Limit of data to fetch per call. 346 n_parallel (int): 347 Number of parallel process to perform. 348 create_composite_pk (bool): 349 If true and table has a composite pk, it will create pk 350 value based on the hash on the json serialized dictionary 351 of the components of the primary key. 352 time_column (str): 353 Column that will be considered on the partitioning of data 354 fetch. 355 356 Returns: 357 Returns a dataframe with all information fetched. 358 359 Raises: 360 No particular raise. 361 """ 362 # Set empty dictionary for dictionary arguments 363 filter_dict = {} if filter_dict is None else filter_dict 364 exclude_dict = {} if exclude_dict is None else exclude_dict 365 original_fields = None if fields is None else fields 366 367 if n_parallel is None: 368 n_parallel = PUMPWOOD_COMUNICATION__N_PARALLEL 369 370 fill_options = self.fill_options( 371 model_class=model_class, auth_header=auth_header) 372 373 # Retrieve PK infomration about primary_key and partitions 374 primary_keys = fill_options["pk"]["extra_info"]['columns'] 375 # partition = fill_options["pk"]["extra_info"]['partition'] 376 377 # Add all primary keys fields to query if create_composite_pk is 378 # set true 379 if create_composite_pk and fields is not None: 380 fields = list(set(fields) | set(primary_keys)) 381 382 # Create a list of month and include start and end dates if not at 383 # the beginning of a month 384 month_sequence = None 385 if (start_date is not None) and (end_date is not None): 386 month_sequence = AuxFlatListByChunks.build_month_partitions( 387 start_date=start_date, 388 end_date=end_date) 389 elif (start_date is not None) or (end_date is not None): 390 # To create the partitions is necessary to have both start and end 391 # date. 392 msg = ( 393 "To break query in chunks using start_date and end_date " 394 "both must be set.\n- start_date: {start_date}\n" 395 "- end_date: {end_date}") 396 raise PumpWoodException( 397 message=msg, payload={ 398 "start_date": start_date, 399 "end_date": end_date}) 400 401 pool_arguments = AuxFlatListByChunks.build_query_partitions( 402 model_class=model_class, time_partitions=month_sequence, 403 filter_dict=filter_dict, exclude_dict=exclude_dict, fields=fields, 404 time_column=time_column, show_deleted=show_deleted, 405 auth_header=auth_header, chunk_size=chunk_size) 406 407 # Perform parallel calls to backend each chucked by chunk_size 408 print("## Starting parallel flat list: %s" % len(pool_arguments)) 409 resp_df = None 410 try: 411 with Pool(n_parallel) as p: 412 results = p.map( 413 self._flat_list_by_chunks_helper, 414 pool_arguments) 415 if len(results) != 0: 416 resp_df = pd.concat(results)\ 417 .reset_index(drop=True) 418 else: 419 resp_df = pd.DataFrame(columns=fields) 420 except Exception as e: 421 PumpWoodException(message=str(e)) 422 print("\n## Finished parallel flat list: %s" % len(pool_arguments)) 423 424 # Add the primary key as a column to the dataframe 425 resp_df = AuxFlatListByChunks.add_pk_column( 426 create_composite_pk=create_composite_pk, 427 primary_key_list=primary_keys, 428 data=resp_df) 429 430 # Limit the return fields 431 if original_fields is not None: 432 if create_composite_pk: 433 return pd.DataFrame( 434 resp_df, columns=['pk'] + original_fields) 435 else: 436 return pd.DataFrame( 437 resp_df, columns=original_fields) 438 else: 439 return resp_df 440 441 def get_pks_from_unique_field(self, model_class: str, field: str, 442 values: List[Any]) -> pd.DataFrame: 443 """Get pk using unique fields values. 444 445 Use unique field values to retrieve pk of the objects. This end-point 446 is usefull for retrieving pks of the objects associated with unique 447 fields such as `description` (unique on most model of pumpwood). 448 449 ```python 450 # Using description to fetch pks from objects 451 data: pd.DataFrame = [data with unique description but without pk] 452 data['attribute_id'] = microservice.get_pks_from_unique_field( 453 model_class="DescriptionAttribute", 454 field="description", values=data['attribute'])['pk'] 455 456 # Using a dimension key to fetch pk of the objects, dimension 457 # key must be unique 458 data['georea_id'] = microservice.get_pks_from_unique_field( 459 model_class="DescriptionGeoarea", field="dimension->city", 460 values=data['city'])['pk'] 461 ``` 462 463 Args: 464 model_class: 465 Model class of the objects. 466 field: 467 Unique field to fetch pk. It is possible to use dimension keys 468 as unique field, for that use `dimension->[key]` notation. 469 values: 470 List of the unique fields used to fetch primary keys. 471 472 Return: 473 Return a dataframe in same order as values with columns: 474 - **pk**: Correspondent primary key of the unique value. 475 - **[field]**: Column with same name of field argument, 476 correspondent to pk. 477 478 Raises: 479 PumpWoodQueryException: 480 Raises if field is not found on the model and it is note 481 associated with a dimension tag. 482 PumpWoodQueryException: 483 Raises if `field` does not have a unique restriction on 484 database. Dimension keys does not check for uniqueness on 485 database, be carefull not to duplicate the lines. 486 """ 487 is_dimension_tag = 'dimensions->' in field 488 if not is_dimension_tag: 489 fill_options = self.fill_options(model_class=model_class) 490 field_details = fill_options.get(field) 491 if field_details is None: 492 msg = ( 493 "Field is not a dimension tag and not found on model " 494 "fields. Field [{field}]") 495 raise PumpWoodQueryException( 496 message=msg, payload={"field": field}) 497 498 is_unique_field = field_details.get("unique", False) 499 if not is_unique_field: 500 msg = "Field [{field}] to get pk from is not unique" 501 raise PumpWoodQueryException( 502 message=msg, payload={"field": field}) 503 504 filter_dict = {field + "__in": list(set(values))} 505 pk_map = None 506 if not is_dimension_tag: 507 list_results = pd.DataFrame(self.list_without_pag( 508 model_class=model_class, filter_dict=filter_dict, 509 fields=["pk", field]), columns=["pk", field]) 510 pk_map = list_results.set_index(field)["pk"] 511 512 # If is dimension tag, fetch dimension and unpack it 513 else: 514 dimension_tag = field.split("->")[1] 515 list_results = pd.DataFrame(self.list_without_pag( 516 model_class=model_class, filter_dict=filter_dict, 517 fields=["pk", "dimensions"])) 518 pk_map = {} 519 if len(list_results) != 0: 520 pk_map = list_results\ 521 .pipe(unpack_dict_columns, columns=["dimensions"])\ 522 .set_index(dimension_tag)["pk"] 523 524 values_series = pd.Series(values) 525 return pd.DataFrame({ 526 "pk": values_series.map(pk_map).to_numpy(), 527 field: values_series 528 })
Abstract class for batch end-points.
28 def aggregate(self, model_class: str, group_by: List[str] | str, agg: dict, 29 filter_dict: None | dict = None, exclude_dict: dict = None, 30 order_by: List[str] = None, auth_header: dict = None, 31 limit: int = None, as_dataframe: bool = False 32 ) -> pd.DataFrame: 33 """Save a list of objects with one request. 34 35 Avaiable aggregate functions: 36 - **sum:** Sum. 37 - **mean:** Mean. 38 - **count:** Count. 39 - **min:** Minimum. 40 - **max:** Maximum. 41 - **stddev_pop:** Population standard deviation. 42 - **stddev_samp:** Sample standard deviation. 43 - **var_pop:** Population variance. 44 - **var_samp:** Sample variance. 45 - **std:** Sample standard deviation. 46 - **var:** Sample variance. 47 48 Args: 49 model_class (str): 50 Model class of the end-point that will be aggregated. 51 group_by (List[str] | str): 52 List of the fields that will be used on aggregation as 53 group by. If a string is passed as argument, it will be 54 considered the sigle group_by column. 55 agg (dict): 56 A dictionary with dictionary items as `field` and `function` 57 specifing the field that will be aggregated using a function. 58 59 The dictinary keys will be used to return the results as 60 columns. 61 filter_dict (dict): 62 Filter that will be applied before the aggregation. 63 exclude_dict (dict): 64 Exclude clause that will be applied before the aggregation. 65 order_by (list): 66 Ordenation acording to grouping elements. It can be used 67 fields created as keys of agg dictinary. 68 auth_header (dict): 69 Authentication header used to impersonation of user. 70 limit (int): 71 Limit number of returned row at aggregation query. 72 as_dataframe (bool): 73 If result should be returned as a dataframe. The columns 74 will be set to match the group_by and agg arguments, this 75 way empty dataframe with respect the columns. 76 77 Returns: 78 Return a DataFrame with aggregation results. 79 80 Example: 81 ``` 82 microservice.aggregate( 83 model_class="ToLoadCalendar", 84 group_by=["calendar_id"], 85 agg={ 86 "n": {"field": "id", "function": "count"}, 87 "mean": {"field": "value", "function": "mean" 88 }}) 89 ``` 90 """ 91 filter_dict = {} if filter_dict is None else filter_dict 92 exclude_dict = {} if exclude_dict is None else exclude_dict 93 order_by = [] if order_by is None else order_by 94 95 # If group_by is a string, convert to a list with this string 96 group_by = [group_by] if isinstance(group_by, str) else group_by 97 if not isinstance(group_by, (list, tuple, set)): 98 error_msg = "Argument `group_by` must be list, tuple, set or str." 99 raise TypeError(error_msg) 100 if not isinstance(agg, (dict)): 101 error_msg = "Argument `agg` must be dict." 102 raise TypeError(error_msg) 103 104 url_str = self._build_aggregate_url(model_class=model_class) 105 data = { 106 'agg': agg, 'group_by': group_by, 'filter_dict': filter_dict, 107 'exclude_dict': exclude_dict, 'order_by': order_by, 108 'limit': limit} 109 return_data = self.request_post( 110 url=url_str, data=data, auth_header=auth_header) 111 if not as_dataframe: 112 return return_data 113 else: 114 # Return the results as a dataframe using the group_by columns 115 # and the columns created at aggregation to ensure that 116 # even empty results would have the correct columns 117 return_columns = group_by + list(agg.keys()) 118 return pd.DataFrame(return_data, columns=return_columns)
Save a list of objects with one request.
Avaiable aggregate functions:
- sum: Sum.
- mean: Mean.
- count: Count.
- min: Minimum.
- max: Maximum.
- stddev_pop: Population standard deviation.
- stddev_samp: Sample standard deviation.
- var_pop: Population variance.
- var_samp: Sample variance.
- std: Sample standard deviation.
- var: Sample variance.
Arguments:
- model_class (str): Model class of the end-point that will be aggregated.
- group_by (List[str] | str): List of the fields that will be used on aggregation as group by. If a string is passed as argument, it will be considered the sigle group_by column.
agg (dict): A dictionary with dictionary items as
fieldandfunctionspecifing the field that will be aggregated using a function.The dictinary keys will be used to return the results as columns.
- filter_dict (dict): Filter that will be applied before the aggregation.
- exclude_dict (dict): Exclude clause that will be applied before the aggregation.
- order_by (list): Ordenation acording to grouping elements. It can be used fields created as keys of agg dictinary.
- auth_header (dict): Authentication header used to impersonation of user.
- limit (int): Limit number of returned row at aggregation query.
- as_dataframe (bool): If result should be returned as a dataframe. The columns will be set to match the group_by and agg arguments, this way empty dataframe with respect the columns.
Returns:
Return a DataFrame with aggregation results.
Example:
microservice.aggregate( model_class="ToLoadCalendar", group_by=["calendar_id"], agg={ "n": {"field": "id", "function": "count"}, "mean": {"field": "value", "function": "mean" }})
124 def pivot(self, model_class: str, columns: None | List[str] = None, 125 format: str = 'list', filter_dict: dict = None, 126 exclude_dict: dict = None, order_by: List[str] = None, 127 variables: List[str] = None, fields: List[str] = None, 128 show_deleted: bool = False, 129 add_pk_column: bool = False, auth_header: dict = None, 130 as_dataframe: bool = False 131 ) -> Union[List[dict], Dict[str, list], pd.DataFrame]: 132 """Pivot object data acording to columns specified. 133 134 Pivoting per-se is not usually used, beeing the name of the function 135 a legacy. Normality data transformation is done at the client level. 136 137 Args: 138 model_class (str): 139 Model class to check search parameters. 140 columns (List[str]): 141 List of fields to be used as columns when pivoting the data. 142 format (str): 143 Format to be used to convert pandas.DataFrame to 144 dictionary, must be in ['dict','list','series', 145 'split', 'records','index']. 146 filter_dict (dict): 147 Same as list function. 148 exclude_dict (dict): 149 Same as list function. 150 order_by (List[str]): 151 Same as list function. 152 fields (List[str]): 153 List of the fields to be returned, if None, the default 154 variables will be returned. Same as fields on list functions. 155 variables (List[str]): 156 List of the fields to be returned, if None, the default 157 variables will be returned. Same as fields on list functions. 158 **DEPRECTED** use fields. 159 show_deleted (bool): 160 Fields with deleted column will have objects with deleted=True 161 omited from results. show_deleted=True will return this 162 information. 163 add_pk_column (bool): 164 If add pk values of the objects at pivot results. Adding 165 pk key on pivot end-points won't be possible to pivot since 166 pk is unique for each entry. 167 auth_header (dict): 168 Auth header to substitute the microservice original 169 at the request (user impersonation). 170 as_dataframe (bool): 171 If results should be returned as a dataframe. 172 173 Returns: 174 Return a list or a dictinary depending on the format set on 175 format parameter. 176 177 Raises: 178 PumpWoodException: 179 'Columns must be a list of elements.'. Indicates that the list 180 argument was not a list. 181 PumpWoodException: 182 'Column chosen as pivot is not at model variables'. Indicates 183 that columns that were set to pivot are not present on model 184 variables. 185 PumpWoodException: 186 "Format must be in ['dict','list','series','split', 187 'records','index']". Indicates that format set as paramenter 188 is not implemented. 189 PumpWoodException: 190 "Can not add pk column and pivot information". If 191 add_pk_column is True (results will have the pk column), it is 192 not possible to pivot the information (pk is an unique value 193 for each object, there is no reason to pivot it). 194 PumpWoodException: 195 "'value' column not at melted data, it is not possible 196 to pivot dataframe.". Indicates that data does not have a value 197 column, it must have it to populate pivoted table. 198 """ 199 # Deprect variables argument 200 if variables is not None: 201 msg = ( 202 "Deprecation Warning: pivot variable is deprected, " 203 "you should use fields instead") 204 logger.warning(msg) 205 fields = fields if fields is not None else variables 206 207 filter_dict = {} if filter_dict is None else filter_dict 208 exclude_dict = {} if exclude_dict is None else exclude_dict 209 order_by = [] if order_by is None else order_by 210 columns = [] if columns is None else columns 211 212 url_str = self._build_pivot_url(model_class) 213 post_data = { 214 'columns': columns, 'format': format, 215 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 216 'order_by': order_by, "variables": variables, 217 "show_deleted": show_deleted, "add_pk_column": add_pk_column} 218 pivot_results = self.request_post( 219 url=url_str, data=post_data, auth_header=auth_header) 220 221 if not add_pk_column: 222 if as_dataframe: 223 # If passed, set the fields as columns to ensure empty 224 # dataframes to have the correct fields 225 return pd.DataFrame(pivot_results, columns=fields) 226 else: 227 return pivot_results 228 else: 229 # If passed, set the fields as columns to ensure empty 230 # dataframes to have the correct fields 231 pd_pivot_results = pd.DataFrame(pivot_results, columns=fields) 232 if len(pd_pivot_results) != 0: 233 fill_options = self.fill_options( 234 model_class=model_class, auth_header=auth_header) 235 primary_keys_data = fill_options["pk"] 236 # Allow legacy pk definition and actual that uses extra_info 237 primary_keys = primary_keys_data\ 238 .get('extra_info', {})\ 239 .get('columns', primary_keys_data['column']) 240 pd_pivot_results["pk"] = pd_pivot_results[primary_keys].apply( 241 CompositePkBase64Converter.dump, 242 primary_keys=primary_keys, axis=1) 243 if as_dataframe: 244 return pd_pivot_results 245 else: 246 return pd_pivot_results.to_dict(format)
Pivot object data acording to columns specified.
Pivoting per-se is not usually used, beeing the name of the function a legacy. Normality data transformation is done at the client level.
Arguments:
- model_class (str): Model class to check search parameters.
- columns (List[str]): List of fields to be used as columns when pivoting the data.
- format (str): Format to be used to convert pandas.DataFrame to dictionary, must be in ['dict','list','series', 'split', 'records','index'].
- filter_dict (dict): Same as list function.
- exclude_dict (dict): Same as list function.
- order_by (List[str]): Same as list function.
- fields (List[str]): List of the fields to be returned, if None, the default variables will be returned. Same as fields on list functions.
- variables (List[str]): List of the fields to be returned, if None, the default variables will be returned. Same as fields on list functions. DEPRECTED use fields.
- show_deleted (bool): Fields with deleted column will have objects with deleted=True omited from results. show_deleted=True will return this information.
- add_pk_column (bool): If add pk values of the objects at pivot results. Adding pk key on pivot end-points won't be possible to pivot since pk is unique for each entry.
- auth_header (dict): Auth header to substitute the microservice original at the request (user impersonation).
- as_dataframe (bool): If results should be returned as a dataframe.
Returns:
Return a list or a dictinary depending on the format set on format parameter.
Raises:
- PumpWoodException: 'Columns must be a list of elements.'. Indicates that the list argument was not a list.
- PumpWoodException: 'Column chosen as pivot is not at model variables'. Indicates that columns that were set to pivot are not present on model variables.
- PumpWoodException: "Format must be in ['dict','list','series','split', 'records','index']". Indicates that format set as paramenter is not implemented.
- PumpWoodException: "Can not add pk column and pivot information". If add_pk_column is True (results will have the pk column), it is not possible to pivot the information (pk is an unique value for each object, there is no reason to pivot it).
- PumpWoodException: "'value' column not at melted data, it is not possible to pivot dataframe.". Indicates that data does not have a value column, it must have it to populate pivoted table.
296 def flat_list_by_chunks(self, model_class: str, filter_dict: dict = None, 297 exclude_dict: dict = None, 298 fields: List[str] = None, 299 show_deleted: bool = False, 300 auth_header: dict = None, 301 chunk_size: int = 1000000, 302 n_parallel: int = None, 303 create_composite_pk: bool = False, 304 start_date: str = None, 305 end_date: str = None, 306 time_column: str = 'time') -> pd.DataFrame: 307 """Incrementally fetch data from pivot end-point. 308 309 Fetch data from pivot end-point paginating by id of chunk_size length. 310 311 If table is partitioned it will split the query acording to partition 312 to facilitate query at the database. 313 314 If start_date and end_date are set, also breaks the query by month 315 retrieving each month data in parallel. 316 317 Args: 318 model_class (str): 319 Model class to be pivoted. 320 filter_dict (dict): 321 Dictionary to to be used in objects.filter argument 322 (Same as list end-point). 323 exclude_dict (dict): 324 Dictionary to to be used in objects.exclude argument 325 (Same as list end-point). 326 fields (List[str] | None): 327 List of the variables to be returned, 328 if None, the default variables will be returned. 329 If fields is set, dataframe will return that columns 330 even if data is empty. 331 start_date (datetime | str): 332 Set a begin date for the query. If begin and end date are 333 set, query will be splited with chucks by month that will be 334 requested in parallel. 335 end_date (datetime | str): 336 Set a end date for the query. If begin and end date are 337 set, query will be splited with chucks by month that will be 338 requested in parallel. 339 show_deleted (bool): 340 If deleted data should be returned. 341 auth_header (dict): 342 Auth header to substitute the microservice original 343 at the request (user impersonation). 344 chunk_size (int): 345 Limit of data to fetch per call. 346 n_parallel (int): 347 Number of parallel process to perform. 348 create_composite_pk (bool): 349 If true and table has a composite pk, it will create pk 350 value based on the hash on the json serialized dictionary 351 of the components of the primary key. 352 time_column (str): 353 Column that will be considered on the partitioning of data 354 fetch. 355 356 Returns: 357 Returns a dataframe with all information fetched. 358 359 Raises: 360 No particular raise. 361 """ 362 # Set empty dictionary for dictionary arguments 363 filter_dict = {} if filter_dict is None else filter_dict 364 exclude_dict = {} if exclude_dict is None else exclude_dict 365 original_fields = None if fields is None else fields 366 367 if n_parallel is None: 368 n_parallel = PUMPWOOD_COMUNICATION__N_PARALLEL 369 370 fill_options = self.fill_options( 371 model_class=model_class, auth_header=auth_header) 372 373 # Retrieve PK infomration about primary_key and partitions 374 primary_keys = fill_options["pk"]["extra_info"]['columns'] 375 # partition = fill_options["pk"]["extra_info"]['partition'] 376 377 # Add all primary keys fields to query if create_composite_pk is 378 # set true 379 if create_composite_pk and fields is not None: 380 fields = list(set(fields) | set(primary_keys)) 381 382 # Create a list of month and include start and end dates if not at 383 # the beginning of a month 384 month_sequence = None 385 if (start_date is not None) and (end_date is not None): 386 month_sequence = AuxFlatListByChunks.build_month_partitions( 387 start_date=start_date, 388 end_date=end_date) 389 elif (start_date is not None) or (end_date is not None): 390 # To create the partitions is necessary to have both start and end 391 # date. 392 msg = ( 393 "To break query in chunks using start_date and end_date " 394 "both must be set.\n- start_date: {start_date}\n" 395 "- end_date: {end_date}") 396 raise PumpWoodException( 397 message=msg, payload={ 398 "start_date": start_date, 399 "end_date": end_date}) 400 401 pool_arguments = AuxFlatListByChunks.build_query_partitions( 402 model_class=model_class, time_partitions=month_sequence, 403 filter_dict=filter_dict, exclude_dict=exclude_dict, fields=fields, 404 time_column=time_column, show_deleted=show_deleted, 405 auth_header=auth_header, chunk_size=chunk_size) 406 407 # Perform parallel calls to backend each chucked by chunk_size 408 print("## Starting parallel flat list: %s" % len(pool_arguments)) 409 resp_df = None 410 try: 411 with Pool(n_parallel) as p: 412 results = p.map( 413 self._flat_list_by_chunks_helper, 414 pool_arguments) 415 if len(results) != 0: 416 resp_df = pd.concat(results)\ 417 .reset_index(drop=True) 418 else: 419 resp_df = pd.DataFrame(columns=fields) 420 except Exception as e: 421 PumpWoodException(message=str(e)) 422 print("\n## Finished parallel flat list: %s" % len(pool_arguments)) 423 424 # Add the primary key as a column to the dataframe 425 resp_df = AuxFlatListByChunks.add_pk_column( 426 create_composite_pk=create_composite_pk, 427 primary_key_list=primary_keys, 428 data=resp_df) 429 430 # Limit the return fields 431 if original_fields is not None: 432 if create_composite_pk: 433 return pd.DataFrame( 434 resp_df, columns=['pk'] + original_fields) 435 else: 436 return pd.DataFrame( 437 resp_df, columns=original_fields) 438 else: 439 return resp_df
Incrementally fetch data from pivot end-point.
Fetch data from pivot end-point paginating by id of chunk_size length.
If table is partitioned it will split the query acording to partition to facilitate query at the database.
If start_date and end_date are set, also breaks the query by month retrieving each month data in parallel.
Arguments:
- model_class (str): Model class to be pivoted.
- filter_dict (dict): Dictionary to to be used in objects.filter argument (Same as list end-point).
- exclude_dict (dict): Dictionary to to be used in objects.exclude argument (Same as list end-point).
- fields (List[str] | None): List of the variables to be returned, if None, the default variables will be returned. If fields is set, dataframe will return that columns even if data is empty.
- start_date (datetime | str): Set a begin date for the query. If begin and end date are set, query will be splited with chucks by month that will be requested in parallel.
- end_date (datetime | str): Set a end date for the query. If begin and end date are set, query will be splited with chucks by month that will be requested in parallel.
- show_deleted (bool): If deleted data should be returned.
- auth_header (dict): Auth header to substitute the microservice original at the request (user impersonation).
- chunk_size (int): Limit of data to fetch per call.
- n_parallel (int): Number of parallel process to perform.
- create_composite_pk (bool): If true and table has a composite pk, it will create pk value based on the hash on the json serialized dictionary of the components of the primary key.
- time_column (str): Column that will be considered on the partitioning of data fetch.
Returns:
Returns a dataframe with all information fetched.
Raises:
- No particular raise.
441 def get_pks_from_unique_field(self, model_class: str, field: str, 442 values: List[Any]) -> pd.DataFrame: 443 """Get pk using unique fields values. 444 445 Use unique field values to retrieve pk of the objects. This end-point 446 is usefull for retrieving pks of the objects associated with unique 447 fields such as `description` (unique on most model of pumpwood). 448 449 ```python 450 # Using description to fetch pks from objects 451 data: pd.DataFrame = [data with unique description but without pk] 452 data['attribute_id'] = microservice.get_pks_from_unique_field( 453 model_class="DescriptionAttribute", 454 field="description", values=data['attribute'])['pk'] 455 456 # Using a dimension key to fetch pk of the objects, dimension 457 # key must be unique 458 data['georea_id'] = microservice.get_pks_from_unique_field( 459 model_class="DescriptionGeoarea", field="dimension->city", 460 values=data['city'])['pk'] 461 ``` 462 463 Args: 464 model_class: 465 Model class of the objects. 466 field: 467 Unique field to fetch pk. It is possible to use dimension keys 468 as unique field, for that use `dimension->[key]` notation. 469 values: 470 List of the unique fields used to fetch primary keys. 471 472 Return: 473 Return a dataframe in same order as values with columns: 474 - **pk**: Correspondent primary key of the unique value. 475 - **[field]**: Column with same name of field argument, 476 correspondent to pk. 477 478 Raises: 479 PumpWoodQueryException: 480 Raises if field is not found on the model and it is note 481 associated with a dimension tag. 482 PumpWoodQueryException: 483 Raises if `field` does not have a unique restriction on 484 database. Dimension keys does not check for uniqueness on 485 database, be carefull not to duplicate the lines. 486 """ 487 is_dimension_tag = 'dimensions->' in field 488 if not is_dimension_tag: 489 fill_options = self.fill_options(model_class=model_class) 490 field_details = fill_options.get(field) 491 if field_details is None: 492 msg = ( 493 "Field is not a dimension tag and not found on model " 494 "fields. Field [{field}]") 495 raise PumpWoodQueryException( 496 message=msg, payload={"field": field}) 497 498 is_unique_field = field_details.get("unique", False) 499 if not is_unique_field: 500 msg = "Field [{field}] to get pk from is not unique" 501 raise PumpWoodQueryException( 502 message=msg, payload={"field": field}) 503 504 filter_dict = {field + "__in": list(set(values))} 505 pk_map = None 506 if not is_dimension_tag: 507 list_results = pd.DataFrame(self.list_without_pag( 508 model_class=model_class, filter_dict=filter_dict, 509 fields=["pk", field]), columns=["pk", field]) 510 pk_map = list_results.set_index(field)["pk"] 511 512 # If is dimension tag, fetch dimension and unpack it 513 else: 514 dimension_tag = field.split("->")[1] 515 list_results = pd.DataFrame(self.list_without_pag( 516 model_class=model_class, filter_dict=filter_dict, 517 fields=["pk", "dimensions"])) 518 pk_map = {} 519 if len(list_results) != 0: 520 pk_map = list_results\ 521 .pipe(unpack_dict_columns, columns=["dimensions"])\ 522 .set_index(dimension_tag)["pk"] 523 524 values_series = pd.Series(values) 525 return pd.DataFrame({ 526 "pk": values_series.map(pk_map).to_numpy(), 527 field: values_series 528 })
Get pk using unique fields values.
Use unique field values to retrieve pk of the objects. This end-point
is usefull for retrieving pks of the objects associated with unique
fields such as description (unique on most model of pumpwood).
# Using description to fetch pks from objects
data: pd.DataFrame = [data with unique description but without pk]
data['attribute_id'] = microservice.get_pks_from_unique_field(
model_class="DescriptionAttribute",
field="description", values=data['attribute'])['pk']
# Using a dimension key to fetch pk of the objects, dimension
# key must be unique
data['georea_id'] = microservice.get_pks_from_unique_field(
model_class="DescriptionGeoarea", field="dimension->city",
values=data['city'])['pk']
Arguments:
- model_class: Model class of the objects.
- field: Unique field to fetch pk. It is possible to use dimension keys
as unique field, for that use
dimension->[key]notation. - values: List of the unique fields used to fetch primary keys.
Return:
Return a dataframe in same order as values with columns:
- pk: Correspondent primary key of the unique value.
- [field]: Column with same name of field argument, correspondent to pk.
Raises:
- PumpWoodQueryException: Raises if field is not found on the model and it is note associated with a dimension tag.
- PumpWoodQueryException: Raises if
fielddoes not have a unique restriction on database. Dimension keys does not check for uniqueness on database, be carefull not to duplicate the lines.
16class ABCSimpleRetriveMicroservice(ABC, PumpWoodMicroServiceBase): 17 """Abstract class for parallel calls at Pumpwood end-points.""" 18 19 @staticmethod 20 def _build_list_one_url(model_class, pk): 21 return "rest/%s/retrieve/%s/" % (model_class.lower(), pk) 22 23 def list_one(self, model_class: str, pk: int, fields: list = None, 24 default_fields: bool = True, foreign_key_fields: bool = False, 25 related_fields: bool = False, auth_header: dict = None, 26 use_disk_cache: bool = False, use_app_cache: bool = False, 27 disk_cache_expire: int = None, 28 base_filter_skip: list = None) -> dict: 29 """Retrieve an object using list serializer (simple). 30 31 **# DEPRECTED #** It is the same as retrieve using 32 `default_fields: bool = True`, if possible migrate to retrieve 33 function. 34 35 Args: 36 model_class: 37 Model class of the end-point 38 pk: 39 Object pk 40 auth_header: 41 Auth header to substitute the microservice original 42 at the request (user impersonation). 43 fields: 44 Set the fields to be returned by the list end-point. 45 default_fields: 46 Boolean, if true and fields arguments None will return the 47 default fields set for list by the backend. 48 foreign_key_fields: 49 Return forenging key objects. It will return the fk 50 corresponding object. Ex: `created_by_id` reference to 51 a user `model_class` the correspondent to User will be 52 returned at `created_by`. 53 related_fields: 54 Return related fields objects. Related field objects are 55 objects that have a forenging key associated with this 56 model_class, results will be returned as a list of 57 dictionaries usually in a field with `_set` at end. 58 Returning related_fields consume backend resorces, use 59 carefully. 60 use_disk_cache (bool): 61 If set true, get request will use local cache to reduce 62 the requests to the backend. 63 use_app_cache (bool): 64 If True, the GET request will use the cache of the application. 65 Defaults to False. 66 disk_cache_expire (int): 67 Time in seconds to expire the cache, it None it will 68 use de default set be PumpwoodCache. 69 base_filter_skip (list[str]): 70 List of base query filter to be skiped, it is necessary to 71 be superuser to skip base query filters. 72 73 Returns: 74 Return object with the correspondent pk. 75 76 Raises: 77 PumpWoodObjectDoesNotExist: 78 If pk not found on database. 79 """ 80 base_filter_skip = ( 81 [] if base_filter_skip is None else base_filter_skip) 82 url_str = self._build_list_one_url(model_class, pk) 83 return self.request_get( 84 url=url_str, parameters={ 85 "fields": fields, "default_fields": default_fields, 86 "foreign_key_fields": foreign_key_fields, 87 "related_fields": related_fields, 88 "base_filter_skip": base_filter_skip, 89 "use_cache": use_app_cache}, 90 auth_header=auth_header, use_disk_cache=use_disk_cache, 91 disk_cache_expire=disk_cache_expire) 92 93 @staticmethod 94 def _build_retrieve_url(model_class: str, pk: int): 95 return "rest/%s/retrieve/%s/" % (model_class.lower(), pk) 96 97 def retrieve(self, model_class: str, pk: int | str | dict, 98 default_fields: bool = False, 99 foreign_key_fields: bool = False, 100 related_fields: bool = False, 101 fields: list = None, 102 auth_header: dict = None, 103 use_disk_cache: bool = False, 104 use_app_cache: bool = False, 105 disk_cache_expire: int = None, 106 base_filter_skip: list = None) -> dict: 107 """Retrieve an object from PumpWood. 108 109 Function to get an object serialized by the retrieve endpoint (more 110 detailed data). It will fetch information for a single object 111 based on the primary key, which may be a simple ID, a composite key 112 passed as a dictionary, or a base64 URL-safe string. 113 114 It is also possible to retrieve single objects using unique fields, 115 such as codes or multiple column uniqueness constraints. This can 116 be done by passing the argument as a base64 string or a dictionary 117 containing the filtering clauses. 118 119 Example: 120 ```python 121 microservice.retrieve( 122 model_class="ModelClassWithUniqueCode", 123 pk={'code': 'unique code for object'}) 124 125 microservice.retrieve( 126 model_class="ModelClassWithCompositeUniqueConstraint", 127 pk={'time': '2026-01-01', 'attribute_id': 1}) 128 ``` 129 130 Args: 131 model_class (str): 132 Model class of the endpoint. 133 pk (Union[int, str, dict]): 134 The primary key or unique identifier for the object. 135 auth_header (dict, optional): 136 Authentication header to substitute the microservice's original 137 credentials (used for user impersonation). Defaults to None. 138 fields (list, optional): 139 Set of fields to be returned by the endpoint. 140 default_fields (bool): 141 If True and 'fields' is None, will return the default fields 142 defined by the backend. Defaults to False. 143 foreign_key_fields (bool): 144 If True, returns full objects for foreign keys instead of just 145 their IDs. For example, 'created_by_id' will also return the 146 user object at 'created_by'. Defaults to False. 147 related_fields (bool): 148 If True, returns related objects (those that have a foreign key 149 pointing to this model). Results are typically returned as a 150 list of dictionaries in a field with a '_set' suffix. 151 Warning: Using this may consume significant backend resources. 152 Defaults to False. 153 use_disk_cache (bool): 154 If True, the GET request will use a local disk cache to reduce 155 backend load. Defaults to False. 156 use_app_cache (bool): 157 If True, the GET request will use the cache of the application. 158 Defaults to False. 159 disk_cache_expire (int, optional): 160 TTL in seconds for the cache. If None, uses the default 161 PumpwoodCache settings. Defaults to None. 162 base_filter_skip (list, optional): 163 List of base query filters to skip. Requires superuser 164 privileges. Defaults to None. 165 166 Returns: 167 dict: The object matching the provided primary key/identifier. 168 169 Raises: 170 PumpWoodObjectDoesNotExist: 171 If the PK is not found in the database. 172 PumpWoodException: 173 For other errors during retrieval or communication. 174 """ 175 # Type checking and complex default values 176 is_allowed_types = isinstance( 177 pk, (numbers.Number, np.number, Decimal, str, dict)) 178 if not is_allowed_types: 179 msg = ( 180 "Retrieve pk must be a number, string or dict," 181 " got type [{type}]") 182 raise PumpWoodException( 183 msg, payload={"type": type(pk).__name__}) 184 base_filter_skip = ( 185 [] if base_filter_skip is None else base_filter_skip) 186 187 # Convert to base64 dict unique queries 188 serialized_pk = None 189 if isinstance(pk, dict): 190 # Use the correct keyword argument 'primary_key_dict' 191 serialized_pk = CompositePkBase64Converter.dump_dict( 192 primary_key_dict=pk) 193 else: 194 serialized_pk = pk 195 196 # Fetch information from Pumpwood 197 url_str = self._build_retrieve_url( 198 model_class=model_class, pk=serialized_pk) 199 return self.request_get( 200 url=url_str, parameters={ 201 "fields": fields, "default_fields": default_fields, 202 "foreign_key_fields": foreign_key_fields, 203 "related_fields": related_fields, 204 "base_filter_skip": base_filter_skip, 205 "use_cache": use_app_cache}, 206 auth_header=auth_header, use_disk_cache=use_disk_cache, 207 disk_cache_expire=disk_cache_expire) 208 209 @staticmethod 210 def _build_retrieve_file_url(model_class: str, pk: int): 211 return "rest/%s/retrieve-file/%s/" % (model_class.lower(), pk) 212 213 def retrieve_file(self, model_class: str, pk: int, file_field: str, 214 auth_header: dict = None, save_file: bool = True, 215 save_path: str = "./", file_name: str = None, 216 if_exists: str = "fail", 217 base_filter_skip: list = None) -> any: 218 """Retrieve a file from PumpWood. 219 220 This function will retrieve file as a single request, depending on the 221 size of the files it would be preferred to use streaming end-point. 222 223 Args: 224 model_class: 225 Class of the model to retrieve file. 226 pk: 227 Pk of the object associeted file. 228 file_field: 229 Field of the file to be downloaded. 230 auth_header: 231 Dictionary containing the auth header. 232 save_file: 233 If data is to be saved as file or return get 234 response. 235 save_path: 236 Path of the directory to save file. 237 file_name: 238 Name of the file, if None it will have same name as 239 saved in PumpWood. 240 if_exists: 241 Values must be in {'fail', 'change_name', 'overwrite', 'skip'}. 242 Set what to do if there is a file with same name. Skip 243 will not download file if there is already with same 244 os.path.join(save_path, file_name), file_name must be set 245 for skip argument. 246 auth_header: 247 Auth header to substitute the microservice original 248 at the request (user impersonation). 249 base_filter_skip (list): 250 List of base query filter to be skiped, it is necessary to 251 be superuser to skip base query filters. 252 253 Returns: 254 May return the file name if save_file=True; If false will return 255 a dictonary with keys `filename` with original file name and 256 `content` with binary data of file content. 257 258 Raises: 259 PumpWoodForbidden: 260 'storage_object attribute not set for view, file operations 261 are disable'. This indicates that storage for this backend 262 was not configured, so it is not possible to make storage 263 operations, 264 PumpWoodForbidden: 265 'file_field must be set on self.file_fields dictionary'. This 266 indicates that the `file_field` parameter is not listed as 267 a file field on the backend. 268 PumpWoodObjectDoesNotExist: 269 'field [{}] not found or null at object'. This indicates that 270 the file field requested is not present on object fields. 271 PumpWoodObjectDoesNotExist: 272 'Object not found in storage [{}]'. This indicates that the 273 file associated with file_field is not avaiable at the 274 storage. This should not ocorrur, it might have a manual 275 update at the model_class table or manual removal/rename of 276 files on storage. 277 """ 278 base_filter_skip = ( 279 [] if base_filter_skip is None else base_filter_skip) 280 281 if if_exists not in ["fail", "change_name", "overwrite", "skip"]: 282 raise PumpWoodException( 283 "if_exists must be in ['fail', 'change_name', 'overwrite', " 284 "'skip']") 285 286 if file_name is not None and if_exists == 'skip': 287 file_path = os.path.join(save_path, file_name) 288 is_file_already = os.path.isfile(file_path) 289 if is_file_already: 290 print("skiping file already exists: ", file_path) 291 return file_path 292 293 url_str = self._build_retrieve_file_url(model_class=model_class, pk=pk) 294 file_response = self.request_get( 295 url=url_str, parameters={ 296 "file-field": file_field, 297 "base_filter_skip": base_filter_skip}, 298 auth_header=auth_header) 299 if not save_file: 300 return file_response 301 302 if not os.path.exists(save_path): 303 raise PumpWoodException( 304 "Path to save retrieved file [{}] does not exist".format( 305 save_path)) 306 307 file_name = secure_filename(file_name or file_response["filename"]) 308 file_path = os.path.join(save_path, file_name) 309 is_file_already = os.path.isfile(file_path) 310 if is_file_already: 311 if if_exists == "change_name": 312 filename, file_extension = os.path.splitext(file_path) 313 too_many_tries = True 314 for i in range(10): 315 new_path = "{filename}__{count}{extension}".format( 316 filename=filename, count=i, 317 extension=file_extension) 318 if not os.path.isfile(new_path): 319 file_path = new_path 320 too_many_tries = False 321 break 322 if too_many_tries: 323 raise PumpWoodException( 324 ("Too many tries to find a not used file name." + 325 " file_path[{}]".format(file_path))) 326 327 elif if_exists == "fail": 328 raise PumpWoodException( 329 ("if_exists set as 'fail' and there is a file with same" + 330 "name. file_path [{}]").format(file_path)) 331 332 with open(file_path, "wb") as file: 333 file.write(file_response["content"]) 334 return file_path 335 336 @staticmethod 337 def _build_retrieve_file_straming_url(model_class: str, pk: int): 338 return "rest/%s/retrieve-file-streaming/%s/" % ( 339 model_class.lower(), pk) 340 341 def retrieve_streaming_file(self, model_class: str, pk: int, 342 file_field: str, file_name: str, 343 auth_header: dict = None, 344 save_path: str = "./", 345 if_exists: str = "fail", 346 base_filter_skip: list = None) -> str: 347 """Retrieve a file from PumpWood using streaming to retrieve content. 348 349 This funcion uses file streaming to retrieve file content, it should be 350 prefered when dealing with large (bigger than 10Mb) files transfer. 351 Using this end-point the file is not loaded on backend memory content 352 is transfered by chucks that are read at the storage and transfered 353 to user. 354 355 It will necessarily save the content as a file, there is not the 356 possibility of retrieving the content directly from request. 357 358 Args: 359 model_class: 360 Class of the model to retrieve file. 361 pk: 362 Pk of the object associeted file. 363 file_field: 364 Field of the file to be downloaded. 365 auth_header: 366 Dictionary containing the auth header. 367 save_path: 368 Path of the directory to save file. 369 file_name: 370 Name of the file, if None it will have same name as 371 saved in PumpWood. 372 if_exists: 373 Values must be in {'fail', 'change_name', 'overwrite'}. 374 Set what to do if there is a file with same name. 375 auth_header: 376 Auth header to substitute the microservice original 377 at the request (user impersonation). 378 base_filter_skip (list): 379 List of base query filter to be skiped, it is necessary to 380 be superuser to skip base query filters. 381 382 Returns: 383 Returns the file path that recived the file content. 384 385 Raises: 386 PumpWoodForbidden: 387 'storage_object attribute not set for view, file operations 388 are disable'. This indicates that storage for this backend 389 was not configured, so it is not possible to make storage 390 operations, 391 PumpWoodForbidden: 392 'file_field must be set on self.file_fields dictionary'. This 393 indicates that the `file_field` parameter is not listed as 394 a file field on the backend. 395 PumpWoodObjectDoesNotExist: 396 'field [{}] not found or null at object'. This indicates that 397 the file field requested is not present on object fields. 398 PumpWoodObjectDoesNotExist: 399 'Object not found in storage [{}]'. This indicates that the 400 file associated with file_field is not avaiable at the 401 storage. This should not ocorrur, it might have a manual 402 update at the model_class table or manual removal/rename of 403 files on storage. 404 """ 405 base_filter_skip = ( 406 [] if base_filter_skip is None else base_filter_skip) 407 408 request_header = self._check_auth_header(auth_header) 409 410 # begin Args check 411 if if_exists not in ["fail", "change_name", "overwrite"]: 412 raise PumpWoodException( 413 "if_exists must be in ['fail', 'change_name', 'overwrite']") 414 415 if not os.path.exists(save_path): 416 raise PumpWoodException( 417 "Path to save retrieved file [{}] does not exist".format( 418 save_path)) 419 # end Args check 420 421 file_path = os.path.join(save_path, file_name) 422 if os.path.isfile(file_path) and if_exists == "change_name": 423 filename, file_extension = os.path.splitext(file_path) 424 too_many_tries = False 425 for i in range(10): 426 new_path = "{filename}__{count}{extension}".format( 427 filename=filename, count=i, 428 extension=file_extension) 429 if not os.path.isfile(new_path): 430 file_path = new_path 431 too_many_tries = True 432 break 433 if not too_many_tries: 434 raise PumpWoodException( 435 ("Too many tries to find a not used file name." + 436 " file_path[{}]".format(file_path))) 437 438 if os.path.isfile(file_path) and if_exists == "fail": 439 raise PumpWoodException( 440 ("if_exists set as 'fail' and there is a file with same" + 441 "name. file_path [{}]").format(file_path)) 442 443 url_str = self._build_retrieve_file_straming_url( 444 model_class=model_class, pk=pk) 445 446 get_url = self.server_url + url_str 447 with requests.get( 448 get_url, verify=self._verify_ssl, headers=request_header, 449 params={ 450 "file-field": file_field, 451 "base_filter_skip": base_filter_skip}, 452 timeout=self._default_timeout) as response: 453 self.error_handler(response) 454 with open(file_path, 'wb') as f: 455 for chunk in response.iter_content(chunk_size=8192): 456 if chunk: 457 f.write(chunk) 458 return file_path
Abstract class for parallel calls at Pumpwood end-points.
23 def list_one(self, model_class: str, pk: int, fields: list = None, 24 default_fields: bool = True, foreign_key_fields: bool = False, 25 related_fields: bool = False, auth_header: dict = None, 26 use_disk_cache: bool = False, use_app_cache: bool = False, 27 disk_cache_expire: int = None, 28 base_filter_skip: list = None) -> dict: 29 """Retrieve an object using list serializer (simple). 30 31 **# DEPRECTED #** It is the same as retrieve using 32 `default_fields: bool = True`, if possible migrate to retrieve 33 function. 34 35 Args: 36 model_class: 37 Model class of the end-point 38 pk: 39 Object pk 40 auth_header: 41 Auth header to substitute the microservice original 42 at the request (user impersonation). 43 fields: 44 Set the fields to be returned by the list end-point. 45 default_fields: 46 Boolean, if true and fields arguments None will return the 47 default fields set for list by the backend. 48 foreign_key_fields: 49 Return forenging key objects. It will return the fk 50 corresponding object. Ex: `created_by_id` reference to 51 a user `model_class` the correspondent to User will be 52 returned at `created_by`. 53 related_fields: 54 Return related fields objects. Related field objects are 55 objects that have a forenging key associated with this 56 model_class, results will be returned as a list of 57 dictionaries usually in a field with `_set` at end. 58 Returning related_fields consume backend resorces, use 59 carefully. 60 use_disk_cache (bool): 61 If set true, get request will use local cache to reduce 62 the requests to the backend. 63 use_app_cache (bool): 64 If True, the GET request will use the cache of the application. 65 Defaults to False. 66 disk_cache_expire (int): 67 Time in seconds to expire the cache, it None it will 68 use de default set be PumpwoodCache. 69 base_filter_skip (list[str]): 70 List of base query filter to be skiped, it is necessary to 71 be superuser to skip base query filters. 72 73 Returns: 74 Return object with the correspondent pk. 75 76 Raises: 77 PumpWoodObjectDoesNotExist: 78 If pk not found on database. 79 """ 80 base_filter_skip = ( 81 [] if base_filter_skip is None else base_filter_skip) 82 url_str = self._build_list_one_url(model_class, pk) 83 return self.request_get( 84 url=url_str, parameters={ 85 "fields": fields, "default_fields": default_fields, 86 "foreign_key_fields": foreign_key_fields, 87 "related_fields": related_fields, 88 "base_filter_skip": base_filter_skip, 89 "use_cache": use_app_cache}, 90 auth_header=auth_header, use_disk_cache=use_disk_cache, 91 disk_cache_expire=disk_cache_expire)
Retrieve an object using list serializer (simple).
# DEPRECTED # It is the same as retrieve using
default_fields: bool = True, if possible migrate to retrieve
function.
Arguments:
- model_class: Model class of the end-point
- pk: Object pk
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- fields: Set the fields to be returned by the list end-point.
- default_fields: Boolean, if true and fields arguments None will return the default fields set for list by the backend.
- foreign_key_fields: Return forenging key objects. It will return the fk
corresponding object. Ex:
created_by_idreference to a usermodel_classthe correspondent to User will be returned atcreated_by. - related_fields: Return related fields objects. Related field objects are
objects that have a forenging key associated with this
model_class, results will be returned as a list of
dictionaries usually in a field with
_setat end. Returning related_fields consume backend resorces, use carefully. - use_disk_cache (bool): If set true, get request will use local cache to reduce the requests to the backend.
- use_app_cache (bool): If True, the GET request will use the cache of the application. Defaults to False.
- disk_cache_expire (int): Time in seconds to expire the cache, it None it will use de default set be PumpwoodCache.
- base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Return object with the correspondent pk.
Raises:
- PumpWoodObjectDoesNotExist: If pk not found on database.
97 def retrieve(self, model_class: str, pk: int | str | dict, 98 default_fields: bool = False, 99 foreign_key_fields: bool = False, 100 related_fields: bool = False, 101 fields: list = None, 102 auth_header: dict = None, 103 use_disk_cache: bool = False, 104 use_app_cache: bool = False, 105 disk_cache_expire: int = None, 106 base_filter_skip: list = None) -> dict: 107 """Retrieve an object from PumpWood. 108 109 Function to get an object serialized by the retrieve endpoint (more 110 detailed data). It will fetch information for a single object 111 based on the primary key, which may be a simple ID, a composite key 112 passed as a dictionary, or a base64 URL-safe string. 113 114 It is also possible to retrieve single objects using unique fields, 115 such as codes or multiple column uniqueness constraints. This can 116 be done by passing the argument as a base64 string or a dictionary 117 containing the filtering clauses. 118 119 Example: 120 ```python 121 microservice.retrieve( 122 model_class="ModelClassWithUniqueCode", 123 pk={'code': 'unique code for object'}) 124 125 microservice.retrieve( 126 model_class="ModelClassWithCompositeUniqueConstraint", 127 pk={'time': '2026-01-01', 'attribute_id': 1}) 128 ``` 129 130 Args: 131 model_class (str): 132 Model class of the endpoint. 133 pk (Union[int, str, dict]): 134 The primary key or unique identifier for the object. 135 auth_header (dict, optional): 136 Authentication header to substitute the microservice's original 137 credentials (used for user impersonation). Defaults to None. 138 fields (list, optional): 139 Set of fields to be returned by the endpoint. 140 default_fields (bool): 141 If True and 'fields' is None, will return the default fields 142 defined by the backend. Defaults to False. 143 foreign_key_fields (bool): 144 If True, returns full objects for foreign keys instead of just 145 their IDs. For example, 'created_by_id' will also return the 146 user object at 'created_by'. Defaults to False. 147 related_fields (bool): 148 If True, returns related objects (those that have a foreign key 149 pointing to this model). Results are typically returned as a 150 list of dictionaries in a field with a '_set' suffix. 151 Warning: Using this may consume significant backend resources. 152 Defaults to False. 153 use_disk_cache (bool): 154 If True, the GET request will use a local disk cache to reduce 155 backend load. Defaults to False. 156 use_app_cache (bool): 157 If True, the GET request will use the cache of the application. 158 Defaults to False. 159 disk_cache_expire (int, optional): 160 TTL in seconds for the cache. If None, uses the default 161 PumpwoodCache settings. Defaults to None. 162 base_filter_skip (list, optional): 163 List of base query filters to skip. Requires superuser 164 privileges. Defaults to None. 165 166 Returns: 167 dict: The object matching the provided primary key/identifier. 168 169 Raises: 170 PumpWoodObjectDoesNotExist: 171 If the PK is not found in the database. 172 PumpWoodException: 173 For other errors during retrieval or communication. 174 """ 175 # Type checking and complex default values 176 is_allowed_types = isinstance( 177 pk, (numbers.Number, np.number, Decimal, str, dict)) 178 if not is_allowed_types: 179 msg = ( 180 "Retrieve pk must be a number, string or dict," 181 " got type [{type}]") 182 raise PumpWoodException( 183 msg, payload={"type": type(pk).__name__}) 184 base_filter_skip = ( 185 [] if base_filter_skip is None else base_filter_skip) 186 187 # Convert to base64 dict unique queries 188 serialized_pk = None 189 if isinstance(pk, dict): 190 # Use the correct keyword argument 'primary_key_dict' 191 serialized_pk = CompositePkBase64Converter.dump_dict( 192 primary_key_dict=pk) 193 else: 194 serialized_pk = pk 195 196 # Fetch information from Pumpwood 197 url_str = self._build_retrieve_url( 198 model_class=model_class, pk=serialized_pk) 199 return self.request_get( 200 url=url_str, parameters={ 201 "fields": fields, "default_fields": default_fields, 202 "foreign_key_fields": foreign_key_fields, 203 "related_fields": related_fields, 204 "base_filter_skip": base_filter_skip, 205 "use_cache": use_app_cache}, 206 auth_header=auth_header, use_disk_cache=use_disk_cache, 207 disk_cache_expire=disk_cache_expire)
Retrieve an object from PumpWood.
Function to get an object serialized by the retrieve endpoint (more detailed data). It will fetch information for a single object based on the primary key, which may be a simple ID, a composite key passed as a dictionary, or a base64 URL-safe string.
It is also possible to retrieve single objects using unique fields, such as codes or multiple column uniqueness constraints. This can be done by passing the argument as a base64 string or a dictionary containing the filtering clauses.
Example:
microservice.retrieve( model_class="ModelClassWithUniqueCode", pk={'code': 'unique code for object'}) microservice.retrieve( model_class="ModelClassWithCompositeUniqueConstraint", pk={'time': '2026-01-01', 'attribute_id': 1})
Arguments:
- model_class (str): Model class of the endpoint.
- pk (Union[int, str, dict]): The primary key or unique identifier for the object.
- auth_header (dict, optional): Authentication header to substitute the microservice's original credentials (used for user impersonation). Defaults to None.
- fields (list, optional): Set of fields to be returned by the endpoint.
- default_fields (bool): If True and 'fields' is None, will return the default fields defined by the backend. Defaults to False.
- foreign_key_fields (bool): If True, returns full objects for foreign keys instead of just their IDs. For example, 'created_by_id' will also return the user object at 'created_by'. Defaults to False.
- related_fields (bool): If True, returns related objects (those that have a foreign key pointing to this model). Results are typically returned as a list of dictionaries in a field with a '_set' suffix. Warning: Using this may consume significant backend resources. Defaults to False.
- use_disk_cache (bool): If True, the GET request will use a local disk cache to reduce backend load. Defaults to False.
- use_app_cache (bool): If True, the GET request will use the cache of the application. Defaults to False.
- disk_cache_expire (int, optional): TTL in seconds for the cache. If None, uses the default PumpwoodCache settings. Defaults to None.
- base_filter_skip (list, optional): List of base query filters to skip. Requires superuser privileges. Defaults to None.
Returns:
dict: The object matching the provided primary key/identifier.
Raises:
- PumpWoodObjectDoesNotExist: If the PK is not found in the database.
- PumpWoodException: For other errors during retrieval or communication.
213 def retrieve_file(self, model_class: str, pk: int, file_field: str, 214 auth_header: dict = None, save_file: bool = True, 215 save_path: str = "./", file_name: str = None, 216 if_exists: str = "fail", 217 base_filter_skip: list = None) -> any: 218 """Retrieve a file from PumpWood. 219 220 This function will retrieve file as a single request, depending on the 221 size of the files it would be preferred to use streaming end-point. 222 223 Args: 224 model_class: 225 Class of the model to retrieve file. 226 pk: 227 Pk of the object associeted file. 228 file_field: 229 Field of the file to be downloaded. 230 auth_header: 231 Dictionary containing the auth header. 232 save_file: 233 If data is to be saved as file or return get 234 response. 235 save_path: 236 Path of the directory to save file. 237 file_name: 238 Name of the file, if None it will have same name as 239 saved in PumpWood. 240 if_exists: 241 Values must be in {'fail', 'change_name', 'overwrite', 'skip'}. 242 Set what to do if there is a file with same name. Skip 243 will not download file if there is already with same 244 os.path.join(save_path, file_name), file_name must be set 245 for skip argument. 246 auth_header: 247 Auth header to substitute the microservice original 248 at the request (user impersonation). 249 base_filter_skip (list): 250 List of base query filter to be skiped, it is necessary to 251 be superuser to skip base query filters. 252 253 Returns: 254 May return the file name if save_file=True; If false will return 255 a dictonary with keys `filename` with original file name and 256 `content` with binary data of file content. 257 258 Raises: 259 PumpWoodForbidden: 260 'storage_object attribute not set for view, file operations 261 are disable'. This indicates that storage for this backend 262 was not configured, so it is not possible to make storage 263 operations, 264 PumpWoodForbidden: 265 'file_field must be set on self.file_fields dictionary'. This 266 indicates that the `file_field` parameter is not listed as 267 a file field on the backend. 268 PumpWoodObjectDoesNotExist: 269 'field [{}] not found or null at object'. This indicates that 270 the file field requested is not present on object fields. 271 PumpWoodObjectDoesNotExist: 272 'Object not found in storage [{}]'. This indicates that the 273 file associated with file_field is not avaiable at the 274 storage. This should not ocorrur, it might have a manual 275 update at the model_class table or manual removal/rename of 276 files on storage. 277 """ 278 base_filter_skip = ( 279 [] if base_filter_skip is None else base_filter_skip) 280 281 if if_exists not in ["fail", "change_name", "overwrite", "skip"]: 282 raise PumpWoodException( 283 "if_exists must be in ['fail', 'change_name', 'overwrite', " 284 "'skip']") 285 286 if file_name is not None and if_exists == 'skip': 287 file_path = os.path.join(save_path, file_name) 288 is_file_already = os.path.isfile(file_path) 289 if is_file_already: 290 print("skiping file already exists: ", file_path) 291 return file_path 292 293 url_str = self._build_retrieve_file_url(model_class=model_class, pk=pk) 294 file_response = self.request_get( 295 url=url_str, parameters={ 296 "file-field": file_field, 297 "base_filter_skip": base_filter_skip}, 298 auth_header=auth_header) 299 if not save_file: 300 return file_response 301 302 if not os.path.exists(save_path): 303 raise PumpWoodException( 304 "Path to save retrieved file [{}] does not exist".format( 305 save_path)) 306 307 file_name = secure_filename(file_name or file_response["filename"]) 308 file_path = os.path.join(save_path, file_name) 309 is_file_already = os.path.isfile(file_path) 310 if is_file_already: 311 if if_exists == "change_name": 312 filename, file_extension = os.path.splitext(file_path) 313 too_many_tries = True 314 for i in range(10): 315 new_path = "{filename}__{count}{extension}".format( 316 filename=filename, count=i, 317 extension=file_extension) 318 if not os.path.isfile(new_path): 319 file_path = new_path 320 too_many_tries = False 321 break 322 if too_many_tries: 323 raise PumpWoodException( 324 ("Too many tries to find a not used file name." + 325 " file_path[{}]".format(file_path))) 326 327 elif if_exists == "fail": 328 raise PumpWoodException( 329 ("if_exists set as 'fail' and there is a file with same" + 330 "name. file_path [{}]").format(file_path)) 331 332 with open(file_path, "wb") as file: 333 file.write(file_response["content"]) 334 return file_path
Retrieve a file from PumpWood.
This function will retrieve file as a single request, depending on the size of the files it would be preferred to use streaming end-point.
Arguments:
- model_class: Class of the model to retrieve file.
- pk: Pk of the object associeted file.
- file_field: Field of the file to be downloaded.
- auth_header: Dictionary containing the auth header.
- save_file: If data is to be saved as file or return get response.
- save_path: Path of the directory to save file.
- file_name: Name of the file, if None it will have same name as saved in PumpWood.
- if_exists: Values must be in {'fail', 'change_name', 'overwrite', 'skip'}. Set what to do if there is a file with same name. Skip will not download file if there is already with same os.path.join(save_path, file_name), file_name must be set for skip argument.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
May return the file name if save_file=True; If false will return a dictonary with keys
filenamewith original file name andcontentwith binary data of file content.
Raises:
- PumpWoodForbidden: 'storage_object attribute not set for view, file operations are disable'. This indicates that storage for this backend was not configured, so it is not possible to make storage operations,
- PumpWoodForbidden: 'file_field must be set on self.file_fields dictionary'. This
indicates that the
file_fieldparameter is not listed as a file field on the backend. - PumpWoodObjectDoesNotExist: 'field [{}] not found or null at object'. This indicates that the file field requested is not present on object fields.
- PumpWoodObjectDoesNotExist: 'Object not found in storage [{}]'. This indicates that the file associated with file_field is not avaiable at the storage. This should not ocorrur, it might have a manual update at the model_class table or manual removal/rename of files on storage.
341 def retrieve_streaming_file(self, model_class: str, pk: int, 342 file_field: str, file_name: str, 343 auth_header: dict = None, 344 save_path: str = "./", 345 if_exists: str = "fail", 346 base_filter_skip: list = None) -> str: 347 """Retrieve a file from PumpWood using streaming to retrieve content. 348 349 This funcion uses file streaming to retrieve file content, it should be 350 prefered when dealing with large (bigger than 10Mb) files transfer. 351 Using this end-point the file is not loaded on backend memory content 352 is transfered by chucks that are read at the storage and transfered 353 to user. 354 355 It will necessarily save the content as a file, there is not the 356 possibility of retrieving the content directly from request. 357 358 Args: 359 model_class: 360 Class of the model to retrieve file. 361 pk: 362 Pk of the object associeted file. 363 file_field: 364 Field of the file to be downloaded. 365 auth_header: 366 Dictionary containing the auth header. 367 save_path: 368 Path of the directory to save file. 369 file_name: 370 Name of the file, if None it will have same name as 371 saved in PumpWood. 372 if_exists: 373 Values must be in {'fail', 'change_name', 'overwrite'}. 374 Set what to do if there is a file with same name. 375 auth_header: 376 Auth header to substitute the microservice original 377 at the request (user impersonation). 378 base_filter_skip (list): 379 List of base query filter to be skiped, it is necessary to 380 be superuser to skip base query filters. 381 382 Returns: 383 Returns the file path that recived the file content. 384 385 Raises: 386 PumpWoodForbidden: 387 'storage_object attribute not set for view, file operations 388 are disable'. This indicates that storage for this backend 389 was not configured, so it is not possible to make storage 390 operations, 391 PumpWoodForbidden: 392 'file_field must be set on self.file_fields dictionary'. This 393 indicates that the `file_field` parameter is not listed as 394 a file field on the backend. 395 PumpWoodObjectDoesNotExist: 396 'field [{}] not found or null at object'. This indicates that 397 the file field requested is not present on object fields. 398 PumpWoodObjectDoesNotExist: 399 'Object not found in storage [{}]'. This indicates that the 400 file associated with file_field is not avaiable at the 401 storage. This should not ocorrur, it might have a manual 402 update at the model_class table or manual removal/rename of 403 files on storage. 404 """ 405 base_filter_skip = ( 406 [] if base_filter_skip is None else base_filter_skip) 407 408 request_header = self._check_auth_header(auth_header) 409 410 # begin Args check 411 if if_exists not in ["fail", "change_name", "overwrite"]: 412 raise PumpWoodException( 413 "if_exists must be in ['fail', 'change_name', 'overwrite']") 414 415 if not os.path.exists(save_path): 416 raise PumpWoodException( 417 "Path to save retrieved file [{}] does not exist".format( 418 save_path)) 419 # end Args check 420 421 file_path = os.path.join(save_path, file_name) 422 if os.path.isfile(file_path) and if_exists == "change_name": 423 filename, file_extension = os.path.splitext(file_path) 424 too_many_tries = False 425 for i in range(10): 426 new_path = "{filename}__{count}{extension}".format( 427 filename=filename, count=i, 428 extension=file_extension) 429 if not os.path.isfile(new_path): 430 file_path = new_path 431 too_many_tries = True 432 break 433 if not too_many_tries: 434 raise PumpWoodException( 435 ("Too many tries to find a not used file name." + 436 " file_path[{}]".format(file_path))) 437 438 if os.path.isfile(file_path) and if_exists == "fail": 439 raise PumpWoodException( 440 ("if_exists set as 'fail' and there is a file with same" + 441 "name. file_path [{}]").format(file_path)) 442 443 url_str = self._build_retrieve_file_straming_url( 444 model_class=model_class, pk=pk) 445 446 get_url = self.server_url + url_str 447 with requests.get( 448 get_url, verify=self._verify_ssl, headers=request_header, 449 params={ 450 "file-field": file_field, 451 "base_filter_skip": base_filter_skip}, 452 timeout=self._default_timeout) as response: 453 self.error_handler(response) 454 with open(file_path, 'wb') as f: 455 for chunk in response.iter_content(chunk_size=8192): 456 if chunk: 457 f.write(chunk) 458 return file_path
Retrieve a file from PumpWood using streaming to retrieve content.
This funcion uses file streaming to retrieve file content, it should be prefered when dealing with large (bigger than 10Mb) files transfer. Using this end-point the file is not loaded on backend memory content is transfered by chucks that are read at the storage and transfered to user.
It will necessarily save the content as a file, there is not the possibility of retrieving the content directly from request.
Arguments:
- model_class: Class of the model to retrieve file.
- pk: Pk of the object associeted file.
- file_field: Field of the file to be downloaded.
- auth_header: Dictionary containing the auth header.
- save_path: Path of the directory to save file.
- file_name: Name of the file, if None it will have same name as saved in PumpWood.
- if_exists: Values must be in {'fail', 'change_name', 'overwrite'}. Set what to do if there is a file with same name.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Returns the file path that recived the file content.
Raises:
- PumpWoodForbidden: 'storage_object attribute not set for view, file operations are disable'. This indicates that storage for this backend was not configured, so it is not possible to make storage operations,
- PumpWoodForbidden: 'file_field must be set on self.file_fields dictionary'. This
indicates that the
file_fieldparameter is not listed as a file field on the backend. - PumpWoodObjectDoesNotExist: 'field [{}] not found or null at object'. This indicates that the file field requested is not present on object fields.
- PumpWoodObjectDoesNotExist: 'Object not found in storage [{}]'. This indicates that the file associated with file_field is not avaiable at the storage. This should not ocorrur, it might have a manual update at the model_class table or manual removal/rename of files on storage.
8class ABCSimpleDeleteMicroservice(ABC, PumpWoodMicroServiceBase): 9 """Abstract class for calls at Pumpwood delete end-points.""" 10 11 @staticmethod 12 def _build_delete_request_url(model_class, pk): 13 return "rest/%s/delete/%s/" % (model_class.lower(), pk) 14 15 def delete(self, model_class: str, pk: int, 16 auth_header: dict = None, 17 base_filter_skip: list[str] | None = None) -> dict: 18 """Send delete request to a PumpWood object. 19 20 Delete (or whatever the PumpWood system have been implemented) the 21 object with the specified pk. 22 23 Args: 24 model_class: 25 Model class to delete the object 26 pk: 27 Object pk to be deleted (or whatever the PumpWood system 28 have been implemented). Some model_class with 'deleted' field 29 does not remove the entry, it will flag deleted=True at this 30 cases. Model class with delete=True will be not retrieved 31 by default on `list` and `list_without_pag` end-points. 32 auth_header: 33 Auth header to substitute the microservice original 34 at the request (user impersonation). 35 base_filter_skip (list[str]): 36 List of base query filter to be skiped, it is necessary to 37 be superuser to skip base query filters. 38 39 Returns: 40 Returns delete object. 41 42 Raises: 43 PumpWoodObjectDoesNotExist: 44 'Requested object {model_class}[{pk}] not found.' This 45 indicates that the pk was not found in database. 46 """ 47 base_filter_skip = ( 48 [] if base_filter_skip is None else base_filter_skip) 49 url_str = self._build_delete_request_url(model_class, pk) 50 return self.request_delete( 51 url=url_str, auth_header=auth_header, 52 parameters={"base_filter_skip": base_filter_skip}) 53 54 @staticmethod 55 def _build_remove_file_field(model_class, pk): 56 return "rest/%s/remove-file-field/%s/" % (model_class.lower(), pk) 57 58 def delete_file(self, model_class: str, pk: int, file_field: str, 59 auth_header: dict = None, 60 base_filter_skip: list[str] | None = None) -> bool: 61 """Send delete request to a PumpWood object. 62 63 Delete (or whatever the PumpWood system have been implemented) the 64 object with the specified pk. 65 66 At previous versions this function was `remove_file_field`. An alias 67 is created for backward compatibility. 68 69 Args: 70 model_class: 71 Model class to delete the object 72 pk: 73 Object pk to be deleted (or whatever the PumpWood system 74 have been implemented). 75 file_field: 76 File field to be removed from storage. 77 auth_header: 78 Auth header to substitute the microservice original 79 at the request (user impersonation). 80 base_filter_skip (list[str]): 81 List of base query filter to be skiped, it is necessary to 82 be superuser to skip base query filters. 83 84 Returns: 85 Return True is file was successful removed 86 87 Raises: 88 PumpWoodForbidden: 89 'storage_object attribute not set for view, file operations 90 are disable'. This indicates that storage_object is not 91 associated with view, not allowing it to make storage 92 operations. 93 PumpWoodForbidden: 94 'file_field must be set on self.file_fields dictionary.'. 95 This indicates that the `file_field` was not set as a file 96 field on the backend. 97 PumpWoodObjectDoesNotExist: 98 'File does not exist. File field [{}] is set as None'. 99 This indicates that the object does not exists on storage, 100 it should not occur. It might have been some manual update 101 of the database or at the storage level. 102 """ 103 base_filter_skip = ( 104 [] if base_filter_skip is None else base_filter_skip) 105 url_str = self._build_remove_file_field(model_class, pk) 106 return self.request_delete( 107 url=url_str, auth_header=auth_header, 108 parameters={ 109 "file-field": file_field, 110 "base_filter_skip": base_filter_skip}) 111 112 # Create an alias for backward compatibility. 113 remove_file_field = delete_file 114 115 @staticmethod 116 def _build_delete_many_request_url(model_class): 117 return "rest/%s/delete/" % (model_class.lower(), ) 118 119 def delete_many(self, model_class: str, filter_dict: None | dict = None, 120 exclude_dict: None | dict = None, 121 auth_header: dict = None, 122 base_filter_skip: list[str] = None) -> bool: 123 """Remove many objects using query to retrict removal. 124 125 CAUTION It is not possible to undo this operation, model_class 126 this deleted field will be removed from database when using this 127 end-point, different from using delete end-point. 128 129 Args: 130 model_class: 131 Model class to delete the object 132 filter_dict: 133 Dictionary to make filter query. 134 exclude_dict: 135 Dictionary to make exclude query. 136 auth_header: 137 Auth header to substitute the microservice original 138 at the request (user impersonation). 139 base_filter_skip (list): 140 List of base query filter to be skiped, it is necessary to 141 be superuser to skip base query filters. 142 143 Returns: 144 True if delete is ok. 145 146 Raises: 147 PumpWoodObjectDeleteException: 148 Raises error if there is any error when commiting object 149 deletion on database. 150 """ 151 filter_dict = {} if filter_dict is None else filter_dict 152 exclude_dict = {} if exclude_dict is None else exclude_dict 153 154 url_str = self._build_delete_many_request_url(model_class) 155 return self.request_post( 156 url=url_str, 157 parameters={'base_filter_skip': base_filter_skip}, 158 data={'filter_dict': filter_dict, 'exclude_dict': exclude_dict}, 159 auth_header=auth_header)
Abstract class for calls at Pumpwood delete end-points.
15 def delete(self, model_class: str, pk: int, 16 auth_header: dict = None, 17 base_filter_skip: list[str] | None = None) -> dict: 18 """Send delete request to a PumpWood object. 19 20 Delete (or whatever the PumpWood system have been implemented) the 21 object with the specified pk. 22 23 Args: 24 model_class: 25 Model class to delete the object 26 pk: 27 Object pk to be deleted (or whatever the PumpWood system 28 have been implemented). Some model_class with 'deleted' field 29 does not remove the entry, it will flag deleted=True at this 30 cases. Model class with delete=True will be not retrieved 31 by default on `list` and `list_without_pag` end-points. 32 auth_header: 33 Auth header to substitute the microservice original 34 at the request (user impersonation). 35 base_filter_skip (list[str]): 36 List of base query filter to be skiped, it is necessary to 37 be superuser to skip base query filters. 38 39 Returns: 40 Returns delete object. 41 42 Raises: 43 PumpWoodObjectDoesNotExist: 44 'Requested object {model_class}[{pk}] not found.' This 45 indicates that the pk was not found in database. 46 """ 47 base_filter_skip = ( 48 [] if base_filter_skip is None else base_filter_skip) 49 url_str = self._build_delete_request_url(model_class, pk) 50 return self.request_delete( 51 url=url_str, auth_header=auth_header, 52 parameters={"base_filter_skip": base_filter_skip})
Send delete request to a PumpWood object.
Delete (or whatever the PumpWood system have been implemented) the object with the specified pk.
Arguments:
- model_class: Model class to delete the object
- pk: Object pk to be deleted (or whatever the PumpWood system
have been implemented). Some model_class with 'deleted' field
does not remove the entry, it will flag deleted=True at this
cases. Model class with delete=True will be not retrieved
by default on
listandlist_without_pagend-points. - auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Returns delete object.
Raises:
- PumpWoodObjectDoesNotExist: 'Requested object {model_class}[{pk}] not found.' This indicates that the pk was not found in database.
58 def delete_file(self, model_class: str, pk: int, file_field: str, 59 auth_header: dict = None, 60 base_filter_skip: list[str] | None = None) -> bool: 61 """Send delete request to a PumpWood object. 62 63 Delete (or whatever the PumpWood system have been implemented) the 64 object with the specified pk. 65 66 At previous versions this function was `remove_file_field`. An alias 67 is created for backward compatibility. 68 69 Args: 70 model_class: 71 Model class to delete the object 72 pk: 73 Object pk to be deleted (or whatever the PumpWood system 74 have been implemented). 75 file_field: 76 File field to be removed from storage. 77 auth_header: 78 Auth header to substitute the microservice original 79 at the request (user impersonation). 80 base_filter_skip (list[str]): 81 List of base query filter to be skiped, it is necessary to 82 be superuser to skip base query filters. 83 84 Returns: 85 Return True is file was successful removed 86 87 Raises: 88 PumpWoodForbidden: 89 'storage_object attribute not set for view, file operations 90 are disable'. This indicates that storage_object is not 91 associated with view, not allowing it to make storage 92 operations. 93 PumpWoodForbidden: 94 'file_field must be set on self.file_fields dictionary.'. 95 This indicates that the `file_field` was not set as a file 96 field on the backend. 97 PumpWoodObjectDoesNotExist: 98 'File does not exist. File field [{}] is set as None'. 99 This indicates that the object does not exists on storage, 100 it should not occur. It might have been some manual update 101 of the database or at the storage level. 102 """ 103 base_filter_skip = ( 104 [] if base_filter_skip is None else base_filter_skip) 105 url_str = self._build_remove_file_field(model_class, pk) 106 return self.request_delete( 107 url=url_str, auth_header=auth_header, 108 parameters={ 109 "file-field": file_field, 110 "base_filter_skip": base_filter_skip})
Send delete request to a PumpWood object.
Delete (or whatever the PumpWood system have been implemented) the object with the specified pk.
At previous versions this function was remove_file_field. An alias
is created for backward compatibility.
Arguments:
- model_class: Model class to delete the object
- pk: Object pk to be deleted (or whatever the PumpWood system have been implemented).
- file_field: File field to be removed from storage.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Return True is file was successful removed
Raises:
- PumpWoodForbidden: 'storage_object attribute not set for view, file operations are disable'. This indicates that storage_object is not associated with view, not allowing it to make storage operations.
- PumpWoodForbidden: 'file_field must be set on self.file_fields dictionary.'.
This indicates that the
file_fieldwas not set as a file field on the backend. - PumpWoodObjectDoesNotExist: 'File does not exist. File field [{}] is set as None'. This indicates that the object does not exists on storage, it should not occur. It might have been some manual update of the database or at the storage level.
58 def delete_file(self, model_class: str, pk: int, file_field: str, 59 auth_header: dict = None, 60 base_filter_skip: list[str] | None = None) -> bool: 61 """Send delete request to a PumpWood object. 62 63 Delete (or whatever the PumpWood system have been implemented) the 64 object with the specified pk. 65 66 At previous versions this function was `remove_file_field`. An alias 67 is created for backward compatibility. 68 69 Args: 70 model_class: 71 Model class to delete the object 72 pk: 73 Object pk to be deleted (or whatever the PumpWood system 74 have been implemented). 75 file_field: 76 File field to be removed from storage. 77 auth_header: 78 Auth header to substitute the microservice original 79 at the request (user impersonation). 80 base_filter_skip (list[str]): 81 List of base query filter to be skiped, it is necessary to 82 be superuser to skip base query filters. 83 84 Returns: 85 Return True is file was successful removed 86 87 Raises: 88 PumpWoodForbidden: 89 'storage_object attribute not set for view, file operations 90 are disable'. This indicates that storage_object is not 91 associated with view, not allowing it to make storage 92 operations. 93 PumpWoodForbidden: 94 'file_field must be set on self.file_fields dictionary.'. 95 This indicates that the `file_field` was not set as a file 96 field on the backend. 97 PumpWoodObjectDoesNotExist: 98 'File does not exist. File field [{}] is set as None'. 99 This indicates that the object does not exists on storage, 100 it should not occur. It might have been some manual update 101 of the database or at the storage level. 102 """ 103 base_filter_skip = ( 104 [] if base_filter_skip is None else base_filter_skip) 105 url_str = self._build_remove_file_field(model_class, pk) 106 return self.request_delete( 107 url=url_str, auth_header=auth_header, 108 parameters={ 109 "file-field": file_field, 110 "base_filter_skip": base_filter_skip})
Send delete request to a PumpWood object.
Delete (or whatever the PumpWood system have been implemented) the object with the specified pk.
At previous versions this function was remove_file_field. An alias
is created for backward compatibility.
Arguments:
- model_class: Model class to delete the object
- pk: Object pk to be deleted (or whatever the PumpWood system have been implemented).
- file_field: File field to be removed from storage.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Return True is file was successful removed
Raises:
- PumpWoodForbidden: 'storage_object attribute not set for view, file operations are disable'. This indicates that storage_object is not associated with view, not allowing it to make storage operations.
- PumpWoodForbidden: 'file_field must be set on self.file_fields dictionary.'.
This indicates that the
file_fieldwas not set as a file field on the backend. - PumpWoodObjectDoesNotExist: 'File does not exist. File field [{}] is set as None'. This indicates that the object does not exists on storage, it should not occur. It might have been some manual update of the database or at the storage level.
119 def delete_many(self, model_class: str, filter_dict: None | dict = None, 120 exclude_dict: None | dict = None, 121 auth_header: dict = None, 122 base_filter_skip: list[str] = None) -> bool: 123 """Remove many objects using query to retrict removal. 124 125 CAUTION It is not possible to undo this operation, model_class 126 this deleted field will be removed from database when using this 127 end-point, different from using delete end-point. 128 129 Args: 130 model_class: 131 Model class to delete the object 132 filter_dict: 133 Dictionary to make filter query. 134 exclude_dict: 135 Dictionary to make exclude query. 136 auth_header: 137 Auth header to substitute the microservice original 138 at the request (user impersonation). 139 base_filter_skip (list): 140 List of base query filter to be skiped, it is necessary to 141 be superuser to skip base query filters. 142 143 Returns: 144 True if delete is ok. 145 146 Raises: 147 PumpWoodObjectDeleteException: 148 Raises error if there is any error when commiting object 149 deletion on database. 150 """ 151 filter_dict = {} if filter_dict is None else filter_dict 152 exclude_dict = {} if exclude_dict is None else exclude_dict 153 154 url_str = self._build_delete_many_request_url(model_class) 155 return self.request_post( 156 url=url_str, 157 parameters={'base_filter_skip': base_filter_skip}, 158 data={'filter_dict': filter_dict, 'exclude_dict': exclude_dict}, 159 auth_header=auth_header)
Remove many objects using query to retrict removal.
CAUTION It is not possible to undo this operation, model_class this deleted field will be removed from database when using this end-point, different from using delete end-point.
Arguments:
- model_class: Model class to delete the object
- filter_dict: Dictionary to make filter query.
- exclude_dict: Dictionary to make exclude query.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
True if delete is ok.
Raises:
- PumpWoodObjectDeleteException: Raises error if there is any error when commiting object deletion on database.
14class ABCSimpleSaveMicroservice(ABC, PumpWoodMicroServiceBase): 15 """Abstract class for parallel calls at Pumpwood end-points.""" 16 17 @staticmethod 18 def _build_save_url(model_class): 19 return "rest/%s/save/" % (model_class.lower()) 20 21 def save(self, obj_dict: dict, files: dict = None, 22 auth_header: dict = None, fields: list = None, 23 default_fields: bool = False, foreign_key_fields: bool = False, 24 related_fields: bool = False, base_filter_skip: list = None, 25 upsert: bool = False) -> dict[str, Any]: 26 """Save or Update a new object. 27 28 Function to save or update a new model_class object. If obj_dict['pk'] 29 is None or not defined a new object will be created. The obj 30 model class is defided at obj_dict['model_class'] and if not defined an 31 PumpWoodObjectSavingException will be raised. 32 33 If files argument is set, request will be transfered using a multipart 34 request file files mapping file key to file field on backend. 35 36 Args: 37 obj_dict: 38 Model data dictionary. It must have 'model_class' 39 key and if 'pk' key is not defined a new object will 40 be created, else object with pk will be updated. 41 files: 42 A dictionary of files to be added to as a multi-part 43 post request. File must be passed as a file object with read 44 bytes. 45 auth_header: 46 Auth header to substitute the microservice original 47 at the request (user impersonation). 48 fields: 49 Set the fields to be returned by the list end-point. 50 default_fields: 51 Boolean, if true and fields arguments None will return the 52 default fields set for list by the backend. 53 foreign_key_fields: 54 Return forenging key objects. It will return the fk 55 corresponding object. Ex: `created_by_id` reference to 56 a user `model_class` the correspondent to User will be 57 returned at `created_by`. 58 related_fields: 59 Return related fields objects. Related field objects are 60 objects that have a forenging key associated with this 61 model_class, results will be returned as a list of 62 dictionaries usually in a field with `_set` at end. 63 Returning related_fields consume backend resorces, use 64 carefully. 65 base_filter_skip (list[str]): 66 List of base query filter to be skiped, it is necessary to 67 be superuser to skip base query filters. 68 upsert (bool): 69 Perform an upsert operation, if the object associated with the 70 pk is not found, it will be inserted on the database. 71 72 Returns: 73 Return updated/created object data. 74 75 Raises: 76 PumpWoodObjectSavingException: 77 'To save an object obj_dict must have model_class defined.' 78 This indicates that the obj_dict must have key `model_class` 79 indicating model class of the object that will be 80 updated/created. 81 PumpWoodObjectDoesNotExist: 82 'Requested object {model_class}[{pk}] not found.'. This 83 indicates that the pk passed on obj_dict was not found on 84 backend database. 85 PumpWoodIntegrityError: 86 Error raised when IntegrityError is raised on database. This 87 might ocorrur when saving objects that does not respect 88 uniqueness restriction on database or other IntegrityError 89 like removal of foreign keys with related data. 90 PumpWoodObjectSavingException: 91 Return error at object validation on de-serializing the 92 object or files with unexpected extensions. 93 """ 94 base_filter_skip = ( 95 [] if base_filter_skip is None else base_filter_skip) 96 model_class = obj_dict.get('model_class') 97 if model_class is None: 98 raise PumpWoodObjectSavingException( 99 'To save an object obj_dict must have model_class defined.') 100 101 url_str = self._build_save_url(model_class) 102 parameters = { 103 "fields": fields, "default_fields": default_fields, 104 "foreign_key_fields": foreign_key_fields, 105 "related_fields": related_fields, 106 "base_filter_skip": base_filter_skip, 107 "upsert": upsert} 108 return self.request_post( 109 url=url_str, data=obj_dict, parameters=parameters, files=files, 110 auth_header=auth_header) 111 112 @staticmethod 113 def _build_save_streaming_file_url(model_class, pk): 114 return "rest/{model_class}/save-file-streaming/{pk}/".format( 115 model_class=model_class.lower(), pk=pk) 116 117 def save_streaming_file(self, model_class: str, pk: int, file_field: str, 118 file: io.BufferedReader, file_name: str = None, 119 auth_header: dict = None, 120 fields: list = None, default_fields: bool = False, 121 foreign_key_fields: bool = False, 122 related_fields: bool = False, 123 base_filter_skip: list = None) -> str: 124 """Stream file to PumpWood. 125 126 Use streaming to transfer a file content to Pumpwood storage, this 127 end-point is prefered when transmiting files bigger than 10Mb. It 128 is necessary to have the object created before the file transfer. 129 130 Args: 131 model_class: 132 Model class of the object. 133 pk: 134 pk of the object. 135 file_field: 136 File field that will receive file stream. 137 file: 138 File to upload as a file object with read bytes option. 139 auth_header: 140 Auth header to substitute the microservice original 141 at the request (user impersonation). 142 file_name: 143 Name of the file, if not set it will be saved as 144 {pk}__{file_field}.{extension at permited extension} 145 fields: 146 Set the fields to be returned by the list end-point. 147 default_fields: 148 Boolean, if true and fields arguments None will return the 149 default fields set for list by the backend. 150 foreign_key_fields: 151 Return forenging key objects. It will return the fk 152 corresponding object. Ex: `created_by_id` reference to 153 a user `model_class` the correspondent to User will be 154 returned at `created_by`. 155 related_fields: 156 Return related fields objects. Related field objects are 157 objects that have a forenging key associated with this 158 model_class, results will be returned as a list of 159 dictionaries usually in a field with `_set` at end. 160 Returning related_fields consume backend resorces, use 161 carefully. 162 base_filter_skip (list[str]): 163 List of base query filter to be skiped, it is necessary to 164 be superuser to skip base query filters. 165 166 Returns: 167 Return the file name associated with data at the storage. 168 169 Raises: 170 PumpWoodForbidden: 171 'file_field must be set on self.file_fields dictionary'. This 172 indicates that the `file_field` passed is not associated 173 with a file field on the backend. 174 PumpWoodException: 175 'Saved bytes in streaming [{}] differ from file bytes [{}].'. 176 This indicates that there was an error when transfering data 177 to storage, the file bytes and transfered bytes does not 178 match. 179 """ 180 request_header = self._check_auth_header(auth_header=auth_header) 181 request_header["Content-Type"] = "application/octet-stream" 182 post_url = self.server_url + self._build_save_streaming_file_url( 183 model_class=model_class, pk=pk) 184 base_filter_skip = ( 185 [] if base_filter_skip is None else base_filter_skip) 186 187 parameters = { 188 "fields": fields, "default_fields": default_fields, 189 "foreign_key_fields": foreign_key_fields, 190 "related_fields": related_fields, "file_field": file_field, 191 "base_filter_skip": base_filter_skip} 192 if file_name is not None: 193 parameters["file_name"] = file_name 194 195 dumped_parameters = self._dump_query_parameters(parameters=parameters) 196 response = requests.post( 197 url=post_url, data=file, params=dumped_parameters, 198 verify=self._verify_ssl, headers=request_header, stream=True, 199 timeout=self._default_timeout) 200 201 file_last_bite = file.tell() 202 self.error_handler(response) 203 json_response = self.angular_json(response) 204 205 if file_last_bite != json_response["bytes_uploaded"]: 206 template = ( 207 "Saved bytes in streaming [{}] differ from file " + 208 "bites [{}].") 209 raise PumpWoodException( 210 template.format( 211 json_response["bytes_uploaded"], file_last_bite)) 212 return json_response["file_path"] 213 214 @staticmethod 215 def _build_bulk_save_url(model_class: str): 216 return "rest/%s/bulk-save/" % (model_class.lower(),) 217 218 def bulk_save(self, model_class: str, data_to_save: list | pd.DataFrame, 219 auth_header: dict = None, 220 base_filter_skip: list = None) -> dict: 221 """Save a list of objects with one request. 222 223 It is used with a unique call save many objects at the same time. It 224 is necessary that the end-point is able to receive bulk save requests 225 and all objects been of the same model class. 226 227 Args: 228 model_class: 229 Data model class. 230 data_to_save: 231 A list of objects to be saved. 232 base_filter_skip (list[str]): 233 List of base query filter to be skiped, it is necessary to 234 be superuser to skip base query filters. 235 auth_header: 236 Auth header to substitute the microservice original 237 at the request (user impersonation). 238 239 Returns: 240 A dictinary with `saved_count` as key indicating the number of 241 objects that were saved in database. 242 243 Raises: 244 PumpWoodException: 245 'Expected columns and data columns do not match: Expected 246 columns: {expected} Data columns: {data_cols}'. Indicates 247 that the expected fields of the object were not met at the 248 objects passed to save. 249 PumpWoodException: 250 Other sqlalchemy and psycopg2 errors not associated with 251 IntegrityError. 252 PumpWoodException: 253 'Bulk save not avaiable.'. Indicates that Bulk save end-point 254 was not configured for this model_class. 255 PumpWoodIntegrityError: 256 Raise integrity errors from sqlalchemy and psycopg2. Usually 257 associated with uniqueness of some column. 258 """ 259 if len(data_to_save) == 0: 260 return False 261 262 base_filter_skip = ( 263 [] if base_filter_skip is None else base_filter_skip) 264 url_str = self._build_bulk_save_url(model_class=model_class) 265 return self.request_post( 266 url=url_str, data=data_to_save, 267 parameters={"base_filter_skip": base_filter_skip}, 268 auth_header=auth_header)
Abstract class for parallel calls at Pumpwood end-points.
21 def save(self, obj_dict: dict, files: dict = None, 22 auth_header: dict = None, fields: list = None, 23 default_fields: bool = False, foreign_key_fields: bool = False, 24 related_fields: bool = False, base_filter_skip: list = None, 25 upsert: bool = False) -> dict[str, Any]: 26 """Save or Update a new object. 27 28 Function to save or update a new model_class object. If obj_dict['pk'] 29 is None or not defined a new object will be created. The obj 30 model class is defided at obj_dict['model_class'] and if not defined an 31 PumpWoodObjectSavingException will be raised. 32 33 If files argument is set, request will be transfered using a multipart 34 request file files mapping file key to file field on backend. 35 36 Args: 37 obj_dict: 38 Model data dictionary. It must have 'model_class' 39 key and if 'pk' key is not defined a new object will 40 be created, else object with pk will be updated. 41 files: 42 A dictionary of files to be added to as a multi-part 43 post request. File must be passed as a file object with read 44 bytes. 45 auth_header: 46 Auth header to substitute the microservice original 47 at the request (user impersonation). 48 fields: 49 Set the fields to be returned by the list end-point. 50 default_fields: 51 Boolean, if true and fields arguments None will return the 52 default fields set for list by the backend. 53 foreign_key_fields: 54 Return forenging key objects. It will return the fk 55 corresponding object. Ex: `created_by_id` reference to 56 a user `model_class` the correspondent to User will be 57 returned at `created_by`. 58 related_fields: 59 Return related fields objects. Related field objects are 60 objects that have a forenging key associated with this 61 model_class, results will be returned as a list of 62 dictionaries usually in a field with `_set` at end. 63 Returning related_fields consume backend resorces, use 64 carefully. 65 base_filter_skip (list[str]): 66 List of base query filter to be skiped, it is necessary to 67 be superuser to skip base query filters. 68 upsert (bool): 69 Perform an upsert operation, if the object associated with the 70 pk is not found, it will be inserted on the database. 71 72 Returns: 73 Return updated/created object data. 74 75 Raises: 76 PumpWoodObjectSavingException: 77 'To save an object obj_dict must have model_class defined.' 78 This indicates that the obj_dict must have key `model_class` 79 indicating model class of the object that will be 80 updated/created. 81 PumpWoodObjectDoesNotExist: 82 'Requested object {model_class}[{pk}] not found.'. This 83 indicates that the pk passed on obj_dict was not found on 84 backend database. 85 PumpWoodIntegrityError: 86 Error raised when IntegrityError is raised on database. This 87 might ocorrur when saving objects that does not respect 88 uniqueness restriction on database or other IntegrityError 89 like removal of foreign keys with related data. 90 PumpWoodObjectSavingException: 91 Return error at object validation on de-serializing the 92 object or files with unexpected extensions. 93 """ 94 base_filter_skip = ( 95 [] if base_filter_skip is None else base_filter_skip) 96 model_class = obj_dict.get('model_class') 97 if model_class is None: 98 raise PumpWoodObjectSavingException( 99 'To save an object obj_dict must have model_class defined.') 100 101 url_str = self._build_save_url(model_class) 102 parameters = { 103 "fields": fields, "default_fields": default_fields, 104 "foreign_key_fields": foreign_key_fields, 105 "related_fields": related_fields, 106 "base_filter_skip": base_filter_skip, 107 "upsert": upsert} 108 return self.request_post( 109 url=url_str, data=obj_dict, parameters=parameters, files=files, 110 auth_header=auth_header)
Save or Update a new object.
Function to save or update a new model_class object. If obj_dict['pk'] is None or not defined a new object will be created. The obj model class is defided at obj_dict['model_class'] and if not defined an PumpWoodObjectSavingException will be raised.
If files argument is set, request will be transfered using a multipart request file files mapping file key to file field on backend.
Arguments:
- obj_dict: Model data dictionary. It must have 'model_class' key and if 'pk' key is not defined a new object will be created, else object with pk will be updated.
- files: A dictionary of files to be added to as a multi-part post request. File must be passed as a file object with read bytes.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- fields: Set the fields to be returned by the list end-point.
- default_fields: Boolean, if true and fields arguments None will return the default fields set for list by the backend.
- foreign_key_fields: Return forenging key objects. It will return the fk
corresponding object. Ex:
created_by_idreference to a usermodel_classthe correspondent to User will be returned atcreated_by. - related_fields: Return related fields objects. Related field objects are
objects that have a forenging key associated with this
model_class, results will be returned as a list of
dictionaries usually in a field with
_setat end. Returning related_fields consume backend resorces, use carefully. - base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
- upsert (bool): Perform an upsert operation, if the object associated with the pk is not found, it will be inserted on the database.
Returns:
Return updated/created object data.
Raises:
- PumpWoodObjectSavingException: 'To save an object obj_dict must have model_class defined.'
This indicates that the obj_dict must have key
model_classindicating model class of the object that will be updated/created. - PumpWoodObjectDoesNotExist: 'Requested object {model_class}[{pk}] not found.'. This indicates that the pk passed on obj_dict was not found on backend database.
- PumpWoodIntegrityError: Error raised when IntegrityError is raised on database. This might ocorrur when saving objects that does not respect uniqueness restriction on database or other IntegrityError like removal of foreign keys with related data.
- PumpWoodObjectSavingException: Return error at object validation on de-serializing the object or files with unexpected extensions.
117 def save_streaming_file(self, model_class: str, pk: int, file_field: str, 118 file: io.BufferedReader, file_name: str = None, 119 auth_header: dict = None, 120 fields: list = None, default_fields: bool = False, 121 foreign_key_fields: bool = False, 122 related_fields: bool = False, 123 base_filter_skip: list = None) -> str: 124 """Stream file to PumpWood. 125 126 Use streaming to transfer a file content to Pumpwood storage, this 127 end-point is prefered when transmiting files bigger than 10Mb. It 128 is necessary to have the object created before the file transfer. 129 130 Args: 131 model_class: 132 Model class of the object. 133 pk: 134 pk of the object. 135 file_field: 136 File field that will receive file stream. 137 file: 138 File to upload as a file object with read bytes option. 139 auth_header: 140 Auth header to substitute the microservice original 141 at the request (user impersonation). 142 file_name: 143 Name of the file, if not set it will be saved as 144 {pk}__{file_field}.{extension at permited extension} 145 fields: 146 Set the fields to be returned by the list end-point. 147 default_fields: 148 Boolean, if true and fields arguments None will return the 149 default fields set for list by the backend. 150 foreign_key_fields: 151 Return forenging key objects. It will return the fk 152 corresponding object. Ex: `created_by_id` reference to 153 a user `model_class` the correspondent to User will be 154 returned at `created_by`. 155 related_fields: 156 Return related fields objects. Related field objects are 157 objects that have a forenging key associated with this 158 model_class, results will be returned as a list of 159 dictionaries usually in a field with `_set` at end. 160 Returning related_fields consume backend resorces, use 161 carefully. 162 base_filter_skip (list[str]): 163 List of base query filter to be skiped, it is necessary to 164 be superuser to skip base query filters. 165 166 Returns: 167 Return the file name associated with data at the storage. 168 169 Raises: 170 PumpWoodForbidden: 171 'file_field must be set on self.file_fields dictionary'. This 172 indicates that the `file_field` passed is not associated 173 with a file field on the backend. 174 PumpWoodException: 175 'Saved bytes in streaming [{}] differ from file bytes [{}].'. 176 This indicates that there was an error when transfering data 177 to storage, the file bytes and transfered bytes does not 178 match. 179 """ 180 request_header = self._check_auth_header(auth_header=auth_header) 181 request_header["Content-Type"] = "application/octet-stream" 182 post_url = self.server_url + self._build_save_streaming_file_url( 183 model_class=model_class, pk=pk) 184 base_filter_skip = ( 185 [] if base_filter_skip is None else base_filter_skip) 186 187 parameters = { 188 "fields": fields, "default_fields": default_fields, 189 "foreign_key_fields": foreign_key_fields, 190 "related_fields": related_fields, "file_field": file_field, 191 "base_filter_skip": base_filter_skip} 192 if file_name is not None: 193 parameters["file_name"] = file_name 194 195 dumped_parameters = self._dump_query_parameters(parameters=parameters) 196 response = requests.post( 197 url=post_url, data=file, params=dumped_parameters, 198 verify=self._verify_ssl, headers=request_header, stream=True, 199 timeout=self._default_timeout) 200 201 file_last_bite = file.tell() 202 self.error_handler(response) 203 json_response = self.angular_json(response) 204 205 if file_last_bite != json_response["bytes_uploaded"]: 206 template = ( 207 "Saved bytes in streaming [{}] differ from file " + 208 "bites [{}].") 209 raise PumpWoodException( 210 template.format( 211 json_response["bytes_uploaded"], file_last_bite)) 212 return json_response["file_path"]
Stream file to PumpWood.
Use streaming to transfer a file content to Pumpwood storage, this end-point is prefered when transmiting files bigger than 10Mb. It is necessary to have the object created before the file transfer.
Arguments:
- model_class: Model class of the object.
- pk: pk of the object.
- file_field: File field that will receive file stream.
- file: File to upload as a file object with read bytes option.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- file_name: Name of the file, if not set it will be saved as {pk}__{file_field}.{extension at permited extension}
- fields: Set the fields to be returned by the list end-point.
- default_fields: Boolean, if true and fields arguments None will return the default fields set for list by the backend.
- foreign_key_fields: Return forenging key objects. It will return the fk
corresponding object. Ex:
created_by_idreference to a usermodel_classthe correspondent to User will be returned atcreated_by. - related_fields: Return related fields objects. Related field objects are
objects that have a forenging key associated with this
model_class, results will be returned as a list of
dictionaries usually in a field with
_setat end. Returning related_fields consume backend resorces, use carefully. - base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Return the file name associated with data at the storage.
Raises:
- PumpWoodForbidden: 'file_field must be set on self.file_fields dictionary'. This
indicates that the
file_fieldpassed is not associated with a file field on the backend. - PumpWoodException: 'Saved bytes in streaming [{}] differ from file bytes [{}].'. This indicates that there was an error when transfering data to storage, the file bytes and transfered bytes does not match.
218 def bulk_save(self, model_class: str, data_to_save: list | pd.DataFrame, 219 auth_header: dict = None, 220 base_filter_skip: list = None) -> dict: 221 """Save a list of objects with one request. 222 223 It is used with a unique call save many objects at the same time. It 224 is necessary that the end-point is able to receive bulk save requests 225 and all objects been of the same model class. 226 227 Args: 228 model_class: 229 Data model class. 230 data_to_save: 231 A list of objects to be saved. 232 base_filter_skip (list[str]): 233 List of base query filter to be skiped, it is necessary to 234 be superuser to skip base query filters. 235 auth_header: 236 Auth header to substitute the microservice original 237 at the request (user impersonation). 238 239 Returns: 240 A dictinary with `saved_count` as key indicating the number of 241 objects that were saved in database. 242 243 Raises: 244 PumpWoodException: 245 'Expected columns and data columns do not match: Expected 246 columns: {expected} Data columns: {data_cols}'. Indicates 247 that the expected fields of the object were not met at the 248 objects passed to save. 249 PumpWoodException: 250 Other sqlalchemy and psycopg2 errors not associated with 251 IntegrityError. 252 PumpWoodException: 253 'Bulk save not avaiable.'. Indicates that Bulk save end-point 254 was not configured for this model_class. 255 PumpWoodIntegrityError: 256 Raise integrity errors from sqlalchemy and psycopg2. Usually 257 associated with uniqueness of some column. 258 """ 259 if len(data_to_save) == 0: 260 return False 261 262 base_filter_skip = ( 263 [] if base_filter_skip is None else base_filter_skip) 264 url_str = self._build_bulk_save_url(model_class=model_class) 265 return self.request_post( 266 url=url_str, data=data_to_save, 267 parameters={"base_filter_skip": base_filter_skip}, 268 auth_header=auth_header)
Save a list of objects with one request.
It is used with a unique call save many objects at the same time. It is necessary that the end-point is able to receive bulk save requests and all objects been of the same model class.
Arguments:
- model_class: Data model class.
- data_to_save: A list of objects to be saved.
- base_filter_skip (list[str]): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
Returns:
A dictinary with
saved_countas key indicating the number of objects that were saved in database.
Raises:
- PumpWoodException: 'Expected columns and data columns do not match: Expected columns: {expected} Data columns: {data_cols}'. Indicates that the expected fields of the object were not met at the objects passed to save.
- PumpWoodException: Other sqlalchemy and psycopg2 errors not associated with IntegrityError.
- PumpWoodException: 'Bulk save not avaiable.'. Indicates that Bulk save end-point was not configured for this model_class.
- PumpWoodIntegrityError: Raise integrity errors from sqlalchemy and psycopg2. Usually associated with uniqueness of some column.
15class ABCSimpleListMicroservice(ABC, PumpWoodMicroServiceBase): 16 """Abstract class for parallel calls at Pumpwood end-points.""" 17 18 @staticmethod 19 def _build_list_url(model_class: str): 20 return "rest/%s/list/" % (model_class.lower(),) 21 22 def list(self, model_class: str, filter_dict: dict = None, 23 exclude_dict: dict = None, order_by: list = None, 24 auth_header: dict = None, fields: list = None, 25 default_fields: bool = False, limit: int = None, 26 foreign_key_fields: bool = False, 27 base_filter_skip: list[str] = None, 28 as_dataframe: bool = False, 29 **kwargs) -> List[dict]: 30 """List objects with pagination. 31 32 List end-point (resumed data) of PumpWood like systems, 33 results will be paginated. To get next pag, send all recived pk at 34 exclude dict (ex.: `exclude_dict={pk__in: [1,2,...,30]}`). 35 36 It is possible to return foreign keys objects associated with 37 `model_class`. Use this with carefull since increase the backend 38 infrastructure consumption, each object is a retrieve call per 39 foreign key (otimization in progress). 40 41 It is possible to use diferent operators using `__` after the name 42 of the field, some of the operators avaiable: 43 44 ### General operators 45 - **__eq:** Check if the value is the same, same results if no 46 operator is passed. 47 - **__gt:** Check if value is greter then argument. 48 - **__lt:** Check if value is less then argument. 49 - **__gte:** Check if value is greter or equal then argument. 50 - **__lte:** Check if value is less or equal then argument. 51 - **__in:** Check if value is at a list, the argument of this operator 52 must be a list. 53 54 ### Text field operators 55 - **__contains:** Check if value contains a string. It is case and 56 accent sensitive. 57 - **__icontains:** Check if a values contains a string, It is case 58 insensitive and accent sensitive. 59 - **__unaccent_icontains:** Check if a values contains a string, It is 60 case insensitive and accent insensitive (consider a, à , á, ã, ... 61 the same). 62 - **__exact:** Same as __eq or not setting operator. 63 - **__iexact:** Same as __eq, but case insensitive and 64 accent sensitive. 65 - **__unaccent_iexact:** Same as __eq, but case insensitive and 66 accent insensitive. 67 - **__startswith:** Check if the value stats with a sub-string. 68 Case sensitive and accent sensitive. 69 - **__istartswith:** Check if the value stats with a sub-string. 70 Case insensitive and accent sensitive. 71 - **__unaccent_istartswith:** Check if the value stats with a 72 sub-string. Case insensitive and accent insensitive. 73 - **__endswith:** Check if the value ends with a sub-string. Case 74 sensitive and accent sensitive. 75 - **__iendswith:** Check if the value ends with a sub-string. Case 76 insensitive and accent sensitive. 77 - **__unaccent_iendswith:** Check if the value ends with a sub-string. 78 Case insensitive and accent insensitive. 79 80 ### Null operators 81 - **__isnull:** Check if field is null, it uses as argument a `boolean` 82 value false will return all non NULL values and true will return 83 NULL values. 84 85 ### Date and datetime operators: 86 - **__range:** Receive as argument a list of two elements and return 87 objects that field dates are between those values. 88 - **__year:** Return object that date field value year is equal to 89 argument. 90 - **__month:** Return object that date field value month is equal to 91 argument. 92 - **__day:** Return object that date field value day is equal to 93 argument. 94 95 ### Dictionary fields operators: 96 - **__json_contained_by:** 97 Uses the function [contained_by](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.contained_by) 98 from SQLAlchemy to test if keys are a proper subset of the keys of 99 the argument jsonb expression (extracted from SQLAlchemy). The 100 argument is a list. 101 - **__json_has_any:** 102 Uses the function [has_any](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_any) 103 from SQLAlchemy to test for presence of a key. Note that the key 104 may be a SQLA expression. (extracted from SQLAlchemy). The 105 argument is a list. 106 - **__json_has_key:** 107 Uses the function [has_key](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_key) 108 from SQLAlchemy to Test for presence of a key. Note that the key 109 may be a SQLA expression. The argument is a str. 110 111 ### Text similarity operators 112 To use similariry querys on Postgres it is necessary to `pg_trgm` be 113 instaled on server. Check [oficial documentation] 114 (https://www.postgresql.org/docs/current/pgtrgm.html). 115 116 - **__similarity:** Check if two strings are similar uses the `%` 117 operador. 118 - **__word_similar_left:** Check if two strings are similar uses the 119 `<%` operador. 120 - **__word_similar_right:** Check if two strings are similar uses the 121 `%>` operador. 122 - **__strict_word__similar_left:** Check if two strings are similar 123 uses the `<<%` operador. 124 - **__strict_word__similar_right:** Check if two strings are similar 125 uses the `%>>` operador. 126 127 Some usage examples: 128 ```python 129 # Return the first 3 results ordered decreasing acording to `time` and 130 # them ordered by `modeling_unit_id`. Results must have time greater 131 # or equal to 2017-01-01 and less or equal to 2017-06-01. It also 132 # must have attribute_id equal to 6 and not contains modeling_unit_id 133 # 3 or 4. 134 microservice.list( 135 model_class="DatabaseVariable", 136 filter_dict={ 137 "time__gte": "2017-01-01 00:00:00", 138 "time__lte": "2017-06-01 00:00:00", 139 "attribute_id": 6}, 140 exclude_dict={ 141 "modeling_unit_id__in": [3, 4]}, 142 order_by=["-time", "modeling_unit_id"], 143 limit=3, 144 fields=["pk", "model_class", "time", "modeling_unit_id", "value"]) 145 146 # Return all elements that dimensions field has a key type with 147 # value contains `selling` insensitive to case and accent. 148 microservice.list( 149 model_class="DatabaseAttribute", 150 filter_dict={ 151 "dimensions->type__unaccent_icontains": "selling"}) 152 ``` 153 154 Args: 155 model_class: 156 Model class of the end-point 157 filter_dict: 158 Filter dict to be used at the query. Filter elements from query 159 return that satifies all statements of the dictonary. 160 exclude_dict: 161 Exclude dict to be used at the query. Remove elements from 162 query return that satifies all statements of the dictonary. 163 order_by: Order results acording to list of strings 164 correspondent to fields. It is possible to use '-' at the 165 begginng of the field name for reverse ordering. Ex.: 166 ['description'] for accendent ordering and ['-description'] 167 for descendent ordering. 168 auth_header: 169 Auth header to substitute the microservice original 170 at the request (user impersonation). 171 fields (list): 172 Set the fields to be returned by the list end-point. 173 default_fields (bool): 174 Boolean, if true and fields arguments None will return the 175 default fields set for list by the backend. 176 limit (int): 177 Set the limit of elements of the returned query. By default, 178 backend usually return 50 elements. 179 foreign_key_fields (bool): 180 Return forenging key objects. It will return the fk 181 corresponding object. Ex: `created_by_id` reference to 182 a user `model_class` the correspondent to User will be 183 returned at `created_by`. 184 base_filter_skip (list): 185 List of base query filter to be skiped, it is necessary to 186 be superuser to skip base query filters. 187 as_dataframe (bool): 188 Return data as dataframe and set the columns acording to 189 fields if set. 190 **kwargs: 191 Other parameters for compatibility. 192 193 Returns: 194 Containing objects serialized by list Serializer. 195 196 Raises: 197 No especific raises. 198 """ # NOQA 199 filter_dict = {} if filter_dict is None else filter_dict 200 exclude_dict = {} if exclude_dict is None else exclude_dict 201 order_by = [] if order_by is None else order_by 202 base_filter_skip = ( 203 [] if base_filter_skip is None else base_filter_skip) 204 205 url_str = self._build_list_url(model_class) 206 post_data = { 207 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 208 'order_by': order_by, 'default_fields': default_fields, 209 'limit': limit, 'foreign_key_fields': foreign_key_fields} 210 if fields is not None: 211 post_data["fields"] = fields 212 return_data = self.request_post( 213 url=url_str, data=post_data, 214 parameters={'base_filter_skip': base_filter_skip}, 215 auth_header=auth_header) 216 if not as_dataframe: 217 return return_data 218 else: 219 # Return the results as a dataframe using the group_by columns 220 # and the columns created at aggregation to ensure that 221 # even empty results would have the correct columns 222 return pd.DataFrame(return_data, columns=fields) 223 224 def list_by_chunks(self, model_class: str, filter_dict: dict = None, 225 exclude_dict: dict = None, auth_header: dict = None, 226 fields: list = None, default_fields: bool = False, 227 chunk_size: int = 50000, limit: int = None, 228 base_filter_skip: list = None, 229 as_dataframe: bool = False, **kwargs 230 ) -> Union[List[dict], pd.DataFrame]: 231 """List objects by fetching them in chunks using PK to paginate. 232 233 Fetch data in chunks to handle large datasets without causing backend 234 timeouts or memory issues. Results are ordered by the 'id' column 235 to ensure consistent pagination. Note that custom ordering is not 236 supported in this method. 237 238 Args: 239 model_class (str): 240 Model class of the end-point. 241 filter_dict (dict): 242 Filter dictionary for the query. 243 exclude_dict (dict): 244 Exclude dictionary for the query. 245 auth_header (dict): 246 Authentication header for user impersonation. 247 fields (list): 248 List of fields to be returned. 249 default_fields (bool): 250 If True and fields is None, return default backend fields. 251 chunk_size (int): 252 Number of objects to fetch per query. Defaults to 50000. 253 base_filter_skip (list): 254 List of base query filters to skip (requires superuser). 255 limit (int): 256 Maximum number of records to return. 257 as_dataframe (bool): 258 If True, returns the results as a pandas DataFrame. 259 **kwargs: 260 Additional arguments for compatibility. 261 262 Returns: 263 Union[List[dict], pd.DataFrame]: 264 A list of dictionaries or a pandas DataFrame containing the 265 serialized objects. 266 267 Raises: 268 PumpWoodException: 269 If there is an error during the request or data processing. 270 """ 271 filter_dict = ( 272 {} if filter_dict is None else filter_dict) 273 exclude_dict = ( 274 {} if exclude_dict is None else exclude_dict) 275 base_filter_skip = ( 276 [] if base_filter_skip is None else base_filter_skip) 277 278 copy_filter_dict = copy.deepcopy(filter_dict) 279 list_all_results = [] 280 max_order_col = None 281 results_count = 0 282 info_msg = ( 283 "# Fetching chunk: results_count[{results_count}] | " 284 "min_id [{max_order_col}] | limit[{limit}]") 285 while True: 286 logger.info( 287 info_msg, results_count=results_count, 288 max_order_col=max_order_col, limit=limit) 289 290 # It is necessary to keep the initial query id__gt in case of 291 # this parameter being passed as argument. 292 if max_order_col is not None: 293 copy_filter_dict["id__gt"] = max_order_col 294 295 temp_results = self.list( 296 model_class=model_class, filter_dict=copy_filter_dict, 297 exclude_dict=exclude_dict, order_by=["id"], 298 auth_header=auth_header, fields=fields, 299 default_fields=default_fields, limit=chunk_size, 300 base_filter_skip=base_filter_skip) 301 results_count = results_count + len(temp_results) 302 303 # Break if results is empty 304 if len(temp_results) == 0: 305 break 306 307 # Extend the list of objects retrieved 308 list_all_results.extend(temp_results) 309 310 # If limit of objects is set, the counter will limit the number 311 # of objects fetched 312 if limit is not None: 313 records_to_fetch = limit - results_count 314 chunk_size = min(records_to_fetch, chunk_size) 315 # Do not request an empty list 316 if chunk_size == 0: 317 break 318 319 # Get the last object id. If pk is a string the pk is a base64 320 # object that will contain the id columns in it. 321 last_pk = temp_results[-1]["pk"] 322 if isinstance(last_pk, str): 323 loaded_pk_dict = CompositePkBase64Converter.load(last_pk) 324 max_order_col = loaded_pk_dict["id"] 325 else: 326 max_order_col = temp_results[-1]["pk"] 327 328 if as_dataframe: 329 # Return the results as a dataframe using the group_by columns 330 # and the columns created at aggregation to ensure that 331 # even empty results would have the correct columns 332 return pd.DataFrame(list_all_results, columns=fields) 333 return list_all_results 334 335 @staticmethod 336 def _build_list_without_pag_url(model_class: str): 337 return "rest/%s/list-without-pag/" % (model_class.lower(),) 338 339 def list_without_pag(self, model_class: str, filter_dict: dict = None, 340 exclude_dict: dict = None, order_by: list = None, 341 auth_header: dict = None, 342 convert_geometry: bool = True, 343 fields: list = None, 344 default_fields: bool = False, 345 foreign_key_fields: bool = False, 346 base_filter_skip: list = None, 347 as_dataframe: bool = False, 348 **kwargs 349 ) -> List[dict]: 350 """List object without pagination. 351 352 Function to post at list end-point (resumed data) of PumpWood like 353 systems, results won't be paginated. 354 **Be carefull with large returns.** 355 356 Args: 357 model_class (str): 358 Model class of the end-point 359 filter_dict (dict): 360 Filter dict to be used at the query. Filter elements from query 361 return that satifies all statements of the dictonary. 362 exclude_dict (dict): 363 Exclude dict to be used at the query. Remove elements from 364 query return that satifies all statements of the dictonary. 365 order_by (bool): 366 Order results acording to list of strings 367 correspondent to fields. It is possible to use '-' at the 368 begginng of the field name for reverse ordering. Ex.: 369 ['description'] for accendent ordering and ['-description'] 370 for descendent ordering. 371 auth_header (dict): 372 Auth header to substitute the microservice original 373 at the request (user impersonation). 374 fields (List[str]): 375 Set the fields to be returned by the list end-point. 376 default_fields (bool): 377 Boolean, if true and fields arguments None will return the 378 default fields set for list by the backend. 379 limit (int): 380 Set the limit of elements of the returned query. By default, 381 backend usually return 50 elements. 382 foreign_key_fields (bool): 383 Return forenging key objects. It will return the fk 384 corresponding object. Ex: `created_by_id` reference to 385 a user `model_class` the correspondent to User will be 386 returned at `created_by`. 387 convert_geometry (bool): 388 If geometry columns should be convert to shapely geometry. 389 Fields with key 'geometry' will be considered geometry. 390 base_filter_skip (list): 391 List of base query filter to be skiped, it is necessary to 392 be superuser to skip base query filters. 393 as_dataframe (bool): 394 Return data as dataframe and set the columns acording to 395 fields if set. 396 **kwargs: 397 Other unused arguments for compatibility. 398 399 Returns: 400 Containing objects serialized by list Serializer. 401 402 Raises: 403 No especific raises. 404 """ 405 filter_dict = {} if filter_dict is None else filter_dict 406 exclude_dict = {} if exclude_dict is None else exclude_dict 407 base_filter_skip = ( 408 [] if base_filter_skip is None else base_filter_skip) 409 base_filter_skip = [] if base_filter_skip is None else base_filter_skip 410 411 url_str = self._build_list_without_pag_url(model_class) 412 413 post_data = { 414 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 415 'order_by': order_by, 'default_fields': default_fields, 416 'foreign_key_fields': foreign_key_fields} 417 418 if fields is not None: 419 post_data["fields"] = fields 420 results = self.request_post( 421 url=url_str, data=post_data, 422 parameters={'base_filter_skip': base_filter_skip}, 423 auth_header=auth_header) 424 425 ################################################## 426 # Converting geometry to Shapely objects in Python 427 geometry_in_results = False 428 if convert_geometry: 429 for obj in results: 430 geometry_value = obj.get("geometry") 431 if geometry_value is not None: 432 obj["geometry"] = geometry.shape(geometry_value) 433 geometry_in_results = True 434 ################################################## 435 436 if not as_dataframe: 437 return results 438 else: 439 convert_to_geopandas = ( 440 (model_class.lower() == "descriptiongeoarea") and 441 geometry_in_results) 442 if convert_to_geopandas: 443 return geopd.GeoDataFrame( 444 results, geometry='geometry', columns=fields) 445 else: 446 return pd.DataFrame( 447 results, columns=fields)
Abstract class for parallel calls at Pumpwood end-points.
22 def list(self, model_class: str, filter_dict: dict = None, 23 exclude_dict: dict = None, order_by: list = None, 24 auth_header: dict = None, fields: list = None, 25 default_fields: bool = False, limit: int = None, 26 foreign_key_fields: bool = False, 27 base_filter_skip: list[str] = None, 28 as_dataframe: bool = False, 29 **kwargs) -> List[dict]: 30 """List objects with pagination. 31 32 List end-point (resumed data) of PumpWood like systems, 33 results will be paginated. To get next pag, send all recived pk at 34 exclude dict (ex.: `exclude_dict={pk__in: [1,2,...,30]}`). 35 36 It is possible to return foreign keys objects associated with 37 `model_class`. Use this with carefull since increase the backend 38 infrastructure consumption, each object is a retrieve call per 39 foreign key (otimization in progress). 40 41 It is possible to use diferent operators using `__` after the name 42 of the field, some of the operators avaiable: 43 44 ### General operators 45 - **__eq:** Check if the value is the same, same results if no 46 operator is passed. 47 - **__gt:** Check if value is greter then argument. 48 - **__lt:** Check if value is less then argument. 49 - **__gte:** Check if value is greter or equal then argument. 50 - **__lte:** Check if value is less or equal then argument. 51 - **__in:** Check if value is at a list, the argument of this operator 52 must be a list. 53 54 ### Text field operators 55 - **__contains:** Check if value contains a string. It is case and 56 accent sensitive. 57 - **__icontains:** Check if a values contains a string, It is case 58 insensitive and accent sensitive. 59 - **__unaccent_icontains:** Check if a values contains a string, It is 60 case insensitive and accent insensitive (consider a, à , á, ã, ... 61 the same). 62 - **__exact:** Same as __eq or not setting operator. 63 - **__iexact:** Same as __eq, but case insensitive and 64 accent sensitive. 65 - **__unaccent_iexact:** Same as __eq, but case insensitive and 66 accent insensitive. 67 - **__startswith:** Check if the value stats with a sub-string. 68 Case sensitive and accent sensitive. 69 - **__istartswith:** Check if the value stats with a sub-string. 70 Case insensitive and accent sensitive. 71 - **__unaccent_istartswith:** Check if the value stats with a 72 sub-string. Case insensitive and accent insensitive. 73 - **__endswith:** Check if the value ends with a sub-string. Case 74 sensitive and accent sensitive. 75 - **__iendswith:** Check if the value ends with a sub-string. Case 76 insensitive and accent sensitive. 77 - **__unaccent_iendswith:** Check if the value ends with a sub-string. 78 Case insensitive and accent insensitive. 79 80 ### Null operators 81 - **__isnull:** Check if field is null, it uses as argument a `boolean` 82 value false will return all non NULL values and true will return 83 NULL values. 84 85 ### Date and datetime operators: 86 - **__range:** Receive as argument a list of two elements and return 87 objects that field dates are between those values. 88 - **__year:** Return object that date field value year is equal to 89 argument. 90 - **__month:** Return object that date field value month is equal to 91 argument. 92 - **__day:** Return object that date field value day is equal to 93 argument. 94 95 ### Dictionary fields operators: 96 - **__json_contained_by:** 97 Uses the function [contained_by](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.contained_by) 98 from SQLAlchemy to test if keys are a proper subset of the keys of 99 the argument jsonb expression (extracted from SQLAlchemy). The 100 argument is a list. 101 - **__json_has_any:** 102 Uses the function [has_any](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_any) 103 from SQLAlchemy to test for presence of a key. Note that the key 104 may be a SQLA expression. (extracted from SQLAlchemy). The 105 argument is a list. 106 - **__json_has_key:** 107 Uses the function [has_key](https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_key) 108 from SQLAlchemy to Test for presence of a key. Note that the key 109 may be a SQLA expression. The argument is a str. 110 111 ### Text similarity operators 112 To use similariry querys on Postgres it is necessary to `pg_trgm` be 113 instaled on server. Check [oficial documentation] 114 (https://www.postgresql.org/docs/current/pgtrgm.html). 115 116 - **__similarity:** Check if two strings are similar uses the `%` 117 operador. 118 - **__word_similar_left:** Check if two strings are similar uses the 119 `<%` operador. 120 - **__word_similar_right:** Check if two strings are similar uses the 121 `%>` operador. 122 - **__strict_word__similar_left:** Check if two strings are similar 123 uses the `<<%` operador. 124 - **__strict_word__similar_right:** Check if two strings are similar 125 uses the `%>>` operador. 126 127 Some usage examples: 128 ```python 129 # Return the first 3 results ordered decreasing acording to `time` and 130 # them ordered by `modeling_unit_id`. Results must have time greater 131 # or equal to 2017-01-01 and less or equal to 2017-06-01. It also 132 # must have attribute_id equal to 6 and not contains modeling_unit_id 133 # 3 or 4. 134 microservice.list( 135 model_class="DatabaseVariable", 136 filter_dict={ 137 "time__gte": "2017-01-01 00:00:00", 138 "time__lte": "2017-06-01 00:00:00", 139 "attribute_id": 6}, 140 exclude_dict={ 141 "modeling_unit_id__in": [3, 4]}, 142 order_by=["-time", "modeling_unit_id"], 143 limit=3, 144 fields=["pk", "model_class", "time", "modeling_unit_id", "value"]) 145 146 # Return all elements that dimensions field has a key type with 147 # value contains `selling` insensitive to case and accent. 148 microservice.list( 149 model_class="DatabaseAttribute", 150 filter_dict={ 151 "dimensions->type__unaccent_icontains": "selling"}) 152 ``` 153 154 Args: 155 model_class: 156 Model class of the end-point 157 filter_dict: 158 Filter dict to be used at the query. Filter elements from query 159 return that satifies all statements of the dictonary. 160 exclude_dict: 161 Exclude dict to be used at the query. Remove elements from 162 query return that satifies all statements of the dictonary. 163 order_by: Order results acording to list of strings 164 correspondent to fields. It is possible to use '-' at the 165 begginng of the field name for reverse ordering. Ex.: 166 ['description'] for accendent ordering and ['-description'] 167 for descendent ordering. 168 auth_header: 169 Auth header to substitute the microservice original 170 at the request (user impersonation). 171 fields (list): 172 Set the fields to be returned by the list end-point. 173 default_fields (bool): 174 Boolean, if true and fields arguments None will return the 175 default fields set for list by the backend. 176 limit (int): 177 Set the limit of elements of the returned query. By default, 178 backend usually return 50 elements. 179 foreign_key_fields (bool): 180 Return forenging key objects. It will return the fk 181 corresponding object. Ex: `created_by_id` reference to 182 a user `model_class` the correspondent to User will be 183 returned at `created_by`. 184 base_filter_skip (list): 185 List of base query filter to be skiped, it is necessary to 186 be superuser to skip base query filters. 187 as_dataframe (bool): 188 Return data as dataframe and set the columns acording to 189 fields if set. 190 **kwargs: 191 Other parameters for compatibility. 192 193 Returns: 194 Containing objects serialized by list Serializer. 195 196 Raises: 197 No especific raises. 198 """ # NOQA 199 filter_dict = {} if filter_dict is None else filter_dict 200 exclude_dict = {} if exclude_dict is None else exclude_dict 201 order_by = [] if order_by is None else order_by 202 base_filter_skip = ( 203 [] if base_filter_skip is None else base_filter_skip) 204 205 url_str = self._build_list_url(model_class) 206 post_data = { 207 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 208 'order_by': order_by, 'default_fields': default_fields, 209 'limit': limit, 'foreign_key_fields': foreign_key_fields} 210 if fields is not None: 211 post_data["fields"] = fields 212 return_data = self.request_post( 213 url=url_str, data=post_data, 214 parameters={'base_filter_skip': base_filter_skip}, 215 auth_header=auth_header) 216 if not as_dataframe: 217 return return_data 218 else: 219 # Return the results as a dataframe using the group_by columns 220 # and the columns created at aggregation to ensure that 221 # even empty results would have the correct columns 222 return pd.DataFrame(return_data, columns=fields)
List objects with pagination.
List end-point (resumed data) of PumpWood like systems,
results will be paginated. To get next pag, send all recived pk at
exclude dict (ex.: exclude_dict={pk__in: [1,2,...,30]}).
It is possible to return foreign keys objects associated with
model_class. Use this with carefull since increase the backend
infrastructure consumption, each object is a retrieve call per
foreign key (otimization in progress).
It is possible to use diferent operators using __ after the name
of the field, some of the operators avaiable:
General operators
- __eq: Check if the value is the same, same results if no operator is passed.
- __gt: Check if value is greter then argument.
- __lt: Check if value is less then argument.
- __gte: Check if value is greter or equal then argument.
- __lte: Check if value is less or equal then argument.
- __in: Check if value is at a list, the argument of this operator must be a list.
Text field operators
- __contains: Check if value contains a string. It is case and accent sensitive.
- __icontains: Check if a values contains a string, It is case insensitive and accent sensitive.
- __unaccent_icontains: Check if a values contains a string, It is case insensitive and accent insensitive (consider a, à , á, ã, ... the same).
- __exact: Same as __eq or not setting operator.
- __iexact: Same as __eq, but case insensitive and accent sensitive.
- __unaccent_iexact: Same as __eq, but case insensitive and accent insensitive.
- __startswith: Check if the value stats with a sub-string. Case sensitive and accent sensitive.
- __istartswith: Check if the value stats with a sub-string. Case insensitive and accent sensitive.
- __unaccent_istartswith: Check if the value stats with a sub-string. Case insensitive and accent insensitive.
- __endswith: Check if the value ends with a sub-string. Case sensitive and accent sensitive.
- __iendswith: Check if the value ends with a sub-string. Case insensitive and accent sensitive.
- __unaccent_iendswith: Check if the value ends with a sub-string. Case insensitive and accent insensitive.
Null operators
- __isnull: Check if field is null, it uses as argument a
booleanvalue false will return all non NULL values and true will return NULL values.
Date and datetime operators:
- __range: Receive as argument a list of two elements and return objects that field dates are between those values.
- __year: Return object that date field value year is equal to argument.
- __month: Return object that date field value month is equal to argument.
- __day: Return object that date field value day is equal to argument.
Dictionary fields operators:
- __json_contained_by: Uses the function contained_by from SQLAlchemy to test if keys are a proper subset of the keys of the argument jsonb expression (extracted from SQLAlchemy). The argument is a list.
- __json_has_any: Uses the function has_any from SQLAlchemy to test for presence of a key. Note that the key may be a SQLA expression. (extracted from SQLAlchemy). The argument is a list.
- __json_has_key: Uses the function has_key from SQLAlchemy to Test for presence of a key. Note that the key may be a SQLA expression. The argument is a str.
Text similarity operators
To use similariry querys on Postgres it is necessary to pg_trgm be
instaled on server. Check [oficial documentation]
(https://www.postgresql.org/docs/current/pgtrgm.html).
- __similarity: Check if two strings are similar uses the
%operador. - __word_similar_left: Check if two strings are similar uses the
<%operador. - __word_similar_right: Check if two strings are similar uses the
%>operador. - __strict_word__similar_left: Check if two strings are similar
uses the
<<%operador. - __strict_word__similar_right: Check if two strings are similar
uses the
%>>operador.
Some usage examples:
# Return the first 3 results ordered decreasing acording to `time` and
# them ordered by `modeling_unit_id`. Results must have time greater
# or equal to 2017-01-01 and less or equal to 2017-06-01. It also
# must have attribute_id equal to 6 and not contains modeling_unit_id
# 3 or 4.
microservice.list(
model_class="DatabaseVariable",
filter_dict={
"time__gte": "2017-01-01 00:00:00",
"time__lte": "2017-06-01 00:00:00",
"attribute_id": 6},
exclude_dict={
"modeling_unit_id__in": [3, 4]},
order_by=["-time", "modeling_unit_id"],
limit=3,
fields=["pk", "model_class", "time", "modeling_unit_id", "value"])
# Return all elements that dimensions field has a key type with
# value contains `selling` insensitive to case and accent.
microservice.list(
model_class="DatabaseAttribute",
filter_dict={
"dimensions->type__unaccent_icontains": "selling"})
Arguments:
- model_class: Model class of the end-point
- filter_dict: Filter dict to be used at the query. Filter elements from query return that satifies all statements of the dictonary.
- exclude_dict: Exclude dict to be used at the query. Remove elements from query return that satifies all statements of the dictonary.
- order_by: Order results acording to list of strings correspondent to fields. It is possible to use '-' at the begginng of the field name for reverse ordering. Ex.: ['description'] for accendent ordering and ['-description'] for descendent ordering.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- fields (list): Set the fields to be returned by the list end-point.
- default_fields (bool): Boolean, if true and fields arguments None will return the default fields set for list by the backend.
- limit (int): Set the limit of elements of the returned query. By default, backend usually return 50 elements.
- foreign_key_fields (bool): Return forenging key objects. It will return the fk
corresponding object. Ex:
created_by_idreference to a usermodel_classthe correspondent to User will be returned atcreated_by. - base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
- as_dataframe (bool): Return data as dataframe and set the columns acording to fields if set.
- **kwargs: Other parameters for compatibility.
Returns:
Containing objects serialized by list Serializer.
Raises:
- No especific raises.
224 def list_by_chunks(self, model_class: str, filter_dict: dict = None, 225 exclude_dict: dict = None, auth_header: dict = None, 226 fields: list = None, default_fields: bool = False, 227 chunk_size: int = 50000, limit: int = None, 228 base_filter_skip: list = None, 229 as_dataframe: bool = False, **kwargs 230 ) -> Union[List[dict], pd.DataFrame]: 231 """List objects by fetching them in chunks using PK to paginate. 232 233 Fetch data in chunks to handle large datasets without causing backend 234 timeouts or memory issues. Results are ordered by the 'id' column 235 to ensure consistent pagination. Note that custom ordering is not 236 supported in this method. 237 238 Args: 239 model_class (str): 240 Model class of the end-point. 241 filter_dict (dict): 242 Filter dictionary for the query. 243 exclude_dict (dict): 244 Exclude dictionary for the query. 245 auth_header (dict): 246 Authentication header for user impersonation. 247 fields (list): 248 List of fields to be returned. 249 default_fields (bool): 250 If True and fields is None, return default backend fields. 251 chunk_size (int): 252 Number of objects to fetch per query. Defaults to 50000. 253 base_filter_skip (list): 254 List of base query filters to skip (requires superuser). 255 limit (int): 256 Maximum number of records to return. 257 as_dataframe (bool): 258 If True, returns the results as a pandas DataFrame. 259 **kwargs: 260 Additional arguments for compatibility. 261 262 Returns: 263 Union[List[dict], pd.DataFrame]: 264 A list of dictionaries or a pandas DataFrame containing the 265 serialized objects. 266 267 Raises: 268 PumpWoodException: 269 If there is an error during the request or data processing. 270 """ 271 filter_dict = ( 272 {} if filter_dict is None else filter_dict) 273 exclude_dict = ( 274 {} if exclude_dict is None else exclude_dict) 275 base_filter_skip = ( 276 [] if base_filter_skip is None else base_filter_skip) 277 278 copy_filter_dict = copy.deepcopy(filter_dict) 279 list_all_results = [] 280 max_order_col = None 281 results_count = 0 282 info_msg = ( 283 "# Fetching chunk: results_count[{results_count}] | " 284 "min_id [{max_order_col}] | limit[{limit}]") 285 while True: 286 logger.info( 287 info_msg, results_count=results_count, 288 max_order_col=max_order_col, limit=limit) 289 290 # It is necessary to keep the initial query id__gt in case of 291 # this parameter being passed as argument. 292 if max_order_col is not None: 293 copy_filter_dict["id__gt"] = max_order_col 294 295 temp_results = self.list( 296 model_class=model_class, filter_dict=copy_filter_dict, 297 exclude_dict=exclude_dict, order_by=["id"], 298 auth_header=auth_header, fields=fields, 299 default_fields=default_fields, limit=chunk_size, 300 base_filter_skip=base_filter_skip) 301 results_count = results_count + len(temp_results) 302 303 # Break if results is empty 304 if len(temp_results) == 0: 305 break 306 307 # Extend the list of objects retrieved 308 list_all_results.extend(temp_results) 309 310 # If limit of objects is set, the counter will limit the number 311 # of objects fetched 312 if limit is not None: 313 records_to_fetch = limit - results_count 314 chunk_size = min(records_to_fetch, chunk_size) 315 # Do not request an empty list 316 if chunk_size == 0: 317 break 318 319 # Get the last object id. If pk is a string the pk is a base64 320 # object that will contain the id columns in it. 321 last_pk = temp_results[-1]["pk"] 322 if isinstance(last_pk, str): 323 loaded_pk_dict = CompositePkBase64Converter.load(last_pk) 324 max_order_col = loaded_pk_dict["id"] 325 else: 326 max_order_col = temp_results[-1]["pk"] 327 328 if as_dataframe: 329 # Return the results as a dataframe using the group_by columns 330 # and the columns created at aggregation to ensure that 331 # even empty results would have the correct columns 332 return pd.DataFrame(list_all_results, columns=fields) 333 return list_all_results
List objects by fetching them in chunks using PK to paginate.
Fetch data in chunks to handle large datasets without causing backend timeouts or memory issues. Results are ordered by the 'id' column to ensure consistent pagination. Note that custom ordering is not supported in this method.
Arguments:
- model_class (str): Model class of the end-point.
- filter_dict (dict): Filter dictionary for the query.
- exclude_dict (dict): Exclude dictionary for the query.
- auth_header (dict): Authentication header for user impersonation.
- fields (list): List of fields to be returned.
- default_fields (bool): If True and fields is None, return default backend fields.
- chunk_size (int): Number of objects to fetch per query. Defaults to 50000.
- base_filter_skip (list): List of base query filters to skip (requires superuser).
- limit (int): Maximum number of records to return.
- as_dataframe (bool): If True, returns the results as a pandas DataFrame.
- **kwargs: Additional arguments for compatibility.
Returns:
Union[List[dict], pd.DataFrame]: A list of dictionaries or a pandas DataFrame containing the serialized objects.
Raises:
- PumpWoodException: If there is an error during the request or data processing.
339 def list_without_pag(self, model_class: str, filter_dict: dict = None, 340 exclude_dict: dict = None, order_by: list = None, 341 auth_header: dict = None, 342 convert_geometry: bool = True, 343 fields: list = None, 344 default_fields: bool = False, 345 foreign_key_fields: bool = False, 346 base_filter_skip: list = None, 347 as_dataframe: bool = False, 348 **kwargs 349 ) -> List[dict]: 350 """List object without pagination. 351 352 Function to post at list end-point (resumed data) of PumpWood like 353 systems, results won't be paginated. 354 **Be carefull with large returns.** 355 356 Args: 357 model_class (str): 358 Model class of the end-point 359 filter_dict (dict): 360 Filter dict to be used at the query. Filter elements from query 361 return that satifies all statements of the dictonary. 362 exclude_dict (dict): 363 Exclude dict to be used at the query. Remove elements from 364 query return that satifies all statements of the dictonary. 365 order_by (bool): 366 Order results acording to list of strings 367 correspondent to fields. It is possible to use '-' at the 368 begginng of the field name for reverse ordering. Ex.: 369 ['description'] for accendent ordering and ['-description'] 370 for descendent ordering. 371 auth_header (dict): 372 Auth header to substitute the microservice original 373 at the request (user impersonation). 374 fields (List[str]): 375 Set the fields to be returned by the list end-point. 376 default_fields (bool): 377 Boolean, if true and fields arguments None will return the 378 default fields set for list by the backend. 379 limit (int): 380 Set the limit of elements of the returned query. By default, 381 backend usually return 50 elements. 382 foreign_key_fields (bool): 383 Return forenging key objects. It will return the fk 384 corresponding object. Ex: `created_by_id` reference to 385 a user `model_class` the correspondent to User will be 386 returned at `created_by`. 387 convert_geometry (bool): 388 If geometry columns should be convert to shapely geometry. 389 Fields with key 'geometry' will be considered geometry. 390 base_filter_skip (list): 391 List of base query filter to be skiped, it is necessary to 392 be superuser to skip base query filters. 393 as_dataframe (bool): 394 Return data as dataframe and set the columns acording to 395 fields if set. 396 **kwargs: 397 Other unused arguments for compatibility. 398 399 Returns: 400 Containing objects serialized by list Serializer. 401 402 Raises: 403 No especific raises. 404 """ 405 filter_dict = {} if filter_dict is None else filter_dict 406 exclude_dict = {} if exclude_dict is None else exclude_dict 407 base_filter_skip = ( 408 [] if base_filter_skip is None else base_filter_skip) 409 base_filter_skip = [] if base_filter_skip is None else base_filter_skip 410 411 url_str = self._build_list_without_pag_url(model_class) 412 413 post_data = { 414 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 415 'order_by': order_by, 'default_fields': default_fields, 416 'foreign_key_fields': foreign_key_fields} 417 418 if fields is not None: 419 post_data["fields"] = fields 420 results = self.request_post( 421 url=url_str, data=post_data, 422 parameters={'base_filter_skip': base_filter_skip}, 423 auth_header=auth_header) 424 425 ################################################## 426 # Converting geometry to Shapely objects in Python 427 geometry_in_results = False 428 if convert_geometry: 429 for obj in results: 430 geometry_value = obj.get("geometry") 431 if geometry_value is not None: 432 obj["geometry"] = geometry.shape(geometry_value) 433 geometry_in_results = True 434 ################################################## 435 436 if not as_dataframe: 437 return results 438 else: 439 convert_to_geopandas = ( 440 (model_class.lower() == "descriptiongeoarea") and 441 geometry_in_results) 442 if convert_to_geopandas: 443 return geopd.GeoDataFrame( 444 results, geometry='geometry', columns=fields) 445 else: 446 return pd.DataFrame( 447 results, columns=fields)
List object without pagination.
Function to post at list end-point (resumed data) of PumpWood like systems, results won't be paginated. Be carefull with large returns.
Arguments:
- model_class (str): Model class of the end-point
- filter_dict (dict): Filter dict to be used at the query. Filter elements from query return that satifies all statements of the dictonary.
- exclude_dict (dict): Exclude dict to be used at the query. Remove elements from query return that satifies all statements of the dictonary.
- order_by (bool): Order results acording to list of strings correspondent to fields. It is possible to use '-' at the begginng of the field name for reverse ordering. Ex.: ['description'] for accendent ordering and ['-description'] for descendent ordering.
- auth_header (dict): Auth header to substitute the microservice original at the request (user impersonation).
- fields (List[str]): Set the fields to be returned by the list end-point.
- default_fields (bool): Boolean, if true and fields arguments None will return the default fields set for list by the backend.
- limit (int): Set the limit of elements of the returned query. By default, backend usually return 50 elements.
- foreign_key_fields (bool): Return forenging key objects. It will return the fk
corresponding object. Ex:
created_by_idreference to a usermodel_classthe correspondent to User will be returned atcreated_by. - convert_geometry (bool): If geometry columns should be convert to shapely geometry. Fields with key 'geometry' will be considered geometry.
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
- as_dataframe (bool): Return data as dataframe and set the columns acording to fields if set.
- **kwargs: Other unused arguments for compatibility.
Returns:
Containing objects serialized by list Serializer.
Raises:
- No especific raises.
8class ABCSimpleDimensionMicroservice(ABC, PumpWoodMicroServiceBase): 9 """Abstract class for parallel calls at Pumpwood end-points.""" 10 11 @staticmethod 12 def _build_list_dimensions(model_class: str): 13 return "rest/%s/list-dimensions/" % (model_class.lower(),) 14 15 def list_dimensions(self, model_class: str, filter_dict: dict = {}, 16 exclude_dict: dict = {}, auth_header: dict = None, 17 base_filter_skip: list = None) -> list[str]: 18 """List dimensions avaiable for model_class. 19 20 It list all keys avaiable at dimension retricting the results with 21 query parameters `filter_dict` and `exclude_dict`. 22 23 Args: 24 model_class: 25 Model class of the end-point 26 filter_dict: 27 Filter dict to be used at the query. Filter elements from query 28 return that satifies all statements of the dictonary. 29 exclude_dict: 30 Exclude dict to be used at the query. Remove elements from 31 query return that satifies all statements of the dictonary. 32 auth_header: 33 Auth header to substitute the microservice original 34 at the request (user impersonation). 35 base_filter_skip (list): 36 List of base query filter to be skiped, it is necessary to 37 be superuser to skip base query filters. 38 39 Returns: 40 List of keys avaiable in results from the query dict. 41 """ 42 url_str = self._build_list_dimensions(model_class) 43 base_filter_skip = ( 44 [] if base_filter_skip is None else base_filter_skip) 45 post_data = {'filter_dict': filter_dict, 'exclude_dict': exclude_dict} 46 return self.request_post( 47 url=url_str, data=post_data, 48 parameters={'base_filter_skip': base_filter_skip}, 49 auth_header=auth_header) 50 51 @staticmethod 52 def _build_list_dimension_values(model_class: str): 53 return "rest/%s/list-dimension-values/" % (model_class.lower(), ) 54 55 def list_dimension_values(self, model_class: str, key: str, 56 filter_dict: dict = {}, exclude_dict: dict = {}, 57 auth_header: dict = None, 58 base_filter_skip: list = None) -> list[any]: 59 """List values associated with dimensions key. 60 61 It list all keys avaiable at dimension retricting the results with 62 query parameters `filter_dict` and `exclude_dict`. 63 64 Args: 65 model_class: 66 Model class of the end-point 67 filter_dict: 68 Filter dict to be used at the query. Filter elements from query 69 return that satifies all statements of the dictonary. 70 exclude_dict: 71 Exclude dict to be used at the query. Remove elements from 72 query return that satifies all statements of the dictonary. 73 auth_header: 74 Auth header to substitute the microservice original 75 at the request (user impersonation). 76 key: 77 Key to list the avaiable values using the query filter 78 and exclude. 79 base_filter_skip (list): 80 List of base query filter to be skiped, it is necessary to 81 be superuser to skip base query filters. 82 83 Returns: 84 List of values associated with dimensions key at the objects that 85 are returned with `filter_dict` and `exclude_dict`. 86 """ 87 url_str = self._build_list_dimension_values(model_class) 88 base_filter_skip = ( 89 [] if base_filter_skip is None else base_filter_skip) 90 base_filter_skip = None 91 post_data = { 92 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 93 'key': key} 94 return self.request_post( 95 url=url_str, data=post_data, 96 parameters={'base_filter_skip': base_filter_skip}, 97 auth_header=auth_header)
Abstract class for parallel calls at Pumpwood end-points.
15 def list_dimensions(self, model_class: str, filter_dict: dict = {}, 16 exclude_dict: dict = {}, auth_header: dict = None, 17 base_filter_skip: list = None) -> list[str]: 18 """List dimensions avaiable for model_class. 19 20 It list all keys avaiable at dimension retricting the results with 21 query parameters `filter_dict` and `exclude_dict`. 22 23 Args: 24 model_class: 25 Model class of the end-point 26 filter_dict: 27 Filter dict to be used at the query. Filter elements from query 28 return that satifies all statements of the dictonary. 29 exclude_dict: 30 Exclude dict to be used at the query. Remove elements from 31 query return that satifies all statements of the dictonary. 32 auth_header: 33 Auth header to substitute the microservice original 34 at the request (user impersonation). 35 base_filter_skip (list): 36 List of base query filter to be skiped, it is necessary to 37 be superuser to skip base query filters. 38 39 Returns: 40 List of keys avaiable in results from the query dict. 41 """ 42 url_str = self._build_list_dimensions(model_class) 43 base_filter_skip = ( 44 [] if base_filter_skip is None else base_filter_skip) 45 post_data = {'filter_dict': filter_dict, 'exclude_dict': exclude_dict} 46 return self.request_post( 47 url=url_str, data=post_data, 48 parameters={'base_filter_skip': base_filter_skip}, 49 auth_header=auth_header)
List dimensions avaiable for model_class.
It list all keys avaiable at dimension retricting the results with
query parameters filter_dict and exclude_dict.
Arguments:
- model_class: Model class of the end-point
- filter_dict: Filter dict to be used at the query. Filter elements from query return that satifies all statements of the dictonary.
- exclude_dict: Exclude dict to be used at the query. Remove elements from query return that satifies all statements of the dictonary.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
List of keys avaiable in results from the query dict.
55 def list_dimension_values(self, model_class: str, key: str, 56 filter_dict: dict = {}, exclude_dict: dict = {}, 57 auth_header: dict = None, 58 base_filter_skip: list = None) -> list[any]: 59 """List values associated with dimensions key. 60 61 It list all keys avaiable at dimension retricting the results with 62 query parameters `filter_dict` and `exclude_dict`. 63 64 Args: 65 model_class: 66 Model class of the end-point 67 filter_dict: 68 Filter dict to be used at the query. Filter elements from query 69 return that satifies all statements of the dictonary. 70 exclude_dict: 71 Exclude dict to be used at the query. Remove elements from 72 query return that satifies all statements of the dictonary. 73 auth_header: 74 Auth header to substitute the microservice original 75 at the request (user impersonation). 76 key: 77 Key to list the avaiable values using the query filter 78 and exclude. 79 base_filter_skip (list): 80 List of base query filter to be skiped, it is necessary to 81 be superuser to skip base query filters. 82 83 Returns: 84 List of values associated with dimensions key at the objects that 85 are returned with `filter_dict` and `exclude_dict`. 86 """ 87 url_str = self._build_list_dimension_values(model_class) 88 base_filter_skip = ( 89 [] if base_filter_skip is None else base_filter_skip) 90 base_filter_skip = None 91 post_data = { 92 'filter_dict': filter_dict, 'exclude_dict': exclude_dict, 93 'key': key} 94 return self.request_post( 95 url=url_str, data=post_data, 96 parameters={'base_filter_skip': base_filter_skip}, 97 auth_header=auth_header)
List values associated with dimensions key.
It list all keys avaiable at dimension retricting the results with
query parameters filter_dict and exclude_dict.
Arguments:
- model_class: Model class of the end-point
- filter_dict: Filter dict to be used at the query. Filter elements from query return that satifies all statements of the dictonary.
- exclude_dict: Exclude dict to be used at the query. Remove elements from query return that satifies all statements of the dictonary.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- key: Key to list the avaiable values using the query filter and exclude.
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
List of values associated with dimensions key at the objects that are returned with
filter_dictandexclude_dict.
8class ABCSimpleActionMicroservice(ABC, PumpWoodMicroServiceBase): 9 """Abstract class for parallel calls at Pumpwood end-points.""" 10 11 def list_actions(self, model_class: str, 12 auth_header: dict = None) -> list[dict]: 13 """Return a list of all actions avaiable at this model class. 14 15 Args: 16 model_class: 17 Model class to list possible actions. 18 auth_header: 19 Auth header to substitute the microservice original 20 at the request (user impersonation). 21 22 Returns: 23 List of possible actions and its descriptions. 24 25 Raises: 26 No particular errors. 27 """ 28 url_str = "rest/%s/actions/" % (model_class.lower()) 29 return self.request_get(url=url_str, auth_header=auth_header) 30 31 @staticmethod 32 def _build_execute_action_url(model_class: str, action: str, 33 pk: int = None): 34 url_str = "rest/%s/actions/%s/" % (model_class.lower(), action) 35 if pk is not None: 36 url_str = url_str + str(pk) + '/' 37 return url_str 38 39 def execute_action(self, model_class: str, action: str, pk: int = None, 40 parameters: dict = {}, files: list = None, 41 auth_header: dict = None, 42 base_filter_skip: list[str] | list[list[str]] = None 43 ) -> dict: 44 """Execute action associated with a model class. 45 46 If action is static or classfunction no pk is necessary. 47 48 Args: 49 pk (int): 50 PK of the object to run action at. If not set action will be 51 considered a classmethod and will run over the class. 52 model_class: 53 Model class to run action the object 54 action: 55 Action that will be performed. 56 auth_header: 57 Auth header to substitute the microservice original 58 at the request (user impersonation). 59 parameters: 60 Dictionary with the function parameters. 61 files: 62 A dictionary of files to be added to as a multi-part 63 post request. File must be passed as a file object with read 64 bytes. 65 base_filter_skip (list): 66 List of base query filter to be skiped, it is necessary to 67 be superuser to skip base query filters. 68 69 Returns: 70 Return a dictonary with keys: 71 - **result:**: Result of the action that was performed. 72 - **action:**: Information of the action that was performed. 73 - **parameters:** Parameters that were passed to perform the 74 action. 75 - **object:** If a pk was passed to execute and action (not 76 classmethod or staticmethod), the object with the correspondent 77 pk is returned. 78 79 Raises: 80 PumpWoodException: 81 'There is no method {action} in rest actions for {class_name}'. 82 This indicates that action requested is not associated with 83 the model_class. 84 PumpWoodActionArgsException: 85 'Function is not static and pk is Null'. This indicate that 86 the action solicitated is not static/class method and a pk 87 was not passed as argument. 88 PumpWoodActionArgsException: 89 'Function is static and pk is not Null'. This indicate that 90 the action solicitated is static/class method and a pk 91 was passed as argument. 92 PumpWoodObjectDoesNotExist: 93 'Requested object {model_class}[{pk}] not found.'. This 94 indicate that pk associated with model class was not found 95 on database. 96 """ 97 base_filter_skip = ( 98 [] if base_filter_skip is None else base_filter_skip) 99 url_str = self._build_execute_action_url( 100 model_class=model_class, action=action, pk=pk) 101 return self.request_post( 102 url=url_str, data=parameters, files=files, auth_header=auth_header, 103 parameters={"base_filter_skip": base_filter_skip})
Abstract class for parallel calls at Pumpwood end-points.
11 def list_actions(self, model_class: str, 12 auth_header: dict = None) -> list[dict]: 13 """Return a list of all actions avaiable at this model class. 14 15 Args: 16 model_class: 17 Model class to list possible actions. 18 auth_header: 19 Auth header to substitute the microservice original 20 at the request (user impersonation). 21 22 Returns: 23 List of possible actions and its descriptions. 24 25 Raises: 26 No particular errors. 27 """ 28 url_str = "rest/%s/actions/" % (model_class.lower()) 29 return self.request_get(url=url_str, auth_header=auth_header)
Return a list of all actions avaiable at this model class.
Arguments:
- model_class: Model class to list possible actions.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
Returns:
List of possible actions and its descriptions.
Raises:
- No particular errors.
39 def execute_action(self, model_class: str, action: str, pk: int = None, 40 parameters: dict = {}, files: list = None, 41 auth_header: dict = None, 42 base_filter_skip: list[str] | list[list[str]] = None 43 ) -> dict: 44 """Execute action associated with a model class. 45 46 If action is static or classfunction no pk is necessary. 47 48 Args: 49 pk (int): 50 PK of the object to run action at. If not set action will be 51 considered a classmethod and will run over the class. 52 model_class: 53 Model class to run action the object 54 action: 55 Action that will be performed. 56 auth_header: 57 Auth header to substitute the microservice original 58 at the request (user impersonation). 59 parameters: 60 Dictionary with the function parameters. 61 files: 62 A dictionary of files to be added to as a multi-part 63 post request. File must be passed as a file object with read 64 bytes. 65 base_filter_skip (list): 66 List of base query filter to be skiped, it is necessary to 67 be superuser to skip base query filters. 68 69 Returns: 70 Return a dictonary with keys: 71 - **result:**: Result of the action that was performed. 72 - **action:**: Information of the action that was performed. 73 - **parameters:** Parameters that were passed to perform the 74 action. 75 - **object:** If a pk was passed to execute and action (not 76 classmethod or staticmethod), the object with the correspondent 77 pk is returned. 78 79 Raises: 80 PumpWoodException: 81 'There is no method {action} in rest actions for {class_name}'. 82 This indicates that action requested is not associated with 83 the model_class. 84 PumpWoodActionArgsException: 85 'Function is not static and pk is Null'. This indicate that 86 the action solicitated is not static/class method and a pk 87 was not passed as argument. 88 PumpWoodActionArgsException: 89 'Function is static and pk is not Null'. This indicate that 90 the action solicitated is static/class method and a pk 91 was passed as argument. 92 PumpWoodObjectDoesNotExist: 93 'Requested object {model_class}[{pk}] not found.'. This 94 indicate that pk associated with model class was not found 95 on database. 96 """ 97 base_filter_skip = ( 98 [] if base_filter_skip is None else base_filter_skip) 99 url_str = self._build_execute_action_url( 100 model_class=model_class, action=action, pk=pk) 101 return self.request_post( 102 url=url_str, data=parameters, files=files, auth_header=auth_header, 103 parameters={"base_filter_skip": base_filter_skip})
Execute action associated with a model class.
If action is static or classfunction no pk is necessary.
Arguments:
- pk (int): PK of the object to run action at. If not set action will be considered a classmethod and will run over the class.
- model_class: Model class to run action the object
- action: Action that will be performed.
- auth_header: Auth header to substitute the microservice original at the request (user impersonation).
- parameters: Dictionary with the function parameters.
- files: A dictionary of files to be added to as a multi-part post request. File must be passed as a file object with read bytes.
- base_filter_skip (list): List of base query filter to be skiped, it is necessary to be superuser to skip base query filters.
Returns:
Return a dictonary with keys:
- result:: Result of the action that was performed.
- action:: Information of the action that was performed.
- parameters: Parameters that were passed to perform the action.
- object: If a pk was passed to execute and action (not classmethod or staticmethod), the object with the correspondent pk is returned.
Raises:
- PumpWoodException: 'There is no method {action} in rest actions for {class_name}'. This indicates that action requested is not associated with the model_class.
- PumpWoodActionArgsException: 'Function is not static and pk is Null'. This indicate that the action solicitated is not static/class method and a pk was not passed as argument.
- PumpWoodActionArgsException: 'Function is static and pk is not Null'. This indicate that the action solicitated is static/class method and a pk was passed as argument.
- PumpWoodObjectDoesNotExist: 'Requested object {model_class}[{pk}] not found.'. This indicate that pk associated with model class was not found on database.
23class ABCSimpleInfoMicroservice(ABC, PumpWoodMicroServiceBase): 24 """Class for infomation calls.""" 25 26 def search_options(self, model_class: str, 27 auth_header: dict = None) -> dict: 28 """Return search options. 29 30 DEPRECATED Use `list_options` function instead. 31 32 Return information of the fields including available options for 33 options fields and model associated with the foreign key. 34 35 Args: 36 model_class (str): 37 Model class to check search parameters. 38 auth_header (dict, optional): 39 Auth header to substitute the microservice original 40 at the request (user impersonation). 41 42 Returns: 43 Return a dictionary with field names as keys and information of 44 them as values. Information at values: 45 - **primary_key [bool]:**: Boolean indicating if field is part 46 of model_class primary key. 47 - **column [str]:**: Name of the column. 48 - **column__verbose [str]:** Name of the column translated using 49 Pumpwood I8s. 50 - **help_text [str]:** Help text associated with column. 51 - **help_text__verbose [str]:** Help text associated with column 52 translated using Pumpwood I8s. 53 - **type [str]:** Python type associated with the column. 54 - **nullable [bool]:** If field can be set as null (None). 55 - **read_only [bool]:** If field is marked as read-only. Passsing 56 information for this field will not be used in save end-point. 57 - **default [any]:** Default value of the field if not set using 58 save end-poin. 59 - **unique [bool]:** If the there is a constrain in database 60 setting this field to be unique. 61 - **extra_info:** Some extra informations used to pass associated 62 model class for foreign key and related fields. 63 - **in [dict]:** At options fields, have their options listed in 64 `in` keys. It will return the values as key and de description 65 and description__verbose (translated by Pumpwood I8s) 66 as values. 67 - **partition:** At pk field, this key indicates if the database 68 if partitioned. Partitioned will perform better in queries if 69 partition is used on filter or exclude clauses. If table has 70 more than one level o partition, at least the first one must 71 be used when retrieving data. 72 73 Raises: 74 No particular raises. 75 """ 76 url_str = "rest/%s/options/" % (model_class.lower(), ) 77 return self.request_get(url=url_str, auth_header=auth_header) 78 79 def fill_options(self, model_class, parcial_obj_dict: dict = None, 80 field: str = None, auth_header: dict = None, 81 use_disk_cache: bool = True): 82 """Return options for object fields. 83 84 DEPRECATED Use `fill_validation` function instead. 85 86 This function send partial object data and return options to finish 87 object filling. 88 89 Args: 90 model_class (str): 91 Model class to check search parameters. 92 auth_header (dict, optional): 93 Auth header to substitute the microservice original 94 at the request (user impersonation). 95 parcial_obj_dict (dict, optional): 96 Partial object that is sent to backend for validation and 97 update fill options according to values passed for each field. 98 field (str, optional): 99 Restrict validation for a specific field if implemented. 100 use_disk_cache (bool): 101 If it will use the disk cache to retrieve information 102 from fill options if it was not passed partial object 103 data (`parcial_obj_dict=None`) or field (`field=None`). 104 105 Returns: 106 Return a dictionary with field names as keys and information of 107 them as values. Information at values: 108 - **primary_key [bool]:**: Boolean indicating if field is part 109 of model_class primary key. 110 - **column [str]:**: Name of the column. 111 - **column__verbose [str]:** Name of the column translated using 112 Pumpwood I8s. 113 - **help_text [str]:** Help text associated with column. 114 - **help_text__verbose [str]:** Help text associated with column 115 translated using Pumpwood I8s. 116 - **type [str]:** Python type associated with the column. 117 - **nullable [bool]:** If field can be set as null (None). 118 - **read_only [bool]:** If field is marked as read-only. Passsing 119 information for this field will not be used in save end-point. 120 - **default [any]:** Default value of the field if not set using 121 save end-poin. 122 - **unique [bool]:** If the there is a constrain in database 123 setting this field to be unique. 124 - **extra_info:** Some extra informations used to pass associated 125 model class for foreign key and related fields. 126 - **in [dict]:** At options fields, have their options listed in 127 `in` keys. It will return the values as key and de description 128 and description__verbose (translated by Pumpwood I8s) 129 as values. 130 - **partition:** At pk field, this key indicates if the database 131 if partitioned. Partitioned will perform better in queries if 132 partition is used on filter or exclude clauses. If table has 133 more than one level o partition, at least the first one must 134 be used when retrieving data. 135 136 Raises: 137 No particular raises. 138 """ 139 parcial_obj_dict = ( 140 parcial_obj_dict if parcial_obj_dict is not None else {}) 141 142 url_str = "rest/%s/options/" % (model_class.lower(), ) 143 if field is not None: 144 url_str = url_str + field 145 146 # The auth header os set as temp to be used locally on this function, 147 # if it substitute the auth_header of the call it will cause 148 # error due to microservice logged and auth_header provided. 149 temp_auth_header = self._check_auth_header(auth_header=auth_header) 150 hash_dict = FillOptionsNoDataCacheHash( 151 authorization=temp_auth_header, model_class=model_class) 152 153 # Check if parameters passed on the request are empty, this will 154 # make it possible to use the cache. 155 are_parameters_empty = not parcial_obj_dict and field is None 156 if are_parameters_empty and use_disk_cache: 157 cache_value = default_cache.get(hash_dict) 158 if cache_value is not None: 159 return cache_value 160 161 return_value = self.request_post( 162 url=url_str, data=parcial_obj_dict, 163 auth_header=auth_header) 164 165 # If cache is to be used, than set it for the next call. 166 if are_parameters_empty and use_disk_cache: 167 default_cache.set(hash_dict=hash_dict, value=return_value) 168 return return_value 169 170 def list_options(self, model_class: str, auth_header: dict) -> dict: 171 """Return options to render list views. 172 173 This function send partial object data and return options to finish 174 object filling. 175 176 Args: 177 model_class (str): 178 Model class to check search parameters. 179 auth_header (dict): 180 Auth header to substitute the microservice original 181 at the request (user impersonation). 182 183 Returns: 184 Dictionary with keys: 185 - **default_list_fields:** Default list field defined on the 186 application backend. 187 - **field_descriptions:** Description of the fields associated 188 with the model class. 189 190 Raises: 191 No particular raise. 192 """ 193 url_str = "rest/{basename}/list-options/".format( 194 basename=model_class.lower()) 195 return self.request_get( 196 url=url_str, auth_header=auth_header) 197 198 def retrieve_options(self, model_class: str, 199 auth_header: dict = None) -> dict: 200 """Return options to render retrieve views. 201 202 Return information of the field sets that can be used to create 203 frontend site. It also return a `verbose_field` which can be used 204 to create the title of the page substituing the values with 205 information of the object. 206 207 Args: 208 model_class (str): 209 Model class to check search parameters. 210 auth_header (dict, optional): 211 Auth header to substitute the microservice original 212 at the request (user impersonation). 213 214 Returns: 215 Return a dictionary with keys: 216 - **verbose_field:** String sugesting how the title of the 217 retrieve might be created. It will use Python format 218 information ex.: `'{pk} | {description}'`. 219 - **fieldset:** An dictionary with organization of data, 220 setting field sets that could be grouped toguether in 221 tabs. 222 223 Raises: 224 No particular raises. 225 """ 226 url_str = "rest/{basename}/retrieve-options/".format( 227 basename=model_class.lower()) 228 return self.request_get( 229 url=url_str, auth_header=auth_header) 230 231 def fill_validation(self, model_class: str, parcial_obj_dict: dict = None, 232 field: str = None, auth_header: dict = None, 233 user_type: str = 'api') -> dict: 234 """Return options for object fields. 235 236 This function send partial object data and return options to finish 237 object filling. 238 239 Args: 240 model_class (str): 241 Model class to check search parameters. 242 auth_header (dict, optional): 243 Auth header to substitute the microservice original 244 at the request (user impersonation). 245 parcial_obj_dict (dict, optional): 246 Partial object data to be validated by the backend. 247 field (str, optional): 248 Set a specific field to be validated if implemented. 249 user_type (str): 250 Set the type of user is requesting fill validation. It is 251 possible to set `api` and `gui`. Gui user_type will return 252 fields listed in gui_readonly as read-only fields to 253 facilitate navigation. 254 255 Returns: 256 Return a dictionary with keys: 257 - **field_descriptions:** Same of fill_options, but setting as 258 read_only=True fields listed on gui_readonly if 259 user_type='gui'. 260 - **gui_readonly:** Return a list of fields that will be 261 considered as read-only if user_type='gui' is requested. 262 263 Raises: 264 No particular raises. 265 """ 266 parcial_obj_dict = ( 267 parcial_obj_dict if parcial_obj_dict is not None else {}) 268 269 url_str = "rest/{basename}/retrieve-options/".format( 270 basename=model_class.lower()) 271 params = {"user_type": user_type} 272 if field is not None: 273 params["field"] = field 274 return self.request_post( 275 url=url_str, auth_header=auth_header, data=parcial_obj_dict, 276 parameters=params)
Class for infomation calls.
26 def search_options(self, model_class: str, 27 auth_header: dict = None) -> dict: 28 """Return search options. 29 30 DEPRECATED Use `list_options` function instead. 31 32 Return information of the fields including available options for 33 options fields and model associated with the foreign key. 34 35 Args: 36 model_class (str): 37 Model class to check search parameters. 38 auth_header (dict, optional): 39 Auth header to substitute the microservice original 40 at the request (user impersonation). 41 42 Returns: 43 Return a dictionary with field names as keys and information of 44 them as values. Information at values: 45 - **primary_key [bool]:**: Boolean indicating if field is part 46 of model_class primary key. 47 - **column [str]:**: Name of the column. 48 - **column__verbose [str]:** Name of the column translated using 49 Pumpwood I8s. 50 - **help_text [str]:** Help text associated with column. 51 - **help_text__verbose [str]:** Help text associated with column 52 translated using Pumpwood I8s. 53 - **type [str]:** Python type associated with the column. 54 - **nullable [bool]:** If field can be set as null (None). 55 - **read_only [bool]:** If field is marked as read-only. Passsing 56 information for this field will not be used in save end-point. 57 - **default [any]:** Default value of the field if not set using 58 save end-poin. 59 - **unique [bool]:** If the there is a constrain in database 60 setting this field to be unique. 61 - **extra_info:** Some extra informations used to pass associated 62 model class for foreign key and related fields. 63 - **in [dict]:** At options fields, have their options listed in 64 `in` keys. It will return the values as key and de description 65 and description__verbose (translated by Pumpwood I8s) 66 as values. 67 - **partition:** At pk field, this key indicates if the database 68 if partitioned. Partitioned will perform better in queries if 69 partition is used on filter or exclude clauses. If table has 70 more than one level o partition, at least the first one must 71 be used when retrieving data. 72 73 Raises: 74 No particular raises. 75 """ 76 url_str = "rest/%s/options/" % (model_class.lower(), ) 77 return self.request_get(url=url_str, auth_header=auth_header)
Return search options.
DEPRECATED Use list_options function instead.
Return information of the fields including available options for options fields and model associated with the foreign key.
Arguments:
- model_class (str): Model class to check search parameters.
- auth_header (dict, optional): Auth header to substitute the microservice original at the request (user impersonation).
Returns:
Return a dictionary with field names as keys and information of them as values. Information at values:
- primary_key [bool]:: Boolean indicating if field is part of model_class primary key.
- column [str]:: Name of the column.
- column__verbose [str]: Name of the column translated using Pumpwood I8s.
- help_text [str]: Help text associated with column.
- help_text__verbose [str]: Help text associated with column translated using Pumpwood I8s.
- type [str]: Python type associated with the column.
- nullable [bool]: If field can be set as null (None).
- read_only [bool]: If field is marked as read-only. Passsing information for this field will not be used in save end-point.
- default [any]: Default value of the field if not set using save end-poin.
- unique [bool]: If the there is a constrain in database setting this field to be unique.
- extra_info: Some extra informations used to pass associated model class for foreign key and related fields.
- in [dict]: At options fields, have their options listed in
inkeys. It will return the values as key and de description and description__verbose (translated by Pumpwood I8s) as values.- partition: At pk field, this key indicates if the database if partitioned. Partitioned will perform better in queries if partition is used on filter or exclude clauses. If table has more than one level o partition, at least the first one must be used when retrieving data.
Raises:
- No particular raises.
79 def fill_options(self, model_class, parcial_obj_dict: dict = None, 80 field: str = None, auth_header: dict = None, 81 use_disk_cache: bool = True): 82 """Return options for object fields. 83 84 DEPRECATED Use `fill_validation` function instead. 85 86 This function send partial object data and return options to finish 87 object filling. 88 89 Args: 90 model_class (str): 91 Model class to check search parameters. 92 auth_header (dict, optional): 93 Auth header to substitute the microservice original 94 at the request (user impersonation). 95 parcial_obj_dict (dict, optional): 96 Partial object that is sent to backend for validation and 97 update fill options according to values passed for each field. 98 field (str, optional): 99 Restrict validation for a specific field if implemented. 100 use_disk_cache (bool): 101 If it will use the disk cache to retrieve information 102 from fill options if it was not passed partial object 103 data (`parcial_obj_dict=None`) or field (`field=None`). 104 105 Returns: 106 Return a dictionary with field names as keys and information of 107 them as values. Information at values: 108 - **primary_key [bool]:**: Boolean indicating if field is part 109 of model_class primary key. 110 - **column [str]:**: Name of the column. 111 - **column__verbose [str]:** Name of the column translated using 112 Pumpwood I8s. 113 - **help_text [str]:** Help text associated with column. 114 - **help_text__verbose [str]:** Help text associated with column 115 translated using Pumpwood I8s. 116 - **type [str]:** Python type associated with the column. 117 - **nullable [bool]:** If field can be set as null (None). 118 - **read_only [bool]:** If field is marked as read-only. Passsing 119 information for this field will not be used in save end-point. 120 - **default [any]:** Default value of the field if not set using 121 save end-poin. 122 - **unique [bool]:** If the there is a constrain in database 123 setting this field to be unique. 124 - **extra_info:** Some extra informations used to pass associated 125 model class for foreign key and related fields. 126 - **in [dict]:** At options fields, have their options listed in 127 `in` keys. It will return the values as key and de description 128 and description__verbose (translated by Pumpwood I8s) 129 as values. 130 - **partition:** At pk field, this key indicates if the database 131 if partitioned. Partitioned will perform better in queries if 132 partition is used on filter or exclude clauses. If table has 133 more than one level o partition, at least the first one must 134 be used when retrieving data. 135 136 Raises: 137 No particular raises. 138 """ 139 parcial_obj_dict = ( 140 parcial_obj_dict if parcial_obj_dict is not None else {}) 141 142 url_str = "rest/%s/options/" % (model_class.lower(), ) 143 if field is not None: 144 url_str = url_str + field 145 146 # The auth header os set as temp to be used locally on this function, 147 # if it substitute the auth_header of the call it will cause 148 # error due to microservice logged and auth_header provided. 149 temp_auth_header = self._check_auth_header(auth_header=auth_header) 150 hash_dict = FillOptionsNoDataCacheHash( 151 authorization=temp_auth_header, model_class=model_class) 152 153 # Check if parameters passed on the request are empty, this will 154 # make it possible to use the cache. 155 are_parameters_empty = not parcial_obj_dict and field is None 156 if are_parameters_empty and use_disk_cache: 157 cache_value = default_cache.get(hash_dict) 158 if cache_value is not None: 159 return cache_value 160 161 return_value = self.request_post( 162 url=url_str, data=parcial_obj_dict, 163 auth_header=auth_header) 164 165 # If cache is to be used, than set it for the next call. 166 if are_parameters_empty and use_disk_cache: 167 default_cache.set(hash_dict=hash_dict, value=return_value) 168 return return_value
Return options for object fields.
DEPRECATED Use fill_validation function instead.
This function send partial object data and return options to finish object filling.
Arguments:
- model_class (str): Model class to check search parameters.
- auth_header (dict, optional): Auth header to substitute the microservice original at the request (user impersonation).
- parcial_obj_dict (dict, optional): Partial object that is sent to backend for validation and update fill options according to values passed for each field.
- field (str, optional): Restrict validation for a specific field if implemented.
- use_disk_cache (bool): If it will use the disk cache to retrieve information
from fill options if it was not passed partial object
data (
parcial_obj_dict=None) or field (field=None).
Returns:
Return a dictionary with field names as keys and information of them as values. Information at values:
- primary_key [bool]:: Boolean indicating if field is part of model_class primary key.
- column [str]:: Name of the column.
- column__verbose [str]: Name of the column translated using Pumpwood I8s.
- help_text [str]: Help text associated with column.
- help_text__verbose [str]: Help text associated with column translated using Pumpwood I8s.
- type [str]: Python type associated with the column.
- nullable [bool]: If field can be set as null (None).
- read_only [bool]: If field is marked as read-only. Passsing information for this field will not be used in save end-point.
- default [any]: Default value of the field if not set using save end-poin.
- unique [bool]: If the there is a constrain in database setting this field to be unique.
- extra_info: Some extra informations used to pass associated model class for foreign key and related fields.
- in [dict]: At options fields, have their options listed in
inkeys. It will return the values as key and de description and description__verbose (translated by Pumpwood I8s) as values.- partition: At pk field, this key indicates if the database if partitioned. Partitioned will perform better in queries if partition is used on filter or exclude clauses. If table has more than one level o partition, at least the first one must be used when retrieving data.
Raises:
- No particular raises.
170 def list_options(self, model_class: str, auth_header: dict) -> dict: 171 """Return options to render list views. 172 173 This function send partial object data and return options to finish 174 object filling. 175 176 Args: 177 model_class (str): 178 Model class to check search parameters. 179 auth_header (dict): 180 Auth header to substitute the microservice original 181 at the request (user impersonation). 182 183 Returns: 184 Dictionary with keys: 185 - **default_list_fields:** Default list field defined on the 186 application backend. 187 - **field_descriptions:** Description of the fields associated 188 with the model class. 189 190 Raises: 191 No particular raise. 192 """ 193 url_str = "rest/{basename}/list-options/".format( 194 basename=model_class.lower()) 195 return self.request_get( 196 url=url_str, auth_header=auth_header)
Return options to render list views.
This function send partial object data and return options to finish object filling.
Arguments:
- model_class (str): Model class to check search parameters.
- auth_header (dict): Auth header to substitute the microservice original at the request (user impersonation).
Returns:
Dictionary with keys:
- default_list_fields: Default list field defined on the application backend.
- field_descriptions: Description of the fields associated with the model class.
Raises:
- No particular raise.
198 def retrieve_options(self, model_class: str, 199 auth_header: dict = None) -> dict: 200 """Return options to render retrieve views. 201 202 Return information of the field sets that can be used to create 203 frontend site. It also return a `verbose_field` which can be used 204 to create the title of the page substituing the values with 205 information of the object. 206 207 Args: 208 model_class (str): 209 Model class to check search parameters. 210 auth_header (dict, optional): 211 Auth header to substitute the microservice original 212 at the request (user impersonation). 213 214 Returns: 215 Return a dictionary with keys: 216 - **verbose_field:** String sugesting how the title of the 217 retrieve might be created. It will use Python format 218 information ex.: `'{pk} | {description}'`. 219 - **fieldset:** An dictionary with organization of data, 220 setting field sets that could be grouped toguether in 221 tabs. 222 223 Raises: 224 No particular raises. 225 """ 226 url_str = "rest/{basename}/retrieve-options/".format( 227 basename=model_class.lower()) 228 return self.request_get( 229 url=url_str, auth_header=auth_header)
Return options to render retrieve views.
Return information of the field sets that can be used to create
frontend site. It also return a verbose_field which can be used
to create the title of the page substituing the values with
information of the object.
Arguments:
- model_class (str): Model class to check search parameters.
- auth_header (dict, optional): Auth header to substitute the microservice original at the request (user impersonation).
Returns:
Return a dictionary with keys:
- verbose_field: String sugesting how the title of the retrieve might be created. It will use Python format information ex.:
'{pk} | {description}'.- fieldset: An dictionary with organization of data, setting field sets that could be grouped toguether in tabs.
Raises:
- No particular raises.
231 def fill_validation(self, model_class: str, parcial_obj_dict: dict = None, 232 field: str = None, auth_header: dict = None, 233 user_type: str = 'api') -> dict: 234 """Return options for object fields. 235 236 This function send partial object data and return options to finish 237 object filling. 238 239 Args: 240 model_class (str): 241 Model class to check search parameters. 242 auth_header (dict, optional): 243 Auth header to substitute the microservice original 244 at the request (user impersonation). 245 parcial_obj_dict (dict, optional): 246 Partial object data to be validated by the backend. 247 field (str, optional): 248 Set a specific field to be validated if implemented. 249 user_type (str): 250 Set the type of user is requesting fill validation. It is 251 possible to set `api` and `gui`. Gui user_type will return 252 fields listed in gui_readonly as read-only fields to 253 facilitate navigation. 254 255 Returns: 256 Return a dictionary with keys: 257 - **field_descriptions:** Same of fill_options, but setting as 258 read_only=True fields listed on gui_readonly if 259 user_type='gui'. 260 - **gui_readonly:** Return a list of fields that will be 261 considered as read-only if user_type='gui' is requested. 262 263 Raises: 264 No particular raises. 265 """ 266 parcial_obj_dict = ( 267 parcial_obj_dict if parcial_obj_dict is not None else {}) 268 269 url_str = "rest/{basename}/retrieve-options/".format( 270 basename=model_class.lower()) 271 params = {"user_type": user_type} 272 if field is not None: 273 params["field"] = field 274 return self.request_post( 275 url=url_str, auth_header=auth_header, data=parcial_obj_dict, 276 parameters=params)
Return options for object fields.
This function send partial object data and return options to finish object filling.
Arguments:
- model_class (str): Model class to check search parameters.
- auth_header (dict, optional): Auth header to substitute the microservice original at the request (user impersonation).
- parcial_obj_dict (dict, optional): Partial object data to be validated by the backend.
- field (str, optional): Set a specific field to be validated if implemented.
- user_type (str): Set the type of user is requesting fill validation. It is
possible to set
apiandgui. Gui user_type will return fields listed in gui_readonly as read-only fields to facilitate navigation.
Returns:
Return a dictionary with keys:
- field_descriptions: Same of fill_options, but setting as read_only=True fields listed on gui_readonly if user_type='gui'.
- gui_readonly: Return a list of fields that will be considered as read-only if user_type='gui' is requested.
Raises:
- No particular raises.