bw-essentials-core 0.1.17__py3-none-any.whl → 0.1.33__py3-none-any.whl

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.
@@ -45,7 +45,16 @@ class UserPortfolio(ApiClient):
45
45
  "basket_details": "userportfolio/basket",
46
46
  "broker_users_holdings": "holding/{}/users",
47
47
  "user_portfolio_thresholds": "alerts/portfolio/thresholds",
48
- "holding_thresholds": "alerts/holding/thresholds"
48
+ "holding_thresholds": "alerts/holding/thresholds",
49
+ "user_portfolio_summary": "userportfolio/user-portfolio/summary",
50
+ "create_user_portfolio": "userportfolio/portfolio",
51
+ "get_user_portfolio": "userportfolio/portfolio",
52
+ "finn_user_order_place": "userorder/place-order/",
53
+ "finn_user_order_update": "userorder/order",
54
+ "finn_user_order_status": "userorder/order/status",
55
+ "finn_user_order_list": "userorder/orders",
56
+ "finn_user_order_portfolio": "userorder/portfolio",
57
+ "finn_user_order_reconcile": "userorder/reconcile/",
49
58
  }
50
59
 
51
60
  def create_holding_transaction(self, payload):
@@ -568,3 +577,391 @@ class UserPortfolio(ApiClient):
568
577
  logger.info(f"{data =}")
569
578
  return data.get("data")
570
579
 
580
+ def get_user_portfolio_summary(self, user_id: str, broker: str, product_type: str):
581
+ """
582
+ Retrieves the summary of a user's portfolio for a given broker and product type.
583
+
584
+ Args:
585
+ user_id (str): ID of the user.
586
+ broker (str): Broker of the user.
587
+ product_type (str): Product type (e.g., 'mtf').
588
+
589
+ Returns:
590
+ dict: Portfolio summary data.
591
+ """
592
+ logger.info(f"In - user_portfolio_summary {user_id = }, {broker = }, {product_type = }")
593
+ params = {
594
+ "user_id": user_id,
595
+ "broker": broker,
596
+ "product_type": product_type
597
+ }
598
+ data = self._get(url=self.base_url, endpoint=self.urls.get("user_portfolio_summary"), params=params)
599
+ logger.info(f"{data =}")
600
+ return data.get("data")
601
+
602
+ def create_user_portfolio(self, payload):
603
+ """
604
+ Create a User Portfolio.
605
+
606
+ This method sends a POST request to create a new user portfolio
607
+ using the provided payload. The payload must contain:
608
+
609
+ - user_id (str): Unique user identifier.
610
+ - name (str): Name of the user.
611
+ - portfolio_id (str): Portfolio identifier.
612
+ - status (str): Portfolio status (e.g., "active").
613
+ - subscription_id (str): Subscription reference.
614
+ - product_type (str): Product type (e.g., "fno").
615
+ - broker (str): Broker associated with the user.
616
+ - strategy (str): Strategy identifier (one_time/rebalance. Default: rebalance)
617
+
618
+ Parameters
619
+ ----------
620
+ payload : dict
621
+ JSON-serialisable dictionary for creating the user portfolio.
622
+
623
+ Returns
624
+ -------
625
+ dict
626
+ The newly created user portfolio (value of the 'data' field).
627
+ """
628
+ logger.info(f"In - create_user_portfolio {payload =}")
629
+
630
+ data = self._post(
631
+ url=self.base_url,
632
+ endpoint=self.urls.get("create_user_portfolio"),
633
+ json=payload
634
+ )
635
+
636
+ logger.info(f"{data =}")
637
+ return data.get("data")
638
+
639
+ def get_user_portfolio(
640
+ self,
641
+ user_id=None,
642
+ portfolio_id=None,
643
+ broker=None,
644
+ status=None,
645
+ investment_status=None,
646
+ subscription_id=None,
647
+ product_type=None,
648
+ strategy=None,
649
+ ):
650
+ """
651
+ Fetch User Portfolio with optional filters.
652
+
653
+ All parameters are optional and sent as query params.
654
+ Any field left as None will be ignored.
655
+
656
+ Parameters
657
+ ----------
658
+ user_id : str, optional
659
+ portfolio_id : str, optional
660
+ broker : str, optional
661
+ status : str, optional
662
+ investment_status : str, optional
663
+ subscription_id : str, optional
664
+ product_type : str, optional
665
+ strategy : str, optional
666
+
667
+ Returns
668
+ -------
669
+ dict or list
670
+ Value of `data` in API response.
671
+ """
672
+ params = {
673
+ "user_id": user_id,
674
+ "portfolio_id": portfolio_id,
675
+ "broker": broker,
676
+ "status": status,
677
+ "investment_status": investment_status,
678
+ "subscription_id": subscription_id,
679
+ "product_type": product_type,
680
+ "strategy": strategy,
681
+ }
682
+ params = {k: v for k, v in params.items() if v is not None}
683
+
684
+ logger.info(f"In - get_user_portfolio {params =}")
685
+
686
+ data = self._get(
687
+ url=self.base_url,
688
+ endpoint=self.urls.get("get_user_portfolio"),
689
+ params=params,
690
+ )
691
+
692
+ logger.info(f"{data =}")
693
+ return data.get("data")
694
+
695
+ def place_user_order(self, payload: dict):
696
+ """
697
+ Place a user order.
698
+
699
+ This method maps to:
700
+ POST /user-portfolio/userorder/place-order/
701
+
702
+ Parameters
703
+ ----------
704
+ payload : dict
705
+ Order placement payload containing:
706
+ - user_id
707
+ - broker_user_id
708
+ - broker
709
+ - product_type
710
+ - symbol
711
+ - broker_symbol
712
+ - category
713
+ - strategy
714
+ - option_type
715
+ - side
716
+ - order_type
717
+ - variety
718
+ - quantity
719
+ - price
720
+ - recommendation_id
721
+ - user_inputs
722
+
723
+ Returns
724
+ -------
725
+ dict
726
+ Placed order response data.
727
+ """
728
+ logger.info(f"In - place_user_order {payload =}")
729
+
730
+ data = self._post(
731
+ url=self.base_url,
732
+ endpoint=self.urls.get("finn_user_order_place"),
733
+ json=payload
734
+ )
735
+
736
+ logger.info(f"{data =}")
737
+ return data.get("data")
738
+
739
+ def update_user_order(self, order_tag: str, payload: dict):
740
+ """
741
+ Update a user order.
742
+
743
+ Maps to:
744
+ PUT /user-portfolio/userorder/order/{id}/update/
745
+
746
+ Parameters
747
+ ----------
748
+ order_tag : str
749
+ Internal Order Tag (path param).
750
+ payload : dict
751
+ Order update payload containing:
752
+ - order_id
753
+ - status
754
+ - filled_quantity
755
+ - pending_quantity
756
+ - cancelled_quantity
757
+ - average_price
758
+ - amount
759
+
760
+ Returns
761
+ -------
762
+ dict
763
+ Updated order data.
764
+ """
765
+ logger.info(
766
+ f"In - update_user_order {order_tag =}, {payload =}"
767
+ )
768
+
769
+ endpoint = f"{self.urls.get('finn_user_order_update')}/{order_tag}/update/"
770
+
771
+ data = self._put(
772
+ url=self.base_url,
773
+ endpoint=endpoint,
774
+ json=payload
775
+ )
776
+
777
+ logger.info(f"{data =}")
778
+ return data.get("data")
779
+
780
+ def get_user_order_status(self, order_id):
781
+ """
782
+ Fetch the current status of one or more user orders.
783
+
784
+ Backend always returns a list.
785
+ Client normalises response shape:
786
+
787
+ - int -> dict
788
+ - list -> list
789
+
790
+ Maps to:
791
+ GET /user-portfolio/userorder/order/status?order_id=1,2,3
792
+ """
793
+ is_single = not isinstance(order_id, (list, tuple, set))
794
+
795
+ if is_single:
796
+ order_id_param = str(order_id)
797
+ else:
798
+ order_id_param = ",".join(str(i) for i in order_id)
799
+
800
+ logger.info(f"In - get_user_order_status {order_id_param =}")
801
+
802
+ response = self._get(
803
+ url=self.base_url,
804
+ endpoint=self.urls.get("finn_user_order_status"),
805
+ params={"order_id": order_id_param},
806
+ )
807
+ data = response.get("data") or []
808
+ logger.info(f"{data =}")
809
+ if is_single:
810
+ return data[0] if data else None
811
+ return data
812
+
813
+ def get_user_orders(
814
+ self,
815
+ product_type: str,
816
+ user_id: str,
817
+ from_date: str = None,
818
+ to_date: str = None,
819
+ status: str = None,
820
+ symbol: str = None,
821
+ ):
822
+ """
823
+ Fetch user orders with filters.
824
+
825
+ Maps to:
826
+ GET /user-portfolio/userorder/orders/
827
+
828
+ Mandatory query params:
829
+ - product_type
830
+ - user_id
831
+
832
+ Optional query params:
833
+ - from_date (YYYY-MM-DD)
834
+ - to_date (YYYY-MM-DD)
835
+ - status
836
+ - symbol
837
+
838
+ Parameters
839
+ ----------
840
+ product_type : str
841
+ user_id : str
842
+ from_date : str, optional
843
+ to_date : str, optional
844
+ status : str, optional
845
+ symbol : str, optional
846
+
847
+ Returns
848
+ -------
849
+ list[dict]
850
+ List of user orders.
851
+ """
852
+ params = {
853
+ "product_type": product_type,
854
+ "user_id": user_id,
855
+ "from_date": from_date,
856
+ "to_date": to_date,
857
+ "status": status,
858
+ "symbol": symbol,
859
+ }
860
+ params = {k: v for k, v in params.items() if v is not None}
861
+ logger.info(f"In - get_user_orders {params =}")
862
+ data = self._get(
863
+ url=self.base_url,
864
+ endpoint=self.urls.get("finn_user_order_list"),
865
+ params=params,
866
+ )
867
+ logger.info(f"{data =}")
868
+ return data.get("data")
869
+
870
+ def get_user_portfolios(
871
+ self,
872
+ user_id: str,
873
+ broker_user_id: str,
874
+ broker: str,
875
+ product_type: str,
876
+ status: str = None,
877
+ limit: int = None,
878
+ offset: int = None,
879
+ ):
880
+ """
881
+ Fetch user order portfolios with filters.
882
+
883
+ Maps to:
884
+ GET /user-portfolio/userorder/portfolio/
885
+
886
+ Mandatory query params:
887
+ - user_id
888
+ - product_type
889
+ - broker_user_id
890
+ - broker
891
+
892
+ Optional query params:
893
+ - status
894
+ - limit
895
+ - offset
896
+
897
+ Parameters
898
+ ----------
899
+ user_id : str
900
+ product_type : str
901
+ broker_user_id : str
902
+ broker : str
903
+ status : str, optional
904
+ limit : int, optional
905
+ offset : int, optional
906
+
907
+ Returns
908
+ -------
909
+ dict
910
+ List of user order portfolios.
911
+ """
912
+ params = {
913
+ "user_id": user_id,
914
+ "product_type": product_type,
915
+ "broker_user_id": broker_user_id,
916
+ "broker": broker,
917
+ "status": status,
918
+ "limit": limit,
919
+ "offset": offset,
920
+ }
921
+ params = {k: v for k, v in params.items() if v is not None}
922
+ logger.info(f"In - get_user_portfolios {params =}")
923
+ data = self._get(
924
+ url=self.base_url,
925
+ endpoint=self.urls.get("finn_user_order_portfolio"),
926
+ params=params,
927
+ )
928
+ logger.info(f"{data =}")
929
+ return data.get("data")
930
+
931
+ def reconcile_user_order(self, payload: dict):
932
+ """
933
+ Reconcile a user order.
934
+
935
+ Maps to:
936
+ POST /user-portfolio/userorder/reconcile/
937
+
938
+ Parameters
939
+ ----------
940
+ payload : dict
941
+ Reconciliation payload containing:
942
+ - user_id
943
+ - broker_user_id
944
+ - broker
945
+ - product_type
946
+ - symbol
947
+ - broker_symbol
948
+ - category
949
+ - strategy
950
+ - option_type
951
+ - order_type
952
+ - variety
953
+ - quantity
954
+
955
+ Returns
956
+ -------
957
+ dict
958
+ Reconciled order data.
959
+ """
960
+ logger.info(f"In - reconcile_user_order {payload =}")
961
+ data = self._post(
962
+ url=self.base_url,
963
+ endpoint=self.urls.get("finn_user_order_reconcile"),
964
+ json=payload,
965
+ )
966
+ logger.info(f"{data =}")
967
+ return data.get("data")
@@ -66,7 +66,8 @@ class UserReporting(ApiClient):
66
66
  "instructions": "reporting/instructions/",
67
67
  "portfolio_performance": "reporting/user/%s/userportfolio/%s/performance/",
68
68
  "portfolio_performance_breakdown": "reporting/user/%s/userportfolio/%s/performance/breakdown/",
69
- 'portfolio_stock_performance': "reporting/user/%s/userportfolio/%s/stock/performance/"
69
+ 'portfolio_stock_performance': "reporting/user/%s/userportfolio/%s/stock/performance/",
70
+ "portfolio_performance_summary": "reporting/user/%s/performance/summary"
70
71
  }
71
72
 
72
73
  def _get_integer_quantity(self, qty, side):
@@ -168,3 +169,21 @@ class UserReporting(ApiClient):
168
169
  endpoint=endpoint)
169
170
  logger.info(f"{data=}")
170
171
  return data.get("data")
172
+
173
+ def get_portfolio_performance_summary(self, user_id: str, product: str = 'equity'):
174
+ """
175
+ Retrieves a summary of the user's portfolio performance.
176
+
177
+ Args:
178
+ user_id (str): ID of the user.
179
+ product (str): Product type (default: 'equity', e.g., 'mtf').
180
+
181
+ Returns:
182
+ dict: Portfolio performance summary data.
183
+ """
184
+ logger.info(f"In - get_portfolio_performance_summary {user_id = }, {product =}")
185
+ endpoint = self.urls.get("portfolio_performance_summary") % user_id
186
+ params = {"product": product}
187
+ data = self._get(url=self.base_url, endpoint=endpoint, params=params)
188
+ logger.info(f"{data =}")
189
+ return data.get("data")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bw-essentials-core
3
- Version: 0.1.17
3
+ Version: 0.1.33
4
4
  Summary: Reusable utilities for S3, email, Data Loch, Microsoft Teams Notifications and more.
5
5
  Author: InvestorAI
6
6
  Author-email: support+tech@investorai.in
@@ -0,0 +1,32 @@
1
+ bw_essentials/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ bw_essentials/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ bw_essentials/constants/services.py,sha256=ozLnxtqS79w9yPH7baTWaJzwGmIgg_mxp1vO8Aqh-dc,930
4
+ bw_essentials/data_loch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ bw_essentials/data_loch/data_loch.py,sha256=iJO0oPhZyQjoUZOaeBP1nsbuc5iR9ZNWoAXj2pGEpT8,11716
6
+ bw_essentials/email_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ bw_essentials/email_client/email_client.py,sha256=zKAhF7TUYWWfo5PuIfUzD8W2XSmmStgpSvwXc9P4LV8,9461
8
+ bw_essentials/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ bw_essentials/notifications/teams_notification_schemas.py,sha256=FvZ8FyIwku4-0uweDn9gpaIJW_AIsNoqV1hB6lQ4OPI,6398
10
+ bw_essentials/notifications/teams_notifications.py,sha256=1tkS3tTJQZ5ByMwK2WXpFgQn7-TobYYOncOj81NBySY,7308
11
+ bw_essentials/s3_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ bw_essentials/s3_utils/s3_utils.py,sha256=wzjVrTX22_8PMX86svKFYGMZwgjBHbOaeEsxO-lR7BY,12922
13
+ bw_essentials/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ bw_essentials/services/api_client.py,sha256=hGgM1rwiI7Yvq0tj0XfqZUV_hSXEm4S5Vw0lyNvSVDY,9553
15
+ bw_essentials/services/broker.py,sha256=pcVmkmLn8hZhjUJseW07245xYcHYtGU6segvS9wsmt4,9567
16
+ bw_essentials/services/compliance.py,sha256=tt46lYPhE15D-4XtQLPQW0rbsMXx5Zld55u2oEzkNVM,2453
17
+ bw_essentials/services/job_scheduler.py,sha256=1fQDWBvNa3bMCm3YzlJehoS-HONUSu4_oquuBv6t0Lg,1966
18
+ bw_essentials/services/market_pricer.py,sha256=5yMsg3lKbZXgbAeGa6NMEyjayP1CZGZ0g_lvHlnTyn4,13581
19
+ bw_essentials/services/master_data.py,sha256=wdkqhYhXH9DtqOgiCh_CMyEiZaU-HfQtGKsJdMelY94,14132
20
+ bw_essentials/services/model_portfolio_reporting.py,sha256=VLSvfRZOdn-nI3MtgfcqP0ZE3aTqBU0NcncLffPVtD8,4407
21
+ bw_essentials/services/notification.py,sha256=nnvtEqUphhCSTavsV75XvLp1ZNVV-kLU_cFc8zLNIec,5396
22
+ bw_essentials/services/payment.py,sha256=l2F7U1t8wKN7wWWgzKxC4iiLqwHImKgm7NxK1YWDEfw,3487
23
+ bw_essentials/services/portfolio_catalogue.py,sha256=FwiXPj-kunauXQkT3EDrhCiHHDVANuVrU6cfJxgkJR0,9999
24
+ bw_essentials/services/portfolio_content.py,sha256=VX5ASf9Kg-w4-ZktiDEoM5YlFqn_mShtqJhL-7v7X8s,3039
25
+ bw_essentials/services/trade_placement.py,sha256=V-CmRQV94ewdfeUzlNIsKrdUDzHFh9GRUAW4Bei_V6Y,29885
26
+ bw_essentials/services/user_app.py,sha256=1z73G91TygW_MgHjkUVA2WIdN4CQjx_oSZWW6NqFit8,11774
27
+ bw_essentials/services/user_portfolio.py,sha256=YqZz-vjBCTuUfXE5nUb0uOp9FXtVDHbfaJOMMcaCgJg,33317
28
+ bw_essentials/services/user_portfolio_reporting.py,sha256=soh68r6ezBoILXrwTDxY-Us9_TyJYXxYHChCQcsxKP8,7585
29
+ bw_essentials_core-0.1.33.dist-info/METADATA,sha256=-yJmupdUfZSmfXOHJ4rOJt0MD2ZIKLls8gq5VaacliA,7502
30
+ bw_essentials_core-0.1.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
+ bw_essentials_core-0.1.33.dist-info/top_level.txt,sha256=gDc5T_y5snwKGXDQUusEus-FEt0RFwG644Yn_58wQOQ,14
32
+ bw_essentials_core-0.1.33.dist-info/RECORD,,
@@ -1,31 +0,0 @@
1
- bw_essentials/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- bw_essentials/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- bw_essentials/constants/services.py,sha256=HxeM05zJP1oCqwLRLb9wLxAOCmbdu-jWgtlmnPE4KFU,900
4
- bw_essentials/data_loch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- bw_essentials/data_loch/data_loch.py,sha256=iJO0oPhZyQjoUZOaeBP1nsbuc5iR9ZNWoAXj2pGEpT8,11716
6
- bw_essentials/email_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- bw_essentials/email_client/email_client.py,sha256=_LzDWUJUkNTSn3C5v0-jjW-Ls537xt493lkcD4noQXY,9116
8
- bw_essentials/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- bw_essentials/notifications/teams_notification_schemas.py,sha256=FvZ8FyIwku4-0uweDn9gpaIJW_AIsNoqV1hB6lQ4OPI,6398
10
- bw_essentials/notifications/teams_notifications.py,sha256=1tkS3tTJQZ5ByMwK2WXpFgQn7-TobYYOncOj81NBySY,7308
11
- bw_essentials/s3_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- bw_essentials/s3_utils/s3_utils.py,sha256=wzjVrTX22_8PMX86svKFYGMZwgjBHbOaeEsxO-lR7BY,12922
13
- bw_essentials/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- bw_essentials/services/api_client.py,sha256=5XFbdw5gfxXz7mQlLWNC8efDrvZgAztM-Oo0Dp1jDF8,8215
15
- bw_essentials/services/broker.py,sha256=W4y31K7dF4LHfQSvnAwVWfGoYKkdz-W2TXqTLhkUxn8,9348
16
- bw_essentials/services/job_scheduler.py,sha256=1fQDWBvNa3bMCm3YzlJehoS-HONUSu4_oquuBv6t0Lg,1966
17
- bw_essentials/services/market_pricer.py,sha256=Qc9lxzAjhefAvjyEKsBPDu60bF6_61cnSpZNfjGMyDg,11755
18
- bw_essentials/services/master_data.py,sha256=2o_r2gdhIKBeTFgn5IIY-becgCEpYBymO4BKmixdV1g,11987
19
- bw_essentials/services/model_portfolio_reporting.py,sha256=iOtm4gyfU8P7C0R1gp6gUJI4ZRxJD5K2GLOalI_gToM,3249
20
- bw_essentials/services/notification.py,sha256=q4jIZzIWIHvn-XaIIbkEjjhkYUV8ZGQWUXaY4NNRFgI,5078
21
- bw_essentials/services/payment.py,sha256=Em8Lvg7aX1Ga4CJDpJimyE_ByZhJZKM6W6rw2cUUIvw,1479
22
- bw_essentials/services/portfolio_catalogue.py,sha256=Eal2wJd2qQB_qk2B_7u0-SwsZTN9nMsSxwgoIud845Y,7201
23
- bw_essentials/services/portfolio_content.py,sha256=etdbPqacUJscM1DqdtqSsuH3IQX6oPJEsE3nM6d8YvI,3038
24
- bw_essentials/services/trade_placement.py,sha256=PrzeU2XXC9HF1IQ1dMDM_ZHxmC491sOl-JbA6GWPwII,20772
25
- bw_essentials/services/user_app.py,sha256=1z73G91TygW_MgHjkUVA2WIdN4CQjx_oSZWW6NqFit8,11774
26
- bw_essentials/services/user_portfolio.py,sha256=uUQUCG8Av8eKBIw-TlNtOPpQluO9HjAkxMzzO5jfzbo,22179
27
- bw_essentials/services/user_portfolio_reporting.py,sha256=pwqfW95LTiGOGufcTphljFxPlOd-G4Q263UtoQURPxM,6772
28
- bw_essentials_core-0.1.17.dist-info/METADATA,sha256=9reGA-4ZeYNOVS7aIbRbjv9MjXrEEhPS0x85ZcXwGgQ,7502
29
- bw_essentials_core-0.1.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- bw_essentials_core-0.1.17.dist-info/top_level.txt,sha256=gDc5T_y5snwKGXDQUusEus-FEt0RFwG644Yn_58wQOQ,14
31
- bw_essentials_core-0.1.17.dist-info/RECORD,,