Skip to content
This repository was archived by the owner on Nov 29, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions assembl/alembic/versions/134299d407b9_add_extract_lang.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""add_extract_lang

Revision ID: 134299d407b9
Revises: 074316d2e8a8
Create Date: 2018-08-02 10:44:11.357095

"""

# revision identifiers, used by Alembic.
revision = '134299d407b9'
down_revision = '074316d2e8a8'

from alembic import context, op
import sqlalchemy as sa
import transaction


def upgrade(pyramid_env):
with context.begin_transaction():
op.add_column(
'extract',
sa.Column(
'locale_id', sa.Integer,
sa.ForeignKey('locale.id')))

from assembl import models as m
db = m.get_session_maker()()
with transaction.manager:
model = m.Extract
query = db.query(model)
for extract in query:
post = extract.get_post()
if post:
entry = post.body.first_original()
if entry:
extract.lang = entry.locale_code


def downgrade(pyramid_env):
with context.begin_transaction():
op.drop_column('extract', 'locale_id')
22 changes: 22 additions & 0 deletions assembl/graphql/docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class ExtractInterface:
creation_date = """The date the Extract was created, in UTC timezone."""
creator_id = """The id of the User who created the extract."""
creator = """The AgentProfile object description of the creator."""
lang = """The lang of the extract."""


class PostExtract:
Expand All @@ -215,6 +216,7 @@ class PostExtract:
xpath_end = TextFragmentIdentifier.xpath_end
offset_start = TextFragmentIdentifier.offset_start
offset_end = TextFragmentIdentifier.offset_end
lang = """The lang of the extract."""


class AddPostsExtract:
Expand Down Expand Up @@ -579,6 +581,7 @@ class AddPostExtract:
post_id = Default.node_id % ("Post")
body = "The body of text defined in this Extract on the Post."
important = "An optional boolean to set the extract as a nugget. The default is False."
lang = """The lang of the extract."""
xpath_start = TextFragmentIdentifier.xpath_start
xpath_end = TextFragmentIdentifier.xpath_end
offset_start = TextFragmentIdentifier.offset_start
Expand Down Expand Up @@ -676,6 +679,24 @@ class DeleteDiscussionPhase:
id = Default.node_id % ("DiscussionPhase")


class Translation:
__doc__ = "A translation from a locale into an other locale."
locale_from = "The source locale of the translation."
locale_into = "The target locale of the translation."


class Preferences:
__doc__ = "The user preferences for a discussion"
harvesting_translation = "The harvesting Translation preference."


class UpdateHarvestingTranslationPreference:
__doc__ = "A mutation to save harversting translation preferences"
id = Default.object_id % ("User",)
translation = Translation.__doc__
preferences = Preferences.__doc__


class AgentProfile:
__doc__ = "A meta-data object describing the characteristics of a User or AgentProfile."
user_id = "The unique database identifier of the User."
Expand All @@ -689,6 +710,7 @@ class AgentProfile:
is_deleted = """A boolean flag that shows if the User is deleted.
If True, the User information is cleansed from the system, and the User can no longer log in."""
is_machine = """A boolean flag describing if the User is a machine user or human user."""
preferences = """The preferences of the User."""


class UpdateUser:
Expand Down
1 change: 1 addition & 0 deletions assembl/graphql/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class Meta:
creator_id = graphene.Int(description=docs.ExtractInterface.creator_id)
creator = graphene.Field(lambda: AgentProfile, description=docs.ExtractInterface.creator)
extract_state = graphene.Field(type=ExtractStates, description=docs.ExtractInterface.extract_state)
lang = graphene.String(description=docs.ExtractInterface.lang)

def resolve_creator(self, args, context, info):
if self.creator_id is not None:
Expand Down
4 changes: 4 additions & 0 deletions assembl/graphql/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class PostExtractEntryFields(graphene.AbstractType):
xpath_start = graphene.String(required=True, description=docs.PostExtract.xpath_start)
xpath_end = graphene.String(required=True, description=docs.PostExtract.xpath_end)
body = graphene.String(required=True, description=docs.PostExtract.body)
lang = graphene.String(required=True, description=docs.PostExtract.lang)


class PostExtractEntry(graphene.ObjectType, PostExtractEntryFields):
Expand Down Expand Up @@ -680,6 +681,7 @@ class Input:
post_id = graphene.ID(required=True, description=docs.AddPostExtract.post_id)
body = graphene.String(required=True, description=docs.AddPostExtract.body)
important = graphene.Boolean(description=docs.AddPostExtract.important)
lang = graphene.String(required=True, description=docs.AddPostExtract.lang)
xpath_start = graphene.String(required=True, description=docs.AddPostExtract.xpath_start)
xpath_end = graphene.String(required=True, description=docs.AddPostExtract.xpath_end)
offset_start = graphene.Int(required=True, description=docs.AddPostExtract.offset_start)
Expand All @@ -706,6 +708,7 @@ def mutate(root, args, context, info):
important=args.get('important', False),
content=post
)
new_extract.lang = args.get('lang')
post.db.add(new_extract)
range = models.TextFragmentIdentifier(
extract=new_extract,
Expand Down Expand Up @@ -760,6 +763,7 @@ def mutate(root, args, context, info):
extract_nature=extract_nature,
extract_state=extract_state
)
new_extract.lang = extract.get('lang')
post.db.add(new_extract)
range = models.TextFragmentIdentifier(
extract=new_extract,
Expand Down
74 changes: 74 additions & 0 deletions assembl/graphql/preferences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import graphene
from graphene.relay import Node

from pyramid.httpexceptions import HTTPUnauthorized

from .utils import abort_transaction_on_exception
from assembl.auth.util import get_permissions
from assembl import models
from assembl.auth import Everyone, CrudPermissions
import assembl.graphql.docstrings as docs


class TranslationFields(graphene.AbstractType):
locale_from = graphene.String(required=True, description=docs.Translation.locale_from)
locale_into = graphene.String(required=True, description=docs.Translation.locale_into)


class Translation(graphene.ObjectType, TranslationFields):
pass


class TranslationInput(graphene.InputObjectType, TranslationFields):
pass


class Preferences(graphene.ObjectType):
harvesting_translation = graphene.Field(
Translation, description=docs.Preferences.harvesting_translation)

def resolve_harvesting_translation(self, args, context, info):
translation = self.get('harvesting_translation', None)
if translation:
return Translation(**translation)


class UpdateHarvestingTranslationPreference(graphene.Mutation):

class Input:
id = graphene.ID(
required=True, description=docs.UpdateHarvestingTranslationPreference.id)
translation = TranslationInput(
required=True, description=docs.UpdateHarvestingTranslationPreference.translation)

preferences = graphene.Field(
Preferences, description=docs.UpdateHarvestingTranslationPreference.preferences)

@staticmethod
@abort_transaction_on_exception
def mutate(root, args, context, info):
model = models.User
discussion_id = context.matchdict['discussion_id']
discussion = models.Discussion.get(discussion_id)
user_id = context.authenticated_userid or Everyone

global_id = args.get('id')
id_ = int(Node.from_global_id(global_id)[1])
user = model.get(id_)
# Global permission check. Permission is checked in the preferences setter
# Is it necessary?
permissions = get_permissions(user_id, discussion_id)
allowed = user.user_can(
user_id, CrudPermissions.UPDATE, permissions)
if not allowed:
raise HTTPUnauthorized("The authenticated user can't update this harvesting locale")

with model.default_db.no_autoflush as db:
preferences = user.get_preferences_for_discussion(discussion)
# Permission check in the preferences setter
# See models.user_key_value and models.preferences preference_data_list
preferences['harvesting_translation'] = args.get('translation')
db.flush()

return UpdateHarvestingTranslationPreference(
preferences=preferences)
2 changes: 2 additions & 0 deletions assembl/graphql/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from assembl.graphql.utils import (
get_fields, get_root_thematic_for_phase,
get_posts_for_phases)
from assembl.graphql.preferences import UpdateHarvestingTranslationPreference
from assembl.lib.locale import strip_country
from assembl.lib.sqla_types import EmailString
from assembl.models.action import SentimentOfPost
Expand Down Expand Up @@ -504,6 +505,7 @@ class Mutations(graphene.ObjectType):
create_discussion_phase = CreateDiscussionPhase.Field(description=docs.CreateDiscussionPhase.__doc__)
update_discussion_phase = UpdateDiscussionPhase.Field(description=docs.CreateDiscussionPhase.__doc__)
delete_discussion_phase = DeleteDiscussionPhase.Field(description=docs.DeleteDiscussionPhase.__doc__)
update_harvesting_translation_preference = UpdateHarvestingTranslationPreference.Field(description=docs.UpdateHarvestingTranslationPreference.__doc__)


Schema = graphene.Schema(query=Query, mutation=Mutations)
Expand Down
7 changes: 7 additions & 0 deletions assembl/graphql/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from assembl.auth.password import random_string
from datetime import datetime
from .permissions_helpers import require_cls_permission
from .preferences import Preferences

_ = TranslationStringFactory('assembl')

Expand All @@ -39,6 +40,7 @@ class Meta:
has_password = graphene.Boolean(description=docs.AgentProfile.has_password)
is_deleted = graphene.Boolean(description=docs.AgentProfile.is_deleted)
is_machine = graphene.Boolean(description=docs.AgentProfile.is_machine)
preferences = graphene.Field(Preferences, description=docs.AgentProfile.preferences)

def resolve_is_deleted(self, args, context, info):
return self.is_deleted or False
Expand Down Expand Up @@ -76,6 +78,11 @@ def resolve_has_password(self, args, context, info):
def resolve_is_machine(self, args, context, info):
return getattr(self, 'is_machine', False)

def resolve_preferences(self, args, context, info):
discussion_id = context.matchdict['discussion_id']
discussion = models.Discussion.get(discussion_id)
return self.get_preferences_for_discussion(discussion)


class UpdateUser(graphene.Mutation):
__doc__ = docs.UpdateUser.__doc__
Expand Down
21 changes: 21 additions & 0 deletions assembl/models/idea_content_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from . import DiscussionBoundBase
from ..lib.sqla import (CrudOperation, get_model_watcher)
from ..lib.clean_input import sanitize_html
from ..lib.locale import to_posix_string
from .discussion import Discussion
from .idea import Idea
from .generic import Content
Expand All @@ -32,6 +33,7 @@
CrudPermissions, P_READ, P_EDIT_IDEA,
P_EDIT_EXTRACT, P_ADD_IDEA, P_ADD_EXTRACT,
P_EDIT_MY_EXTRACT)
from .langstrings import Locale


class IdeaContentLink(DiscussionBoundBase):
Expand Down Expand Up @@ -326,6 +328,10 @@ class Extract(IdeaContentPositiveLink):

important = Column('important', Boolean, server_default='0', doc=docs.ExtractInterface.important)

locale_id = Column(Integer, ForeignKey('locale.id'))

locale = relationship(Locale, foreign_keys=[locale_id])

extract_nature = Column(
'extract_nature', ExtractNatureVocabulary.pg_enum,
ForeignKey(ExtractNatureVocabulary.id), doc=docs.ExtractInterface.extract_nature)
Expand Down Expand Up @@ -380,6 +386,21 @@ def target(self):
retval['url'] = self.content.url
return retval

@property
def lang(self):
if self.locale_id:
return Locale.code_for_id(self.locale_id)

return self.locale.code

@lang.setter
def lang(self, code):
assert(code)
posix = to_posix_string(code)
locale = Locale.get_or_create(posix, self.db)
self.locale = locale
self.locale_id = locale.id

def __repr__(self):
r = super(Extract, self).__repr__()
body = self.body or ""
Expand Down
10 changes: 10 additions & 0 deletions assembl/models/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,16 @@ def get_preference_data_list(self):
"allow_user_override": None,
"modification_permission": P_ADMIN_DISC,
"default": None
},
# Harvesting translation
{
"id": "harvesting_translation",
"name": _("Harvesting translation"),
"value_type": "dict_of_string_to_string",
"show_in_preferences": True,
"description": _("Harvesting translation"),
"allow_user_override": P_READ,
"default": None
}
]

Expand Down
2 changes: 1 addition & 1 deletion assembl/static2/flow/README
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The graphql_types.flow.js was generated with the following commands:
from assembl.graphql.schema import generate_schema_json; generate_schema_json()
ctr+d
cd assembl/static2
yarn add apollo-codegen
yarn add apollo-codegen@0.19.1
./node_modules/.bin/apollo-codegen generate js/app/graphql/*.graphql js/app/graphql/fragments/*.graphql js/app/graphql/mutations/*.graphql --schema /tmp/schema.json --target flow --output flow/graphql_types.flow.js
./node_modules/.bin/prettier-eslint --write flow/graphql_types.flow.js
git checkout package.json yarn.lock
Expand Down
Loading