python-easyverein 0.2.4__tar.gz → 0.2.7__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 (33) hide show
  1. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/PKG-INFO +1 -1
  2. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/__init__.py +1 -1
  3. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/api.py +5 -1
  4. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/client.py +19 -4
  5. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/protocol.py +5 -10
  6. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/types.py +1 -0
  7. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/contact_details.py +1 -0
  8. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/custom_field.py +10 -6
  9. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/invoice.py +13 -3
  10. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/invoice_item.py +1 -0
  11. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/member.py +5 -4
  12. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/mixins/empty_strings_mixin.py +1 -0
  13. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/mixins/required_attributes.py +1 -0
  14. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/contact_details.py +1 -0
  15. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/custom_field.py +1 -0
  16. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/invoice.py +9 -1
  17. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/invoice_item.py +1 -0
  18. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/member.py +1 -0
  19. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/member_custom_field.py +1 -0
  20. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/pyproject.toml +1 -1
  21. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/LICENSE +0 -0
  22. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/README.md +0 -0
  23. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/__init__.py +0 -0
  24. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/exceptions.py +0 -0
  25. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/core/validators.py +0 -0
  26. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/__init__.py +0 -0
  27. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/base.py +0 -0
  28. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/member_custom_field.py +0 -0
  29. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/models/mixins/__init__.py +0 -0
  30. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/__init__.py +0 -0
  31. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/mixins/__init__.py +0 -0
  32. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/mixins/crud.py +0 -0
  33. {python_easyverein-0.2.4 → python_easyverein-0.2.7}/easyverein/modules/mixins/recycle_bin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-easyverein
3
- Version: 0.2.4
3
+ Version: 0.2.7
4
4
  Summary: Python library to interact with the EasyVerein API
5
5
  Author: Daniel Herrmann
6
6
  Author-email: daniel.herrmann1@gmail.com
@@ -2,7 +2,7 @@
2
2
  Middleware for FastAPI that supports authenticating users against Keycloak
3
3
  """
4
4
 
5
- __version__ = "0.2.4"
5
+ __version__ = "0.2.7"
6
6
 
7
7
  # Export EasyVerein API directly
8
8
  from .api import EasyvereinAPI # noqa: F401
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Main EasyVerein API class
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from .core.client import EasyvereinClient
@@ -19,6 +20,7 @@ class EasyvereinAPI:
19
20
  api_version="v1.7",
20
21
  base_url: str = "https://hexa.easyverein.com/api/",
21
22
  logger: logging.Logger = None,
23
+ auto_retry=False,
22
24
  ):
23
25
  """
24
26
  Constructor setting API key and logger. Test
@@ -31,7 +33,9 @@ class EasyvereinAPI:
31
33
  else:
32
34
  self.logger = logging.getLogger("easyverein")
33
35
 
34
- self.c = EasyvereinClient(api_key, api_version, base_url, self.logger, self)
36
+ self.c = EasyvereinClient(
37
+ api_key, api_version, base_url, self.logger, self, auto_retry
38
+ )
35
39
 
36
40
  # Add methods
37
41
 
@@ -1,10 +1,12 @@
1
1
  """
2
2
  Main EasyVerein API class
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  import logging
7
8
  from pathlib import Path
9
+ from time import sleep
8
10
  from typing import TYPE_CHECKING, Any, TypeVar
9
11
 
10
12
  import requests
@@ -35,6 +37,7 @@ class EasyvereinClient:
35
37
  base_url,
36
38
  logger: logging.Logger,
37
39
  instance: EasyvereinAPI,
40
+ auto_retry=False,
38
41
  ):
39
42
  """
40
43
  Constructor setting API key and logger
@@ -44,6 +47,7 @@ class EasyvereinClient:
44
47
  self.api_version = api_version
45
48
  self.logger = logger
46
49
  self.api_instance = instance
50
+ self.auto_retry = auto_retry
47
51
 
48
52
  def _get_header(self):
49
53
  """
@@ -118,10 +122,21 @@ class EasyvereinClient:
118
122
  "Request returned status code 429, too many requests. Wait %d seconds",
119
123
  retry_after,
120
124
  )
121
- raise EasyvereinAPITooManyRetriesException(
122
- f"Too many requests, please wait {retry_after} seconds and try again.",
123
- retry_after=retry_after,
124
- )
125
+ if self.auto_retry:
126
+ self.logger.warning("Retrying after %d seconds sleep.", retry_after)
127
+ sleep(retry_after)
128
+ if files:
129
+ for v in files.values():
130
+ v.seek(
131
+ 0
132
+ ) # reset file seek, as it has been moved by the previous call
133
+
134
+ return self._do_request(method, url, binary, data, headers, files)
135
+ else:
136
+ raise EasyvereinAPITooManyRetriesException(
137
+ f"Too many requests, please wait {retry_after} seconds and try again.",
138
+ retry_after=retry_after,
139
+ )
125
140
 
126
141
  if res.status_code == 404:
127
142
  self.logger.warning("Request returned status code 404, resource not found")
@@ -9,21 +9,16 @@ from .client import EasyvereinClient
9
9
  # noinspection PyPropertyDefinition
10
10
  class IsEVClientProtocol(Protocol):
11
11
  @property
12
- def logger(self) -> logging.Logger:
13
- ...
12
+ def logger(self) -> logging.Logger: ...
14
13
 
15
14
  @property
16
- def c(self) -> EasyvereinClient:
17
- ...
15
+ def c(self) -> EasyvereinClient: ...
18
16
 
19
17
  @property
20
- def endpoint_name(self) -> str:
21
- ...
18
+ def endpoint_name(self) -> str: ...
22
19
 
23
20
  @property
24
- def model_class(self) -> TypeVar:
25
- ...
21
+ def model_class(self) -> TypeVar: ...
26
22
 
27
23
  @property
28
- def return_type(self) -> Type[BaseModel]:
29
- ...
24
+ def return_type(self) -> Type[BaseModel]: ...
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Custom types used for model validation
3
3
  """
4
+
4
5
  import datetime
5
6
  import json
6
7
  from typing import Annotated
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Contact Details related models
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  from typing import Any, Literal
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Member related models
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  from typing import Literal
@@ -36,9 +37,9 @@ class CustomFieldBase(EasyVereinBase):
36
37
  color: HexColor = None
37
38
  short: str | None = Field(default=None, max_length=4)
38
39
  orderSequence: PositiveIntWithZero | None = None
39
- settings_type: Literal[
40
- "t", "f", "z", "d", "c", "r", "s", "a", "b", "m"
41
- ] | None = None
40
+ settings_type: Literal["t", "f", "z", "d", "c", "r", "s", "a", "b", "m"] | None = (
41
+ None
42
+ )
42
43
  """
43
44
  Settings type defines which type of field this custom field should be. Possible values:
44
45
 
@@ -55,9 +56,12 @@ class CustomFieldBase(EasyVereinBase):
55
56
 
56
57
  If type is set to s or a, the possible options need to be defined in the additional field
57
58
  """
58
- kind: Literal[
59
- "a", "b", "ba", "ca", "iv", "t", "u", "ic", "c", "e", "h", "j", "i", "k"
60
- ] | None = None
59
+ kind: (
60
+ Literal[
61
+ "a", "b", "ba", "ca", "iv", "t", "u", "ic", "c", "e", "h", "j", "i", "k"
62
+ ]
63
+ | None
64
+ ) = None
61
65
  """
62
66
  Kind defines in which context this custom field is used. Unfortunately only some possible values are
63
67
  documented in the API spec:
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Invoice related models
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  from typing import Literal
@@ -42,9 +43,18 @@ class InvoiceBase(EasyVereinBase):
42
43
  taxName: str | None = None
43
44
  relatedAddress: ContactDetails | EasyVereinReference | None = None
44
45
  path: EasyVereinReference | None = None
45
- kind: Literal[
46
- "balance", "donation", "membership", "revenue", "expense", "cancel", "credit"
47
- ] | None = None
46
+ kind: (
47
+ Literal[
48
+ "balance",
49
+ "donation",
50
+ "membership",
51
+ "revenue",
52
+ "expense",
53
+ "cancel",
54
+ "credit",
55
+ ]
56
+ | None
57
+ ) = None
48
58
  # TODO: Add reference to BillingAccount once implemented
49
59
  selectionAcc: EasyVereinReference | None = None
50
60
  refNumber: str | None = None
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Invoice Item model
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  from typing import Annotated
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Member related models
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
 
6
7
  from typing import Literal
@@ -73,7 +74,7 @@ class MemberBase(EasyVereinBase):
73
74
  """
74
75
  Alias for `_applicationDate` field. See [Pydantic Models](../usage.md#pydantic-models) for details.
75
76
  """
76
- applicationWasAcceptedAt: DateTime | None = Field(
77
+ applicationWasAcceptedAt: Date | None = Field(
77
78
  default=None, alias="_applicationWasAcceptedAt"
78
79
  )
79
80
  """
@@ -178,13 +179,13 @@ class MemberFilter(BaseModel):
178
179
  applicationDate__isnull: Date = Field(
179
180
  default=None, serialization_alias="_applicationDate__isnull"
180
181
  )
181
- applicationWasAcceptedAt: DateTime = Field(
182
+ applicationWasAcceptedAt: Date = Field(
182
183
  default=None, serialization_alias="_applicationWasAcceptedAt"
183
184
  )
184
- applicationWasAcceptedAt__gte: DateTime = Field(
185
+ applicationWasAcceptedAt__gte: Date = Field(
185
186
  default=None, serialization_alias="_applicationWasAcceptedAt__gte"
186
187
  )
187
- applicationWasAcceptedAt__lte: DateTime = Field(
188
+ applicationWasAcceptedAt__lte: Date = Field(
188
189
  default=None, serialization_alias="_applicationWasAcceptedAt__lte"
189
190
  )
190
191
  applicationWasAcceptedAt__isnull: bool = Field(
@@ -2,6 +2,7 @@
2
2
  This module contains a generic plugin for Pydantic models that removes
3
3
  empty strings and converts them to None.
4
4
  """
5
+
5
6
  from typing import Any
6
7
 
7
8
  from pydantic import model_validator
@@ -1,6 +1,7 @@
1
1
  """
2
2
  This module contains a class factory returning a Mixin class
3
3
  """
4
+
4
5
  from typing import Self
5
6
 
6
7
  from pydantic import BaseModel, model_validator
@@ -1,6 +1,7 @@
1
1
  """
2
2
  All methods related to contact details
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from ..core.client import EasyvereinClient
@@ -1,6 +1,7 @@
1
1
  """
2
2
  All methods related to custom fields
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from ..core.client import EasyvereinClient
@@ -1,4 +1,6 @@
1
1
  import logging
2
+ import re
3
+ import urllib
2
4
  from pathlib import Path
3
5
  from typing import List
4
6
 
@@ -169,4 +171,10 @@ class InvoiceMixin(
169
171
  "Unable to obtain a valid path for given invoice."
170
172
  )
171
173
 
172
- return self.c.fetch_file(path)
174
+ # Fix for unencoded characters - should probably be fixed in easyverein API
175
+ m = re.fullmatch(r"^(.*\&path=)(.*)(&storedInS3=True)$", path.unicode_string())
176
+ url_components = list(m.groups())
177
+ if "%" not in url_components[1]:
178
+ url_components[1] = urllib.parse.quote(url_components[1])
179
+
180
+ return self.c.fetch_file("".join(url_components))
@@ -1,6 +1,7 @@
1
1
  """
2
2
  All methods related to invoices
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from ..core.client import EasyvereinClient
@@ -1,6 +1,7 @@
1
1
  """
2
2
  All methods related to invoices
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from ..core.client import EasyvereinClient
@@ -1,6 +1,7 @@
1
1
  """
2
2
  All methods related to invoices
3
3
  """
4
+
4
5
  import logging
5
6
 
6
7
  from ..core.client import EasyvereinClient
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-easyverein"
3
- version = "0.2.4"
3
+ version = "0.2.7"
4
4
  description = "Python library to interact with the EasyVerein API"
5
5
  authors = ["Daniel Herrmann <daniel.herrmann1@gmail.com>"]
6
6
  readme = "README.md"