Skip to content

Commit

Permalink
[UPD] Multiple Changes
Browse files Browse the repository at this point in the history
- Added the `id` and `method` arguments from the `read` method of the `DirecrusResponse` class
  to the cache key
- Changed the translation related methods and the `parse_translations` function
- Added translation examples and documentation
- Minor code changes
  • Loading branch information
MultiJohnen committed Feb 12, 2024
1 parent 8f2891b commit 1885c91
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 97 deletions.
41 changes: 41 additions & 0 deletions docs/docs/translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Custom Translations

We provide the `get_translations` and `create_translations` methods of the `Directus` class client
for you to retrieve and create new translation records on the `Directus` backend.

## Retrieve Translations

Retrieving translation records

```python
...
# A list of translation records in dictionary format
translations = await directus.get_translations()

# A dictionary of translation records grouped by the `key` field
# {
# "<key>": {
# "<language>": "<value>"
# }
# }
translations = await directus.get_translations(clean=True)
...
```

> Note: The automatic retrieval of all Directus translation records is supported by the `async_init` function
> when the `load_translations` argument is set to `True`.
> You can access the translations from the `pydirectus.translations` global. The global is in `clean` format.
## Create Translations

Creating a new translation record

```python
...
# Register a translation record for the given values (language: 'en-GB')
directus_response = await directus.create_translations("some")

# Register a translation record for the given values with specific language
directus_response = await directus.create_translations(tuple(["some", "el-GR"]))
...
```
35 changes: 35 additions & 0 deletions examples/translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import asyncio

from dotenv import dotenv_values

from py_directus import DirectusUser, Directus


config = dotenv_values(".env")


async def main():
# directus = await Directus.create(config["DIRECTUS_URL"], email=config["DIRECTUS_EMAIL"], password=config["DIRECTUS_PASSWORD"])
directus = await Directus(config["DIRECTUS_URL"], email=config["DIRECTUS_EMAIL"], password=config["DIRECTUS_PASSWORD"])

# Retrieving translation records
translations = await directus.get_translations()
print(f"TRANSLATIONS: {translations}")

# Creating a new translation record
directus_response = await directus.create_translations(tuple(["some", "el-GR"]))
print(f"DIRECTUS RESPONSE: {directus_response}")

# Retrieving translation records again
translations = await directus.get_translations()
print(f"TRANSLATIONS (AGAIN): {translations}")

# Logout
await directus.logout()

# Manually close connection
await directus.close_connection()


if __name__ == "__main__":
asyncio.run(main())
43 changes: 23 additions & 20 deletions py_directus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@
except ImportError:
pass

DirectusActivity: Type[BaseDirectusActivity] = BaseDirectusActivity
DirectusRevision: Type[BaseDirectusRevision] = BaseDirectusRevision
DirectusRole: Type[BaseDirectusRole] = BaseDirectusRole
DirectusRoles: Type[BaseDirectusRoles] = BaseDirectusRoles
DirectusUser: Type[BaseDirectusUser] = BaseDirectusUser
DirectusFile: Type[BaseDirectusFile] = BaseDirectusFile
DirectusFolder: Type[BaseDirectusFolder] = BaseDirectusFolder
DirectusPermission: Type[BaseDirectusPermission] = BaseDirectusPermission
DirectusRelationSchema: Type[BaseDirectusRelationSchema] = BaseDirectusRelationSchema
DirectusRelationMeta: Type[BaseDirectusRelationMeta] = BaseDirectusRelationMeta
DirectusRelation: Type[BaseDirectusRelation] = BaseDirectusRelation
DirectusSettings: Type[BaseDirectusSettings] = BaseDirectusSettings
DirectusTranslation: Type[BaseDirectusTranslation] = BaseDirectusTranslation
DirectusVersion: Type[BaseDirectusVersion] = BaseDirectusVersion
DirectusActivity: Type['BaseDirectusActivity'] = BaseDirectusActivity
DirectusRevision: Type['BaseDirectusRevision'] = BaseDirectusRevision
DirectusRole: Type['BaseDirectusRole'] = BaseDirectusRole
DirectusRoles: Type['BaseDirectusRoles'] = BaseDirectusRoles
DirectusUser: Type['BaseDirectusUser'] = BaseDirectusUser
DirectusFile: Type['BaseDirectusFile'] = BaseDirectusFile
DirectusFolder: Type['BaseDirectusFolder'] = BaseDirectusFolder
DirectusPermission: Type['BaseDirectusPermission'] = BaseDirectusPermission
DirectusRelationSchema: Type['BaseDirectusRelationSchema'] = BaseDirectusRelationSchema
DirectusRelationMeta: Type['BaseDirectusRelationMeta'] = BaseDirectusRelationMeta
DirectusRelation: Type['BaseDirectusRelation'] = BaseDirectusRelation
DirectusSettings: Type['BaseDirectusSettings'] = BaseDirectusSettings
DirectusTranslation: Type['BaseDirectusTranslation'] = BaseDirectusTranslation
DirectusVersion: Type['BaseDirectusVersion'] = BaseDirectusVersion

cached_directus_instances = dict[str, Directus]()

Expand All @@ -41,6 +41,7 @@
# Public directus
directus_public: Optional[Directus] = None

# Directus Translations
translations = dict[str, dict[str, str]]()


Expand Down Expand Up @@ -75,7 +76,7 @@ def rebuild_models():
DirectusVersion.model_rebuild(raise_errors=False)


def setup_models(directus_models: Type[BaseDirectusModels] = BaseDirectusModels):
def setup_models(directus_models: Type['BaseDirectusModels'] = BaseDirectusModels):
global DirectusActivity
global DirectusRevision
global DirectusRole
Expand Down Expand Up @@ -137,15 +138,16 @@ def setup_models(directus_models: Type[BaseDirectusModels] = BaseDirectusModels)
rebuild_models()


async def async_init(directus_base_url: str, directus_admin_token: str = None,
load_translations: bool = False,
directus_models: Type[BaseDirectusModels] = BaseDirectusModels):
async def async_init(directus_base_url: str, directus_admin_token: str = None,
directus_models: Type[BaseDirectusModels] = BaseDirectusModels,
load_translations: bool = False):
global directus_admin
global directus_public
global directus_url

global translations

# Setup defaults
setup_models(directus_models)

directus_url = directus_base_url
Expand All @@ -154,8 +156,9 @@ async def async_init(directus_base_url: str, directus_admin_token: str = None,
if directus_admin_token:
directus_admin = await Directus(directus_url, token=directus_admin_token, connection=directus_session)

# if load_translations:
# translations = await directus_public.get_translations()# TODO
# Load Directus translations at startup
if load_translations:
translations = await directus_public.get_translations(clean=True)


rebuild_models()
123 changes: 66 additions & 57 deletions py_directus/directus.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re
from io import BytesIO
# import aiofiles
from typing import Union, Optional, Type
from typing import Union, Optional, Type, List, Tuple

import magic
from httpx import AsyncClient, Auth, Response
Expand All @@ -22,7 +22,6 @@
try:
# from fastapi import UploadFile
from starlette.datastructures import UploadFile

except ImportError:
pass

Expand All @@ -43,8 +42,9 @@ class Directus:
"""

def __init__(
self, url: str, email: str = None, password: str = None, token: str = None, refresh_token: str = None,
connection: AsyncClient = None
self, url: str, email: str = None, password: str = None,
token: str = None, refresh_token: str = None,
connection: AsyncClient = None
):
self.expires = None
self.expiration_time = None
Expand All @@ -69,15 +69,7 @@ def __init__(
self.cache: Union[SimpleMemoryCache, None] = None

# Any async tasks for later gathering
self.tasks: list[DirectusResponse] = []

async def gather(self):
"""
Gather all async tasks.
"""
print("Gathering tasks", self.tasks)
await asyncio.gather(*[task.gather_response() for task in self.tasks])
self.tasks.clear()
self.tasks: List[DirectusResponse] = []

def __await__(self):
async def closure():
Expand All @@ -92,6 +84,14 @@ async def closure():

return closure().__await__()

async def gather(self):
"""
Gather all async tasks.
"""
print("Gathering tasks", self.tasks)
await asyncio.gather(*[task.gather_response() for task in self.tasks])
self.tasks.clear()

def collection(self, collection: Union[Type[BaseModel], str]) -> DirectusRequest:
"""
Set collection to be used.
Expand Down Expand Up @@ -129,39 +129,6 @@ async def roles(self) -> DirectusResponse:
response_obj = await self.collection(py_directus.DirectusRole).read()
return response_obj

async def read_settings(self) -> DirectusResponse:
collection_name = py_directus.DirectusSettings.model_config.get("collection", None)

assert collection_name is not None

response_obj = await DirectusRequest(self, collection_name).read(method='get')
return response_obj

async def update_settings(self, data) -> DirectusResponse:
collection_name = py_directus.DirectusSettings.model_config.get("collection", None)

assert collection_name is not None

response_obj = await DirectusRequest(self, collection_name).update(None, data)
return response_obj

async def read_translations(self) -> dict[str, dict[str, str]]:
"""
NOTE: TO BE REDESIGNED
"""
items = await self.collection("translations").fields(
'key', 'translations.languages_code', 'translations.translation'
).read().items

return parse_translations(items)

async def create_translations(self, keys: list[str]):
"""
NOTE: TO BE REDESIGNED
"""
response_obj = await self.collection("translations").create([{"key": key} for key in keys])
return response_obj

@classmethod
def get_file_url(cls, file_id: str, img_format: Optional[str] = None, **kwargs) -> str:
url = f"{py_directus.directus_url}/assets/{file_id}"
Expand Down Expand Up @@ -274,16 +241,47 @@ async def upload_file(self, to_upload: Union[str, UploadFile], folder: str = Non

return DirectusResponse(response)

async def clear_cache(self, clear_all: bool = False):
async def get_translations(self, clean: bool = False) -> dict[str, dict[str, str]]:
"""
Clear cache:
Retrieve dictionary with all translation records.
:param clear_all: If set to `True`, absolutely ALL records will be deleted.
:param clean: Returns the records grouped by `key` value when set as `True`.
"""
return await self.cache.clear(clear_all)
items = (await self.collection(py_directus.DirectusTranslation).read()).items_as_dict()

async def __aenter__(self):
return self
return parse_translations(items) if clean else items

async def create_translations(self, *keys: Union[str, Tuple[str, str]]):
"""
Create translation records for given keys.
"""
# Payload
payload = [(
lambda x: (
{"key": x, "language": "en-GB", "value": ""}
if isinstance(x, str) else
{"key": x[0], "language": x[1], "value": ""}
)
)(key) for key in keys]

response_obj = await self.collection(py_directus.DirectusTranslation).create(payload)
return response_obj

async def read_settings(self) -> DirectusResponse:
collection_name = py_directus.DirectusSettings.model_config.get("collection", None)

assert collection_name is not None

response_obj = await DirectusRequest(self, collection_name).read(method='get')
return response_obj

async def update_settings(self, data) -> DirectusResponse:
collection_name = py_directus.DirectusSettings.model_config.get("collection", None)

assert collection_name is not None

response_obj = await DirectusRequest(self, collection_name).update(None, data)
return response_obj

@property
def token(self):
Expand Down Expand Up @@ -328,11 +326,6 @@ async def login(self):
}
await self.auth_request(endpoint, payload)

async def start_cache(self):
assert self._token is not None

self.cache = SimpleMemoryCache(self._token)

async def refresh(self):
endpoint = "auth/refresh"
payload = {
Expand All @@ -342,6 +335,19 @@ async def refresh(self):
await self.auth_request(endpoint, payload)
await self.clear_cache(True)

async def start_cache(self):
assert self._token is not None

self.cache = SimpleMemoryCache(self._token)

async def clear_cache(self, clear_all: bool = False):
"""
Clear cache:
:param clear_all: If set to `True`, absolutely ALL records will be deleted.
"""
return await self.cache.clear(clear_all)

async def logout(self) -> bool:
url = f"{self.url}/auth/logout"
response = await self.connection.post(url)
Expand All @@ -361,6 +367,9 @@ async def logout(self) -> bool:
async def close_connection(self):
await self.connection.aclose()

async def __aenter__(self):
return self

async def __aexit__(self, *args):
print("Closing connection")
# Exception handling here
Expand Down
Loading

0 comments on commit 1885c91

Please sign in to comment.