python-easyverein 0.2.1__tar.gz → 0.2.2__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.1 → python_easyverein-0.2.2}/PKG-INFO +1 -1
  2. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/__init__.py +1 -1
  3. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/client.py +27 -3
  4. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/invoice.py +2 -2
  5. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/invoice.py +45 -0
  6. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/pyproject.toml +1 -1
  7. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/LICENSE +0 -0
  8. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/README.md +0 -0
  9. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/api.py +0 -0
  10. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/__init__.py +0 -0
  11. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/exceptions.py +0 -0
  12. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/protocol.py +0 -0
  13. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/types.py +0 -0
  14. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/core/validators.py +0 -0
  15. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/__init__.py +0 -0
  16. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/base.py +0 -0
  17. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/contact_details.py +0 -0
  18. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/custom_field.py +0 -0
  19. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/invoice_item.py +0 -0
  20. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/member.py +0 -0
  21. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/member_custom_field.py +0 -0
  22. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/mixins/__init__.py +0 -0
  23. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/mixins/empty_strings_mixin.py +0 -0
  24. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/models/mixins/required_attributes.py +0 -0
  25. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/__init__.py +0 -0
  26. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/contact_details.py +0 -0
  27. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/custom_field.py +0 -0
  28. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/invoice_item.py +0 -0
  29. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/member.py +0 -0
  30. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/member_custom_field.py +0 -0
  31. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/mixins/__init__.py +0 -0
  32. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/easyverein/modules/mixins/crud.py +0 -0
  33. {python_easyverein-0.2.1 → python_easyverein-0.2.2}/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.1
3
+ Version: 0.2.2
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.1"
5
+ __version__ = "0.2.2"
6
6
 
7
7
  # Export EasyVerein API directly
8
8
  from .api import EasyvereinAPI # noqa: F401
@@ -5,10 +5,11 @@ from __future__ import annotations
5
5
 
6
6
  import logging
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING, TypeVar
8
+ from typing import TYPE_CHECKING, Any, TypeVar
9
9
 
10
10
  import requests
11
11
  from pydantic import BaseModel
12
+ from requests.structures import CaseInsensitiveDict
12
13
 
13
14
  from .exceptions import (
14
15
  EasyvereinAPIException,
@@ -76,8 +77,8 @@ class EasyvereinClient:
76
77
  return url
77
78
 
78
79
  def _do_request( # noqa: PLR0913
79
- self, method, url, data=None, headers=None, files=None
80
- ):
80
+ self, method, url, binary=False, data=None, headers=None, files=None
81
+ ) -> tuple[int, dict[str, Any] | requests.Response | None]:
81
82
  """
82
83
  Helper method that performs an actual call against the API,
83
84
  fetching the most common errors
@@ -130,6 +131,10 @@ class EasyvereinClient:
130
131
  if res.content == b"":
131
132
  return res.status_code, None
132
133
 
134
+ # If content is supposed to be binary, return the entire response to maintain headers
135
+ if binary:
136
+ return res.status_code, res
137
+
133
138
  # Try to parse response as JSON and return it for further processing
134
139
  try:
135
140
  content = res.json()
@@ -235,6 +240,25 @@ class EasyvereinClient:
235
240
 
236
241
  return data, total_count
237
242
 
243
+ def fetch_file(self, url: str) -> tuple[bytes, CaseInsensitiveDict[str]]:
244
+ """
245
+ Helper method that fetches a file from the API including the authentication header
246
+
247
+ Returns the raw bytes object and the entire header for further processing
248
+ """
249
+ status_code, res = self._do_request("get", url, binary=True)
250
+
251
+ # Check if status code is 200
252
+ if status_code != 200:
253
+ self.logger.error(
254
+ f"Request to download file failed with unexpected status code {status_code}"
255
+ )
256
+ raise EasyvereinAPIException(
257
+ f"Request to download file failed with unexpected status code {status_code}"
258
+ )
259
+
260
+ return res.content, res.headers
261
+
238
262
  def fetch_one(self, url, model: type[T] = None) -> T | None:
239
263
  """
240
264
  Helper method that fetches a result from an API call
@@ -39,8 +39,7 @@ class Invoice(EasyVereinBase):
39
39
  tax: float | None = None
40
40
  taxRate: float | None = None
41
41
  taxName: str | None = None
42
- # TODO: Add reference to ContactDetails once implemented
43
- relatedAddress: EasyVereinReference | None = None
42
+ relatedAddress: ContactDetails | EasyVereinReference | None = None
44
43
  path: EasyVereinReference | None = None
45
44
  kind: Literal[
46
45
  "balance", "donation", "membership", "revenue", "expense", "cancel", "credit"
@@ -129,5 +128,6 @@ class InvoiceFilter(BaseModel):
129
128
  search: str | None = None
130
129
 
131
130
 
131
+ from .contact_details import ContactDetails # noqa: E402
132
132
  from .invoice_item import InvoiceItem # noqa: E402
133
133
  from .member import Member # noqa: E402
@@ -2,6 +2,8 @@ import logging
2
2
  from pathlib import Path
3
3
  from typing import List
4
4
 
5
+ from requests.structures import CaseInsensitiveDict
6
+
5
7
  from ..core.client import EasyvereinClient
6
8
  from ..core.exceptions import EasyvereinAPIException
7
9
  from ..models.invoice import Invoice, InvoiceCreate, InvoiceFilter, InvoiceUpdate
@@ -125,3 +127,46 @@ class InvoiceMixin(
125
127
  inv = self.get_by_id(inv.id)
126
128
 
127
129
  return inv
130
+
131
+ def get_attachment(
132
+ self, invoice: Invoice | int
133
+ ) -> tuple[bytes, CaseInsensitiveDict[str]]:
134
+ """
135
+ This method downloads and returns the invoice attachment if available.
136
+
137
+ It accepts either an invoice object or its id. If the invoice is given, and the path attribute is
138
+ set, it will simply use this path to download and return the file. In all other cases, it first retrieves
139
+ the invoice object by id and then proceeds to download the file.
140
+
141
+ Returns a tuple, where the first element is the file and the second contains the headers of the response
142
+
143
+ **Usage**
144
+
145
+ ```python
146
+ invoice = ev_connection.invoice.get_by_id(invoice_id, query="{id,path}")
147
+
148
+ attachment, headers = ev_connection.invoice.get_attachment(invoice)
149
+ ```
150
+
151
+ Args:
152
+ invoice: The invoice object or its id for which the attachment should be retrieved
153
+ """
154
+ if isinstance(invoice, Invoice) and invoice.path:
155
+ self.logger.info(
156
+ "Invoice already has the path attribute set, using that path."
157
+ )
158
+ path = invoice.path
159
+ else:
160
+ self.logger.info(
161
+ "Invoice is either given by id or doesn't contain the path attribute"
162
+ )
163
+ invoice_id = invoice.id if isinstance(invoice, Invoice) else invoice
164
+ fetched_invoice = self.get_by_id(invoice_id, query="{id,path}")
165
+ path = fetched_invoice.path
166
+
167
+ if not path:
168
+ raise EasyvereinAPIException(
169
+ "Unable to obtain a valid path for given invoice."
170
+ )
171
+
172
+ return self.c.fetch_file(path)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-easyverein"
3
- version = "0.2.1"
3
+ version = "0.2.2"
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"