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                  show_deleted: bool = False,
 33                  ) -> pd.DataFrame:
 34        """Save a list of objects with one request.
 35
 36        Avaiable aggregate functions:
 37            - **sum:** Sum.
 38            - **mean:** Mean.
 39            - **count:** Count.
 40            - **min:** Minimum.
 41            - **max:** Maximum.
 42            - **stddev_pop:** Population standard deviation.
 43            - **stddev_samp:** Sample standard deviation.
 44            - **var_pop:** Population variance.
 45            - **var_samp:** Sample variance.
 46            - **std:** Sample standard deviation.
 47            - **var:** Sample variance.
 48
 49        Args:
 50            model_class (str):
 51                Model class of the end-point that will be aggregated.
 52            group_by (List[str] | str):
 53                List of the fields that will be used on aggregation as
 54                group by. If a string is passed as argument, it will be
 55                considered the sigle group_by column.
 56            agg (dict):
 57                A dictionary with dictionary items as `field` and `function`
 58                specifing the field that will be aggregated using a function.
 59
 60                The dictinary keys will be used to return the results as
 61                columns.
 62            show_deleted (bool):
 63                If deleted data should be returned.
 64            filter_dict (dict):
 65                Filter that will be applied before the aggregation.
 66            exclude_dict (dict):
 67                Exclude clause that will be applied before the aggregation.
 68            order_by (list):
 69                Ordenation acording to grouping elements. It can be used
 70                fields created as keys of agg dictinary.
 71            auth_header (dict):
 72                Authentication header used to impersonation of user.
 73            limit (int):
 74                Limit number of returned row at aggregation query.
 75            as_dataframe (bool):
 76                If result should be returned as a dataframe. The columns
 77                will be set to match the group_by and agg arguments, this
 78                way empty dataframe with respect the columns.
 79
 80        Returns:
 81            Return a DataFrame with aggregation results.
 82
 83        Example:
 84            ```
 85            microservice.aggregate(
 86                model_class="ToLoadCalendar",
 87                group_by=["calendar_id"],
 88                agg={
 89                    "n": {"field": "id", "function": "count"},
 90                    "mean": {"field": "value", "function": "mean"
 91                }})
 92            ```
 93        """
 94        filter_dict = {} if filter_dict is None else filter_dict
 95        exclude_dict = {} if exclude_dict is None else exclude_dict
 96        order_by = [] if order_by is None else order_by
 97
 98        # If group_by is a string, convert to a list with this string
 99        group_by = [group_by] if isinstance(group_by, str) else group_by
100        if not isinstance(group_by, (list, tuple, set)):
101            error_msg = "Argument `group_by` must be list, tuple, set or str."
102            raise TypeError(error_msg)
103        if not isinstance(agg, (dict)):
104            error_msg = "Argument `agg` must be dict."
105            raise TypeError(error_msg)
106
107        url_str = self._build_aggregate_url(model_class=model_class)
108        data = {
109            'agg': agg, 'group_by': group_by, 'filter_dict': filter_dict,
110            'exclude_dict': exclude_dict, 'order_by': order_by,
111            'limit': limit, 'show_deleted': show_deleted}
112        return_data = self.request_post(
113            url=url_str, data=data, auth_header=auth_header)
114        if not as_dataframe:
115            return return_data
116        else:
117            # Return the results as a dataframe using the group_by columns
118            # and the columns created at aggregation to ensure that
119            # even empty results would have the correct columns
120            return_columns = group_by + list(agg.keys())
121            return pd.DataFrame(return_data, columns=return_columns)
122
123    @staticmethod
124    def _build_pivot_url(model_class):
125        return "rest/%s/pivot/" % (model_class.lower(), )
126
127    def pivot(self, model_class: str, columns: None | List[str] = None,
128              format: str = 'list', filter_dict: dict = None,
129              exclude_dict: dict = None, order_by: List[str] = None,
130              variables: List[str] = None, fields: List[str] = None,
131              show_deleted: bool = False,
132              add_pk_column: bool = False, auth_header: dict = None,
133              as_dataframe: bool = False
134              ) -> Union[List[dict], Dict[str, list], pd.DataFrame]:
135        """Pivot object data acording to columns specified.
136
137        Pivoting per-se is not usually used, beeing the name of the function
138        a legacy. Normality data transformation is done at the client level.
139
140        Args:
141            model_class (str):
142                Model class to check search parameters.
143            columns (List[str]):
144                List of fields to be used as columns when pivoting the data.
145            format (str):
146                Format to be used to convert pandas.DataFrame to
147                dictionary, must be in ['dict','list','series',
148                'split', 'records','index'].
149            filter_dict (dict):
150                Same as list function.
151            exclude_dict (dict):
152                Same as list function.
153            order_by (List[str]):
154                 Same as list function.
155            fields (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            variables (List[str]):
159                List of the fields to be returned, if None, the default
160                variables will be returned. Same as fields on list functions.
161                **DEPRECTED** use fields.
162            show_deleted (bool):
163                Fields with deleted column will have objects with deleted=True
164                omited from results. show_deleted=True will return this
165                information.
166            add_pk_column (bool):
167                If add pk values of the objects at pivot results. Adding
168                pk key on pivot end-points won't be possible to pivot since
169                pk is unique for each entry.
170            auth_header (dict):
171                Auth header to substitute the microservice original
172                at the request (user impersonation).
173            as_dataframe (bool):
174                If results should be returned as a dataframe.
175
176        Returns:
177            Return a list or a dictinary depending on the format set on
178            format parameter.
179
180        Raises:
181            PumpWoodException:
182                'Columns must be a list of elements.'. Indicates that the list
183                argument was not a list.
184            PumpWoodException:
185                'Column chosen as pivot is not at model variables'. Indicates
186                that columns that were set to pivot are not present on model
187                variables.
188            PumpWoodException:
189                "Format must be in ['dict','list','series','split',
190                'records','index']". Indicates that format set as paramenter
191                is not implemented.
192            PumpWoodException:
193                "Can not add pk column and pivot information". If
194                add_pk_column is True (results will have the pk column), it is
195                not possible to pivot the information (pk is an unique value
196                for each object, there is no reason to pivot it).
197            PumpWoodException:
198                "'value' column not at melted data, it is not possible
199                to pivot dataframe.". Indicates that data does not have a value
200                column, it must have it to populate pivoted table.
201        """
202        # Deprect variables argument
203        if variables is not None:
204            msg = (
205                "Deprecation Warning: pivot variable is deprected, "
206                "you should use fields instead")
207            logger.warning(msg)
208        fields = fields if fields is not None else variables
209
210        filter_dict = {} if filter_dict is None else filter_dict
211        exclude_dict = {} if exclude_dict is None else exclude_dict
212        order_by = [] if order_by is None else order_by
213        columns = [] if columns is None else columns
214
215        url_str = self._build_pivot_url(model_class)
216        post_data = {
217            'columns': columns, 'format': format,
218            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
219            'order_by': order_by, "variables": variables,
220            "show_deleted": show_deleted, "add_pk_column": add_pk_column}
221        pivot_results = self.request_post(
222            url=url_str, data=post_data, auth_header=auth_header)
223
224        if not add_pk_column:
225            if as_dataframe:
226                # If passed, set the fields as columns to ensure empty
227                # dataframes to have the correct fields
228                return pd.DataFrame(pivot_results, columns=fields)
229            else:
230                return pivot_results
231        else:
232            # If passed, set the fields as columns to ensure empty
233            # dataframes to have the correct fields
234            pd_pivot_results = pd.DataFrame(pivot_results, columns=fields)
235            if len(pd_pivot_results) != 0:
236                fill_options = self.fill_options(
237                    model_class=model_class, auth_header=auth_header)
238                primary_keys_data = fill_options["pk"]
239                # Allow legacy pk definition and actual that uses extra_info
240                primary_keys = primary_keys_data\
241                    .get('extra_info', {})\
242                    .get('columns', primary_keys_data['column'])
243                pd_pivot_results["pk"] = pd_pivot_results[primary_keys].apply(
244                    CompositePkBase64Converter.dump,
245                    primary_keys=primary_keys, axis=1)
246            if as_dataframe:
247                return pd_pivot_results
248            else:
249                return pd_pivot_results.to_dict(format)
250
251    def _flat_list_by_chunks_helper(self, args):
252        try:
253            # Unpacking arguments
254            model_class = args["model_class"]
255            filter_dict = args["filter_dict"]
256            exclude_dict = args["exclude_dict"]
257            fields = args["fields"]
258            show_deleted = args["show_deleted"]
259            auth_header = args["auth_header"]
260            chunk_size = args["chunk_size"]
261
262            temp_filter_dict = copy.deepcopy(filter_dict)
263            url_str = self._build_pivot_url(model_class)
264            max_pk = 0
265
266            # Fetch data until an empty result is returned
267            list_dataframes = []
268            while True:
269                sys.stdout.write(".")
270                sys.stdout.flush()
271                temp_filter_dict["id__gt"] = max_pk
272                post_data = {
273                    'format': 'list',
274                    'filter_dict': temp_filter_dict,
275                    'exclude_dict': exclude_dict,
276                    'order_by': ["id"], "variables": fields,
277                    "show_deleted": show_deleted,
278                    "limit": chunk_size,
279                    "add_pk_column": True}
280                temp_dateframe = pd.DataFrame(self.request_post(
281                    url=url_str, data=post_data, auth_header=auth_header))
282
283                # Break if results are less than chunk size, so no more results
284                # are avaiable
285                if len(temp_dateframe) < chunk_size:
286                    list_dataframes.append(temp_dateframe)
287                    break
288
289                max_pk = int(temp_dateframe["id"].max())
290                list_dataframes.append(temp_dateframe)
291
292            if len(list_dataframes) == 0:
293                return pd.DataFrame()
294            else:
295                return pd.concat(list_dataframes)
296        except Exception as e:
297            raise Exception("Exception at flat_list_by_chunks:", str(e))
298
299    def flat_list_by_chunks(self, model_class: str, filter_dict: dict = None,
300                            exclude_dict: dict = None,
301                            fields: List[str] = None,
302                            show_deleted: bool = False,
303                            auth_header: dict = None,
304                            chunk_size: int = 1000000,
305                            n_parallel: int = None,
306                            create_composite_pk: bool = False,
307                            start_date: str = None,
308                            end_date: str = None,
309                            time_column: str = 'time') -> pd.DataFrame:
310        """Incrementally fetch data from pivot end-point.
311
312        Fetch data from pivot end-point paginating by id of chunk_size length.
313
314        If table is partitioned it will split the query acording to partition
315        to facilitate query at the database.
316
317        If start_date and end_date are set, also breaks the query by month
318        retrieving each month data in parallel.
319
320        Args:
321            model_class (str):
322                Model class to be pivoted.
323            filter_dict (dict):
324                Dictionary to to be used in objects.filter argument
325                (Same as list end-point).
326            exclude_dict (dict):
327                Dictionary to to be used in objects.exclude argument
328                (Same as list end-point).
329            fields (List[str] | None):
330                List of the variables to be returned,
331                if None, the default variables will be returned.
332                If fields is set, dataframe will return that columns
333                even if data is empty.
334            start_date (datetime | str):
335                Set a begin date for the query. If begin and end date are
336                set, query will be splited with chucks by month that will be
337                requested in parallel.
338            end_date (datetime | str):
339                Set a end date for the query. If begin and end date are
340                set, query will be splited with chucks by month that will be
341                requested in parallel.
342            show_deleted (bool):
343                If deleted data should be returned.
344            auth_header (dict):
345                Auth header to substitute the microservice original
346                at the request (user impersonation).
347            chunk_size (int):
348                Limit of data to fetch per call.
349            n_parallel (int):
350                Number of parallel process to perform.
351            create_composite_pk (bool):
352                If true and table has a composite pk, it will create pk
353                value based on the hash on the json serialized dictionary
354                of the components of the primary key.
355            time_column (str):
356                Column that will be considered on the partitioning of data
357                fetch.
358
359        Returns:
360            Returns a dataframe with all information fetched.
361
362        Raises:
363            No particular raise.
364        """
365        # Set empty dictionary for dictionary arguments
366        filter_dict = {} if filter_dict is None else filter_dict
367        exclude_dict = {} if exclude_dict is None else exclude_dict
368        original_fields = None if fields is None else fields
369
370        if n_parallel is None:
371            n_parallel = PUMPWOOD_COMUNICATION__N_PARALLEL
372
373        fill_options = self.fill_options(
374            model_class=model_class, auth_header=auth_header)
375
376        # Retrieve PK infomration about primary_key and partitions
377        primary_keys = fill_options["pk"]["extra_info"]['columns']
378        # partition = fill_options["pk"]["extra_info"]['partition']
379
380        # Add all primary keys fields to query if create_composite_pk is
381        # set true
382        if create_composite_pk and fields is not None:
383            fields = list(set(fields) | set(primary_keys))
384
385        # Create a list of month and include start and end dates if not at
386        # the beginning of a month
387        month_sequence = None
388        if (start_date is not None) and (end_date is not None):
389            month_sequence = AuxFlatListByChunks.build_month_partitions(
390                start_date=start_date,
391                end_date=end_date)
392        elif (start_date is not None) or (end_date is not None):
393            # To create the partitions is necessary to have both start and end
394            # date.
395            msg = (
396                "To break query in chunks using start_date and end_date "
397                "both must be set.\n- start_date: {start_date}\n"
398                "- end_date: {end_date}")
399            raise PumpWoodException(
400                message=msg, payload={
401                    "start_date": start_date,
402                    "end_date": end_date})
403
404        pool_arguments = AuxFlatListByChunks.build_query_partitions(
405            model_class=model_class, time_partitions=month_sequence,
406            filter_dict=filter_dict, exclude_dict=exclude_dict, fields=fields,
407            time_column=time_column, show_deleted=show_deleted,
408            auth_header=auth_header, chunk_size=chunk_size)
409
410        # Perform parallel calls to backend each chucked by chunk_size
411        print("## Starting parallel flat list: %s" % len(pool_arguments))
412        resp_df = None
413        try:
414            with Pool(n_parallel) as p:
415                results = p.map(
416                    self._flat_list_by_chunks_helper,
417                    pool_arguments)
418            if len(results) != 0:
419                resp_df = pd.concat(results)\
420                    .reset_index(drop=True)
421            else:
422                resp_df = pd.DataFrame(columns=fields)
423        except Exception as e:
424            PumpWoodException(message=str(e))
425        print("\n## Finished parallel flat list: %s" % len(pool_arguments))
426
427        # Add the primary key as a column to the dataframe
428        resp_df = AuxFlatListByChunks.add_pk_column(
429            create_composite_pk=create_composite_pk,
430            primary_key_list=primary_keys,
431            data=resp_df)
432
433        # Limit the return fields
434        if original_fields is not None:
435            if create_composite_pk:
436                return pd.DataFrame(
437                    resp_df, columns=['pk'] + original_fields)
438            else:
439                return pd.DataFrame(
440                    resp_df, columns=original_fields)
441        else:
442            return resp_df
443
444    def get_pks_from_unique_field(self, model_class: str, field: str,
445                                  values: List[Any]) -> pd.DataFrame:
446        """Get pk using unique fields values.
447
448        Use unique field values to retrieve pk of the objects. This end-point
449        is usefull for retrieving pks of the objects associated with unique
450        fields such as `description` (unique on most model of pumpwood).
451
452        ```python
453        # Using description to fetch pks from objects
454        data: pd.DataFrame = [data with unique description but without pk]
455        data['attribute_id'] = microservice.get_pks_from_unique_field(
456            model_class="DescriptionAttribute",
457            field="description", values=data['attribute'])['pk']
458
459        # Using a dimension key to fetch pk of the objects, dimension
460        # key must be unique
461        data['georea_id'] = microservice.get_pks_from_unique_field(
462            model_class="DescriptionGeoarea", field="dimension->city",
463            values=data['city'])['pk']
464        ```
465
466        Args:
467            model_class:
468                Model class of the objects.
469            field:
470                Unique field to fetch pk. It is possible to use dimension keys
471                as unique field, for that use `dimension->[key]` notation.
472            values:
473                List of the unique fields used to fetch primary keys.
474
475        Return:
476            Return a dataframe in same order as values with columns:
477            - **pk**: Correspondent primary key of the unique value.
478            - **[field]**: Column with same name of field argument,
479                correspondent to pk.
480
481        Raises:
482            PumpWoodQueryException:
483                Raises if field is not found on the model and it is note
484                associated with a dimension tag.
485            PumpWoodQueryException:
486                Raises if `field` does not have a unique restriction on
487                database. Dimension keys does not check for uniqueness on
488                database, be carefull not to duplicate the lines.
489        """
490        is_dimension_tag = 'dimensions->' in field
491        if not is_dimension_tag:
492            fill_options = self.fill_options(model_class=model_class)
493            field_details = fill_options.get(field)
494            if field_details is None:
495                msg = (
496                    "Field is not a dimension tag and not found on model "
497                    "fields. Field [{field}]")
498                raise PumpWoodQueryException(
499                    message=msg, payload={"field": field})
500
501            is_unique_field = field_details.get("unique", False)
502            if not is_unique_field:
503                msg = "Field [{field}] to get pk from is not unique"
504                raise PumpWoodQueryException(
505                    message=msg, payload={"field": field})
506
507        filter_dict = {field + "__in": list(set(values))}
508        pk_map = None
509        if not is_dimension_tag:
510            list_results = pd.DataFrame(self.list_without_pag(
511                model_class=model_class, filter_dict=filter_dict,
512                fields=["pk", field]), columns=["pk", field])
513            pk_map = list_results.set_index(field)["pk"]
514
515        # If is dimension tag, fetch dimension and unpack it
516        else:
517            dimension_tag = field.split("->")[1]
518            list_results = pd.DataFrame(self.list_without_pag(
519                model_class=model_class, filter_dict=filter_dict,
520                fields=["pk", "dimensions"]))
521            pk_map = {}
522            if len(list_results) != 0:
523                pk_map = list_results\
524                    .pipe(unpack_dict_columns, columns=["dimensions"])\
525                    .set_index(dimension_tag)["pk"]
526
527        values_series = pd.Series(values)
528        return pd.DataFrame({
529            "pk": values_series.map(pk_map).to_numpy(),
530            field: values_series
531        })

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, show_deleted: 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                  show_deleted: bool = False,
 33                  ) -> pd.DataFrame:
 34        """Save a list of objects with one request.
 35
 36        Avaiable aggregate functions:
 37            - **sum:** Sum.
 38            - **mean:** Mean.
 39            - **count:** Count.
 40            - **min:** Minimum.
 41            - **max:** Maximum.
 42            - **stddev_pop:** Population standard deviation.
 43            - **stddev_samp:** Sample standard deviation.
 44            - **var_pop:** Population variance.
 45            - **var_samp:** Sample variance.
 46            - **std:** Sample standard deviation.
 47            - **var:** Sample variance.
 48
 49        Args:
 50            model_class (str):
 51                Model class of the end-point that will be aggregated.
 52            group_by (List[str] | str):
 53                List of the fields that will be used on aggregation as
 54                group by. If a string is passed as argument, it will be
 55                considered the sigle group_by column.
 56            agg (dict):
 57                A dictionary with dictionary items as `field` and `function`
 58                specifing the field that will be aggregated using a function.
 59
 60                The dictinary keys will be used to return the results as
 61                columns.
 62            show_deleted (bool):
 63                If deleted data should be returned.
 64            filter_dict (dict):
 65                Filter that will be applied before the aggregation.
 66            exclude_dict (dict):
 67                Exclude clause that will be applied before the aggregation.
 68            order_by (list):
 69                Ordenation acording to grouping elements. It can be used
 70                fields created as keys of agg dictinary.
 71            auth_header (dict):
 72                Authentication header used to impersonation of user.
 73            limit (int):
 74                Limit number of returned row at aggregation query.
 75            as_dataframe (bool):
 76                If result should be returned as a dataframe. The columns
 77                will be set to match the group_by and agg arguments, this
 78                way empty dataframe with respect the columns.
 79
 80        Returns:
 81            Return a DataFrame with aggregation results.
 82
 83        Example:
 84            ```
 85            microservice.aggregate(
 86                model_class="ToLoadCalendar",
 87                group_by=["calendar_id"],
 88                agg={
 89                    "n": {"field": "id", "function": "count"},
 90                    "mean": {"field": "value", "function": "mean"
 91                }})
 92            ```
 93        """
 94        filter_dict = {} if filter_dict is None else filter_dict
 95        exclude_dict = {} if exclude_dict is None else exclude_dict
 96        order_by = [] if order_by is None else order_by
 97
 98        # If group_by is a string, convert to a list with this string
 99        group_by = [group_by] if isinstance(group_by, str) else group_by
100        if not isinstance(group_by, (list, tuple, set)):
101            error_msg = "Argument `group_by` must be list, tuple, set or str."
102            raise TypeError(error_msg)
103        if not isinstance(agg, (dict)):
104            error_msg = "Argument `agg` must be dict."
105            raise TypeError(error_msg)
106
107        url_str = self._build_aggregate_url(model_class=model_class)
108        data = {
109            'agg': agg, 'group_by': group_by, 'filter_dict': filter_dict,
110            'exclude_dict': exclude_dict, 'order_by': order_by,
111            'limit': limit, 'show_deleted': show_deleted}
112        return_data = self.request_post(
113            url=url_str, data=data, auth_header=auth_header)
114        if not as_dataframe:
115            return return_data
116        else:
117            # Return the results as a dataframe using the group_by columns
118            # and the columns created at aggregation to ensure that
119            # even empty results would have the correct columns
120            return_columns = group_by + list(agg.keys())
121            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.

  • show_deleted (bool): If deleted data should be returned.
  • 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]:
127    def pivot(self, model_class: str, columns: None | List[str] = None,
128              format: str = 'list', filter_dict: dict = None,
129              exclude_dict: dict = None, order_by: List[str] = None,
130              variables: List[str] = None, fields: List[str] = None,
131              show_deleted: bool = False,
132              add_pk_column: bool = False, auth_header: dict = None,
133              as_dataframe: bool = False
134              ) -> Union[List[dict], Dict[str, list], pd.DataFrame]:
135        """Pivot object data acording to columns specified.
136
137        Pivoting per-se is not usually used, beeing the name of the function
138        a legacy. Normality data transformation is done at the client level.
139
140        Args:
141            model_class (str):
142                Model class to check search parameters.
143            columns (List[str]):
144                List of fields to be used as columns when pivoting the data.
145            format (str):
146                Format to be used to convert pandas.DataFrame to
147                dictionary, must be in ['dict','list','series',
148                'split', 'records','index'].
149            filter_dict (dict):
150                Same as list function.
151            exclude_dict (dict):
152                Same as list function.
153            order_by (List[str]):
154                 Same as list function.
155            fields (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            variables (List[str]):
159                List of the fields to be returned, if None, the default
160                variables will be returned. Same as fields on list functions.
161                **DEPRECTED** use fields.
162            show_deleted (bool):
163                Fields with deleted column will have objects with deleted=True
164                omited from results. show_deleted=True will return this
165                information.
166            add_pk_column (bool):
167                If add pk values of the objects at pivot results. Adding
168                pk key on pivot end-points won't be possible to pivot since
169                pk is unique for each entry.
170            auth_header (dict):
171                Auth header to substitute the microservice original
172                at the request (user impersonation).
173            as_dataframe (bool):
174                If results should be returned as a dataframe.
175
176        Returns:
177            Return a list or a dictinary depending on the format set on
178            format parameter.
179
180        Raises:
181            PumpWoodException:
182                'Columns must be a list of elements.'. Indicates that the list
183                argument was not a list.
184            PumpWoodException:
185                'Column chosen as pivot is not at model variables'. Indicates
186                that columns that were set to pivot are not present on model
187                variables.
188            PumpWoodException:
189                "Format must be in ['dict','list','series','split',
190                'records','index']". Indicates that format set as paramenter
191                is not implemented.
192            PumpWoodException:
193                "Can not add pk column and pivot information". If
194                add_pk_column is True (results will have the pk column), it is
195                not possible to pivot the information (pk is an unique value
196                for each object, there is no reason to pivot it).
197            PumpWoodException:
198                "'value' column not at melted data, it is not possible
199                to pivot dataframe.". Indicates that data does not have a value
200                column, it must have it to populate pivoted table.
201        """
202        # Deprect variables argument
203        if variables is not None:
204            msg = (
205                "Deprecation Warning: pivot variable is deprected, "
206                "you should use fields instead")
207            logger.warning(msg)
208        fields = fields if fields is not None else variables
209
210        filter_dict = {} if filter_dict is None else filter_dict
211        exclude_dict = {} if exclude_dict is None else exclude_dict
212        order_by = [] if order_by is None else order_by
213        columns = [] if columns is None else columns
214
215        url_str = self._build_pivot_url(model_class)
216        post_data = {
217            'columns': columns, 'format': format,
218            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
219            'order_by': order_by, "variables": variables,
220            "show_deleted": show_deleted, "add_pk_column": add_pk_column}
221        pivot_results = self.request_post(
222            url=url_str, data=post_data, auth_header=auth_header)
223
224        if not add_pk_column:
225            if as_dataframe:
226                # If passed, set the fields as columns to ensure empty
227                # dataframes to have the correct fields
228                return pd.DataFrame(pivot_results, columns=fields)
229            else:
230                return pivot_results
231        else:
232            # If passed, set the fields as columns to ensure empty
233            # dataframes to have the correct fields
234            pd_pivot_results = pd.DataFrame(pivot_results, columns=fields)
235            if len(pd_pivot_results) != 0:
236                fill_options = self.fill_options(
237                    model_class=model_class, auth_header=auth_header)
238                primary_keys_data = fill_options["pk"]
239                # Allow legacy pk definition and actual that uses extra_info
240                primary_keys = primary_keys_data\
241                    .get('extra_info', {})\
242                    .get('columns', primary_keys_data['column'])
243                pd_pivot_results["pk"] = pd_pivot_results[primary_keys].apply(
244                    CompositePkBase64Converter.dump,
245                    primary_keys=primary_keys, axis=1)
246            if as_dataframe:
247                return pd_pivot_results
248            else:
249                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:
299    def flat_list_by_chunks(self, model_class: str, filter_dict: dict = None,
300                            exclude_dict: dict = None,
301                            fields: List[str] = None,
302                            show_deleted: bool = False,
303                            auth_header: dict = None,
304                            chunk_size: int = 1000000,
305                            n_parallel: int = None,
306                            create_composite_pk: bool = False,
307                            start_date: str = None,
308                            end_date: str = None,
309                            time_column: str = 'time') -> pd.DataFrame:
310        """Incrementally fetch data from pivot end-point.
311
312        Fetch data from pivot end-point paginating by id of chunk_size length.
313
314        If table is partitioned it will split the query acording to partition
315        to facilitate query at the database.
316
317        If start_date and end_date are set, also breaks the query by month
318        retrieving each month data in parallel.
319
320        Args:
321            model_class (str):
322                Model class to be pivoted.
323            filter_dict (dict):
324                Dictionary to to be used in objects.filter argument
325                (Same as list end-point).
326            exclude_dict (dict):
327                Dictionary to to be used in objects.exclude argument
328                (Same as list end-point).
329            fields (List[str] | None):
330                List of the variables to be returned,
331                if None, the default variables will be returned.
332                If fields is set, dataframe will return that columns
333                even if data is empty.
334            start_date (datetime | str):
335                Set a begin date for the query. If begin and end date are
336                set, query will be splited with chucks by month that will be
337                requested in parallel.
338            end_date (datetime | str):
339                Set a end date for the query. If begin and end date are
340                set, query will be splited with chucks by month that will be
341                requested in parallel.
342            show_deleted (bool):
343                If deleted data should be returned.
344            auth_header (dict):
345                Auth header to substitute the microservice original
346                at the request (user impersonation).
347            chunk_size (int):
348                Limit of data to fetch per call.
349            n_parallel (int):
350                Number of parallel process to perform.
351            create_composite_pk (bool):
352                If true and table has a composite pk, it will create pk
353                value based on the hash on the json serialized dictionary
354                of the components of the primary key.
355            time_column (str):
356                Column that will be considered on the partitioning of data
357                fetch.
358
359        Returns:
360            Returns a dataframe with all information fetched.
361
362        Raises:
363            No particular raise.
364        """
365        # Set empty dictionary for dictionary arguments
366        filter_dict = {} if filter_dict is None else filter_dict
367        exclude_dict = {} if exclude_dict is None else exclude_dict
368        original_fields = None if fields is None else fields
369
370        if n_parallel is None:
371            n_parallel = PUMPWOOD_COMUNICATION__N_PARALLEL
372
373        fill_options = self.fill_options(
374            model_class=model_class, auth_header=auth_header)
375
376        # Retrieve PK infomration about primary_key and partitions
377        primary_keys = fill_options["pk"]["extra_info"]['columns']
378        # partition = fill_options["pk"]["extra_info"]['partition']
379
380        # Add all primary keys fields to query if create_composite_pk is
381        # set true
382        if create_composite_pk and fields is not None:
383            fields = list(set(fields) | set(primary_keys))
384
385        # Create a list of month and include start and end dates if not at
386        # the beginning of a month
387        month_sequence = None
388        if (start_date is not None) and (end_date is not None):
389            month_sequence = AuxFlatListByChunks.build_month_partitions(
390                start_date=start_date,
391                end_date=end_date)
392        elif (start_date is not None) or (end_date is not None):
393            # To create the partitions is necessary to have both start and end
394            # date.
395            msg = (
396                "To break query in chunks using start_date and end_date "
397                "both must be set.\n- start_date: {start_date}\n"
398                "- end_date: {end_date}")
399            raise PumpWoodException(
400                message=msg, payload={
401                    "start_date": start_date,
402                    "end_date": end_date})
403
404        pool_arguments = AuxFlatListByChunks.build_query_partitions(
405            model_class=model_class, time_partitions=month_sequence,
406            filter_dict=filter_dict, exclude_dict=exclude_dict, fields=fields,
407            time_column=time_column, show_deleted=show_deleted,
408            auth_header=auth_header, chunk_size=chunk_size)
409
410        # Perform parallel calls to backend each chucked by chunk_size
411        print("## Starting parallel flat list: %s" % len(pool_arguments))
412        resp_df = None
413        try:
414            with Pool(n_parallel) as p:
415                results = p.map(
416                    self._flat_list_by_chunks_helper,
417                    pool_arguments)
418            if len(results) != 0:
419                resp_df = pd.concat(results)\
420                    .reset_index(drop=True)
421            else:
422                resp_df = pd.DataFrame(columns=fields)
423        except Exception as e:
424            PumpWoodException(message=str(e))
425        print("\n## Finished parallel flat list: %s" % len(pool_arguments))
426
427        # Add the primary key as a column to the dataframe
428        resp_df = AuxFlatListByChunks.add_pk_column(
429            create_composite_pk=create_composite_pk,
430            primary_key_list=primary_keys,
431            data=resp_df)
432
433        # Limit the return fields
434        if original_fields is not None:
435            if create_composite_pk:
436                return pd.DataFrame(
437                    resp_df, columns=['pk'] + original_fields)
438            else:
439                return pd.DataFrame(
440                    resp_df, columns=original_fields)
441        else:
442            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:
444    def get_pks_from_unique_field(self, model_class: str, field: str,
445                                  values: List[Any]) -> pd.DataFrame:
446        """Get pk using unique fields values.
447
448        Use unique field values to retrieve pk of the objects. This end-point
449        is usefull for retrieving pks of the objects associated with unique
450        fields such as `description` (unique on most model of pumpwood).
451
452        ```python
453        # Using description to fetch pks from objects
454        data: pd.DataFrame = [data with unique description but without pk]
455        data['attribute_id'] = microservice.get_pks_from_unique_field(
456            model_class="DescriptionAttribute",
457            field="description", values=data['attribute'])['pk']
458
459        # Using a dimension key to fetch pk of the objects, dimension
460        # key must be unique
461        data['georea_id'] = microservice.get_pks_from_unique_field(
462            model_class="DescriptionGeoarea", field="dimension->city",
463            values=data['city'])['pk']
464        ```
465
466        Args:
467            model_class:
468                Model class of the objects.
469            field:
470                Unique field to fetch pk. It is possible to use dimension keys
471                as unique field, for that use `dimension->[key]` notation.
472            values:
473                List of the unique fields used to fetch primary keys.
474
475        Return:
476            Return a dataframe in same order as values with columns:
477            - **pk**: Correspondent primary key of the unique value.
478            - **[field]**: Column with same name of field argument,
479                correspondent to pk.
480
481        Raises:
482            PumpWoodQueryException:
483                Raises if field is not found on the model and it is note
484                associated with a dimension tag.
485            PumpWoodQueryException:
486                Raises if `field` does not have a unique restriction on
487                database. Dimension keys does not check for uniqueness on
488                database, be carefull not to duplicate the lines.
489        """
490        is_dimension_tag = 'dimensions->' in field
491        if not is_dimension_tag:
492            fill_options = self.fill_options(model_class=model_class)
493            field_details = fill_options.get(field)
494            if field_details is None:
495                msg = (
496                    "Field is not a dimension tag and not found on model "
497                    "fields. Field [{field}]")
498                raise PumpWoodQueryException(
499                    message=msg, payload={"field": field})
500
501            is_unique_field = field_details.get("unique", False)
502            if not is_unique_field:
503                msg = "Field [{field}] to get pk from is not unique"
504                raise PumpWoodQueryException(
505                    message=msg, payload={"field": field})
506
507        filter_dict = {field + "__in": list(set(values))}
508        pk_map = None
509        if not is_dimension_tag:
510            list_results = pd.DataFrame(self.list_without_pag(
511                model_class=model_class, filter_dict=filter_dict,
512                fields=["pk", field]), columns=["pk", field])
513            pk_map = list_results.set_index(field)["pk"]
514
515        # If is dimension tag, fetch dimension and unpack it
516        else:
517            dimension_tag = field.split("->")[1]
518            list_results = pd.DataFrame(self.list_without_pag(
519                model_class=model_class, filter_dict=filter_dict,
520                fields=["pk", "dimensions"]))
521            pk_map = {}
522            if len(list_results) != 0:
523                pk_map = list_results\
524                    .pipe(unpack_dict_columns, columns=["dimensions"])\
525                    .set_index(dimension_tag)["pk"]
526
527        values_series = pd.Series(values)
528        return pd.DataFrame({
529            "pk": values_series.map(pk_map).to_numpy(),
530            field: values_series
531        })

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 = self._resolve_base_filter_skip(
 81            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        
185        base_filter_skip = self._resolve_base_filter_skip(
186            base_filter_skip)
187
188        # Convert to base64 dict unique queries
189        serialized_pk = None
190        if isinstance(pk, dict):
191            # Use the correct keyword argument 'primary_key_dict'
192            serialized_pk = CompositePkBase64Converter.dump_dict(
193                primary_key_dict=pk)
194        else:
195            serialized_pk = pk
196
197        # Fetch information from Pumpwood
198        url_str = self._build_retrieve_url(
199            model_class=model_class, pk=serialized_pk)
200        return self.request_get(
201            url=url_str, parameters={
202                "fields": fields, "default_fields": default_fields,
203                "foreign_key_fields": foreign_key_fields,
204                "related_fields": related_fields,
205                "base_filter_skip": base_filter_skip,
206                "use_cache": use_app_cache},
207            auth_header=auth_header, use_disk_cache=use_disk_cache,
208            disk_cache_expire=disk_cache_expire)
209
210    @staticmethod
211    def _build_retrieve_file_url(model_class: str, pk: int):
212        return "rest/%s/retrieve-file/%s/" % (model_class.lower(), pk)
213
214    def retrieve_file(self, model_class: str, pk: int, file_field: str,
215                      auth_header: dict = None, save_file: bool = True,
216                      save_path: str = "./", file_name: str = None,
217                      if_exists: str = "fail",
218                      base_filter_skip: list = None) -> any:
219        """Retrieve a file from PumpWood.
220
221        This function will retrieve file as a single request, depending on the
222        size of the files it would be preferred to use streaming end-point.
223
224        Args:
225            model_class:
226                Class of the model to retrieve file.
227            pk:
228                Pk of the object associeted file.
229            file_field:
230                Field of the file to be downloaded.
231            auth_header:
232                Dictionary containing the auth header.
233            save_file:
234                If data is to be saved as file or return get
235                response.
236            save_path:
237                Path of the directory to save file.
238            file_name:
239                Name of the file, if None it will have same name as
240                saved in PumpWood.
241            if_exists:
242                Values must be in {'fail', 'change_name', 'overwrite', 'skip'}.
243                Set what to do if there is a file with same name. Skip
244                will not download file if there is already with same
245                os.path.join(save_path, file_name), file_name must be set
246                for skip argument.
247            auth_header:
248                Auth header to substitute the microservice original
249                at the request (user impersonation).
250            base_filter_skip (list):
251                List of base query filter to be skiped, it is necessary to
252                be superuser to skip base query filters.
253
254        Returns:
255            May return the file name if save_file=True; If false will return
256            a dictonary with keys `filename` with original file name and
257            `content` with binary data of file content.
258
259        Raises:
260            PumpWoodForbidden:
261                'storage_object attribute not set for view, file operations
262                are disable'. This indicates that storage for this backend
263                was not configured, so it is not possible to make storage
264                operations,
265            PumpWoodForbidden:
266                'file_field must be set on self.file_fields dictionary'. This
267                indicates that the `file_field` parameter is not listed as
268                a file field on the backend.
269            PumpWoodObjectDoesNotExist:
270                'field [{}] not found or null at object'. This indicates that
271                the file field requested is not present on object fields.
272            PumpWoodObjectDoesNotExist:
273                'Object not found in storage [{}]'. This indicates that the
274                file associated with file_field is not avaiable at the
275                storage. This should not ocorrur, it might have a manual
276                update at the model_class table or manual removal/rename of
277                files on storage.
278        """
279        base_filter_skip = self._resolve_base_filter_skip(
280            base_filter_skip)
281
282        if if_exists not in ["fail", "change_name", "overwrite", "skip"]:
283            raise PumpWoodException(
284                "if_exists must be in ['fail', 'change_name', 'overwrite', "
285                "'skip']")
286
287        if file_name is not None and if_exists == 'skip':
288            file_path = os.path.join(save_path, file_name)
289            is_file_already = os.path.isfile(file_path)
290            if is_file_already:
291                print("skiping file already exists: ", file_path)
292                return file_path
293
294        url_str = self._build_retrieve_file_url(model_class=model_class, pk=pk)
295        file_response = self.request_get(
296            url=url_str, parameters={
297                "file-field": file_field,
298                "base_filter_skip": base_filter_skip},
299            auth_header=auth_header)
300        if not save_file:
301            return file_response
302
303        if not os.path.exists(save_path):
304            raise PumpWoodException(
305                "Path to save retrieved file [{}] does not exist".format(
306                    save_path))
307
308        file_name = secure_filename(file_name or file_response["filename"])
309        file_path = os.path.join(save_path, file_name)
310        is_file_already = os.path.isfile(file_path)
311        if is_file_already:
312            if if_exists == "change_name":
313                filename, file_extension = os.path.splitext(file_path)
314                too_many_tries = True
315                for i in range(10):
316                    new_path = "{filename}__{count}{extension}".format(
317                        filename=filename, count=i,
318                        extension=file_extension)
319                    if not os.path.isfile(new_path):
320                        file_path = new_path
321                        too_many_tries = False
322                        break
323                if too_many_tries:
324                    raise PumpWoodException(
325                        ("Too many tries to find a not used file name." +
326                         " file_path[{}]".format(file_path)))
327
328            elif if_exists == "fail":
329                raise PumpWoodException(
330                    ("if_exists set as 'fail' and there is a file with same" +
331                     "name. file_path [{}]").format(file_path))
332
333        with open(file_path, "wb") as file:
334            file.write(file_response["content"])
335        return file_path
336
337    @staticmethod
338    def _build_retrieve_file_straming_url(model_class: str, pk: int):
339        return "rest/%s/retrieve-file-streaming/%s/" % (
340            model_class.lower(), pk)
341
342    def retrieve_streaming_file(self, model_class: str, pk: int,
343                                file_field: str, file_name: str,
344                                auth_header: dict = None,
345                                save_path: str = "./",
346                                if_exists: str = "fail",
347                                base_filter_skip: list = None) -> str:
348        """Retrieve a file from PumpWood using streaming to retrieve content.
349
350        This funcion uses file streaming to retrieve file content, it should be
351        prefered when dealing with large (bigger than 10Mb) files transfer.
352        Using this end-point the file is not loaded on backend memory content
353        is transfered by chucks that are read at the storage and transfered
354        to user.
355
356        It will necessarily save the content as a file, there is not the
357        possibility of retrieving the content directly from request.
358
359        Args:
360            model_class:
361                Class of the model to retrieve file.
362            pk:
363                Pk of the object associeted file.
364            file_field:
365                Field of the file to be downloaded.
366            auth_header:
367                Dictionary containing the auth header.
368            save_path:
369                Path of the directory to save file.
370            file_name:
371                Name of the file, if None it will have same name as
372                saved in PumpWood.
373            if_exists:
374                Values must be in {'fail', 'change_name', 'overwrite'}.
375                Set what to do if there is a file with same name.
376            auth_header:
377                Auth header to substitute the microservice original
378                at the request (user impersonation).
379            base_filter_skip (list):
380                List of base query filter to be skiped, it is necessary to
381                be superuser to skip base query filters.
382
383        Returns:
384            Returns the file path that recived the file content.
385
386        Raises:
387            PumpWoodForbidden:
388                'storage_object attribute not set for view, file operations
389                are disable'. This indicates that storage for this backend
390                was not configured, so it is not possible to make storage
391                operations,
392            PumpWoodForbidden:
393                'file_field must be set on self.file_fields dictionary'. This
394                indicates that the `file_field` parameter is not listed as
395                a file field on the backend.
396            PumpWoodObjectDoesNotExist:
397                'field [{}] not found or null at object'. This indicates that
398                the file field requested is not present on object fields.
399            PumpWoodObjectDoesNotExist:
400                'Object not found in storage [{}]'. This indicates that the
401                file associated with file_field is not avaiable at the
402                storage. This should not ocorrur, it might have a manual
403                update at the model_class table or manual removal/rename of
404                files on storage.
405        """
406        base_filter_skip = self._resolve_base_filter_skip(
407            base_filter_skip)
408        request_header = self._check_auth_header(auth_header)
409
410        if if_exists not in ["fail", "change_name", "overwrite"]:
411            raise PumpWoodException(
412                "if_exists must be in ['fail', 'change_name', 'overwrite']")
413        if not os.path.exists(save_path):
414            raise PumpWoodException(
415                "Path to save retrieved file [{}] does not exist".format(
416                    save_path))
417
418        file_path = os.path.join(save_path, file_name)
419        if os.path.isfile(file_path) and if_exists == "change_name":
420            filename, file_extension = os.path.splitext(file_path)
421            too_many_tries = False
422            for i in range(10):
423                new_path = "{filename}__{count}{extension}".format(
424                    filename=filename, count=i,
425                    extension=file_extension)
426                if not os.path.isfile(new_path):
427                    file_path = new_path
428                    too_many_tries = True
429                    break
430            if not too_many_tries:
431                raise PumpWoodException(
432                    ("Too many tries to find a not used file name." +
433                     " file_path[{}]".format(file_path)))
434
435        if os.path.isfile(file_path) and if_exists == "fail":
436            raise PumpWoodException(
437                ("if_exists set as 'fail' and there is a file with same" +
438                 "name. file_path [{}]").format(file_path))
439
440        url_str = self._build_retrieve_file_straming_url(
441            model_class=model_class, pk=pk)
442
443        get_url = self.server_url + url_str
444        get_params = {
445            "file-field": file_field,
446            "base_filter_skip": base_filter_skip}
447        dumped_parameters = self._dump_query_parameters(parameters=get_params)
448        with requests.get(
449                get_url, verify=self._verify_ssl, headers=request_header,
450                params=dumped_parameters,
451                timeout=self._default_timeout) as response:
452            self.error_handler(response)
453            with open(file_path, 'wb') as f:
454                for chunk in response.iter_content(chunk_size=8192):
455                    if chunk:
456                        f.write(chunk)
457        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 = self._resolve_base_filter_skip(
81            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        
185        base_filter_skip = self._resolve_base_filter_skip(
186            base_filter_skip)
187
188        # Convert to base64 dict unique queries
189        serialized_pk = None
190        if isinstance(pk, dict):
191            # Use the correct keyword argument 'primary_key_dict'
192            serialized_pk = CompositePkBase64Converter.dump_dict(
193                primary_key_dict=pk)
194        else:
195            serialized_pk = pk
196
197        # Fetch information from Pumpwood
198        url_str = self._build_retrieve_url(
199            model_class=model_class, pk=serialized_pk)
200        return self.request_get(
201            url=url_str, parameters={
202                "fields": fields, "default_fields": default_fields,
203                "foreign_key_fields": foreign_key_fields,
204                "related_fields": related_fields,
205                "base_filter_skip": base_filter_skip,
206                "use_cache": use_app_cache},
207            auth_header=auth_header, use_disk_cache=use_disk_cache,
208            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>:
214    def retrieve_file(self, model_class: str, pk: int, file_field: str,
215                      auth_header: dict = None, save_file: bool = True,
216                      save_path: str = "./", file_name: str = None,
217                      if_exists: str = "fail",
218                      base_filter_skip: list = None) -> any:
219        """Retrieve a file from PumpWood.
220
221        This function will retrieve file as a single request, depending on the
222        size of the files it would be preferred to use streaming end-point.
223
224        Args:
225            model_class:
226                Class of the model to retrieve file.
227            pk:
228                Pk of the object associeted file.
229            file_field:
230                Field of the file to be downloaded.
231            auth_header:
232                Dictionary containing the auth header.
233            save_file:
234                If data is to be saved as file or return get
235                response.
236            save_path:
237                Path of the directory to save file.
238            file_name:
239                Name of the file, if None it will have same name as
240                saved in PumpWood.
241            if_exists:
242                Values must be in {'fail', 'change_name', 'overwrite', 'skip'}.
243                Set what to do if there is a file with same name. Skip
244                will not download file if there is already with same
245                os.path.join(save_path, file_name), file_name must be set
246                for skip argument.
247            auth_header:
248                Auth header to substitute the microservice original
249                at the request (user impersonation).
250            base_filter_skip (list):
251                List of base query filter to be skiped, it is necessary to
252                be superuser to skip base query filters.
253
254        Returns:
255            May return the file name if save_file=True; If false will return
256            a dictonary with keys `filename` with original file name and
257            `content` with binary data of file content.
258
259        Raises:
260            PumpWoodForbidden:
261                'storage_object attribute not set for view, file operations
262                are disable'. This indicates that storage for this backend
263                was not configured, so it is not possible to make storage
264                operations,
265            PumpWoodForbidden:
266                'file_field must be set on self.file_fields dictionary'. This
267                indicates that the `file_field` parameter is not listed as
268                a file field on the backend.
269            PumpWoodObjectDoesNotExist:
270                'field [{}] not found or null at object'. This indicates that
271                the file field requested is not present on object fields.
272            PumpWoodObjectDoesNotExist:
273                'Object not found in storage [{}]'. This indicates that the
274                file associated with file_field is not avaiable at the
275                storage. This should not ocorrur, it might have a manual
276                update at the model_class table or manual removal/rename of
277                files on storage.
278        """
279        base_filter_skip = self._resolve_base_filter_skip(
280            base_filter_skip)
281
282        if if_exists not in ["fail", "change_name", "overwrite", "skip"]:
283            raise PumpWoodException(
284                "if_exists must be in ['fail', 'change_name', 'overwrite', "
285                "'skip']")
286
287        if file_name is not None and if_exists == 'skip':
288            file_path = os.path.join(save_path, file_name)
289            is_file_already = os.path.isfile(file_path)
290            if is_file_already:
291                print("skiping file already exists: ", file_path)
292                return file_path
293
294        url_str = self._build_retrieve_file_url(model_class=model_class, pk=pk)
295        file_response = self.request_get(
296            url=url_str, parameters={
297                "file-field": file_field,
298                "base_filter_skip": base_filter_skip},
299            auth_header=auth_header)
300        if not save_file:
301            return file_response
302
303        if not os.path.exists(save_path):
304            raise PumpWoodException(
305                "Path to save retrieved file [{}] does not exist".format(
306                    save_path))
307
308        file_name = secure_filename(file_name or file_response["filename"])
309        file_path = os.path.join(save_path, file_name)
310        is_file_already = os.path.isfile(file_path)
311        if is_file_already:
312            if if_exists == "change_name":
313                filename, file_extension = os.path.splitext(file_path)
314                too_many_tries = True
315                for i in range(10):
316                    new_path = "{filename}__{count}{extension}".format(
317                        filename=filename, count=i,
318                        extension=file_extension)
319                    if not os.path.isfile(new_path):
320                        file_path = new_path
321                        too_many_tries = False
322                        break
323                if too_many_tries:
324                    raise PumpWoodException(
325                        ("Too many tries to find a not used file name." +
326                         " file_path[{}]".format(file_path)))
327
328            elif if_exists == "fail":
329                raise PumpWoodException(
330                    ("if_exists set as 'fail' and there is a file with same" +
331                     "name. file_path [{}]").format(file_path))
332
333        with open(file_path, "wb") as file:
334            file.write(file_response["content"])
335        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:
342    def retrieve_streaming_file(self, model_class: str, pk: int,
343                                file_field: str, file_name: str,
344                                auth_header: dict = None,
345                                save_path: str = "./",
346                                if_exists: str = "fail",
347                                base_filter_skip: list = None) -> str:
348        """Retrieve a file from PumpWood using streaming to retrieve content.
349
350        This funcion uses file streaming to retrieve file content, it should be
351        prefered when dealing with large (bigger than 10Mb) files transfer.
352        Using this end-point the file is not loaded on backend memory content
353        is transfered by chucks that are read at the storage and transfered
354        to user.
355
356        It will necessarily save the content as a file, there is not the
357        possibility of retrieving the content directly from request.
358
359        Args:
360            model_class:
361                Class of the model to retrieve file.
362            pk:
363                Pk of the object associeted file.
364            file_field:
365                Field of the file to be downloaded.
366            auth_header:
367                Dictionary containing the auth header.
368            save_path:
369                Path of the directory to save file.
370            file_name:
371                Name of the file, if None it will have same name as
372                saved in PumpWood.
373            if_exists:
374                Values must be in {'fail', 'change_name', 'overwrite'}.
375                Set what to do if there is a file with same name.
376            auth_header:
377                Auth header to substitute the microservice original
378                at the request (user impersonation).
379            base_filter_skip (list):
380                List of base query filter to be skiped, it is necessary to
381                be superuser to skip base query filters.
382
383        Returns:
384            Returns the file path that recived the file content.
385
386        Raises:
387            PumpWoodForbidden:
388                'storage_object attribute not set for view, file operations
389                are disable'. This indicates that storage for this backend
390                was not configured, so it is not possible to make storage
391                operations,
392            PumpWoodForbidden:
393                'file_field must be set on self.file_fields dictionary'. This
394                indicates that the `file_field` parameter is not listed as
395                a file field on the backend.
396            PumpWoodObjectDoesNotExist:
397                'field [{}] not found or null at object'. This indicates that
398                the file field requested is not present on object fields.
399            PumpWoodObjectDoesNotExist:
400                'Object not found in storage [{}]'. This indicates that the
401                file associated with file_field is not avaiable at the
402                storage. This should not ocorrur, it might have a manual
403                update at the model_class table or manual removal/rename of
404                files on storage.
405        """
406        base_filter_skip = self._resolve_base_filter_skip(
407            base_filter_skip)
408        request_header = self._check_auth_header(auth_header)
409
410        if if_exists not in ["fail", "change_name", "overwrite"]:
411            raise PumpWoodException(
412                "if_exists must be in ['fail', 'change_name', 'overwrite']")
413        if not os.path.exists(save_path):
414            raise PumpWoodException(
415                "Path to save retrieved file [{}] does not exist".format(
416                    save_path))
417
418        file_path = os.path.join(save_path, file_name)
419        if os.path.isfile(file_path) and if_exists == "change_name":
420            filename, file_extension = os.path.splitext(file_path)
421            too_many_tries = False
422            for i in range(10):
423                new_path = "{filename}__{count}{extension}".format(
424                    filename=filename, count=i,
425                    extension=file_extension)
426                if not os.path.isfile(new_path):
427                    file_path = new_path
428                    too_many_tries = True
429                    break
430            if not too_many_tries:
431                raise PumpWoodException(
432                    ("Too many tries to find a not used file name." +
433                     " file_path[{}]".format(file_path)))
434
435        if os.path.isfile(file_path) and if_exists == "fail":
436            raise PumpWoodException(
437                ("if_exists set as 'fail' and there is a file with same" +
438                 "name. file_path [{}]").format(file_path))
439
440        url_str = self._build_retrieve_file_straming_url(
441            model_class=model_class, pk=pk)
442
443        get_url = self.server_url + url_str
444        get_params = {
445            "file-field": file_field,
446            "base_filter_skip": base_filter_skip}
447        dumped_parameters = self._dump_query_parameters(parameters=get_params)
448        with requests.get(
449                get_url, verify=self._verify_ssl, headers=request_header,
450                params=dumped_parameters,
451                timeout=self._default_timeout) as response:
452            self.error_handler(response)
453            with open(file_path, 'wb') as f:
454                for chunk in response.iter_content(chunk_size=8192):
455                    if chunk:
456                        f.write(chunk)
457        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, force_delete: bool = False,
 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 (int):
 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            force_delete (bool):
 33                If True, the object will be deleted even if it is not
 34                soft deleted.
 35            auth_header (dict):
 36                Auth header to substitute the microservice original
 37                at the request (user impersonation).
 38            base_filter_skip (list[str]):
 39                List of base query filter to be skiped, it is necessary to
 40                be superuser to skip base query filters.
 41
 42        Returns:
 43            Returns delete object.
 44
 45        Raises:
 46            PumpWoodObjectDoesNotExist:
 47                'Requested object {model_class}[{pk}] not found.' This
 48                indicates that the pk was not found in database.
 49        """
 50        base_filter_skip = self._resolve_base_filter_skip(
 51            base_filter_skip)
 52
 53        url_str = self._build_delete_request_url(model_class, pk)
 54        return self.request_delete(
 55            url=url_str, auth_header=auth_header,
 56            parameters={
 57                "base_filter_skip": base_filter_skip,
 58                "force_delete": force_delete})
 59
 60    @staticmethod
 61    def _build_remove_file_field(model_class, pk):
 62        return "rest/%s/remove-file-field/%s/" % (model_class.lower(), pk)
 63
 64    def delete_file(self, model_class: str, pk: int, file_field: str,
 65                    auth_header: dict = None,
 66                    base_filter_skip: list[str] | None = None) -> bool:
 67        """Send delete request to a PumpWood object.
 68
 69        Delete (or whatever the PumpWood system have been implemented) the
 70        object with the specified pk.
 71
 72        At previous versions this function was `remove_file_field`. An alias
 73        is created for backward compatibility.
 74
 75        Args:
 76            model_class:
 77                Model class to delete the object
 78            pk:
 79                Object pk to be deleted (or whatever the PumpWood system
 80                have been implemented).
 81            file_field:
 82                File field to be removed from storage.
 83            auth_header:
 84                Auth header to substitute the microservice original
 85                at the request (user impersonation).
 86            base_filter_skip (list[str]):
 87                List of base query filter to be skiped, it is necessary to
 88                be superuser to skip base query filters.
 89
 90        Returns:
 91            Return True is file was successful removed
 92
 93        Raises:
 94            PumpWoodForbidden:
 95                'storage_object attribute not set for view, file operations
 96                are disable'. This indicates that storage_object is not
 97                associated with view, not allowing it to make storage
 98                operations.
 99            PumpWoodForbidden:
100                'file_field must be set on self.file_fields dictionary.'.
101                This indicates that the `file_field` was not set as a file
102                field on the backend.
103            PumpWoodObjectDoesNotExist:
104                'File does not exist. File field [{}] is set as None'.
105                This indicates that the object does not exists on storage,
106                it should not occur. It might have been some manual update
107                of the database or at the storage level.
108        """
109        base_filter_skip = self._resolve_base_filter_skip(
110            base_filter_skip)
111
112        url_str = self._build_remove_file_field(model_class, pk)
113        return self.request_delete(
114            url=url_str, auth_header=auth_header,
115            parameters={
116                "file-field": file_field,
117                "base_filter_skip": base_filter_skip})
118
119    # Create an alias for backward compatibility.
120    remove_file_field = delete_file
121
122    @staticmethod
123    def _build_delete_many_request_url(model_class):
124        return "rest/%s/delete/" % (model_class.lower(), )
125
126    def delete_many(self, model_class: str, filter_dict: None | dict = None,
127                    exclude_dict: None | dict = None,
128                    force_delete: bool = False,
129                    auth_header: dict = None,
130                    base_filter_skip: list[str] = None) -> bool:
131        """Remove many objects using query to retrict removal.
132
133        CAUTION It is not possible to undo this operation, model_class
134        this deleted field will be removed from database when using this
135        end-point, different from using delete end-point.
136
137        Args:
138            model_class:
139                Model class to delete the object
140            filter_dict:
141                Dictionary to make filter query.
142            exclude_dict:
143                Dictionary to make exclude query.
144            auth_header:
145                Auth header to substitute the microservice original
146                at the request (user impersonation).
147            base_filter_skip (list):
148                List of base query filter to be skiped, it is necessary to
149                be superuser to skip base query filters.
150
151        Returns:
152            True if delete is ok.
153
154        Raises:
155            PumpWoodObjectDeleteException:
156                Raises error if there is any error when commiting object
157                deletion on database.
158        """
159        filter_dict = {} if filter_dict is None else filter_dict
160        exclude_dict = {} if exclude_dict is None else exclude_dict
161        base_filter_skip = self._resolve_base_filter_skip(
162            base_filter_skip)
163        
164        if force_delete:
165            raise NotImplementedError("Force delete is not implemented yet.")
166
167        url_str = self._build_delete_many_request_url(model_class)
168        return self.request_post(
169            url=url_str,
170            parameters={
171                'base_filter_skip': base_filter_skip,
172                'force_delete': force_delete},
173            data={'filter_dict': filter_dict, 'exclude_dict': exclude_dict},
174            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, force_delete: bool = False, base_filter_skip: list[str] | None = None) -> dict:
15    def delete(self, model_class: str, pk: int,
16               auth_header: dict = None, force_delete: bool = False,
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 (int):
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            force_delete (bool):
33                If True, the object will be deleted even if it is not
34                soft deleted.
35            auth_header (dict):
36                Auth header to substitute the microservice original
37                at the request (user impersonation).
38            base_filter_skip (list[str]):
39                List of base query filter to be skiped, it is necessary to
40                be superuser to skip base query filters.
41
42        Returns:
43            Returns delete object.
44
45        Raises:
46            PumpWoodObjectDoesNotExist:
47                'Requested object {model_class}[{pk}] not found.' This
48                indicates that the pk was not found in database.
49        """
50        base_filter_skip = self._resolve_base_filter_skip(
51            base_filter_skip)
52
53        url_str = self._build_delete_request_url(model_class, pk)
54        return self.request_delete(
55            url=url_str, auth_header=auth_header,
56            parameters={
57                "base_filter_skip": base_filter_skip,
58                "force_delete": force_delete})

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 (int): 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.
  • force_delete (bool): If True, the object will be deleted even if it is not soft deleted.
  • auth_header (dict): 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:
 64    def delete_file(self, model_class: str, pk: int, file_field: str,
 65                    auth_header: dict = None,
 66                    base_filter_skip: list[str] | None = None) -> bool:
 67        """Send delete request to a PumpWood object.
 68
 69        Delete (or whatever the PumpWood system have been implemented) the
 70        object with the specified pk.
 71
 72        At previous versions this function was `remove_file_field`. An alias
 73        is created for backward compatibility.
 74
 75        Args:
 76            model_class:
 77                Model class to delete the object
 78            pk:
 79                Object pk to be deleted (or whatever the PumpWood system
 80                have been implemented).
 81            file_field:
 82                File field to be removed from storage.
 83            auth_header:
 84                Auth header to substitute the microservice original
 85                at the request (user impersonation).
 86            base_filter_skip (list[str]):
 87                List of base query filter to be skiped, it is necessary to
 88                be superuser to skip base query filters.
 89
 90        Returns:
 91            Return True is file was successful removed
 92
 93        Raises:
 94            PumpWoodForbidden:
 95                'storage_object attribute not set for view, file operations
 96                are disable'. This indicates that storage_object is not
 97                associated with view, not allowing it to make storage
 98                operations.
 99            PumpWoodForbidden:
100                'file_field must be set on self.file_fields dictionary.'.
101                This indicates that the `file_field` was not set as a file
102                field on the backend.
103            PumpWoodObjectDoesNotExist:
104                'File does not exist. File field [{}] is set as None'.
105                This indicates that the object does not exists on storage,
106                it should not occur. It might have been some manual update
107                of the database or at the storage level.
108        """
109        base_filter_skip = self._resolve_base_filter_skip(
110            base_filter_skip)
111
112        url_str = self._build_remove_file_field(model_class, pk)
113        return self.request_delete(
114            url=url_str, auth_header=auth_header,
115            parameters={
116                "file-field": file_field,
117                "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:
 64    def delete_file(self, model_class: str, pk: int, file_field: str,
 65                    auth_header: dict = None,
 66                    base_filter_skip: list[str] | None = None) -> bool:
 67        """Send delete request to a PumpWood object.
 68
 69        Delete (or whatever the PumpWood system have been implemented) the
 70        object with the specified pk.
 71
 72        At previous versions this function was `remove_file_field`. An alias
 73        is created for backward compatibility.
 74
 75        Args:
 76            model_class:
 77                Model class to delete the object
 78            pk:
 79                Object pk to be deleted (or whatever the PumpWood system
 80                have been implemented).
 81            file_field:
 82                File field to be removed from storage.
 83            auth_header:
 84                Auth header to substitute the microservice original
 85                at the request (user impersonation).
 86            base_filter_skip (list[str]):
 87                List of base query filter to be skiped, it is necessary to
 88                be superuser to skip base query filters.
 89
 90        Returns:
 91            Return True is file was successful removed
 92
 93        Raises:
 94            PumpWoodForbidden:
 95                'storage_object attribute not set for view, file operations
 96                are disable'. This indicates that storage_object is not
 97                associated with view, not allowing it to make storage
 98                operations.
 99            PumpWoodForbidden:
100                'file_field must be set on self.file_fields dictionary.'.
101                This indicates that the `file_field` was not set as a file
102                field on the backend.
103            PumpWoodObjectDoesNotExist:
104                'File does not exist. File field [{}] is set as None'.
105                This indicates that the object does not exists on storage,
106                it should not occur. It might have been some manual update
107                of the database or at the storage level.
108        """
109        base_filter_skip = self._resolve_base_filter_skip(
110            base_filter_skip)
111
112        url_str = self._build_remove_file_field(model_class, pk)
113        return self.request_delete(
114            url=url_str, auth_header=auth_header,
115            parameters={
116                "file-field": file_field,
117                "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, force_delete: bool = False, auth_header: dict = None, base_filter_skip: list[str] = None) -> bool:
126    def delete_many(self, model_class: str, filter_dict: None | dict = None,
127                    exclude_dict: None | dict = None,
128                    force_delete: bool = False,
129                    auth_header: dict = None,
130                    base_filter_skip: list[str] = None) -> bool:
131        """Remove many objects using query to retrict removal.
132
133        CAUTION It is not possible to undo this operation, model_class
134        this deleted field will be removed from database when using this
135        end-point, different from using delete end-point.
136
137        Args:
138            model_class:
139                Model class to delete the object
140            filter_dict:
141                Dictionary to make filter query.
142            exclude_dict:
143                Dictionary to make exclude query.
144            auth_header:
145                Auth header to substitute the microservice original
146                at the request (user impersonation).
147            base_filter_skip (list):
148                List of base query filter to be skiped, it is necessary to
149                be superuser to skip base query filters.
150
151        Returns:
152            True if delete is ok.
153
154        Raises:
155            PumpWoodObjectDeleteException:
156                Raises error if there is any error when commiting object
157                deletion on database.
158        """
159        filter_dict = {} if filter_dict is None else filter_dict
160        exclude_dict = {} if exclude_dict is None else exclude_dict
161        base_filter_skip = self._resolve_base_filter_skip(
162            base_filter_skip)
163        
164        if force_delete:
165            raise NotImplementedError("Force delete is not implemented yet.")
166
167        url_str = self._build_delete_many_request_url(model_class)
168        return self.request_post(
169            url=url_str,
170            parameters={
171                'base_filter_skip': base_filter_skip,
172                'force_delete': force_delete},
173            data={'filter_dict': filter_dict, 'exclude_dict': exclude_dict},
174            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 = self._resolve_base_filter_skip(
 95            base_filter_skip)
 96
 97        model_class = obj_dict.get('model_class')
 98        if model_class is None:
 99            raise PumpWoodObjectSavingException(
100                'To save an object obj_dict must have model_class defined.')
101
102        url_str = self._build_save_url(model_class)
103        parameters = {
104            "fields": fields, "default_fields": default_fields,
105            "foreign_key_fields": foreign_key_fields,
106            "related_fields": related_fields,
107            "base_filter_skip": base_filter_skip,
108            "upsert": upsert}
109        return self.request_post(
110            url=url_str, data=obj_dict, parameters=parameters, files=files,
111            auth_header=auth_header)
112
113    @staticmethod
114    def _build_save_streaming_file_url(model_class, pk):
115        return "rest/{model_class}/save-file-streaming/{pk}/".format(
116            model_class=model_class.lower(), pk=pk)
117
118    def save_streaming_file(self, model_class: str, pk: int, file_field: str,
119                            file: io.BufferedReader, file_name: str = None,
120                            auth_header: dict = None,
121                            fields: list = None, default_fields: bool = False,
122                            foreign_key_fields: bool = False,
123                            related_fields: bool = False,
124                            base_filter_skip: list = None) -> str:
125        """Stream file to PumpWood.
126
127        Use streaming to transfer a file content to Pumpwood storage, this
128        end-point is prefered when transmiting files bigger than 10Mb. It
129        is necessary to have the object created before the file transfer.
130
131        Args:
132            model_class:
133                Model class of the object.
134            pk:
135                pk of the object.
136            file_field:
137                File field that will receive file stream.
138            file:
139                File to upload as a file object with read bytes option.
140            auth_header:
141                Auth header to substitute the microservice original
142                at the request (user impersonation).
143            file_name:
144                Name of the file, if not set it will be saved as
145                {pk}__{file_field}.{extension at permited extension}
146            fields:
147                Set the fields to be returned by the list end-point.
148            default_fields:
149                Boolean, if true and fields arguments None will return the
150                default fields set for list by the backend.
151            foreign_key_fields:
152                Return forenging key objects. It will return the fk
153                corresponding object. Ex: `created_by_id` reference to
154                a user `model_class` the correspondent to User will be
155                returned at `created_by`.
156            related_fields:
157                Return related fields objects. Related field objects are
158                objects that have a forenging key associated with this
159                model_class, results will be returned as a list of
160                dictionaries usually in a field with `_set` at end.
161                Returning related_fields consume backend resorces, use
162                carefully.
163            base_filter_skip (list[str]):
164                List of base query filter to be skiped, it is necessary to
165                be superuser to skip base query filters.
166
167        Returns:
168            Return the file name associated with data at the storage.
169
170        Raises:
171            PumpWoodForbidden:
172                'file_field must be set on self.file_fields dictionary'. This
173                indicates that the `file_field` passed is not associated
174                with a file field on the backend.
175            PumpWoodException:
176                'Saved bytes in streaming [{}] differ from file bytes [{}].'.
177                This indicates that there was an error when transfering data
178                to storage, the file bytes and transfered bytes does not
179                match.
180        """
181        request_header = self._check_auth_header(auth_header=auth_header)
182        request_header["Content-Type"] = "application/octet-stream"
183        post_url = self.server_url + self._build_save_streaming_file_url(
184            model_class=model_class, pk=pk)
185        base_filter_skip = self._resolve_base_filter_skip(
186            base_filter_skip)
187
188        parameters = {
189            "fields": fields, "default_fields": default_fields,
190            "foreign_key_fields": foreign_key_fields,
191            "related_fields": related_fields, "file_field": file_field,
192            "base_filter_skip": base_filter_skip}
193        if file_name is not None:
194            parameters["file_name"] = file_name
195
196        dumped_parameters = self._dump_query_parameters(parameters=parameters)
197        response = requests.post(
198            url=post_url, data=file, params=dumped_parameters,
199            verify=self._verify_ssl, headers=request_header, stream=True,
200            timeout=self._default_timeout)
201
202        file_last_bite = file.tell()
203        self.error_handler(response)
204        json_response = self.angular_json(response)
205
206        if file_last_bite != json_response["bytes_uploaded"]:
207            template = (
208                "Saved bytes in streaming [{}] differ from file " +
209                "bites [{}].")
210            raise PumpWoodException(
211                    template.format(
212                        json_response["bytes_uploaded"], file_last_bite))
213        return json_response["file_path"]
214
215    @staticmethod
216    def _build_bulk_save_url(model_class: str):
217        return "rest/%s/bulk-save/" % (model_class.lower(),)
218
219    def bulk_save(self, model_class: str, data_to_save: list | pd.DataFrame,
220                  auth_header: dict = None,
221                  base_filter_skip: list = None) -> dict:
222        """Save a list of objects with one request.
223
224        It is used with a unique call save many objects at the same time. It
225        is necessary that the end-point is able to receive bulk save requests
226        and all objects been of the same model class.
227
228        Args:
229            model_class:
230                Data model class.
231            data_to_save:
232                A list of objects to be saved.
233            base_filter_skip (list[str]):
234                List of base query filter to be skiped, it is necessary to
235                be superuser to skip base query filters.
236            auth_header:
237                Auth header to substitute the microservice original
238                at the request (user impersonation).
239
240        Returns:
241            A dictinary with `saved_count` as key indicating the number of
242            objects that were saved in database.
243
244        Raises:
245            PumpWoodException:
246                'Expected columns and data columns do not match: Expected
247                columns: {expected} Data columns: {data_cols}'. Indicates
248                that the expected fields of the object were not met at the
249                objects passed to save.
250            PumpWoodException:
251                Other sqlalchemy and psycopg2 errors not associated with
252                IntegrityError.
253            PumpWoodException:
254                'Bulk save not avaiable.'. Indicates that Bulk save end-point
255                was not configured for this model_class.
256            PumpWoodIntegrityError:
257                Raise integrity errors from sqlalchemy and psycopg2. Usually
258                associated with uniqueness of some column.
259        """
260        if len(data_to_save) == 0:
261            return False
262
263        base_filter_skip = self._resolve_base_filter_skip(
264            base_filter_skip)
265        url_str = self._build_bulk_save_url(model_class=model_class)
266        return self.request_post(
267            url=url_str, data=data_to_save,
268            parameters={"base_filter_skip": base_filter_skip},
269            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 = self._resolve_base_filter_skip(
 95            base_filter_skip)
 96
 97        model_class = obj_dict.get('model_class')
 98        if model_class is None:
 99            raise PumpWoodObjectSavingException(
100                'To save an object obj_dict must have model_class defined.')
101
102        url_str = self._build_save_url(model_class)
103        parameters = {
104            "fields": fields, "default_fields": default_fields,
105            "foreign_key_fields": foreign_key_fields,
106            "related_fields": related_fields,
107            "base_filter_skip": base_filter_skip,
108            "upsert": upsert}
109        return self.request_post(
110            url=url_str, data=obj_dict, parameters=parameters, files=files,
111            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:
118    def save_streaming_file(self, model_class: str, pk: int, file_field: str,
119                            file: io.BufferedReader, file_name: str = None,
120                            auth_header: dict = None,
121                            fields: list = None, default_fields: bool = False,
122                            foreign_key_fields: bool = False,
123                            related_fields: bool = False,
124                            base_filter_skip: list = None) -> str:
125        """Stream file to PumpWood.
126
127        Use streaming to transfer a file content to Pumpwood storage, this
128        end-point is prefered when transmiting files bigger than 10Mb. It
129        is necessary to have the object created before the file transfer.
130
131        Args:
132            model_class:
133                Model class of the object.
134            pk:
135                pk of the object.
136            file_field:
137                File field that will receive file stream.
138            file:
139                File to upload as a file object with read bytes option.
140            auth_header:
141                Auth header to substitute the microservice original
142                at the request (user impersonation).
143            file_name:
144                Name of the file, if not set it will be saved as
145                {pk}__{file_field}.{extension at permited extension}
146            fields:
147                Set the fields to be returned by the list end-point.
148            default_fields:
149                Boolean, if true and fields arguments None will return the
150                default fields set for list by the backend.
151            foreign_key_fields:
152                Return forenging key objects. It will return the fk
153                corresponding object. Ex: `created_by_id` reference to
154                a user `model_class` the correspondent to User will be
155                returned at `created_by`.
156            related_fields:
157                Return related fields objects. Related field objects are
158                objects that have a forenging key associated with this
159                model_class, results will be returned as a list of
160                dictionaries usually in a field with `_set` at end.
161                Returning related_fields consume backend resorces, use
162                carefully.
163            base_filter_skip (list[str]):
164                List of base query filter to be skiped, it is necessary to
165                be superuser to skip base query filters.
166
167        Returns:
168            Return the file name associated with data at the storage.
169
170        Raises:
171            PumpWoodForbidden:
172                'file_field must be set on self.file_fields dictionary'. This
173                indicates that the `file_field` passed is not associated
174                with a file field on the backend.
175            PumpWoodException:
176                'Saved bytes in streaming [{}] differ from file bytes [{}].'.
177                This indicates that there was an error when transfering data
178                to storage, the file bytes and transfered bytes does not
179                match.
180        """
181        request_header = self._check_auth_header(auth_header=auth_header)
182        request_header["Content-Type"] = "application/octet-stream"
183        post_url = self.server_url + self._build_save_streaming_file_url(
184            model_class=model_class, pk=pk)
185        base_filter_skip = self._resolve_base_filter_skip(
186            base_filter_skip)
187
188        parameters = {
189            "fields": fields, "default_fields": default_fields,
190            "foreign_key_fields": foreign_key_fields,
191            "related_fields": related_fields, "file_field": file_field,
192            "base_filter_skip": base_filter_skip}
193        if file_name is not None:
194            parameters["file_name"] = file_name
195
196        dumped_parameters = self._dump_query_parameters(parameters=parameters)
197        response = requests.post(
198            url=post_url, data=file, params=dumped_parameters,
199            verify=self._verify_ssl, headers=request_header, stream=True,
200            timeout=self._default_timeout)
201
202        file_last_bite = file.tell()
203        self.error_handler(response)
204        json_response = self.angular_json(response)
205
206        if file_last_bite != json_response["bytes_uploaded"]:
207            template = (
208                "Saved bytes in streaming [{}] differ from file " +
209                "bites [{}].")
210            raise PumpWoodException(
211                    template.format(
212                        json_response["bytes_uploaded"], file_last_bite))
213        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:
219    def bulk_save(self, model_class: str, data_to_save: list | pd.DataFrame,
220                  auth_header: dict = None,
221                  base_filter_skip: list = None) -> dict:
222        """Save a list of objects with one request.
223
224        It is used with a unique call save many objects at the same time. It
225        is necessary that the end-point is able to receive bulk save requests
226        and all objects been of the same model class.
227
228        Args:
229            model_class:
230                Data model class.
231            data_to_save:
232                A list of objects to be saved.
233            base_filter_skip (list[str]):
234                List of base query filter to be skiped, it is necessary to
235                be superuser to skip base query filters.
236            auth_header:
237                Auth header to substitute the microservice original
238                at the request (user impersonation).
239
240        Returns:
241            A dictinary with `saved_count` as key indicating the number of
242            objects that were saved in database.
243
244        Raises:
245            PumpWoodException:
246                'Expected columns and data columns do not match: Expected
247                columns: {expected} Data columns: {data_cols}'. Indicates
248                that the expected fields of the object were not met at the
249                objects passed to save.
250            PumpWoodException:
251                Other sqlalchemy and psycopg2 errors not associated with
252                IntegrityError.
253            PumpWoodException:
254                'Bulk save not avaiable.'. Indicates that Bulk save end-point
255                was not configured for this model_class.
256            PumpWoodIntegrityError:
257                Raise integrity errors from sqlalchemy and psycopg2. Usually
258                associated with uniqueness of some column.
259        """
260        if len(data_to_save) == 0:
261            return False
262
263        base_filter_skip = self._resolve_base_filter_skip(
264            base_filter_skip)
265        url_str = self._build_bulk_save_url(model_class=model_class)
266        return self.request_post(
267            url=url_str, data=data_to_save,
268            parameters={"base_filter_skip": base_filter_skip},
269            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        
203        base_filter_skip = self._resolve_base_filter_skip(
204            base_filter_skip)
205
206        url_str = self._build_list_url(model_class)
207        post_data = {
208            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
209            'order_by': order_by, 'default_fields': default_fields,
210            'limit': limit, 'foreign_key_fields': foreign_key_fields}
211        if fields is not None:
212            post_data["fields"] = fields
213        return_data = self.request_post(
214            url=url_str, data=post_data,
215            parameters={'base_filter_skip': base_filter_skip},
216            auth_header=auth_header)
217        if not as_dataframe:
218            return return_data
219        else:
220            # Return the results as a dataframe using the group_by columns
221            # and the columns created at aggregation to ensure that
222            # even empty results would have the correct columns
223            return pd.DataFrame(return_data, columns=fields)
224
225    def list_by_chunks(self, model_class: str, filter_dict: dict = None,
226                       exclude_dict: dict = None, auth_header: dict = None,
227                       fields: list = None, default_fields: bool = False,
228                       chunk_size: int = 50000, limit: int = None,
229                       base_filter_skip: list = None,
230                       as_dataframe: bool = False, **kwargs
231                       ) -> Union[List[dict], pd.DataFrame]:
232        """List objects by fetching them in chunks using PK to paginate.
233
234        Fetch data in chunks to handle large datasets without causing backend
235        timeouts or memory issues. Results are ordered by the 'id' column
236        to ensure consistent pagination. Note that custom ordering is not
237        supported in this method.
238
239        Args:
240            model_class (str):
241                Model class of the end-point.
242            filter_dict (dict):
243                Filter dictionary for the query.
244            exclude_dict (dict):
245                Exclude dictionary for the query.
246            auth_header (dict):
247                Authentication header for user impersonation.
248            fields (list):
249                List of fields to be returned.
250            default_fields (bool):
251                If True and fields is None, return default backend fields.
252            chunk_size (int):
253                Number of objects to fetch per query. Defaults to 50000.
254            base_filter_skip (list):
255                List of base query filters to skip (requires superuser).
256            limit (int):
257                Maximum number of records to return.
258            as_dataframe (bool):
259                If True, returns the results as a pandas DataFrame.
260            **kwargs:
261                Additional arguments for compatibility.
262
263        Returns:
264            Union[List[dict], pd.DataFrame]:
265                A list of dictionaries or a pandas DataFrame containing the
266                serialized objects.
267
268        Raises:
269            PumpWoodException:
270                If there is an error during the request or data processing.
271        """
272        filter_dict = (
273            {} if filter_dict is None else filter_dict)
274        exclude_dict = (
275            {} if exclude_dict is None else exclude_dict)
276        
277        base_filter_skip = self._resolve_base_filter_skip(
278            base_filter_skip)
279
280        copy_filter_dict = copy.deepcopy(filter_dict)
281        list_all_results = []
282        max_order_col = None
283        results_count = 0
284        info_msg = (
285            "# Fetching chunk: results_count[{results_count}] | "
286            "min_id [{max_order_col}] | limit[{limit}]")
287        while True:
288            logger.info(
289                info_msg, results_count=results_count,
290                max_order_col=max_order_col, limit=limit)
291
292            # It is necessary to keep the initial query id__gt in case of
293            # this parameter being passed as argument.
294            if max_order_col is not None:
295                copy_filter_dict["id__gt"] = max_order_col
296
297            temp_results = self.list(
298                model_class=model_class, filter_dict=copy_filter_dict,
299                exclude_dict=exclude_dict, order_by=["id"],
300                auth_header=auth_header, fields=fields,
301                default_fields=default_fields, limit=chunk_size,
302                base_filter_skip=base_filter_skip)
303            results_count = results_count + len(temp_results)
304
305            # Break if results is empty
306            if len(temp_results) == 0:
307                break
308
309            # Extend the list of objects retrieved
310            list_all_results.extend(temp_results)
311
312            # If limit of objects is set, the counter will limit the number
313            # of objects fetched
314            if limit is not None:
315                records_to_fetch = limit - results_count
316                chunk_size = min(records_to_fetch, chunk_size)
317                # Do not request an empty list
318                if chunk_size == 0:
319                    break
320
321            # Get the last object id. If pk is a string the pk is a base64
322            # object that will contain the id columns in it.
323            last_pk = temp_results[-1]["pk"]
324            if isinstance(last_pk, str):
325                loaded_pk_dict = CompositePkBase64Converter.load(last_pk)
326                max_order_col = loaded_pk_dict["id"]
327            else:
328                max_order_col = temp_results[-1]["pk"]
329
330        if as_dataframe:
331            # Return the results as a dataframe using the group_by columns
332            # and the columns created at aggregation to ensure that
333            # even empty results would have the correct columns
334            return pd.DataFrame(list_all_results, columns=fields)
335        return list_all_results
336
337    @staticmethod
338    def _build_list_without_pag_url(model_class: str):
339        return "rest/%s/list-without-pag/" % (model_class.lower(),)
340
341    def list_without_pag(self, model_class: str, filter_dict: dict = None,
342                         exclude_dict: dict = None, order_by: list = None,
343                         auth_header: dict = None,
344                         convert_geometry: bool = True,
345                         fields: list = None,
346                         default_fields: bool = False,
347                         foreign_key_fields: bool = False,
348                         base_filter_skip: list = None,
349                         as_dataframe: bool = False,
350                         **kwargs
351                         ) -> List[dict]:
352        """List object without pagination.
353
354        Function to post at list end-point (resumed data) of PumpWood like
355        systems, results won't be paginated.
356        **Be carefull with large returns.**
357
358        Args:
359            model_class (str):
360                Model class of the end-point
361            filter_dict (dict):
362                Filter dict to be used at the query. Filter elements from query
363                return that satifies all statements of the dictonary.
364            exclude_dict (dict):
365                Exclude dict to be used at the query. Remove elements from
366                query return that satifies all statements of the dictonary.
367            order_by (bool):
368                Order results acording to list of strings
369                correspondent to fields. It is possible to use '-' at the
370                begginng of the field name for reverse ordering. Ex.:
371                ['description'] for accendent ordering and ['-description']
372                for descendent ordering.
373            auth_header (dict):
374                Auth header to substitute the microservice original
375                at the request (user impersonation).
376            fields (List[str]):
377                Set the fields to be returned by the list end-point.
378            default_fields (bool):
379                Boolean, if true and fields arguments None will return the
380                default fields set for list by the backend.
381            limit (int):
382                Set the limit of elements of the returned query. By default,
383                backend usually return 50 elements.
384            foreign_key_fields (bool):
385                Return forenging key objects. It will return the fk
386                corresponding object. Ex: `created_by_id` reference to
387                a user `model_class` the correspondent to User will be
388                returned at `created_by`.
389            convert_geometry (bool):
390                If geometry columns should be convert to shapely geometry.
391                Fields with key 'geometry' will be considered geometry.
392            base_filter_skip (list):
393                List of base query filter to be skiped, it is necessary to
394                be superuser to skip base query filters.
395            as_dataframe (bool):
396                Return data as dataframe and set the columns acording to
397                fields if set.
398            **kwargs:
399                Other unused arguments for compatibility.
400
401        Returns:
402          Containing objects serialized by list Serializer.
403
404        Raises:
405          No especific raises.
406        """
407        filter_dict = {} if filter_dict is None else filter_dict
408        exclude_dict = {} if exclude_dict is None else exclude_dict
409        url_str = self._build_list_without_pag_url(model_class)
410
411        base_filter_skip = self._resolve_base_filter_skip(
412            base_filter_skip)
413
414        post_data = {
415            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
416            'order_by': order_by, 'default_fields': default_fields,
417            'foreign_key_fields': foreign_key_fields}
418
419        if fields is not None:
420            post_data["fields"] = fields
421        results = self.request_post(
422            url=url_str, data=post_data,
423            parameters={'base_filter_skip': base_filter_skip},
424            auth_header=auth_header)
425
426        ##################################################
427        # Converting geometry to Shapely objects in Python
428        geometry_in_results = False
429        if convert_geometry:
430            for obj in results:
431                geometry_value = obj.get("geometry")
432                if geometry_value is not None:
433                    obj["geometry"] = geometry.shape(geometry_value)
434                    geometry_in_results = True
435        ##################################################
436
437        if not as_dataframe:
438            return results
439        else:
440            convert_to_geopandas = (
441                (model_class.lower() == "descriptiongeoarea") and
442                geometry_in_results)
443            if convert_to_geopandas:
444                return geopd.GeoDataFrame(
445                    results, geometry='geometry', columns=fields)
446            else:
447                return pd.DataFrame(
448                    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        
203        base_filter_skip = self._resolve_base_filter_skip(
204            base_filter_skip)
205
206        url_str = self._build_list_url(model_class)
207        post_data = {
208            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
209            'order_by': order_by, 'default_fields': default_fields,
210            'limit': limit, 'foreign_key_fields': foreign_key_fields}
211        if fields is not None:
212            post_data["fields"] = fields
213        return_data = self.request_post(
214            url=url_str, data=post_data,
215            parameters={'base_filter_skip': base_filter_skip},
216            auth_header=auth_header)
217        if not as_dataframe:
218            return return_data
219        else:
220            # Return the results as a dataframe using the group_by columns
221            # and the columns created at aggregation to ensure that
222            # even empty results would have the correct columns
223            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]:
225    def list_by_chunks(self, model_class: str, filter_dict: dict = None,
226                       exclude_dict: dict = None, auth_header: dict = None,
227                       fields: list = None, default_fields: bool = False,
228                       chunk_size: int = 50000, limit: int = None,
229                       base_filter_skip: list = None,
230                       as_dataframe: bool = False, **kwargs
231                       ) -> Union[List[dict], pd.DataFrame]:
232        """List objects by fetching them in chunks using PK to paginate.
233
234        Fetch data in chunks to handle large datasets without causing backend
235        timeouts or memory issues. Results are ordered by the 'id' column
236        to ensure consistent pagination. Note that custom ordering is not
237        supported in this method.
238
239        Args:
240            model_class (str):
241                Model class of the end-point.
242            filter_dict (dict):
243                Filter dictionary for the query.
244            exclude_dict (dict):
245                Exclude dictionary for the query.
246            auth_header (dict):
247                Authentication header for user impersonation.
248            fields (list):
249                List of fields to be returned.
250            default_fields (bool):
251                If True and fields is None, return default backend fields.
252            chunk_size (int):
253                Number of objects to fetch per query. Defaults to 50000.
254            base_filter_skip (list):
255                List of base query filters to skip (requires superuser).
256            limit (int):
257                Maximum number of records to return.
258            as_dataframe (bool):
259                If True, returns the results as a pandas DataFrame.
260            **kwargs:
261                Additional arguments for compatibility.
262
263        Returns:
264            Union[List[dict], pd.DataFrame]:
265                A list of dictionaries or a pandas DataFrame containing the
266                serialized objects.
267
268        Raises:
269            PumpWoodException:
270                If there is an error during the request or data processing.
271        """
272        filter_dict = (
273            {} if filter_dict is None else filter_dict)
274        exclude_dict = (
275            {} if exclude_dict is None else exclude_dict)
276        
277        base_filter_skip = self._resolve_base_filter_skip(
278            base_filter_skip)
279
280        copy_filter_dict = copy.deepcopy(filter_dict)
281        list_all_results = []
282        max_order_col = None
283        results_count = 0
284        info_msg = (
285            "# Fetching chunk: results_count[{results_count}] | "
286            "min_id [{max_order_col}] | limit[{limit}]")
287        while True:
288            logger.info(
289                info_msg, results_count=results_count,
290                max_order_col=max_order_col, limit=limit)
291
292            # It is necessary to keep the initial query id__gt in case of
293            # this parameter being passed as argument.
294            if max_order_col is not None:
295                copy_filter_dict["id__gt"] = max_order_col
296
297            temp_results = self.list(
298                model_class=model_class, filter_dict=copy_filter_dict,
299                exclude_dict=exclude_dict, order_by=["id"],
300                auth_header=auth_header, fields=fields,
301                default_fields=default_fields, limit=chunk_size,
302                base_filter_skip=base_filter_skip)
303            results_count = results_count + len(temp_results)
304
305            # Break if results is empty
306            if len(temp_results) == 0:
307                break
308
309            # Extend the list of objects retrieved
310            list_all_results.extend(temp_results)
311
312            # If limit of objects is set, the counter will limit the number
313            # of objects fetched
314            if limit is not None:
315                records_to_fetch = limit - results_count
316                chunk_size = min(records_to_fetch, chunk_size)
317                # Do not request an empty list
318                if chunk_size == 0:
319                    break
320
321            # Get the last object id. If pk is a string the pk is a base64
322            # object that will contain the id columns in it.
323            last_pk = temp_results[-1]["pk"]
324            if isinstance(last_pk, str):
325                loaded_pk_dict = CompositePkBase64Converter.load(last_pk)
326                max_order_col = loaded_pk_dict["id"]
327            else:
328                max_order_col = temp_results[-1]["pk"]
329
330        if as_dataframe:
331            # Return the results as a dataframe using the group_by columns
332            # and the columns created at aggregation to ensure that
333            # even empty results would have the correct columns
334            return pd.DataFrame(list_all_results, columns=fields)
335        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]:
341    def list_without_pag(self, model_class: str, filter_dict: dict = None,
342                         exclude_dict: dict = None, order_by: list = None,
343                         auth_header: dict = None,
344                         convert_geometry: bool = True,
345                         fields: list = None,
346                         default_fields: bool = False,
347                         foreign_key_fields: bool = False,
348                         base_filter_skip: list = None,
349                         as_dataframe: bool = False,
350                         **kwargs
351                         ) -> List[dict]:
352        """List object without pagination.
353
354        Function to post at list end-point (resumed data) of PumpWood like
355        systems, results won't be paginated.
356        **Be carefull with large returns.**
357
358        Args:
359            model_class (str):
360                Model class of the end-point
361            filter_dict (dict):
362                Filter dict to be used at the query. Filter elements from query
363                return that satifies all statements of the dictonary.
364            exclude_dict (dict):
365                Exclude dict to be used at the query. Remove elements from
366                query return that satifies all statements of the dictonary.
367            order_by (bool):
368                Order results acording to list of strings
369                correspondent to fields. It is possible to use '-' at the
370                begginng of the field name for reverse ordering. Ex.:
371                ['description'] for accendent ordering and ['-description']
372                for descendent ordering.
373            auth_header (dict):
374                Auth header to substitute the microservice original
375                at the request (user impersonation).
376            fields (List[str]):
377                Set the fields to be returned by the list end-point.
378            default_fields (bool):
379                Boolean, if true and fields arguments None will return the
380                default fields set for list by the backend.
381            limit (int):
382                Set the limit of elements of the returned query. By default,
383                backend usually return 50 elements.
384            foreign_key_fields (bool):
385                Return forenging key objects. It will return the fk
386                corresponding object. Ex: `created_by_id` reference to
387                a user `model_class` the correspondent to User will be
388                returned at `created_by`.
389            convert_geometry (bool):
390                If geometry columns should be convert to shapely geometry.
391                Fields with key 'geometry' will be considered geometry.
392            base_filter_skip (list):
393                List of base query filter to be skiped, it is necessary to
394                be superuser to skip base query filters.
395            as_dataframe (bool):
396                Return data as dataframe and set the columns acording to
397                fields if set.
398            **kwargs:
399                Other unused arguments for compatibility.
400
401        Returns:
402          Containing objects serialized by list Serializer.
403
404        Raises:
405          No especific raises.
406        """
407        filter_dict = {} if filter_dict is None else filter_dict
408        exclude_dict = {} if exclude_dict is None else exclude_dict
409        url_str = self._build_list_without_pag_url(model_class)
410
411        base_filter_skip = self._resolve_base_filter_skip(
412            base_filter_skip)
413
414        post_data = {
415            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
416            'order_by': order_by, 'default_fields': default_fields,
417            'foreign_key_fields': foreign_key_fields}
418
419        if fields is not None:
420            post_data["fields"] = fields
421        results = self.request_post(
422            url=url_str, data=post_data,
423            parameters={'base_filter_skip': base_filter_skip},
424            auth_header=auth_header)
425
426        ##################################################
427        # Converting geometry to Shapely objects in Python
428        geometry_in_results = False
429        if convert_geometry:
430            for obj in results:
431                geometry_value = obj.get("geometry")
432                if geometry_value is not None:
433                    obj["geometry"] = geometry.shape(geometry_value)
434                    geometry_in_results = True
435        ##################################################
436
437        if not as_dataframe:
438            return results
439        else:
440            convert_to_geopandas = (
441                (model_class.lower() == "descriptiongeoarea") and
442                geometry_in_results)
443            if convert_to_geopandas:
444                return geopd.GeoDataFrame(
445                    results, geometry='geometry', columns=fields)
446            else:
447                return pd.DataFrame(
448                    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 = self._resolve_base_filter_skip(
44            base_filter_skip)
45
46        post_data = {'filter_dict': filter_dict, 'exclude_dict': exclude_dict}
47        return self.request_post(
48            url=url_str, data=post_data,
49            parameters={'base_filter_skip': base_filter_skip},
50            auth_header=auth_header)
51
52    @staticmethod
53    def _build_list_dimension_values(model_class: str):
54        return "rest/%s/list-dimension-values/" % (model_class.lower(), )
55
56    def list_dimension_values(self, model_class: str, key: str,
57                              filter_dict: dict = {}, exclude_dict: dict = {},
58                              auth_header: dict = None,
59                              base_filter_skip: list = None) -> list[any]:
60        """List values associated with dimensions key.
61
62        It list all keys avaiable at dimension retricting the results with
63        query parameters `filter_dict` and `exclude_dict`.
64
65        Args:
66            model_class:
67                Model class of the end-point
68            filter_dict:
69                Filter dict to be used at the query. Filter elements from query
70                return that satifies all statements of the dictonary.
71            exclude_dict:
72                Exclude dict to be used at the query. Remove elements from
73                query return that satifies all statements of the dictonary.
74            auth_header:
75                Auth header to substitute the microservice original
76                at the request (user impersonation).
77            key:
78                Key to list the avaiable values using the query filter
79                and exclude.
80            base_filter_skip (list):
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            List of values associated with dimensions key at the objects that
86            are returned with `filter_dict` and `exclude_dict`.
87        """
88        url_str = self._build_list_dimension_values(model_class)
89        base_filter_skip = self._resolve_base_filter_skip(
90            base_filter_skip)
91
92        post_data = {
93            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
94            'key': key}
95        return self.request_post(
96            url=url_str, data=post_data,
97            parameters={'base_filter_skip': base_filter_skip},
98            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 = self._resolve_base_filter_skip(
44            base_filter_skip)
45
46        post_data = {'filter_dict': filter_dict, 'exclude_dict': exclude_dict}
47        return self.request_post(
48            url=url_str, data=post_data,
49            parameters={'base_filter_skip': base_filter_skip},
50            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]:
56    def list_dimension_values(self, model_class: str, key: str,
57                              filter_dict: dict = {}, exclude_dict: dict = {},
58                              auth_header: dict = None,
59                              base_filter_skip: list = None) -> list[any]:
60        """List values associated with dimensions key.
61
62        It list all keys avaiable at dimension retricting the results with
63        query parameters `filter_dict` and `exclude_dict`.
64
65        Args:
66            model_class:
67                Model class of the end-point
68            filter_dict:
69                Filter dict to be used at the query. Filter elements from query
70                return that satifies all statements of the dictonary.
71            exclude_dict:
72                Exclude dict to be used at the query. Remove elements from
73                query return that satifies all statements of the dictonary.
74            auth_header:
75                Auth header to substitute the microservice original
76                at the request (user impersonation).
77            key:
78                Key to list the avaiable values using the query filter
79                and exclude.
80            base_filter_skip (list):
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            List of values associated with dimensions key at the objects that
86            are returned with `filter_dict` and `exclude_dict`.
87        """
88        url_str = self._build_list_dimension_values(model_class)
89        base_filter_skip = self._resolve_base_filter_skip(
90            base_filter_skip)
91
92        post_data = {
93            'filter_dict': filter_dict, 'exclude_dict': exclude_dict,
94            'key': key}
95        return self.request_post(
96            url=url_str, data=post_data,
97            parameters={'base_filter_skip': base_filter_skip},
98            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 = self._resolve_base_filter_skip(
 98            base_filter_skip)
 99
100        url_str = self._build_execute_action_url(
101            model_class=model_class, action=action, pk=pk)
102        return self.request_post(
103            url=url_str, data=parameters, files=files, auth_header=auth_header,
104            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 = self._resolve_base_filter_skip(
 98            base_filter_skip)
 99
100        url_str = self._build_execute_action_url(
101            model_class=model_class, action=action, pk=pk)
102        return self.request_post(
103            url=url_str, data=parameters, files=files, auth_header=auth_header,
104            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.