from onegov.file import File
from operator import attrgetter
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from onegov.core.orm import SessionManager
from onegov.swissvotes.models import SwissVote
from onegov.swissvotes.models import TranslatablePage
from sqlalchemy.orm import relationship
from typing import Protocol
from typing import TypeVar
[docs]
FileT = TypeVar('FileT', bound=File)
class HasFiles(Protocol[FileT]):
files: relationship[list[FileT]]
class HasFilesAndSessionManager(HasFiles[FileT], Protocol):
@property
def session_manager(self) -> SessionManager | None: ...
[docs]
class SwissVoteFile(File):
""" An attachment to a vote. """
[docs]
__mapper_args__ = {'polymorphic_identity': 'swissvote'}
if TYPE_CHECKING:
# backrefs created through associated
[docs]
linked_swissvotes: relationship[list[SwissVote]]
@property
[docs]
def locale(self) -> str:
return self.name.split('-')[1]
@property
[docs]
def filename(self) -> str:
return self.reference.filename
[docs]
class TranslatablePageFile(File):
""" An attachment to a translatable content page. """
[docs]
__mapper_args__ = {'polymorphic_identity': 'swissvotes_page'}
if TYPE_CHECKING:
# backrefs created through associated
[docs]
linked_swissvotes_page: relationship[list[TranslatablePage]]
@property
[docs]
def locale(self) -> str:
return self.name.split('-')[0]
@property
[docs]
def filename(self) -> str:
return self.reference.filename
[docs]
class FileSubCollection:
""" A subset of files prefixed by the descriptor's name. """
[docs]
def __set_name__(self, owner: type['HasFiles[FileT]'], name: str) -> None:
self.name = name
[docs]
def __get__(
self,
instance: 'HasFiles[FileT] | None',
owner: type['HasFiles[FileT]']
) -> list['FileT']:
if instance:
return sorted((
file for file in instance.files
if file.name.startswith(self.name)
), key=attrgetter('name'))
return []
[docs]
class LocalizedFile:
""" A helper for localized files.
Automatically choses the file according to the currently used locale. The
files are internally stored as normal files using the filename to identify
the wanted file.
Example:
class MyModel(Base, AssociatedFiles):
pdf = LocalizedFile()
"""
def __init__(
self,
extension: str,
label: str,
static_views: dict[str, str]
) -> None:
[docs]
self.extension = extension
[docs]
self.static_views = static_views or {}
[docs]
def __set_name__(self, owner: type['HasFiles[FileT]'], name: str) -> None:
self.name = name
[docs]
def __get_localized_name__(
self,
instance: 'HasFilesAndSessionManager[FileT]',
locale: str | None = None
) -> str:
if not locale:
assert instance.session_manager is not None
locale = instance.session_manager.current_locale
return f'{self.name}-{locale}'
[docs]
def __get_by_locale__(
self,
instance: 'HasFilesAndSessionManager[FileT] | None',
locale: str | None = None
) -> 'FileT | None':
if instance:
name = self.__get_localized_name__(instance, locale)
for file in instance.files:
if file.name == name:
return file
return None
[docs]
def __get__(
self,
instance: 'HasFilesAndSessionManager[FileT] | None',
owner: type['HasFilesAndSessionManager[FileT]']
) -> 'FileT | None':
return self.__get_by_locale__(instance)
[docs]
def __set_by_locale__(
self,
instance: 'HasFilesAndSessionManager[FileT]',
value: 'FileT',
locale: str | None = None
) -> None:
value.name = self.__get_localized_name__(instance, locale)
self.__delete_by_locale__(instance, locale)
instance.files.append(value)
[docs]
def __set__(
self,
instance: 'HasFilesAndSessionManager[FileT]',
value: 'FileT'
) -> None:
return self.__set_by_locale__(instance, value)
[docs]
def __delete_by_locale__(
self,
instance: 'HasFilesAndSessionManager[FileT]',
locale: str | None = None
) -> None:
name = self.__get_localized_name__(instance, locale)
# create a copy of the list since we remove elements
for file in tuple(instance.files):
if file.name == name:
instance.files.remove(file)
[docs]
def __delete__(self, instance: 'HasFilesAndSessionManager[FileT]') -> None:
self.__delete_by_locale__(instance)
[docs]
class LocalizedFiles:
@classmethod
[docs]
def localized_files(cls) -> dict[str, LocalizedFile]:
return {
name: attribute
for name, attribute in cls.__dict__.items()
if isinstance(attribute, LocalizedFile)
}