New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Store serializers #4677
Store serializers #4677
Changes from all commits
c97f547
2366966
09fa636
cfba944
c1f6d6a
e228081
e40e19f
e1bb21e
60e1f9c
27c41a7
bc994e8
3e3ea96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -316,6 +316,27 @@ directly on the file system. | |
deleted from the database. Handle with care! | ||
|
||
|
||
.. django-admin:: list_serializers | ||
|
||
list_serializers | ||
^^^^^^^^^^^^^^^^ | ||
|
||
.. versionadded:: 2.8.0 | ||
|
||
List the installed serializers and deserializers on your system. | ||
|
||
Available options: | ||
|
||
.. django-admin-option:: -m, --model | ||
|
||
List serializers for specified model. The model should be expressed as a | ||
contenttype label - eg ``app_name``.``model_name`` | ||
|
||
.. django-admin-option:: -d, --deserializers | ||
|
||
List available deserializers set up for our system. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No reference in release notes to new command? |
||
|
||
|
||
.. django-admin:: list_languages | ||
|
||
list_languages | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
import os | ||
|
||
os.environ['DJANGO_SETTINGS_MODULE'] = 'pootle.settings' | ||
|
||
from django.contrib.contenttypes.models import ContentType | ||
from django.core.management.base import BaseCommand, CommandError | ||
|
||
from pootle.core.delegate import serializers, deserializers | ||
|
||
|
||
class Command(BaseCommand): | ||
help = "Manage serialization for Projects." | ||
|
||
def add_arguments(self, parser): | ||
super(Command, self).add_arguments(parser) | ||
parser.add_argument( | ||
'-m --model', | ||
dest="model", | ||
action="store", | ||
help='List de/serializers for given model.') | ||
parser.add_argument( | ||
'-d --deserializers', | ||
action="store_true", | ||
dest="deserializers", | ||
help='List deserializers') | ||
|
||
def model_from_content_type(self, ct_name): | ||
if not ct_name: | ||
return | ||
if "." not in ct_name: | ||
raise CommandError( | ||
"Model name should be contenttype " | ||
"$app_name.$label") | ||
try: | ||
return ContentType.objects.get_by_natural_key( | ||
*ct_name.split(".")).model_class() | ||
except ContentType.DoesNotExist as e: | ||
raise CommandError(e) | ||
|
||
def handle(self, **kwargs): | ||
model = self.model_from_content_type(kwargs["model"]) | ||
if kwargs["deserializers"]: | ||
return self.print_serializers_list( | ||
deserializers.gather(model), | ||
serializer_type="deserializers") | ||
return self.print_serializers_list( | ||
serializers.gather(model)) | ||
|
||
def print_serializers_list(self, serials, serializer_type="serializers"): | ||
if not serials.keys(): | ||
self.stdout.write( | ||
"There are no %s set up on your system" % serializer_type) | ||
if not serials.keys(): | ||
return | ||
heading = serializer_type.capitalize() | ||
self.stdout.write("\n%s" % heading) | ||
self.stdout.write("-" * len(heading)) | ||
for name, serializer in serials.items(): | ||
self.stdout.write( | ||
"{: <30} {: <50} ".format(name, serializer)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
import io | ||
|
||
from translate.storage.factory import getclass | ||
|
||
from django.utils.functional import cached_property | ||
|
||
from pootle.core.delegate import config, deserializers | ||
|
||
|
||
class StoreDeserialization(object): | ||
"""Calls configured deserializers for Store""" | ||
|
||
def __init__(self, store): | ||
self.store = store | ||
|
||
@property | ||
def project_deserializers(self): | ||
project = self.store.translation_project.project | ||
return ( | ||
config.get( | ||
project.__class__, | ||
instance=project, | ||
key="pootle.core.deserializers") | ||
or []) | ||
|
||
@cached_property | ||
def deserializers(self): | ||
available_deserializers = deserializers.gather( | ||
self.store.__class__, instance=self.store) | ||
found_deserializers = [] | ||
for deserializer in self.project_deserializers: | ||
found_deserializers.append(available_deserializers[deserializer]) | ||
return found_deserializers | ||
|
||
def pipeline(self, data): | ||
if not self.deserializers: | ||
return data | ||
for deserializer in self.deserializers: | ||
data = deserializer(self, data).output | ||
return data | ||
|
||
def fromstring(self, data): | ||
return getclass(io.BytesIO(data))(data) | ||
|
||
def deserialize(self, data): | ||
return self.fromstring(self.pipeline(data)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
from django.core.cache import caches | ||
from django.utils.functional import cached_property | ||
|
||
from pootle.core.delegate import config, serializers | ||
|
||
|
||
class StoreSerialization(object): | ||
"""Calls configured deserializers for Store""" | ||
|
||
def __init__(self, store): | ||
self.store = store | ||
|
||
@property | ||
def project_serializers(self): | ||
project = self.store.translation_project.project | ||
return ( | ||
config.get( | ||
project.__class__, | ||
instance=project, | ||
key="pootle.core.serializers") | ||
or []) | ||
|
||
@property | ||
def pootle_path(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having here a property for a property in a object present in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. allows you to override it |
||
return self.store.pootle_path | ||
|
||
@cached_property | ||
def max_unit_revision(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am wondering if this should be instead cached on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, maybe but tbh i dont like adding things like that on models - imho this most likely needs to be denormalized |
||
return self.store.get_max_unit_revision() | ||
|
||
@cached_property | ||
def serializers(self): | ||
available_serializers = serializers.gather( | ||
self.store.__class__, instance=self.store) | ||
found_serializers = [] | ||
for serializer in self.project_serializers: | ||
found_serializers.append(available_serializers[serializer]) | ||
return found_serializers | ||
|
||
def tostring(self): | ||
storeclass = self.store.get_file_class() | ||
store = self.store.convert(storeclass) | ||
if hasattr(store, "updateheader"): | ||
# FIXME We need those headers on import | ||
# However some formats just don't support setting metadata | ||
store.updateheader(add=True, X_Pootle_Path=self.pootle_path) | ||
store.updateheader(add=True, X_Pootle_Revision=self.max_unit_revision) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we can't lose these kinda PO specific hacks? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ive been pondering where these should be - they could potentially even be in a serializer plugin - but im not sure how that would work tbh |
||
return str(store) | ||
|
||
def pipeline(self, data): | ||
if not self.serializers: | ||
return data | ||
for serializer in self.serializers: | ||
data = serializer(self, data).output | ||
return data | ||
|
||
def serialize(self): | ||
cache = caches["exports"] | ||
ret = cache.get( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does "ret" mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. retinaculum There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know that this is carried over code (noticed after my first comment), but perhaps we can use a better word. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im gonna leave as is for now, im thinking this i might change this a little soon, but i would rather keep as was for now. |
||
self.pootle_path, | ||
version=self.max_unit_revision) | ||
if not ret: | ||
ret = self.pipeline(self.tostring()) | ||
cache.set( | ||
self.pootle_path, | ||
ret, | ||
version=self.max_unit_revision) | ||
return ret |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
|
||
class Serializer(object): | ||
|
||
def __init__(self, context, data): | ||
self.context = context | ||
self.original_data = data | ||
|
||
@property | ||
def data(self): | ||
return self.original_data | ||
|
||
|
||
class Deserializer(object): | ||
|
||
def __init__(self, context, data): | ||
self.context = context | ||
self.original_data = data | ||
|
||
@property | ||
def data(self): | ||
return self.original_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expanding in a separate section (and linking to it from here) on what serializers are, what they do and whether users need to care about them or not would be highly needed. Otherwise I feel like anyone reading the release notes or documentation will be pretty lost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #4723.