oplnk-python-utils 1.2.2__tar.gz → 1.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {oplnk_python_utils-1.2.2/oplnk_python_utils.egg-info → oplnk_python_utils-1.2.4}/PKG-INFO +33 -8
  2. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/README.md +32 -7
  3. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/__init__.py +1 -1
  4. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/query/query_builder.py +46 -18
  5. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4/oplnk_python_utils.egg-info}/PKG-INFO +33 -8
  6. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/setup.py +1 -1
  7. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/LICENSE +0 -0
  8. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/MANIFEST.in +0 -0
  9. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/cache/__init__.py +0 -0
  10. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/cache/cache_utils.py +0 -0
  11. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/core/__init__.py +0 -0
  12. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/core/data.py +0 -0
  13. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/core/parsing.py +0 -0
  14. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/core/serialization.py +0 -0
  15. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/query/__init__.py +0 -0
  16. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils/reports/__init__.py +0 -0
  17. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils.egg-info/SOURCES.txt +0 -0
  18. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils.egg-info/dependency_links.txt +0 -0
  19. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils.egg-info/not-zip-safe +0 -0
  20. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils.egg-info/requires.txt +0 -0
  21. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/oplnk_python_utils.egg-info/top_level.txt +0 -0
  22. {oplnk_python_utils-1.2.2 → oplnk_python_utils-1.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.2
3
+ Version: 1.2.4
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -642,11 +642,11 @@ query = {
642
642
  "populate": ["creation_user", "modification_user"]
643
643
  }
644
644
 
645
- # Result will include (original ID automatically removed, replaced by populated object):
645
+ # Result will include (original ID removed, replaced by populated object):
646
646
  # {
647
647
  # "data": [{
648
648
  # "_id": "123",
649
- # "creation_user": { // Replaces the original ID field
649
+ # "creation_user_info": { // Populated object
650
650
  # "_id": "user_id_string",
651
651
  # "name": "John Doe",
652
652
  # "person": "person_id"
@@ -657,8 +657,8 @@ query = {
657
657
 
658
658
  **Reserved fields:**
659
659
 
660
- - `creation_user` → automatically populated, original ID is excluded
661
- - `modification_user` → automatically populated, original ID is excluded
660
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
661
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
662
662
 
663
663
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
664
664
 
@@ -666,10 +666,10 @@ These fields are only populated when explicitly requested via the `populate` par
666
666
 
667
667
  The behavior of original ID fields depends on whether they are being populated:
668
668
 
669
- #### Without populate - ID field shown as-is:
669
+ #### Without populate - ID field shown as-is (from database):
670
670
 
671
671
  ```python
672
- # No populate - field shown normally
672
+ # No populate - field shown normally from database
673
673
  query = {
674
674
  "fields": ["name", "establishment_id"]
675
675
  }
@@ -728,7 +728,32 @@ query = {
728
728
  # }
729
729
  ```
730
730
 
731
- **For audit fields:** The original ID is always excluded and never shown (no `creation_user_id` field exists to request).
731
+ #### Audit fields behavior:
732
+
733
+ ```python
734
+ # Without populate - original field shown
735
+ query = {
736
+ "fields": ["name", "creation_user"]
737
+ }
738
+ # Response:
739
+ # { "creation_user": "user_123" }
740
+
741
+ # With populate - original field removed, *_info shown instead
742
+ query = {
743
+ "populate": ["creation_user"]
744
+ }
745
+ # Response:
746
+ # { "creation_user_info": { "_id": "user_123", ... } }
747
+ # Note: creation_user field is NOT shown
748
+
749
+ # With populate AND explicit request for original - show both
750
+ query = {
751
+ "populate": ["creation_user"],
752
+ "fields": ["creation_user"] # Explicitly request the ID
753
+ }
754
+ # Response:
755
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
756
+ ```
732
757
 
733
758
  ### Custom Pipelines
734
759
 
@@ -600,11 +600,11 @@ query = {
600
600
  "populate": ["creation_user", "modification_user"]
601
601
  }
602
602
 
603
- # Result will include (original ID automatically removed, replaced by populated object):
603
+ # Result will include (original ID removed, replaced by populated object):
604
604
  # {
605
605
  # "data": [{
606
606
  # "_id": "123",
607
- # "creation_user": { // Replaces the original ID field
607
+ # "creation_user_info": { // Populated object
608
608
  # "_id": "user_id_string",
609
609
  # "name": "John Doe",
610
610
  # "person": "person_id"
@@ -615,8 +615,8 @@ query = {
615
615
 
616
616
  **Reserved fields:**
617
617
 
618
- - `creation_user` → automatically populated, original ID is excluded
619
- - `modification_user` → automatically populated, original ID is excluded
618
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
619
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
620
620
 
621
621
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
622
622
 
@@ -624,10 +624,10 @@ These fields are only populated when explicitly requested via the `populate` par
624
624
 
625
625
  The behavior of original ID fields depends on whether they are being populated:
626
626
 
627
- #### Without populate - ID field shown as-is:
627
+ #### Without populate - ID field shown as-is (from database):
628
628
 
629
629
  ```python
630
- # No populate - field shown normally
630
+ # No populate - field shown normally from database
631
631
  query = {
632
632
  "fields": ["name", "establishment_id"]
633
633
  }
@@ -686,7 +686,32 @@ query = {
686
686
  # }
687
687
  ```
688
688
 
689
- **For audit fields:** The original ID is always excluded and never shown (no `creation_user_id` field exists to request).
689
+ #### Audit fields behavior:
690
+
691
+ ```python
692
+ # Without populate - original field shown
693
+ query = {
694
+ "fields": ["name", "creation_user"]
695
+ }
696
+ # Response:
697
+ # { "creation_user": "user_123" }
698
+
699
+ # With populate - original field removed, *_info shown instead
700
+ query = {
701
+ "populate": ["creation_user"]
702
+ }
703
+ # Response:
704
+ # { "creation_user_info": { "_id": "user_123", ... } }
705
+ # Note: creation_user field is NOT shown
706
+
707
+ # With populate AND explicit request for original - show both
708
+ query = {
709
+ "populate": ["creation_user"],
710
+ "fields": ["creation_user"] # Explicitly request the ID
711
+ }
712
+ # Response:
713
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
714
+ ```
690
715
 
691
716
  ### Custom Pipelines
692
717
 
@@ -7,7 +7,7 @@ from . import core
7
7
  from . import cache
8
8
  from . import query
9
9
 
10
- __version__ = "1.2.2"
10
+ __version__ = "1.2.4"
11
11
  __author__ = "OpenLink SpA"
12
12
 
13
13
  __all__ = ["core", "cache", "query", "reports", "__version__"]
@@ -21,7 +21,7 @@ RESERVED_AUDIT_FIELDS = {
21
21
  "creation_user": {
22
22
  "collection": "users",
23
23
  "field_name": "creation_user",
24
- "alias": "creation_user",
24
+ "alias": "creation_user_info",
25
25
  "field_type": "id",
26
26
  "project_exclude": {
27
27
  "role": 0,
@@ -49,7 +49,7 @@ RESERVED_AUDIT_FIELDS = {
49
49
  "modification_user": {
50
50
  "collection": "users",
51
51
  "field_name": "modification_user",
52
- "alias": "modification_user",
52
+ "alias": "modification_user_info",
53
53
  "field_type": "id",
54
54
  "project_exclude": {
55
55
  "role": 0,
@@ -626,10 +626,11 @@ class QueryBuilder:
626
626
  return {"$sort": sort_pipeline}
627
627
 
628
628
  def build_fields_pipeline(
629
- self, fields_params: List[str], populate_params: Optional[List] = None
629
+ self, fields_params: List[str], populate_params: Optional[Any] = None
630
630
  ) -> Dict[str, Any]:
631
631
  """
632
632
  Build fields projection pipeline with support for dot notation and array notation.
633
+ Automatically includes aliases for populated fields when fields is specified.
633
634
  """
634
635
  fields_pipeline = {}
635
636
 
@@ -648,15 +649,43 @@ class QueryBuilder:
648
649
 
649
650
  # Process populate_params if provided
650
651
  if populate_params:
651
- for field in populate_params:
652
+ # Convert to list if it's a dict
653
+ populate_list = (
654
+ populate_params
655
+ if isinstance(populate_params, list)
656
+ else list(populate_params.keys())
657
+ )
658
+
659
+ for field in populate_list:
652
660
  try:
661
+ # Add the original field so sanitization can work
653
662
  normalized_field = normalize_field_path(field)
654
663
  if validate_field_path(normalized_field):
655
664
  fields_pipeline[normalized_field] = 1
665
+
666
+ # Add the populated alias for audit fields
667
+ if field in RESERVED_AUDIT_FIELDS:
668
+ alias = RESERVED_AUDIT_FIELDS[field]["alias"]
669
+ normalized_alias = normalize_field_path(alias)
670
+ if validate_field_path(normalized_alias):
671
+ fields_pipeline[normalized_alias] = 1
656
672
  else:
657
- print(
658
- f"Warning: Invalid populate field path '{field}', skipping in projection"
659
- )
673
+ # For normal relations, find the alias and add it
674
+ for relation in self.relations:
675
+ relation_name = relation[1]
676
+ base_name = relation_name.replace("_ids", "").replace(
677
+ "_id", ""
678
+ )
679
+ if base_name == field:
680
+ # Use custom alias if provided (5th element)
681
+ if len(relation) > 4 and relation[4]:
682
+ alias = relation[4]
683
+ else:
684
+ alias = base_name
685
+ normalized_alias = normalize_field_path(alias)
686
+ if validate_field_path(normalized_alias):
687
+ fields_pipeline[normalized_alias] = 1
688
+ break
660
689
  except Exception as e:
661
690
  print(
662
691
  f"Warning: Error processing populate field '{field}' in projection: {e}"
@@ -847,11 +876,16 @@ class QueryBuilder:
847
876
  populated_fields = self._get_populated_fields_list(query["populate"])
848
877
 
849
878
  for doc in data_result:
850
- # Always remove temporary audit field copies
851
- for field in RESERVED_AUDIT_FIELDS.keys():
852
- temp_field = f"_original_{field}"
853
- if temp_field in doc:
854
- del doc[temp_field]
879
+ # For audit fields: remove original if populated version exists and not requested in fields
880
+ for field_name, field_config in RESERVED_AUDIT_FIELDS.items():
881
+ if field_name in populated_fields:
882
+ # If the populated alias exists and original field is not explicitly requested
883
+ if (
884
+ field_config["alias"] in doc
885
+ and field_name not in fields_requested
886
+ ):
887
+ if field_name in doc:
888
+ del doc[field_name]
855
889
 
856
890
  # For normal relations: remove original ID unless explicitly requested in fields
857
891
  for populated_field in populated_fields:
@@ -883,12 +917,6 @@ class QueryBuilder:
883
917
  field_config = RESERVED_AUDIT_FIELDS[field_name]
884
918
 
885
919
  lookup_pipeline = [
886
- # Save the original ID before lookup (will be removed during sanitization)
887
- {
888
- "$addFields": {
889
- f"_original_{field_config['field_name']}": f"${field_config['field_name']}"
890
- }
891
- },
892
920
  {
893
921
  "$lookup": {
894
922
  "from": field_config["collection"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.2
3
+ Version: 1.2.4
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -642,11 +642,11 @@ query = {
642
642
  "populate": ["creation_user", "modification_user"]
643
643
  }
644
644
 
645
- # Result will include (original ID automatically removed, replaced by populated object):
645
+ # Result will include (original ID removed, replaced by populated object):
646
646
  # {
647
647
  # "data": [{
648
648
  # "_id": "123",
649
- # "creation_user": { // Replaces the original ID field
649
+ # "creation_user_info": { // Populated object
650
650
  # "_id": "user_id_string",
651
651
  # "name": "John Doe",
652
652
  # "person": "person_id"
@@ -657,8 +657,8 @@ query = {
657
657
 
658
658
  **Reserved fields:**
659
659
 
660
- - `creation_user` → automatically populated, original ID is excluded
661
- - `modification_user` → automatically populated, original ID is excluded
660
+ - `creation_user` → automatically populated as `creation_user_info`, original ID is excluded
661
+ - `modification_user` → automatically populated as `modification_user_info`, original ID is excluded
662
662
 
663
663
  These fields are only populated when explicitly requested via the `populate` parameter, following the Strapi philosophy of on-demand loading.
664
664
 
@@ -666,10 +666,10 @@ These fields are only populated when explicitly requested via the `populate` par
666
666
 
667
667
  The behavior of original ID fields depends on whether they are being populated:
668
668
 
669
- #### Without populate - ID field shown as-is:
669
+ #### Without populate - ID field shown as-is (from database):
670
670
 
671
671
  ```python
672
- # No populate - field shown normally
672
+ # No populate - field shown normally from database
673
673
  query = {
674
674
  "fields": ["name", "establishment_id"]
675
675
  }
@@ -728,7 +728,32 @@ query = {
728
728
  # }
729
729
  ```
730
730
 
731
- **For audit fields:** The original ID is always excluded and never shown (no `creation_user_id` field exists to request).
731
+ #### Audit fields behavior:
732
+
733
+ ```python
734
+ # Without populate - original field shown
735
+ query = {
736
+ "fields": ["name", "creation_user"]
737
+ }
738
+ # Response:
739
+ # { "creation_user": "user_123" }
740
+
741
+ # With populate - original field removed, *_info shown instead
742
+ query = {
743
+ "populate": ["creation_user"]
744
+ }
745
+ # Response:
746
+ # { "creation_user_info": { "_id": "user_123", ... } }
747
+ # Note: creation_user field is NOT shown
748
+
749
+ # With populate AND explicit request for original - show both
750
+ query = {
751
+ "populate": ["creation_user"],
752
+ "fields": ["creation_user"] # Explicitly request the ID
753
+ }
754
+ # Response:
755
+ # { "creation_user": "user_123", "creation_user_info": { "_id": "user_123", ... } }
756
+ ```
732
757
 
733
758
  ### Custom Pipelines
734
759
 
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="oplnk-python-utils",
5
- version="1.2.2",
5
+ version="1.2.4",
6
6
  author="OpenLink SpA",
7
7
  author_email="contacto@openlink.cl",
8
8
  description="Reusable utilities for caching, data manipulation, parsing, and MongoDB query building",