MambuPy 2.1.0__py3-none-any.whl → 2.2.1__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.
Files changed (55) hide show
  1. MambuPy/__init__.py +2 -4
  2. MambuPy/api/connector/rest.py +81 -23
  3. MambuPy/mambuconfig.py +12 -13
  4. MambuPy/mambuutil.py +16 -62
  5. MambuPy/orm/schema_groups.py +3 -4
  6. MambuPy/orm/schema_orm.py +0 -2
  7. MambuPy/rest/__init__.py +27 -45
  8. MambuPy/rest/mambuactivity.py +689 -32
  9. mambupy/__init__.py +2 -4
  10. mambupy/api/connector/rest.py +81 -23
  11. mambupy/mambuconfig.py +12 -13
  12. mambupy/mambuutil.py +16 -62
  13. mambupy/orm/schema_groups.py +3 -4
  14. mambupy/orm/schema_orm.py +0 -2
  15. mambupy/rest/__init__.py +27 -45
  16. mambupy/rest/mambuactivity.py +689 -32
  17. {mambupy-2.1.0.dist-info → mambupy-2.2.1.dist-info}/METADATA +5 -4
  18. {mambupy-2.1.0.dist-info → mambupy-2.2.1.dist-info}/RECORD +21 -55
  19. {mambupy-2.1.0.dist-info → mambupy-2.2.1.dist-info}/WHEEL +1 -1
  20. MambuPy/rest/mambubranch.py +0 -122
  21. MambuPy/rest/mambucentre.py +0 -104
  22. MambuPy/rest/mambuclient.py +0 -317
  23. MambuPy/rest/mambugroup.py +0 -372
  24. MambuPy/rest/mambuloan.py +0 -687
  25. MambuPy/rest/mambuproduct.py +0 -180
  26. MambuPy/rest/mamburepayment.py +0 -99
  27. MambuPy/rest/mamburestutils.py +0 -119
  28. MambuPy/rest/mamburoles.py +0 -98
  29. MambuPy/rest/mambusaving.py +0 -114
  30. MambuPy/rest/mambusavingfundingrepayment.py +0 -94
  31. MambuPy/rest/mambusavingtransaction.py +0 -163
  32. MambuPy/rest/mambustruct.py +0 -1066
  33. MambuPy/rest/mambutask.py +0 -163
  34. MambuPy/rest/mambutransaction.py +0 -103
  35. MambuPy/rest/mambutransactionchannel.py +0 -101
  36. MambuPy/rest/mambuuser.py +0 -229
  37. mambupy/rest/mambubranch.py +0 -122
  38. mambupy/rest/mambucentre.py +0 -104
  39. mambupy/rest/mambuclient.py +0 -317
  40. mambupy/rest/mambugroup.py +0 -372
  41. mambupy/rest/mambuloan.py +0 -687
  42. mambupy/rest/mambuproduct.py +0 -180
  43. mambupy/rest/mamburepayment.py +0 -99
  44. mambupy/rest/mamburestutils.py +0 -119
  45. mambupy/rest/mamburoles.py +0 -98
  46. mambupy/rest/mambusaving.py +0 -114
  47. mambupy/rest/mambusavingfundingrepayment.py +0 -94
  48. mambupy/rest/mambusavingtransaction.py +0 -163
  49. mambupy/rest/mambustruct.py +0 -1066
  50. mambupy/rest/mambutask.py +0 -163
  51. mambupy/rest/mambutransaction.py +0 -103
  52. mambupy/rest/mambutransactionchannel.py +0 -101
  53. mambupy/rest/mambuuser.py +0 -229
  54. {mambupy-2.1.0.dist-info → mambupy-2.2.1.dist-info/licenses}/LICENSE +0 -0
  55. {mambupy-2.1.0.dist-info → mambupy-2.2.1.dist-info}/top_level.txt +0 -0
MambuPy/__init__.py CHANGED
@@ -8,15 +8,13 @@
8
8
  MambuPy.api
9
9
  MambuPy.api.connector
10
10
  MambuPy.orm
11
- MambuPy.rest
12
11
 
13
12
 
14
13
  Currently, there are two different ways to access Mambu objects:
15
14
 
16
15
  1) Objects using `Mambu REST API
17
16
  <https://developer.mambu.com/customer/en/portal/articles/1162276-rest-apis-overview/>`_
18
- , they live at the :any:`MambuPy.api` package (the old-and-soon-to-be-deprecated,
19
- V1-compatible code lives at :any:`MambuPy.rest`
17
+ , they live at the :any:`MambuPy.api` package
20
18
 
21
19
  2) An ORM using a `DB backup retrieved from Mambu
22
20
  <https://support.mambu.com/docs/mambu-data-dictionary>`_
@@ -39,7 +37,7 @@ import sys
39
37
  from importlib.abc import MetaPathFinder
40
38
  from importlib.util import spec_from_file_location
41
39
 
42
- __version__ = "2.1.0"
40
+ __version__ = "2.2.1"
43
41
  """The version of this module."""
44
42
 
45
43
 
@@ -36,12 +36,82 @@ from MambuPy.mambuutil import (
36
36
  apiurl,
37
37
  apiuser,
38
38
  setup_logging,
39
+ activate_request_session_objects,
39
40
  )
40
41
 
41
42
 
42
43
  logger = setup_logging(__name__)
43
44
 
44
45
 
46
+ def _configure_retry_strategy(session, retries=5):
47
+ """Configure retry strategy for a session.
48
+
49
+ Args:
50
+ session (requests.Session): The session to configure
51
+ retries (int, optional): Number of retries. Defaults to 5.
52
+ """
53
+ retry_strategy = Retry(
54
+ total=retries,
55
+ status_forcelist=[429, 500, 502, 503, 504],
56
+ backoff_factor=1,
57
+ allowed_methods=[
58
+ "HEAD",
59
+ "GET",
60
+ "OPTIONS",
61
+ "POST",
62
+ "PUT",
63
+ "DELETE",
64
+ "TRACE",
65
+ "PATCH",
66
+ ],
67
+ )
68
+ adapter = HTTPAdapter(max_retries=retry_strategy)
69
+ session.mount("https://", adapter)
70
+ session.mount("http://", adapter)
71
+
72
+
73
+ class SessionSingleton:
74
+ """Singleton class to manage HTTP sessions for MambuPy.
75
+
76
+ This class ensures that only one requests.Session object is created and reused
77
+ across all requests to the Mambu API. This helps improve performance by reusing
78
+ TCP connections.
79
+
80
+ Example:
81
+ >>> session = SessionSingleton()
82
+ >>> session.get_session() # Returns a requests.Session object
83
+ >>> session2 = SessionSingleton()
84
+ >>> session2.get_session() # Returns the same requests.Session object
85
+ """
86
+
87
+ __instance = None
88
+ __session = None
89
+ _RETRIES = 5
90
+
91
+ def __new__(cls):
92
+ """Create a new instance of SessionSingleton if one doesn't exist.
93
+
94
+ Returns:
95
+ SessionSingleton: The singleton instance.
96
+ """
97
+ if cls.__instance is None:
98
+ cls.__instance = super(SessionSingleton, cls).__new__(cls)
99
+ return cls.__instance
100
+
101
+ def get_session(self):
102
+ """Get the requests.Session object.
103
+
104
+ If no session exists, create a new one. Otherwise, return the existing one.
105
+
106
+ Returns:
107
+ requests.Session: The session object to use for HTTP requests.
108
+ """
109
+ if self.__session is None:
110
+ self.__session = requests.Session()
111
+ _configure_retry_strategy(self.__session, self._RETRIES)
112
+ return self.__session
113
+
114
+
45
115
  class MambuConnectorREST(MambuConnector, MambuConnectorReader, MambuConnectorWriter):
46
116
  """A connector for Mambu REST API"""
47
117
 
@@ -55,6 +125,9 @@ class MambuConnectorREST(MambuConnector, MambuConnectorReader, MambuConnectorWri
55
125
  }
56
126
  self.__set_authorization_header(user, pwd)
57
127
  self.__set_url(url)
128
+ self._session = None
129
+ if activate_request_session_objects.lower() == "true":
130
+ self._session = SessionSingleton().get_session()
58
131
 
59
132
  def __set_authorization_header(self, user, pwd):
60
133
  self._headers["Authorization"] = "Basic {}".format(
@@ -108,26 +181,6 @@ class MambuConnectorREST(MambuConnector, MambuConnectorReader, MambuConnectorWri
108
181
  params = self.__request_params(params)
109
182
  data = self.__request_data(data)
110
183
 
111
- retry_strategy = Retry(
112
- total=self._RETRIES,
113
- status_forcelist=[429, 500, 502, 503, 504],
114
- backoff_factor=1,
115
- allowed_methods=[
116
- "HEAD",
117
- "GET",
118
- "OPTIONS",
119
- "POST",
120
- "PUT",
121
- "DELETE",
122
- "TRACE",
123
- "PATCH",
124
- ],
125
- )
126
- adapter = HTTPAdapter(max_retries=retry_strategy)
127
- http = requests.Session()
128
- http.mount("https://", adapter)
129
- http.mount("http://", adapter)
130
-
131
184
  resp = ""
132
185
  try:
133
186
  logger.debug(
@@ -139,10 +192,15 @@ url %s, params %s, data %s, headers %s",
139
192
  data,
140
193
  [(k, v) for k, v in headers.items() if k != "Authorization"],
141
194
  )
142
- resp = http.request(method, url, params=params, data=data, headers=headers)
195
+ if self._session:
196
+ resp = self._session.request(method, url, params=params, data=data, headers=headers)
197
+ else:
198
+ http = requests.Session()
199
+ _configure_retry_strategy(http, self._RETRIES)
200
+ resp = http.request(method, url, params=params, data=data, headers=headers)
143
201
  resp.raise_for_status()
144
202
  except requests.exceptions.HTTPError as httperr:
145
- logger.error(
203
+ logger.warning(
146
204
  "%s on %s request: params %s, data %s, headers %s",
147
205
  str(httperr),
148
206
  method,
@@ -151,7 +209,7 @@ url %s, params %s, data %s, headers %s",
151
209
  [(k, v) for k, v in headers.items() if k != "Authorization"],
152
210
  )
153
211
  if hasattr(resp, "content"): # pragma: no cover
154
- logger.error("HTTPError, resp content: %s", resp.content)
212
+ logger.warning("HTTPError, resp content: %s", resp.content)
155
213
  try:
156
214
  content = json.loads(resp.content.decode())
157
215
  except ValueError:
MambuPy/mambuconfig.py CHANGED
@@ -24,6 +24,7 @@ RC files must have the following format:
24
24
  apiuser=API_user
25
25
  apipwd=API_password
26
26
  apipagination=API_pagination_limit
27
+ activate_request_session_objects=API_persistent_session
27
28
  [DB]
28
29
  dbname=Database_name
29
30
  dbuser=Database_user
@@ -75,6 +76,10 @@ user on your Mambu instance
75
76
 
76
77
  The db* configs refer to Database configurations which hold a backup of
77
78
  your Mambu Database on some server.
79
+
80
+ The activate_request_session_objects config enables or disables HTTP persistent sessions
81
+ for API requests. When enabled, it will reuse the same TCP connection for
82
+ multiple requests, improving performance. Default is False.
78
83
  """
79
84
 
80
85
  default_configs = {
@@ -83,6 +88,7 @@ default_configs = {
83
88
  "apiuser": "mambu_api_user",
84
89
  "apipwd": "mambu_api_password",
85
90
  "apipagination": "50",
91
+ "activate_request_session_objects": "False",
86
92
  # Mambu DB configurations
87
93
  "dbname": "mambu_db",
88
94
  "dbuser": "mambu_db_user",
@@ -99,20 +105,10 @@ You may edit this to your own liking. But beware of pricking eyes!
99
105
  """
100
106
 
101
107
  import os
108
+ import configparser
109
+ from configparser import NoSectionError
102
110
 
103
- # import ConfigParser depending on Python version
104
- import sys
105
-
106
- if sys.version_info.major < 3:
107
- import ConfigParser
108
- from ConfigParser import NoSectionError
109
-
110
- config = ConfigParser.ConfigParser(defaults=default_configs)
111
- else:
112
- import configparser
113
- from configparser import NoSectionError
114
-
115
- config = configparser.ConfigParser(defaults=default_configs)
111
+ config = configparser.ConfigParser(defaults=default_configs)
116
112
 
117
113
  # argparse for command line arguments overriding
118
114
  import argparse
@@ -123,6 +119,7 @@ argparser.add_argument("--mambupy_apiurl")
123
119
  argparser.add_argument("--mambupy_apiuser")
124
120
  argparser.add_argument("--mambupy_apipwd")
125
121
  argparser.add_argument("--mambupy_apipagination")
122
+ argparser.add_argument("--mambupy_activate_request_session_objects")
126
123
  argparser.add_argument("--mambupy_dbname")
127
124
  argparser.add_argument("--mambupy_dbuser")
128
125
  argparser.add_argument("--mambupy_dbpwd")
@@ -184,6 +181,8 @@ apipwd = get_conf(config, "API", "apipwd")
184
181
  """Password to access Mambu API"""
185
182
  apipagination = get_conf(config, "API", "apipagination")
186
183
  """Pagination default limit for requests to Mambu API"""
184
+ activate_request_session_objects = get_conf(config, "API", "activate_request_session_objects")
185
+ """Whether to use persistent HTTP sessions for API requests"""
187
186
  dbname = get_conf(config, "DB", "dbname")
188
187
  """Name of the DB with a backup of Mambu's DB"""
189
188
  dbuser = get_conf(config, "DB", "dbuser")
MambuPy/mambuutil.py CHANGED
@@ -28,16 +28,9 @@ from codecs import open as copen
28
28
 
29
29
  import yaml
30
30
 
31
- from .mambuconfig import apipagination, apipwd, apiurl, apiuser, loggingconf
31
+ from .mambuconfig import apipagination, apipwd, apiurl, apiuser, loggingconf, activate_request_session_objects
32
32
  from .mambugeturl import getmambuurl
33
33
 
34
- try:
35
- # python2
36
- unicode
37
- except NameError:
38
- # python3
39
- unicode = str
40
-
41
34
  import json
42
35
  import sys
43
36
  from datetime import datetime, timezone
@@ -186,21 +179,23 @@ def date_format(field, formato=None, as_utc=False):
186
179
 
187
180
  if not formato:
188
181
  formato = "%Y-%m-%dT%H:%M:%S"
189
- if sys.version_info < (3, 0):
190
- return datetime.strptime(
191
- datetime.strptime(field, "%Y-%m-%dT%H:%M:%S+0000").strftime(formato),
182
+
183
+ field_with_tz = datetime.fromisoformat(field)
184
+
185
+ if as_utc and field_with_tz.tzinfo is not None:
186
+ # Convertir a UTC mientras aún tiene tzinfo
187
+ field_as_dt = field_with_tz.astimezone(timezone.utc)
188
+ field_as_dt = datetime.strptime(
189
+ field_as_dt.strftime(formato),
192
190
  formato,
193
191
  )
194
192
  else:
195
193
  field_as_dt = datetime.strptime(
196
- datetime.fromisoformat(field).strftime(formato),
194
+ field_with_tz.strftime(formato),
197
195
  formato,
198
196
  )
199
- if as_utc:
200
- field_as_dt = field_as_dt.astimezone(
201
- timezone.utc
202
- ).replace(tzinfo=None)
203
- return field_as_dt
197
+
198
+ return field_as_dt
204
199
 
205
200
 
206
201
  def strip_tags(html):
@@ -221,10 +216,7 @@ def strip_tags(html):
221
216
  """
222
217
 
223
218
  def __init__(self):
224
- try:
225
- super().__init__() # required for python3
226
- except TypeError: # pragma: no cover
227
- pass # with python2 raises TypeError
219
+ super().__init__()
228
220
  self.reset()
229
221
  self.fed = []
230
222
 
@@ -256,12 +248,7 @@ def strip_consecutive_repeated_char(s, ch):
256
248
  return sdest
257
249
 
258
250
 
259
- if sys.version_info >= (3, 0):
260
- # python3
261
- from future.moves.urllib import parse as urlparse
262
- else: # pragma: no cover
263
- # python2
264
- import urlparse
251
+ from urllib import parse as urlparse
265
252
 
266
253
 
267
254
  def iri_to_uri(iri):
@@ -277,35 +264,9 @@ def iri_to_uri(iri):
277
264
  unicode chars there. Using this I solved the problem.
278
265
  """
279
266
 
280
- def url_encode_non_ascii(b):
281
- """Encode Non ASCII chars to URL-friendly chars.
282
-
283
- Sometimes unicode gets in the way. A shame, I know. And perhaps the
284
- biggest shame is not me correctly handling it.
285
- """
286
- import re
287
-
288
- return re.sub("[\x80-\xFF]", lambda c: "%%%02x" % ord(c.group(0)), b)
289
-
290
267
  parts = urlparse.urlparse(iri)
291
- if sys.version_info < (3, 0): # pragma: no cover
292
- # python2
293
- partes = []
294
- for parti, part in enumerate(parts):
295
- try:
296
- if parti != 1:
297
- partes.append(url_encode_non_ascii(part.encode("utf-8")))
298
- else:
299
- partes.append(part.encode("idna"))
300
- except UnicodeDecodeError:
301
- partes.append(url_encode_non_ascii(part.decode("latin")))
302
- except Exception:
303
- raise Exception
304
- return urlparse.urlunparse(partes)
305
- else:
306
- # python3
307
- uri = [part.decode("utf8") for parti, part in enumerate(parts.encode("utf8"))]
308
- return urlparse.urlunparse(uri)
268
+ uri = [part.decode("utf8") for parti, part in enumerate(parts.encode("utf8"))]
269
+ return urlparse.urlunparse(uri)
309
270
 
310
271
 
311
272
  def encoded_dict(in_dict):
@@ -315,13 +276,6 @@ def encoded_dict(in_dict):
315
276
  """
316
277
  out_dict = {}
317
278
  for k, v in in_dict.items():
318
- if isinstance(v, unicode):
319
- if sys.version_info < (3, 0): # pragma: no cover
320
- v = v.encode("utf8")
321
- elif isinstance(v, str):
322
- # Must be encoded in UTF-8
323
- if sys.version_info < (3, 0): # pragma: no cover
324
- v.decode("utf8")
325
279
  out_dict[k] = v
326
280
  return out_dict
327
281
 
@@ -15,10 +15,9 @@ from .schema_users import User
15
15
 
16
16
  # technical debt, cannot import name schema_clients
17
17
  try:
18
- from . import \
19
- schema_clients # this should work for python2 and python3, but doesnt
20
- except Exception as e:
21
- import schema_clients # python 2
18
+ from . import schema_clients
19
+ except Exception:
20
+ import schema_clients
22
21
 
23
22
  from sqlalchemy import (Column, DateTime, ForeignKey, Integer, Numeric, String,
24
23
  Text)
MambuPy/orm/schema_orm.py CHANGED
@@ -23,8 +23,6 @@ every schema_*.py you need.
23
23
  This last requirement also applies for the Base, or for the engine and
24
24
  the sessionmaker for that matter.
25
25
  """
26
- from __future__ import absolute_import
27
-
28
26
  from sqlalchemy.ext.declarative import declarative_base
29
27
  from sqlalchemy.orm import sessionmaker, scoped_session
30
28
 
MambuPy/rest/__init__.py CHANGED
@@ -1,49 +1,31 @@
1
- """MambuPy's REST package (SOON TO BE DEPRECATED).
1
+ # coding: utf-8
2
+ """MambuPy REST module - DEPRECATED.
2
3
 
3
- Objects using `Mambu REST API
4
- <https://developer.mambu.com/customer/en/portal/articles/1162276-rest-apis-overview/>`_
4
+ .. warning::
5
5
 
6
- .. autosummary::
7
- :toctree: _autosummary
6
+ DEPRECATED: This module uses Mambu API v1 which is no longer supported.
7
+ This code is maintained only for backwards compatibility with legacy systems.
8
+ It will be removed in a future version of MambuPy.
8
9
 
9
- MambuPy.rest.mambustruct
10
- MambuPy.rest.mambuclient
11
- MambuPy.rest.mambugroup
12
- MambuPy.rest.mambuloan
13
- MambuPy.rest.mamburepayment
14
- MambuPy.rest.mambutransaction
15
- MambuPy.rest.mambuproduct
16
- MambuPy.rest.mambubranch
17
- MambuPy.rest.mambucentre
18
- MambuPy.rest.mambuactivity
19
- MambuPy.rest.mambutask
20
- MambuPy.rest.mambusaving
21
- MambuPy.rest.mambusavingtransaction
22
- MambuPy.rest.mambutransactionchannel
23
- MambuPy.rest.mambuuser
24
- MambuPy.rest.mamburoles
25
-
26
- .. warning:: This package is soon to be deprecated. Use the :any:`MambuPy.api` package instead.
27
-
28
- The REST API way is the currently most developed code on *MambuPy*.
29
-
30
- Lives under the :any:`MambuPy.rest` package
31
-
32
- It has a lot of objects which model some Mambu entity.
33
-
34
- Every object inherits from the parent :any:`mambustruct.MambuStruct`
35
- class. Start at the documentation there for more info on how it works.
36
-
37
- .. todo:: Implement objects to make POST requests. The suggestion may be to use
38
- MambuStruct, to default the __init__ to make a POST request (via the data
39
- argument) and the attrs attribute to store the elements of the response that
40
- Mambu gives when a successful POST is achieved.
41
- .. todo:: Implement a lot of other Mambu entities available through GET requests on
42
- Mambu.
43
- .. todo:: Implement a lot of lacking GET filters on the currently available Mambu
44
- objects, inside the urlfuncs on the mambuutil module.
45
- .. todo:: A lot of TODO comments are inserted inside the pydocs of the code
46
- itself. Please read them for suggestions on work need to be done.
10
+ Only mambuactivity is available in this legacy module.
47
11
  """
48
-
49
- from .. import mambuconfig, mambuutil
12
+ import warnings
13
+
14
+ warnings.warn(
15
+ "MambuPy.rest module is deprecated and will be removed in a future version. "
16
+ "Mambu API v1 is no longer supported by Mambu.",
17
+ DeprecationWarning,
18
+ stacklevel=2,
19
+ )
20
+
21
+ from .mambuactivity import (
22
+ MambuActivity,
23
+ MambuActivities,
24
+ getactivitiesurl,
25
+ )
26
+
27
+ __all__ = [
28
+ "MambuActivity",
29
+ "MambuActivities",
30
+ "getactivitiesurl",
31
+ ]