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]
class ABCSimpleBatchMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def aggregate( self, model_class: str, group_by: Union[List[str], str], agg: dict, filter_dict: None | dict = None, exclude_dict: dict = None, order_by: List[str] = None, auth_header: dict = None, limit: int = None, as_dataframe: bool = False) -> pandas.DataFrame:
 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 field and function specifing 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"
    }})
def pivot( self, model_class: str, columns: Optional[List[str]] = None, format: str = 'list', filter_dict: dict = None, exclude_dict: dict = None, order_by: List[str] = None, variables: List[str] = None, fields: List[str] = None, show_deleted: bool = False, add_pk_column: bool = False, auth_header: dict = None, as_dataframe: bool = False) -> Union[List[dict], Dict[str, list], pandas.DataFrame]:
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.
def flat_list_by_chunks( self, model_class: str, filter_dict: dict = None, exclude_dict: dict = None, fields: List[str] = None, show_deleted: bool = False, auth_header: dict = None, chunk_size: int = 1000000, n_parallel: int = None, create_composite_pk: bool = False, start_date: str = None, end_date: str = None, time_column: str = 'time') -> pandas.DataFrame:
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.
def get_pks_from_unique_field( self, model_class: str, field: str, values: List[Any]) -> pandas.DataFrame:
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 field does not have a unique restriction on database. Dimension keys does not check for uniqueness on database, be carefull not to duplicate the lines.
class ABCSimpleRetriveMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def list_one( self, model_class: str, pk: int, fields: list = None, default_fields: bool = True, foreign_key_fields: bool = False, related_fields: bool = False, auth_header: dict = None, use_disk_cache: bool = False, use_app_cache: bool = False, disk_cache_expire: int = None, base_filter_skip: list = None) -> dict:
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_id reference to a user model_class the correspondent to User will be returned at created_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 _set at 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.
def retrieve( self, model_class: str, pk: int | str | dict, default_fields: bool = False, foreign_key_fields: bool = False, related_fields: bool = False, fields: list = None, auth_header: dict = None, use_disk_cache: bool = False, use_app_cache: bool = False, disk_cache_expire: int = None, base_filter_skip: list = None) -> dict:
 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.
def retrieve_file( self, model_class: str, pk: int, file_field: str, auth_header: dict = None, save_file: bool = True, save_path: str = './', file_name: str = None, if_exists: str = 'fail', base_filter_skip: list = None) -> <built-in function any>:
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 filename with original file name and content with 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_field parameter 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.
def retrieve_streaming_file( self, model_class: str, pk: int, file_field: str, file_name: str, auth_header: dict = None, save_path: str = './', if_exists: str = 'fail', base_filter_skip: list = None) -> str:
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_field parameter 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.
class ABCSimpleDeleteMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
  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.

def delete( self, model_class: str, pk: int, auth_header: dict = None, base_filter_skip: list[str] | None = None) -> dict:
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 list and list_without_pag end-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.
def delete_file( self, model_class: str, pk: int, file_field: str, auth_header: dict = None, base_filter_skip: list[str] | None = None) -> bool:
 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_field was 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.
def remove_file_field( self, model_class: str, pk: int, file_field: str, auth_header: dict = None, base_filter_skip: list[str] | None = None) -> bool:
 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_field was 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.
def delete_many( self, model_class: str, filter_dict: None | dict = None, exclude_dict: None | dict = None, auth_header: dict = None, base_filter_skip: list[str] = None) -> bool:
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.
class ABCSimpleSaveMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def save( self, obj_dict: dict, files: dict = None, auth_header: dict = None, fields: list = None, default_fields: bool = False, foreign_key_fields: bool = False, related_fields: bool = False, base_filter_skip: list = None, upsert: bool = False) -> dict[str, typing.Any]:
 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_id reference to a user model_class the correspondent to User will be returned at created_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 _set at 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_class indicating 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.
def save_streaming_file( self, model_class: str, pk: int, file_field: str, file: _io.BufferedReader, file_name: str = None, auth_header: dict = None, fields: list = None, default_fields: bool = False, foreign_key_fields: bool = False, related_fields: bool = False, base_filter_skip: list = None) -> str:
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_id reference to a user model_class the correspondent to User will be returned at created_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 _set at 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_field passed 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.
def bulk_save( self, model_class: str, data_to_save: list | pandas.DataFrame, auth_header: dict = None, base_filter_skip: list = None) -> dict:
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_count as 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.
class ABCSimpleListMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def list( self, model_class: str, filter_dict: dict = None, exclude_dict: dict = None, order_by: list = None, auth_header: dict = None, fields: list = None, default_fields: bool = False, limit: int = None, foreign_key_fields: bool = False, base_filter_skip: list[str] = None, as_dataframe: bool = False, **kwargs) -> List[dict]:
 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 boolean value 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_id reference to a user model_class the correspondent to User will be returned at created_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.
def list_by_chunks( self, model_class: str, filter_dict: dict = None, exclude_dict: dict = None, auth_header: dict = None, fields: <function ABCSimpleListMicroservice.list> = None, default_fields: bool = False, chunk_size: int = 50000, limit: int = None, base_filter_skip: <function ABCSimpleListMicroservice.list> = None, as_dataframe: bool = False, **kwargs) -> Union[List[dict], pandas.DataFrame]:
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.
def list_without_pag( self, model_class: str, filter_dict: dict = None, exclude_dict: dict = None, order_by: <function ABCSimpleListMicroservice.list> = None, auth_header: dict = None, convert_geometry: bool = True, fields: <function ABCSimpleListMicroservice.list> = None, default_fields: bool = False, foreign_key_fields: bool = False, base_filter_skip: <function ABCSimpleListMicroservice.list> = None, as_dataframe: bool = False, **kwargs) -> List[dict]:
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_id reference to a user model_class the correspondent to User will be returned at created_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.
class ABCSimpleDimensionMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def list_dimensions( self, model_class: str, filter_dict: dict = {}, exclude_dict: dict = {}, auth_header: dict = None, base_filter_skip: list = None) -> list[str]:
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.

def list_dimension_values( self, model_class: str, key: str, filter_dict: dict = {}, exclude_dict: dict = {}, auth_header: dict = None, base_filter_skip: list = None) -> list[any]:
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_dict and exclude_dict.

class ABCSimpleActionMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
  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.

def list_actions(self, model_class: str, auth_header: dict = None) -> list[dict]:
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.
def execute_action( self, model_class: str, action: str, pk: int = None, parameters: dict = {}, files: list = None, auth_header: dict = None, base_filter_skip: list[str] | list[list[str]] = None) -> dict:
 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.
class ABCSimpleInfoMicroservice(abc.ABC, pumpwood_communication.microservice_abc.base.base.PumpWoodMicroServiceBase):
 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.

def search_options(self, model_class: str, auth_header: dict = None) -> dict:
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 in keys. 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.
def fill_options( self, model_class, parcial_obj_dict: dict = None, field: str = None, auth_header: dict = None, use_disk_cache: bool = True):
 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 in keys. 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.
def list_options(self, model_class: str, auth_header: dict) -> dict:
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.
def retrieve_options(self, model_class: str, auth_header: dict = None) -> dict:
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.
def fill_validation( self, model_class: str, parcial_obj_dict: dict = None, field: str = None, auth_header: dict = None, user_type: str = 'api') -> dict:
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 api and gui. 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.