<commit_before># -*- coding: utf-8 -*-
# Copyright 2014 - 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains functions for performing events on rooms."""
from twisted.internet import defer

from ._base import BaseHandler

from synapse.types import UserID, RoomAlias, RoomID, RoomStreamToken
from synapse.api.constants import (
    EventTypes, JoinRules, RoomCreationPreset
)
from synapse.api.errors import AuthError, StoreError, SynapseError
from synapse.util import stringutils
from synapse.visibility import filter_events_for_client

from collections import OrderedDict

import logging
import math
import string

logger = logging.getLogger(__name__)

id_server_scheme = "https://"

class RoomCreationHandler(BaseHandler):

    PRESETS_DICT = {
        RoomCreationPreset.PRIVATE_CHAT: {
            "join_rules": JoinRules.INVITE,
            "history_visibility": "shared",
            "original_invitees_have_ops": False,
            "guest_can_join": True,
        },
        RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
            "join_rules": JoinRules.INVITE,
            "history_visibility": "shared",
            "original_invitees_have_ops": True,
            "guest_can_join": True,
        },
        RoomCreationPreset.PUBLIC_CHAT: {
            "join_rules": JoinRules.PUBLIC,
            "history_visibility": "shared",
            "original_invitees_have_ops": False,
            "guest_can_join": False,
        },
    }

    @defer.inlineCallbacks
    def create_room(self, requester, config):
        """ Creates a new room.

        Args:
            requester (Requester): The user who requested the room creation.
            config (dict) : A dict of configuration options.
        Returns:
            The new room ID.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
        """
        user_id = requester.user.to_string()

        self.ratelimit(requester)

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias.create(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias
            )

            if mapping:
                raise SynapseError(400, "Room alias already taken")
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except:
                raise SynapseError(400, "Invalid user_id: %s" % (i,))

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        # autogen room IDs and try to create it. We may clash, so just
        # try a few times till one goes through, giving up eventually.
        attempts = 0
        room_id = None
        while attempts < 5:
            try:
                random_string = stringutils.random_string(18)
                gen_room_id = RoomID.create(
                    random_string,
                    self.hs.hostname,
                )
                yield self.store.store_room(
                    room_id=gen_room_id.to_string(),
                    room_creator_user_id=user_id,
                    is_public=is_public
                )
                room_id = gen_room_id.to_string()
                break
            except StoreError:
                attempts += 1
        if not room_id:
            raise StoreError(500, "Couldn't generate a room ID.")

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                user_id=user_id,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
            )

        preset_config = config.get(
            "preset",
            RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private"
            else RoomCreationPreset.PUBLIC_CHAT
        )

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key", ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        msg_handler = self.hs.get_handlers().message_handler
        room_member_handler = self.hs.get_handlers().room_member_handler

        yield self._send_events_for_new_room(
            requester,
            room_id,
            msg_handler,
            room_member_handler,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
        )

        if "name" in config:
            name = config["name"]
            yield msg_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"name": name},
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield msg_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"topic": topic},
                },
                ratelimit=False)

        content = {}
        is_direct = config.get("is_direct", None)
        if is_direct:
            content["is_direct"] = is_direct

        for invitee in invite_list:
            yield room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_handlers().room_member_handler.do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, user_id, room_id
            )

        defer.returnValue(result)

    @defer.inlineCallbacks
    def _send_events_for_new_room(
            self,
            creator,  # A Requester object.
            room_id,
            msg_handler,
            room_member_handler,
            preset_config,
            invite_list,
            initial_state,
            creation_content,
            room_alias
    ):
        def create(etype, content, **kwargs):
            e = {
                "type": etype,
                "content": content,
            }

            e.update(event_keys)
            e.update(kwargs)

            return e

        @defer.inlineCallbacks
        def send(etype, content, **kwargs):
            event = create(etype, content, **kwargs)
            yield msg_handler.create_and_send_nonmember_event(
                creator,
                event,
                ratelimit=False
            )

        config = RoomCreationHandler.PRESETS_DICT[preset_config]

        creator_id = creator.user.to_string()

        event_keys = {
            "room_id": room_id,
            "sender": creator_id,
            "state_key": "",
        }

        creation_content.update({"creator": creator_id})
        yield send(
            etype=EventTypes.Create,
            content=creation_content,
        )

        yield room_member_handler.update_membership(
            creator,
            creator.user,
            room_id,
            "join",
            ratelimit=False,
        )

        if (EventTypes.PowerLevels, '') not in initial_state:
            power_level_content = {
                "users": {
                    creator_id: 100,
                },
                "users_default": 0,
                "events": {
                    EventTypes.Name: 50,
                    EventTypes.PowerLevels: 100,
                    EventTypes.RoomHistoryVisibility: 100,
                    EventTypes.CanonicalAlias: 50,
                    EventTypes.RoomAvatar: 50,
                },
                "events_default": 0,
                "state_default": 50,
                "ban": 50,
                "kick": 50,
                "redact": 50,
                "invite": 0,
            }

            if config["original_invitees_have_ops"]:
                for invitee in invite_list:
                    power_level_content["users"][invitee] = 100

            yield send(
                etype=EventTypes.PowerLevels,
                content=power_level_content,
            )

        if room_alias and (EventTypes.CanonicalAlias, '') not in initial_state:
            yield send(
                etype=EventTypes.CanonicalAlias,
                content={"alias": room_alias.to_string()},
            )

        if (EventTypes.JoinRules, '') not in initial_state:
            yield send(
                etype=EventTypes.JoinRules,
                content={"join_rule": config["join_rules"]},
            )

        if (EventTypes.RoomHistoryVisibility, '') not in initial_state:
            yield send(
                etype=EventTypes.RoomHistoryVisibility,
                content={"history_visibility": config["history_visibility"]}
            )

        if config["guest_can_join"]:
            if (EventTypes.GuestAccess, '') not in initial_state:
                yield send(
                    etype=EventTypes.GuestAccess,
                    content={"guest_access": "can_join"}
                )

        for (etype, state_key), content in initial_state.items():
            yield send(
                etype=etype,
                state_key=state_key,
                content=content,
            )

class RoomContextHandler(BaseHandler):
    @defer.inlineCallbacks
    def get_event_context(self, user, room_id, event_id, limit, is_guest):
        """Retrieves events, pagination tokens and state around a given event
        in a room.

        Args:
            user (UserID)
            room_id (str)
            event_id (str)
            limit (int): The maximum number of events to return in total
                (excluding state).

        Returns:
            dict, or None if the event isn't found
        """
        before_limit = math.floor(limit / 2.)
        after_limit = limit - before_limit

        now_token = yield self.hs.get_event_sources().get_current_token()

        def filter_evts(events):
            return filter_events_for_client(
                self.store,
                user.to_string(),
                events,
                is_peeking=is_guest
            )

        event = yield self.store.get_event(event_id, get_prev_content=True,
                                           allow_none=True)
        if not event:
            defer.returnValue(None)
            return

        filtered = yield(filter_evts([event]))
        if not filtered:
            raise AuthError(
                403,
                "You don't have permission to access that event."
            )

        results = yield self.store.get_events_around(
            room_id, event_id, before_limit, after_limit
        )

        results["events_before"] = yield filter_evts(results["events_before"])
        results["events_after"] = yield filter_evts(results["events_after"])
        results["event"] = event

        if results["events_after"]:
            last_event_id = results["events_after"][-1].event_id
        else:
            last_event_id = event_id

        state = yield self.store.get_state_for_events(
            [last_event_id], None
        )
        results["state"] = state[last_event_id].values()

        results["start"] = now_token.copy_and_replace(
            "room_key", results["start"]
        ).to_string()

        results["end"] = now_token.copy_and_replace(
            "room_key", results["end"]
        ).to_string()

        defer.returnValue(results)

class RoomEventSource(object):
    def __init__(self, hs):
        self.store = hs.get_datastore()

    @defer.inlineCallbacks
    def get_new_events(
            self,
            user,
            from_key,
            limit,
            room_ids,
            is_guest,
            explicit_room_id=None,
    ):
        # We just ignore the key for now.

        to_key = yield self.get_current_key()

        from_token = RoomStreamToken.parse(from_key)
        if from_token.topological:
            logger.warn("Stream has topological part!!!! %r", from_key)
            from_key = "s%s" % (from_token.stream,)

        app_service = self.store.get_app_service_by_user_id(
            user.to_string()
        )
        if app_service:
            events, end_key = yield self.store.get_appservice_room_stream(
                service=app_service,
                from_key=from_key,
                to_key=to_key,
                limit=limit,
            )
        else:
            room_events = yield self.store.get_membership_changes_for_user(
                user.to_string(), from_key, to_key
            )

            room_to_events = yield self.store.get_room_events_stream_for_rooms(
                room_ids=room_ids,
                from_key=from_key,
                to_key=to_key,
                limit=limit or 10,
                order='ASC',
            )

            events = list(room_events)
            events.extend(e for evs, _ in room_to_events.values() for e in evs)

            events.sort(key=lambda e: e.internal_metadata.order)

            if limit:
                events[:] = events[:limit]

            if events:
                end_key = events[-1].internal_metadata.after
            else:
                end_key = to_key

        defer.returnValue((events, end_key))

    def get_current_key(self):
        return self.store.get_room_events_max_id()

    def get_current_key_for_room(self, room_id):
        return self.store.get_room_events_max_id(room_id)

    @defer.inlineCallbacks
    def get_pagination_rows(self, user, config, key):
        events, next_key = yield self.store.paginate_room_events(
            room_id=key,
            from_key=config.from_key,
            to_key=config.to_key,
            direction=config.direction,
            limit=config.limit,
        )

        defer.returnValue((events, next_key))
<commit_msg>Fix /context/ visibiltiy rules<commit_after># -*- coding: utf-8 -*-
# Copyright 2014 - 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains functions for performing events on rooms."""
from twisted.internet import defer

from ._base import BaseHandler

from synapse.types import UserID, RoomAlias, RoomID, RoomStreamToken
from synapse.api.constants import (
    EventTypes, JoinRules, RoomCreationPreset
)
from synapse.api.errors import AuthError, StoreError, SynapseError
from synapse.util import stringutils
from synapse.visibility import filter_events_for_client

from collections import OrderedDict

import logging
import math
import string

logger = logging.getLogger(__name__)

id_server_scheme = "https://"

class RoomCreationHandler(BaseHandler):

    PRESETS_DICT = {
        RoomCreationPreset.PRIVATE_CHAT: {
            "join_rules": JoinRules.INVITE,
            "history_visibility": "shared",
            "original_invitees_have_ops": False,
            "guest_can_join": True,
        },
        RoomCreationPreset.TRUSTED_PRIVATE_CHAT: {
            "join_rules": JoinRules.INVITE,
            "history_visibility": "shared",
            "original_invitees_have_ops": True,
            "guest_can_join": True,
        },
        RoomCreationPreset.PUBLIC_CHAT: {
            "join_rules": JoinRules.PUBLIC,
            "history_visibility": "shared",
            "original_invitees_have_ops": False,
            "guest_can_join": False,
        },
    }

    @defer.inlineCallbacks
    def create_room(self, requester, config):
        """ Creates a new room.

        Args:
            requester (Requester): The user who requested the room creation.
            config (dict) : A dict of configuration options.
        Returns:
            The new room ID.
        Raises:
            SynapseError if the room ID couldn't be stored, or something went
            horribly wrong.
        """
        user_id = requester.user.to_string()

        self.ratelimit(requester)

        if "room_alias_name" in config:
            for wchar in string.whitespace:
                if wchar in config["room_alias_name"]:
                    raise SynapseError(400, "Invalid characters in room alias")

            room_alias = RoomAlias.create(
                config["room_alias_name"],
                self.hs.hostname,
            )
            mapping = yield self.store.get_association_from_room_alias(
                room_alias
            )

            if mapping:
                raise SynapseError(400, "Room alias already taken")
        else:
            room_alias = None

        invite_list = config.get("invite", [])
        for i in invite_list:
            try:
                UserID.from_string(i)
            except:
                raise SynapseError(400, "Invalid user_id: %s" % (i,))

        invite_3pid_list = config.get("invite_3pid", [])

        visibility = config.get("visibility", None)
        is_public = visibility == "public"

        # autogen room IDs and try to create it. We may clash, so just
        # try a few times till one goes through, giving up eventually.
        attempts = 0
        room_id = None
        while attempts < 5:
            try:
                random_string = stringutils.random_string(18)
                gen_room_id = RoomID.create(
                    random_string,
                    self.hs.hostname,
                )
                yield self.store.store_room(
                    room_id=gen_room_id.to_string(),
                    room_creator_user_id=user_id,
                    is_public=is_public
                )
                room_id = gen_room_id.to_string()
                break
            except StoreError:
                attempts += 1
        if not room_id:
            raise StoreError(500, "Couldn't generate a room ID.")

        if room_alias:
            directory_handler = self.hs.get_handlers().directory_handler
            yield directory_handler.create_association(
                user_id=user_id,
                room_id=room_id,
                room_alias=room_alias,
                servers=[self.hs.hostname],
            )

        preset_config = config.get(
            "preset",
            RoomCreationPreset.PRIVATE_CHAT
            if visibility == "private"
            else RoomCreationPreset.PUBLIC_CHAT
        )

        raw_initial_state = config.get("initial_state", [])

        initial_state = OrderedDict()
        for val in raw_initial_state:
            initial_state[(val["type"], val.get("state_key", ""))] = val["content"]

        creation_content = config.get("creation_content", {})

        msg_handler = self.hs.get_handlers().message_handler
        room_member_handler = self.hs.get_handlers().room_member_handler

        yield self._send_events_for_new_room(
            requester,
            room_id,
            msg_handler,
            room_member_handler,
            preset_config=preset_config,
            invite_list=invite_list,
            initial_state=initial_state,
            creation_content=creation_content,
            room_alias=room_alias,
        )

        if "name" in config:
            name = config["name"]
            yield msg_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Name,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"name": name},
                },
                ratelimit=False)

        if "topic" in config:
            topic = config["topic"]
            yield msg_handler.create_and_send_nonmember_event(
                requester,
                {
                    "type": EventTypes.Topic,
                    "room_id": room_id,
                    "sender": user_id,
                    "state_key": "",
                    "content": {"topic": topic},
                },
                ratelimit=False)

        content = {}
        is_direct = config.get("is_direct", None)
        if is_direct:
            content["is_direct"] = is_direct

        for invitee in invite_list:
            yield room_member_handler.update_membership(
                requester,
                UserID.from_string(invitee),
                room_id,
                "invite",
                ratelimit=False,
                content=content,
            )

        for invite_3pid in invite_3pid_list:
            id_server = invite_3pid["id_server"]
            address = invite_3pid["address"]
            medium = invite_3pid["medium"]
            yield self.hs.get_handlers().room_member_handler.do_3pid_invite(
                room_id,
                requester.user,
                medium,
                address,
                id_server,
                requester,
                txn_id=None,
            )

        result = {"room_id": room_id}

        if room_alias:
            result["room_alias"] = room_alias.to_string()
            yield directory_handler.send_room_alias_update_event(
                requester, user_id, room_id
            )

        defer.returnValue(result)

    @defer.inlineCallbacks
    def _send_events_for_new_room(
            self,
            creator,  # A Requester object.
            room_id,
            msg_handler,
            room_member_handler,
            preset_config,
            invite_list,
            initial_state,
            creation_content,
            room_alias
    ):
        def create(etype, content, **kwargs):
            e = {
                "type": etype,
                "content": content,
            }

            e.update(event_keys)
            e.update(kwargs)

            return e

        @defer.inlineCallbacks
        def send(etype, content, **kwargs):
            event = create(etype, content, **kwargs)
            yield msg_handler.create_and_send_nonmember_event(
                creator,
                event,
                ratelimit=False
            )

        config = RoomCreationHandler.PRESETS_DICT[preset_config]

        creator_id = creator.user.to_string()

        event_keys = {
            "room_id": room_id,
            "sender": creator_id,
            "state_key": "",
        }

        creation_content.update({"creator": creator_id})
        yield send(
            etype=EventTypes.Create,
            content=creation_content,
        )

        yield room_member_handler.update_membership(
            creator,
            creator.user,
            room_id,
            "join",
            ratelimit=False,
        )

        if (EventTypes.PowerLevels, '') not in initial_state:
            power_level_content = {
                "users": {
                    creator_id: 100,
                },
                "users_default": 0,
                "events": {
                    EventTypes.Name: 50,
                    EventTypes.PowerLevels: 100,
                    EventTypes.RoomHistoryVisibility: 100,
                    EventTypes.CanonicalAlias: 50,
                    EventTypes.RoomAvatar: 50,
                },
                "events_default": 0,
                "state_default": 50,
                "ban": 50,
                "kick": 50,
                "redact": 50,
                "invite": 0,
            }

            if config["original_invitees_have_ops"]:
                for invitee in invite_list:
                    power_level_content["users"][invitee] = 100

            yield send(
                etype=EventTypes.PowerLevels,
                content=power_level_content,
            )

        if room_alias and (EventTypes.CanonicalAlias, '') not in initial_state:
            yield send(
                etype=EventTypes.CanonicalAlias,
                content={"alias": room_alias.to_string()},
            )

        if (EventTypes.JoinRules, '') not in initial_state:
            yield send(
                etype=EventTypes.JoinRules,
                content={"join_rule": config["join_rules"]},
            )

        if (EventTypes.RoomHistoryVisibility, '') not in initial_state:
            yield send(
                etype=EventTypes.RoomHistoryVisibility,
                content={"history_visibility": config["history_visibility"]}
            )

        if config["guest_can_join"]:
            if (EventTypes.GuestAccess, '') not in initial_state:
                yield send(
                    etype=EventTypes.GuestAccess,
                    content={"guest_access": "can_join"}
                )

        for (etype, state_key), content in initial_state.items():
            yield send(
                etype=etype,
                state_key=state_key,
                content=content,
            )

class RoomContextHandler(BaseHandler):
    @defer.inlineCallbacks
    def get_event_context(self, user, room_id, event_id, limit, is_guest):
        """Retrieves events, pagination tokens and state around a given event
        in a room.

        Args:
            user (UserID)
            room_id (str)
            event_id (str)
            limit (int): The maximum number of events to return in total
                (excluding state).

        Returns:
            dict, or None if the event isn't found
        """
        before_limit = math.floor(limit / 2.)
        after_limit = limit - before_limit

        now_token = yield self.hs.get_event_sources().get_current_token()

        users = yield self.store.get_users_in_room(room_id)
        is_peeking = user.to_string() not in users

        def filter_evts(events):
            return filter_events_for_client(
                self.store,
                user.to_string(),
                events,
                is_peeking=is_peeking
            )

        event = yield self.store.get_event(event_id, get_prev_content=True,
                                           allow_none=True)
        if not event:
            defer.returnValue(None)
            return

        filtered = yield(filter_evts([event]))
        if not filtered:
            raise AuthError(
                403,
                "You don't have permission to access that event."
            )

        results = yield self.store.get_events_around(
            room_id, event_id, before_limit, after_limit
        )

        results["events_before"] = yield filter_evts(results["events_before"])
        results["events_after"] = yield filter_evts(results["events_after"])
        results["event"] = event

        if results["events_after"]:
            last_event_id = results["events_after"][-1].event_id
        else:
            last_event_id = event_id

        state = yield self.store.get_state_for_events(
            [last_event_id], None
        )
        results["state"] = state[last_event_id].values()

        results["start"] = now_token.copy_and_replace(
            "room_key", results["start"]
        ).to_string()

        results["end"] = now_token.copy_and_replace(
            "room_key", results["end"]
        ).to_string()

        defer.returnValue(results)

class RoomEventSource(object):
    def __init__(self, hs):
        self.store = hs.get_datastore()

    @defer.inlineCallbacks
    def get_new_events(
            self,
            user,
            from_key,
            limit,
            room_ids,
            is_guest,
            explicit_room_id=None,
    ):
        # We just ignore the key for now.

        to_key = yield self.get_current_key()

        from_token = RoomStreamToken.parse(from_key)
        if from_token.topological:
            logger.warn("Stream has topological part!!!! %r", from_key)
            from_key = "s%s" % (from_token.stream,)

        app_service = self.store.get_app_service_by_user_id(
            user.to_string()
        )
        if app_service:
            events, end_key = yield self.store.get_appservice_room_stream(
                service=app_service,
                from_key=from_key,
                to_key=to_key,
                limit=limit,
            )
        else:
            room_events = yield self.store.get_membership_changes_for_user(
                user.to_string(), from_key, to_key
            )

            room_to_events = yield self.store.get_room_events_stream_for_rooms(
                room_ids=room_ids,
                from_key=from_key,
                to_key=to_key,
                limit=limit or 10,
                order='ASC',
            )

            events = list(room_events)
            events.extend(e for evs, _ in room_to_events.values() for e in evs)

            events.sort(key=lambda e: e.internal_metadata.order)

            if limit:
                events[:] = events[:limit]

            if events:
                end_key = events[-1].internal_metadata.after
            else:
                end_key = to_key

        defer.returnValue((events, end_key))

    def get_current_key(self):
        return self.store.get_room_events_max_id()

    def get_current_key_for_room(self, room_id):
        return self.store.get_room_events_max_id(room_id)

    @defer.inlineCallbacks
    def get_pagination_rows(self, user, config, key):
        events, next_key = yield self.store.paginate_room_events(
            room_id=key,
            from_key=config.from_key,
            to_key=config.to_key,
            direction=config.direction,
            limit=config.limit,
        )

        defer.returnValue((events, next_key))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2010, Sebastian Wiesner <lunaryorn@googlemail.com>
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""
    synaptiks.kde.widgets
    =====================

    Common KDE widgets for synaptiks.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@googlemail.com>
"""

from __future__ import (print_function, division, unicode_literals,
                        absolute_import)

import os
from functools import partial

import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4.uic import loadUi
from PyQt4.QtCore import pyqtSignal, QRegExp
from PyQt4.QtGui import QWidget, QHBoxLayout
from PyKDE4.kdecore import ki18nc, i18nc
from PyKDE4.kdeui import KIconLoader, KTabWidget, KComboBox, KCModule

from synaptiks.config import get_touchpad_defaults
from synaptiks.kde import make_about_data

PACKAGE_DIRECTORY = os.path.dirname(__file__)

class DynamicUserInterfaceMixin(object):
    """
    Mixin class for widgets to load their user interface dynamically from the
    :mod:`synaptiks.kde` package.  It provides a single method
    :meth:`_load_userinterface()`, which loads the user interface into the
    instance.
    """

    def _load_userinterface(self):
        """
        Load the user interface for this object.

        The user interface is loaded from a user interface file with the
        lower-cased class name in ``ui/`` sub-directory of this package.  For
        instance, the user interface file for class ``FooBar`` would be
        ``ui/foobar.ui``.
        """
        ui_description_filename = os.path.join(
            PACKAGE_DIRECTORY, 'ui',
            self.__class__.__name__.lower() + '.ui')
        loadUi(ui_description_filename, self)

class MouseButtonComboBox(KComboBox):
    def __init__(self, parent=None):
        KComboBox.__init__(self, parent)
        self.addItems([
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Disabled'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Left mouse button'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Middle mouse button'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                   'Right mouse button')
            ])

class TouchpadInformationWidget(QWidget, DynamicUserInterfaceMixin):
    """
    A widget which shows some information about a touchpad.

    This currently includes:

    - the device name of the touchpad,
    - what physical buttons, the touchpad has,
    - how many fingers it can detect,
    - and whether it supports two-finger emulation.
    """

    def __init__(self, parent=None):
        """
        Create a new information widget.
        """
        QWidget.__init__(self, parent)
        self._load_userinterface()

    def show_touchpad(self, touchpad):
        """
        Show information about the given ``touchpad`` in this widget.

        ``touchpad`` is a :class:`~synaptiks.touchpad.Touchpad` object.
        """
        self.nameLabel.setText(i18nc(
            '@info touchpad name', '<title><resources>%1</resource></title>',
            touchpad.name))

        pixmaps = {True: 'dialog-ok', False: 'dialog-cancel'}
        for key in pixmaps:
            pixmaps[key] = KIconLoader.global_().loadIcon(
                pixmaps[key], KIconLoader.Small)

        button_widgets = ('left', 'middle', 'right')
        for widget_name, is_supported in zip(button_widgets, touchpad.buttons):
            widget = getattr(self, '{0}Button'.format(widget_name))
            widget.setPixmap(pixmaps[is_supported])

        self.fingerDetection.setValue(touchpad.finger_detection)

        self.fingerWidthDetection.setPixmap(
            pixmaps[touchpad.has_finger_width_detection])
        self.pressureDetection.setPixmap(
            pixmaps[touchpad.has_pressure_detection])
        self.twoFingerEmulation.setPixmap(
            pixmaps[touchpad.has_two_finger_emulation])

class MotionPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure the settings for cursor motion on the
    touchpad.
    """

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()

class ScrollingPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure scrolling.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        # HACK: the designer is seems unable to set this property, so we set it
        # in code.
        self.touchpad_coasting_speed.setSpecialValueText(
            i18nc('@item coasting speed special value', 'Disabled'))
        self.touchpad_coasting_speed.valueChanged.connect(
            self._coasting_speed_value_changed)
        if touchpad.finger_detection >= 2 or touchpad.has_two_finger_emulation:
            two_finger_widgets = self.findChildren(
                QWidget, QRegExp('touchpad_(.*)_two_finger_scrolling'))
            for widget in two_finger_widgets:
                widget.setEnabled(True)

    def _coasting_speed_value_changed(self, value):
        self.touchpad_corner_coasting.setEnabled(value != 0)

class TappingPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure tapping.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        finger_tap_actions = self.findChildren(
            KComboBox, QRegExp('touchpad_f[1-3]_tap_action'))

        for widget in finger_tap_actions[touchpad.finger_detection:]:
            self._set_enabled(widget, False)
        if touchpad.has_two_finger_emulation:
            self._set_enabled(self.touchpad_f2_tap_action, True)

    def _set_enabled(self, widget, enabled):
        widget.setEnabled(enabled)
        self.fingerButtonsLayout.labelForField(widget).setEnabled(enabled)

class HardwarePage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page for hardware settings.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        self.information.show_touchpad(touchpad)

class TouchpadConfigurationWidget(KTabWidget):
    """
    A tab widget to configure the touchpad properties.

    This basically aggregates all page classes in this module.
    """

    configurationChanged = pyqtSignal(bool)

    PROPERTY_MAP = dict(
        QCheckBox='checked', QRadioButton='checked', QGroupBox='checked',
        MouseButtonComboBox='currentIndex', KComboBox='currentIndex',
        KIntNumInput='value', KDoubleNumInput='value'
        )

    CHANGED_SIGNAL_MAP = dict(
        QCheckBox='toggled', QRadioButton='toggled', QGroupBox='toggled',
        MouseButtonComboBox='currentIndexChanged',
        KComboBox='currentIndexChanged',
        KIntNumInput='valueChanged', KDoubleNumInput='valueChanged'
        )

    def __init__(self, config, parent=None):
        """
        Create a new configuration widget for the given ``touchpad``.

        ``config`` is the :class:`~synaptiks.config.TouchpadConfiguration`
        object displayed by this widget.  ``parent`` is the parent
        :class:`~PyQt4.QtGui.QWidget` (can be ``None``).
        """
        KTabWidget.__init__(self, parent)
        self.touchpad_config = config
        self._changed_widgets = set()
        pages = [HardwarePage(self.touchpad, self), MotionPage(self),
                 ScrollingPage(self.touchpad, self),
                 TappingPage(self.touchpad, self)]
        for page in pages:
            self.addTab(page, page.windowTitle())
        self.setWindowTitle(
            i18nc('@title:window', 'Touchpad configuration'))
        self.load_configuration()
        for widget in self._find_touchpad_configuration_widgets():
            signalname = self.CHANGED_SIGNAL_MAP[type(widget).__name__]
            signal = getattr(widget, signalname)
            signal.connect(partial(self._check_for_changes, widget))

    @property
    def touchpad(self):
        """
        The :class:`~synaptiks.touchpad.Touchpad` object associated with this
        widget.
        """
        return self.touchpad_config.touchpad

    def _check_for_changes(self, origin, changed_value):
        touchpad_property = self._get_touchpad_property(origin)
        name = origin.objectName()
        current_value = self.touchpad_config[touchpad_property]
        if current_value == changed_value:
            self._changed_widgets.remove(name)
        else:
            self._changed_widgets.add(name)
        self.configurationChanged.emit(bool(self._changed_widgets))

    def _get_touchpad_property(self, widget):
        """
        Return the touchpad property name associated with the given ``widget``.
        """
        return widget.objectName()[9:]

    def _find_touchpad_configuration_widgets(self):
        """
        Find all widgets which correspond to a touchpad properties.
        """
        return self.findChildren(QWidget, QRegExp('touchpad_.*'))

    def _load_mapping_into_widgets(self, mapping):
        for widget in self._find_touchpad_configuration_widgets():
            touchpad_property = self._get_touchpad_property(widget)
            value = mapping[touchpad_property]
            widget_property = self.PROPERTY_MAP[type(widget).__name__]
            widget.setProperty(widget_property, value)

    def load_defaults(self, defaults=None):
        """
        Load the given default configuration into the configuration widgets.

        If ``defaults`` is ``None``, the default configuration is implicitly
        loaded from disk (see
        :func:`synaptiks.config.get_touchpad_defaults()`).

        ``defaults`` is a mapping with the default touchpad configuration or
        ``None``.
        """
        if defaults is None:
            defaults = get_touchpad_defaults()
        self._load_mapping_into_widgets(defaults)

    def load_configuration(self):
        """
        Load the configuration of the associated touchpad into the
        configuration widgets.
        """
        self._load_mapping_into_widgets(self.touchpad_config)

    def apply_configuration(self):
        """
        Apply the contents of the configuration widgets to the associated
        touchpad.
        """
        for widget in self._find_touchpad_configuration_widgets():
            touchpad_property = self._get_touchpad_property(widget)
            widget_property = self.PROPERTY_MAP[type(widget).__name__]
            value = widget.property(widget_property)
            self.touchpad_config[touchpad_property] = value

class TouchpadConfigurationKCM(KCModule):
    """
    Synaptiks system settings module.
    """

    def __init__(self, touchpad_config, component_data, parent=None):
        """
        Create a new synaptiks module.

        ``touchpad_config`` is the
        :class:`synaptiks.config.TouchpadConfiguration`, which is handled by
        this module.  ``component_data`` and ``parent`` come from the
        ``KCModule`` constructor and are passed from the plugin entry point.
        """
        KCModule.__init__(self, component_data, parent)
        # keep a reference to the generated about data to prevent it from being
        # deleted by the GC
        self._about = make_about_data(
            ki18nc('kcmodule description', 'Touchpad configuration'))
        self.setQuickHelp(i18nc(
            '@info:tooltip synaptiks kcmodule',
            '<title>Touchpad configuration</title>'
            '<para>This module lets you configure your touchpad.</para>'))
        self.setAboutData(self._about)
        self.touchpad_config = touchpad_config
        self.setLayout(QHBoxLayout(self))
        self.config_widget = TouchpadConfigurationWidget(self.touchpad_config)
        self.config_widget.configurationChanged.connect(self.changed)
        self.layout().addWidget(self.config_widget)

    def defaults(self, defaults=None):
        """
        Load the default settings into the widgets.

        ``defaults`` is a mapping with the default touchpad configuration, or
        ``None``.  In the latter case, the defaults are implicitly loaded from
        disk.
        """
        self.config_widget.load_defaults(defaults)

    def load(self):
        """
        Load the touchpad configuration into the widgets.
        """
        self.config_widget.load_configuration()

    def save(self):
        """
        Apply and save touchpad configuration.
        """
        self.config_widget.apply_configuration()
        self.touchpad_config.save()
<commit_msg>Added property to check, if the configuration is currently changed<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2010, Sebastian Wiesner <lunaryorn@googlemail.com>
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""
    synaptiks.kde.widgets
    =====================

    Common KDE widgets for synaptiks.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@googlemail.com>
"""

from __future__ import (print_function, division, unicode_literals,
                        absolute_import)

import os
from functools import partial

import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4.uic import loadUi
from PyQt4.QtCore import pyqtSignal, QRegExp
from PyQt4.QtGui import QWidget, QHBoxLayout
from PyKDE4.kdecore import ki18nc, i18nc
from PyKDE4.kdeui import KIconLoader, KTabWidget, KComboBox, KCModule

from synaptiks.config import get_touchpad_defaults
from synaptiks.kde import make_about_data

PACKAGE_DIRECTORY = os.path.dirname(__file__)

class DynamicUserInterfaceMixin(object):
    """
    Mixin class for widgets to load their user interface dynamically from the
    :mod:`synaptiks.kde` package.  It provides a single method
    :meth:`_load_userinterface()`, which loads the user interface into the
    instance.
    """

    def _load_userinterface(self):
        """
        Load the user interface for this object.

        The user interface is loaded from a user interface file with the
        lower-cased class name in ``ui/`` sub-directory of this package.  For
        instance, the user interface file for class ``FooBar`` would be
        ``ui/foobar.ui``.
        """
        ui_description_filename = os.path.join(
            PACKAGE_DIRECTORY, 'ui',
            self.__class__.__name__.lower() + '.ui')
        loadUi(ui_description_filename, self)

class MouseButtonComboBox(KComboBox):
    def __init__(self, parent=None):
        KComboBox.__init__(self, parent)
        self.addItems([
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Disabled'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Left mouse button'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                  'Middle mouse button'),
            i18nc('@item:inlistbox mouse button triggered by tapping',
                   'Right mouse button')
            ])

class TouchpadInformationWidget(QWidget, DynamicUserInterfaceMixin):
    """
    A widget which shows some information about a touchpad.

    This currently includes:

    - the device name of the touchpad,
    - what physical buttons, the touchpad has,
    - how many fingers it can detect,
    - and whether it supports two-finger emulation.
    """

    def __init__(self, parent=None):
        """
        Create a new information widget.
        """
        QWidget.__init__(self, parent)
        self._load_userinterface()

    def show_touchpad(self, touchpad):
        """
        Show information about the given ``touchpad`` in this widget.

        ``touchpad`` is a :class:`~synaptiks.touchpad.Touchpad` object.
        """
        self.nameLabel.setText(i18nc(
            '@info touchpad name', '<title><resources>%1</resource></title>',
            touchpad.name))

        pixmaps = {True: 'dialog-ok', False: 'dialog-cancel'}
        for key in pixmaps:
            pixmaps[key] = KIconLoader.global_().loadIcon(
                pixmaps[key], KIconLoader.Small)

        button_widgets = ('left', 'middle', 'right')
        for widget_name, is_supported in zip(button_widgets, touchpad.buttons):
            widget = getattr(self, '{0}Button'.format(widget_name))
            widget.setPixmap(pixmaps[is_supported])

        self.fingerDetection.setValue(touchpad.finger_detection)

        self.fingerWidthDetection.setPixmap(
            pixmaps[touchpad.has_finger_width_detection])
        self.pressureDetection.setPixmap(
            pixmaps[touchpad.has_pressure_detection])
        self.twoFingerEmulation.setPixmap(
            pixmaps[touchpad.has_two_finger_emulation])

class MotionPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure the settings for cursor motion on the
    touchpad.
    """

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()

class ScrollingPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure scrolling.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        # HACK: the designer is seems unable to set this property, so we set it
        # in code.
        self.touchpad_coasting_speed.setSpecialValueText(
            i18nc('@item coasting speed special value', 'Disabled'))
        self.touchpad_coasting_speed.valueChanged.connect(
            self._coasting_speed_value_changed)
        if touchpad.finger_detection >= 2 or touchpad.has_two_finger_emulation:
            two_finger_widgets = self.findChildren(
                QWidget, QRegExp('touchpad_(.*)_two_finger_scrolling'))
            for widget in two_finger_widgets:
                widget.setEnabled(True)

    def _coasting_speed_value_changed(self, value):
        self.touchpad_corner_coasting.setEnabled(value != 0)

class TappingPage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page to configure tapping.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        finger_tap_actions = self.findChildren(
            KComboBox, QRegExp('touchpad_f[1-3]_tap_action'))

        for widget in finger_tap_actions[touchpad.finger_detection:]:
            self._set_enabled(widget, False)
        if touchpad.has_two_finger_emulation:
            self._set_enabled(self.touchpad_f2_tap_action, True)

    def _set_enabled(self, widget, enabled):
        widget.setEnabled(enabled)
        self.fingerButtonsLayout.labelForField(widget).setEnabled(enabled)

class HardwarePage(QWidget, DynamicUserInterfaceMixin):
    """
    Configuration page for hardware settings.
    """

    def __init__(self, touchpad, parent=None):
        QWidget.__init__(self, parent)
        self._load_userinterface()
        self.information.show_touchpad(touchpad)

class TouchpadConfigurationWidget(KTabWidget):
    """
    A tab widget to configure the touchpad properties.

    This basically aggregates all page classes in this module.
    """

    configurationChanged = pyqtSignal(bool)

    PROPERTY_MAP = dict(
        QCheckBox='checked', QRadioButton='checked', QGroupBox='checked',
        MouseButtonComboBox='currentIndex', KComboBox='currentIndex',
        KIntNumInput='value', KDoubleNumInput='value'
        )

    CHANGED_SIGNAL_MAP = dict(
        QCheckBox='toggled', QRadioButton='toggled', QGroupBox='toggled',
        MouseButtonComboBox='currentIndexChanged',
        KComboBox='currentIndexChanged',
        KIntNumInput='valueChanged', KDoubleNumInput='valueChanged'
        )

    def __init__(self, config, parent=None):
        """
        Create a new configuration widget for the given ``touchpad``.

        ``config`` is the :class:`~synaptiks.config.TouchpadConfiguration`
        object displayed by this widget.  ``parent`` is the parent
        :class:`~PyQt4.QtGui.QWidget` (can be ``None``).
        """
        KTabWidget.__init__(self, parent)
        self.touchpad_config = config
        self._changed_widgets = set()
        pages = [HardwarePage(self.touchpad, self), MotionPage(self),
                 ScrollingPage(self.touchpad, self),
                 TappingPage(self.touchpad, self)]
        for page in pages:
            self.addTab(page, page.windowTitle())
        self.setWindowTitle(
            i18nc('@title:window', 'Touchpad configuration'))
        self.load_configuration()
        for widget in self._find_touchpad_configuration_widgets():
            signalname = self.CHANGED_SIGNAL_MAP[type(widget).__name__]
            signal = getattr(widget, signalname)
            signal.connect(partial(self._check_for_changes, widget))

    @property
    def touchpad(self):
        """
        The :class:`~synaptiks.touchpad.Touchpad` object associated with this
        widget.
        """
        return self.touchpad_config.touchpad

    @property
    def is_configuration_changed(self):
        """
        ``True``, if the configuration shown in the widgets is different from
        the actual touchpad configuration.  This usually means, that the user
        has changed some setting.
        """
        return bool(self._changed_widgets)

    def _check_for_changes(self, origin, changed_value):
        touchpad_property = self._get_touchpad_property(origin)
        name = origin.objectName()
        current_value = self.touchpad_config[touchpad_property]
        if current_value == changed_value:
            self._changed_widgets.remove(name)
        else:
            self._changed_widgets.add(name)
        self.configurationChanged.emit(self.is_configuration_changed)

    def _get_touchpad_property(self, widget):
        """
        Return the touchpad property name associated with the given ``widget``.
        """
        return widget.objectName()[9:]

    def _find_touchpad_configuration_widgets(self):
        """
        Find all widgets which correspond to a touchpad properties.
        """
        return self.findChildren(QWidget, QRegExp('touchpad_.*'))

    def _load_mapping_into_widgets(self, mapping):
        for widget in self._find_touchpad_configuration_widgets():
            touchpad_property = self._get_touchpad_property(widget)
            value = mapping[touchpad_property]
            widget_property = self.PROPERTY_MAP[type(widget).__name__]
            widget.setProperty(widget_property, value)

    def load_defaults(self, defaults=None):
        """
        Load the given default configuration into the configuration widgets.

        If ``defaults`` is ``None``, the default configuration is implicitly
        loaded from disk (see
        :func:`synaptiks.config.get_touchpad_defaults()`).

        ``defaults`` is a mapping with the default touchpad configuration or
        ``None``.
        """
        if defaults is None:
            defaults = get_touchpad_defaults()
        self._load_mapping_into_widgets(defaults)

    def load_configuration(self):
        """
        Load the configuration of the associated touchpad into the
        configuration widgets.
        """
        self._load_mapping_into_widgets(self.touchpad_config)

    def apply_configuration(self):
        """
        Apply the contents of the configuration widgets to the associated
        touchpad.
        """
        for widget in self._find_touchpad_configuration_widgets():
            touchpad_property = self._get_touchpad_property(widget)
            widget_property = self.PROPERTY_MAP[type(widget).__name__]
            value = widget.property(widget_property)
            self.touchpad_config[touchpad_property] = value

class TouchpadConfigurationKCM(KCModule):
    """
    Synaptiks system settings module.
    """

    def __init__(self, touchpad_config, component_data, parent=None):
        """
        Create a new synaptiks module.

        ``touchpad_config`` is the
        :class:`synaptiks.config.TouchpadConfiguration`, which is handled by
        this module.  ``component_data`` and ``parent`` come from the
        ``KCModule`` constructor and are passed from the plugin entry point.
        """
        KCModule.__init__(self, component_data, parent)
        # keep a reference to the generated about data to prevent it from being
        # deleted by the GC
        self._about = make_about_data(
            ki18nc('kcmodule description', 'Touchpad configuration'))
        self.setQuickHelp(i18nc(
            '@info:tooltip synaptiks kcmodule',
            '<title>Touchpad configuration</title>'
            '<para>This module lets you configure your touchpad.</para>'))
        self.setAboutData(self._about)
        self.touchpad_config = touchpad_config
        self.setLayout(QHBoxLayout(self))
        self.config_widget = TouchpadConfigurationWidget(self.touchpad_config)
        self.config_widget.configurationChanged.connect(self.changed)
        self.layout().addWidget(self.config_widget)

    def defaults(self, defaults=None):
        """
        Load the default settings into the widgets.

        ``defaults`` is a mapping with the default touchpad configuration, or
        ``None``.  In the latter case, the defaults are implicitly loaded from
        disk.
        """
        self.config_widget.load_defaults(defaults)

    def load(self):
        """
        Load the touchpad configuration into the widgets.
        """
        self.config_widget.load_configuration()

    def save(self):
        """
        Apply and save touchpad configuration.
        """
        self.config_widget.apply_configuration()
        self.touchpad_config.save()
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    extract FileInfo object for files in AWS S3
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from sync_app.file_list import FileInfo, FileList
from sync_app.s3_instance import S3Instance

class FileInfoS3(FileInfo):
    __slots__ = FileInfo.__slots__ + ['bucket']

    def __init__(self, fn='', md5='', fs=None, bk=''):
        FileInfo.__init__(self, fn=fn, md5=md5, fs=fs)
        self.bucket = bk

class FileListS3(FileList):
    def __init__(self, filelist=None, bucket=None):
        FileList.__init__(self, filelist=filelist, basedir=bucket, filelist_type='s3')
        self.bucketlist = []
        self.s3 = None
        pass

    def append_item(self, item):
        finfo = FileInfoS3()
        finfo.filename = item.key
        finfo.bucket = item.bucket.name
        finfo.urlname = 's3://%s/%s' % (finfo.bucket, finfo.filename)
        finfo.md5sum = item.etag.replace('"','')
        if finfo.filename in self.filelist_name_dict:
            for ffn in self.filelist_name_dict[finfo.filename]:
                if ffn.md5sum == finfo.md5sum:
                    return self.filelist_name_dict[finfo.filename]
        self.append(finfo)
        return finfo

    def fill_file_list_s3(self, bucket=None):
        self.s3 = S3Instance()
        self.s3.get_list_of_keys(callback_fn=self.append_item)
<commit_msg>docstrings<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    extract FileInfo object for files in AWS S3
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from sync_app.file_list import FileInfo, FileList
from sync_app.s3_instance import S3Instance

class FileInfoS3(FileInfo):
    """ File Info for S3, add bucket metadata """
    __slots__ = FileInfo.__slots__ + ['bucket']

    def __init__(self, fn='', md5='', fs=None, bk=''):
        FileInfo.__init__(self, fn=fn, md5=md5, fs=fs)
        self.bucket = bk

class FileListS3(FileList):
    """ File list for S3 """

    def __init__(self, filelist=None, bucket=None):
        """ Init function """
        FileList.__init__(self, filelist=filelist, basedir=bucket,
                          filelist_type='s3')
        self.bucketlist = []
        self.s3 = None
        pass

    def append_item(self, item):
        """ append S3 item to filelist """
        finfo = FileInfoS3()
        finfo.filename = item.key
        finfo.bucket = item.bucket.name
        finfo.urlname = 's3://%s/%s' % (finfo.bucket, finfo.filename)
        finfo.md5sum = item.etag.replace('"','')
        if finfo.filename in self.filelist_name_dict:
            for ffn in self.filelist_name_dict[finfo.filename]:
                if ffn.md5sum == finfo.md5sum:
                    return self.filelist_name_dict[finfo.filename]
        self.append(finfo)
        return finfo

    def fill_file_list_s3(self, bucket=None):
        """ fill s3 filelist  """
        self.s3 = S3Instance()
        self.s3.get_list_of_keys(callback_fn=self.append_item)
<|endoftext|>


<commit_before>__author__ = 'Bohdan Mushkevych'

from subprocess import PIPE, STDOUT
import sys

import psutil
from psutil import TimeoutExpired
from launch import get_python, PROJECT_ROOT, PROCESS_STARTER
from system.process_context import ProcessContext
from settings import settings

def get_process_pid(process_name):
    """ check for process' pid file and returns pid from there """
    try:
        pid_filename = ProcessContext.get_pid_filename(process_name)
        pf = file(pid_filename, 'r')
        pid = int(pf.read().strip())
        pf.close()
    except IOError:
        pid = None
    return pid

def kill_process(process_name):
    """ method is called to kill a running process """
    try:
        sys.stdout.write('killing: %s { \n' % process_name)
        pid = get_process_pid(process_name)
        if pid is not None and psutil.pid_exists(int(pid)):
            p = psutil.Process(pid)
            p.kill()
            p.wait()
            ProcessContext.remove_pid_file(process_name)
    except Exception as e:
        sys.stderr.write('Exception on killing %s : %s \n' % (process_name, str(e)))
    finally:
        sys.stdout.write('}')

def start_process(process_name, *args):
    try:
        sys.stdout.write('starting: %s { \n' % process_name)
        cmd = [get_python(), PROJECT_ROOT + '/' + PROCESS_STARTER, process_name]
        if not args:
            # this blocks triggers when args is either None or an empty list
            pass
        else:
            cmd.extend(*args)

        p = psutil.Popen(cmd,
                         close_fds=True,
                         cwd=settings['process_cwd'],
                         stdin=PIPE,
                         stdout=PIPE,
                         stderr=STDOUT)
        sys.stdout.write('Started %s with pid = %r \n' % (process_name, p.pid))
    except Exception as e:
        sys.stderr.write('Exception on starting %s : %s \n' % (process_name, str(e)))
    finally:
        sys.stdout.write('}')

def poll_process(process_name):
    """ between killing a process and its actual termination lies poorly documented requirement -
        <purging process' io pipes and reading exit status>.
        this can be done either by os.wait() or process.wait()
        :return True if the process is alive and OK and False is the process was terminated """
    try:
        pid = get_process_pid(process_name)
        p = psutil.Process(pid)

        return_code = p.wait(timeout=0.01)
        if return_code is None:
            # process is already terminated
            sys.stdout.write('Process %s is terminated \n' % process_name)
            return False
        else:
            # process is terminated; possibly by OS
            sys.stdout.write('Process %s got terminated \n' % process_name)
            return False
    except TimeoutExpired:
        sys.stdout.write('Process %s is alive and OK \n' % process_name)
        return True
<commit_msg>Addressing false negative of process termination<commit_after>__author__ = 'Bohdan Mushkevych'

from subprocess import PIPE, STDOUT
import sys

import psutil
from psutil import TimeoutExpired
from launch import get_python, PROJECT_ROOT, PROCESS_STARTER
from system.process_context import ProcessContext
from settings import settings

def get_process_pid(process_name):
    """ check for process' pid file and returns pid from there """
    try:
        pid_filename = ProcessContext.get_pid_filename(process_name)
        pf = file(pid_filename, 'r')
        pid = int(pf.read().strip())
        pf.close()
    except IOError:
        pid = None
    return pid

def kill_process(process_name):
    """ method is called to kill a running process """
    try:
        sys.stdout.write('killing: %s { \n' % process_name)
        pid = get_process_pid(process_name)
        if pid is not None and psutil.pid_exists(int(pid)):
            p = psutil.Process(pid)
            p.kill()
            p.wait()
            ProcessContext.remove_pid_file(process_name)
    except Exception as e:
        sys.stderr.write('Exception on killing %s : %s \n' % (process_name, str(e)))
    finally:
        sys.stdout.write('}')

def start_process(process_name, *args):
    try:
        sys.stdout.write('starting: %s { \n' % process_name)
        cmd = [get_python(), PROJECT_ROOT + '/' + PROCESS_STARTER, process_name]
        if not args:
            # this blocks triggers when args is either None or an empty list
            pass
        else:
            cmd.extend(*args)

        p = psutil.Popen(cmd,
                         close_fds=True,
                         cwd=settings['process_cwd'],
                         stdin=PIPE,
                         stdout=PIPE,
                         stderr=STDOUT)
        sys.stdout.write('Started %s with pid = %r \n' % (process_name, p.pid))
    except Exception as e:
        sys.stderr.write('Exception on starting %s : %s \n' % (process_name, str(e)))
    finally:
        sys.stdout.write('}')

def poll_process(process_name):
    """ between killing a process and its actual termination lies poorly documented requirement -
        <purging process' io pipes and reading exit status>.
        this can be done either by os.wait() or process.wait()
        :return True if the process is alive and OK and False is the process was terminated """
    try:
        pid = get_process_pid(process_name)
        if pid is None:
            sys.stdout.write('PID file was not found. Process %s is likely terminated.\n' % process_name)
            return False

        p = psutil.Process(pid)
        return_code = p.wait(timeout=0.01)

        if return_code is None:
            # process is already terminated
            sys.stdout.write('Process %s is terminated \n' % process_name)
            return False
        else:
            # process is terminated; possibly by OS
            sys.stdout.write('Process %s got terminated \n' % process_name)
            return False
    except TimeoutExpired:
        sys.stdout.write('Process %s is alive and OK \n' % process_name)
        return True
<|endoftext|>


<commit_before>""" Filesystem utilities
"""
import contextlib
import os
import re
import shutil
from subprocess import check_call, CalledProcessError, Popen, PIPE

RUN_RE = '\d{6}_[a-zA-Z\d\-]+_\d{4}_[AB0][A-Z\d]'
PROJECT_RE = '[a-zA-Z]+\.[a-zA-Z]+_\d{2}_\d{2}'

@contextlib.contextmanager
def chdir(new_dir):
    """Context manager to temporarily change to a new directory.
    """
    cur_dir = os.getcwd()
    # This is weird behavior. I'm removing and and we'll see if anything breaks.
    #safe_makedir(new_dir)
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(cur_dir)

def create_folder(target_folder):
    """ Ensure that a folder exists and create it if it doesn't, including any
        parent folders, as necessary.

        :param target_folder: the target folder
        :returns: True if the folder exists or was created, False if the folder
        does not exists and could not be created
    """
    try:
        os.makedirs(target_folder)
    except OSError as e:
        pass
    return os.path.exists(target_folder)

def is_in_swestore(f):
    """ Checks if a file exists in Swestore

    :param f str: File to check
    :returns bool: True if the file is already in Swestore, False otherwise
    """
    with open(os.devnull, 'w') as null:
        try:
            check_call(['ils', f], stdout=null, stderr=null)
        except CalledProcessError:
            # ils will fail if the file does not exist in swestore
            return False
        else:
            return True

def list_runs_in_swestore(path, pattern=RUN_RE, no_ext=False):
    """
        Will list runs that exist in swestore

        :param str path: swestore path to list runs
        :param str pattern: regex pattern for runs
    """
    try:
        status = check_call(['icd', path])
        proc = Popen(['ils'], stdout=PIPE)
        contents = [c.strip() for c in proc.stdout.readlines()]
        runs = [r for r in contents if re.match(pattern, r)]
        if no_ext:
            runs = [r.split('.')[0] for r in runs]
        return runs
    except CalledProcessError:
        return []

def is_in_file(file_path, text):
    """ Looks for text appearing in a file.

    :param str file_path: Path to the source file
    :param str text: Text to find in the file
    :raises OSError: If the file does not exist
    :returns bool: True is text is in file, False otherwise
    """
    with open(file_path, 'r') as f:
        content = f.read()
    return text in content
<commit_msg>added function to clean fastq file names<commit_after>""" Filesystem utilities
"""
import contextlib
import os
import re
import shutil
from subprocess import check_call, CalledProcessError, Popen, PIPE

RUN_RE = '\d{6}_[a-zA-Z\d\-]+_\d{4}_[AB0][A-Z\d]'
PROJECT_RE = '[a-zA-Z]+\.[a-zA-Z]+_\d{2}_\d{2}'

@contextlib.contextmanager
def chdir(new_dir):
    """Context manager to temporarily change to a new directory.
    """
    cur_dir = os.getcwd()
    # This is weird behavior. I'm removing and and we'll see if anything breaks.
    #safe_makedir(new_dir)
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(cur_dir)

def create_folder(target_folder):
    """ Ensure that a folder exists and create it if it doesn't, including any
        parent folders, as necessary.

        :param target_folder: the target folder
        :returns: True if the folder exists or was created, False if the folder
        does not exists and could not be created
    """
    try:
        os.makedirs(target_folder)
    except OSError as e:
        pass
    return os.path.exists(target_folder)

def is_in_swestore(f):
    """ Checks if a file exists in Swestore

    :param f str: File to check
    :returns bool: True if the file is already in Swestore, False otherwise
    """
    with open(os.devnull, 'w') as null:
        try:
            check_call(['ils', f], stdout=null, stderr=null)
        except CalledProcessError:
            # ils will fail if the file does not exist in swestore
            return False
        else:
            return True

def list_runs_in_swestore(path, pattern=RUN_RE, no_ext=False):
    """
        Will list runs that exist in swestore

        :param str path: swestore path to list runs
        :param str pattern: regex pattern for runs
    """
    try:
        status = check_call(['icd', path])
        proc = Popen(['ils'], stdout=PIPE)
        contents = [c.strip() for c in proc.stdout.readlines()]
        runs = [r for r in contents if re.match(pattern, r)]
        if no_ext:
            runs = [r.split('.')[0] for r in runs]
        return runs
    except CalledProcessError:
        return []

def is_in_file(file_path, text):
    """ Looks for text appearing in a file.

    :param str file_path: Path to the source file
    :param str text: Text to find in the file
    :raises OSError: If the file does not exist
    :returns bool: True is text is in file, False otherwise
    """
    with open(file_path, 'r') as f:
        content = f.read()
    return text in content

def control_fastq_filename(demux_folder):
    """Looks for fastq files with a hyphen in the sample nsame
    and turns it in an underscore.

    :param str demux_folder: path to the demultiplexed folder
    """
    pattern=re.compile("^(P[0-9]+)-[0-9]{3,4}.+fastq.*$")
    with root, dirs, files in os.walk(demux_folder):
        for f in files:
            matches=pattern.search(f)
            if matches:
                new_name=f.replace("{}-{}".format(matches.group(1), matches.group(2)), "{}_{}".format(matches.group(1), matches.group(2)))
                os.move(os.path.join(root, f), os.path.join(root, new_name))

<|endoftext|>


<commit_before>"""
Django settings for task_project project.

For more information on this file, see
https://docs.djangoproject.com/en/1.7/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.7/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '52(evj!y98)!5!b6ps$lq3xjt0(tt^_^@7@aip9nfmi#@f^-#n'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'task',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'task_project.urls'

WSGI_APPLICATION = 'task_project.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/

LANGUAGE_CODE = 'pt-br'

TIME_ZONE = 'America/Sao_Paulo'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/

STATIC_URL = '/static/'

'''
TEMPLATE_DIRS = (
    os.path.join(BASE_DIR, 'templates_admin'),
)
'''
<commit_msg>LOGIN_URL<commit_after>import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '52(evj!y98)!5!b6ps$lq3xjt0(tt^_^@7@aip9nfmi#@f^-#n'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'task',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'task_project.urls'

WSGI_APPLICATION = 'task_project.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

LANGUAGE_CODE = 'pt-br'

TIME_ZONE = 'America/Sao_Paulo'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

LOGIN_URL = '/admin/login'

'''
TEMPLATE_DIRS = (
    os.path.join(BASE_DIR, 'templates_admin'),
)
'''
<|endoftext|>


<commit_before># Copyright (C) 2007 Samalyse SARL
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://svn.parisson.org/telemeta/TelemetaLicense.
#
# Author: Olivier Guilyardi <olivier@samalyse.com>

from django.db.models import Manager, Q
from django.db.models.query import QuerySet

class CoreQuerySet(QuerySet):
    "Base class for all query sets"

    def none(self): # redundant with none() in recent Django svn
        "Return an empty result set"
        return self.extra(where = ["0 = 1"])

class CoreManager(Manager):
    "Base class for all models managers"

    def none(self, *args, **kwargs):
        return self.get_query_set().none(*args, **kwargs)

class MediaCollectionQuerySet(CoreQuerySet):

    def quick_search(self, pattern):
        "Perform a quick search on id, title and creator name"
        return self.filter(
            Q(id__icontains=pattern) |
            Q(title__icontains=pattern) |
            Q(creator__icontains=pattern)
        )

    def by_country(self, country):
        "Find collections by country"
        return self.extra(where = ["id IN (SELECT collection_id "
            "FROM telemeta_item WHERE etat = %s)"],
            params=[country]);

    def by_continent(self, continent):
        "Find collections by continent"
        return self.extra(where = ["id IN (SELECT collection_id "
            "FROM telemeta_item WHERE continent = %s)"],
            params=[continent]);

    def by_recording_date(self, pattern):
        return self.filter(annee_enr__icontains=pattern)

    def by_publish_date(self, pattern):
        return self.filter(date_published__icontains=pattern)

    def by_ethnic_group(self, group):
        return self.filter(items__ethnie_grsocial=group).distinct()

class MediaCollectionManager(CoreManager):
    "Manage collection queries"

    def get_query_set(self):
        return MediaCollectionQuerySet(self.model)

    def quick_search(self, *args, **kwargs):
        return self.get_query_set().quick_search(*args, **kwargs)

    def by_country(self, *args, **kwargs):
        return self.get_query_set().by_country(*args, **kwargs)

    def by_continent(self, *args, **kwargs):
        return self.get_query_set().by_continent(*args, **kwargs)

    def by_recording_date(self, *args, **kwargs):
        return self.get_query_set().by_recording_date(*args, **kwargs)

    def by_publish_date(self, *args, **kwargs):
        return self.get_query_set().by_publish_date(*args, **kwargs)

    def by_ethnic_group(self, *args, **kwargs):
        return self.get_query_set().by_ethnic_group(*args, **kwargs)

    def stat_continents(self, order_by='num'):
        "Return the number of collections by continents and countries as a tree"
        from django.db import connection
        cursor = connection.cursor()
        if order_by == 'num':
            order_by = 'items_num DESC'
        else:
            order_by = 'etat'
        cursor.execute("SELECT continent, etat, count(*) AS items_num "
            "FROM telemeta_collection INNER JOIN telemeta_item "
            "ON telemeta_collection.id = telemeta_item.collection_id "
            "WHERE (continent IN "
            "  ('EUROPE', 'OCEANIE', 'ASIE', 'AMERIQUE', 'AFRIQUE')) "
            "AND etat <> '' "
            "GROUP BY etat ORDER BY continent, " + order_by)
        result_set = cursor.fetchall()
        stat = {}
        for continent, country, count in result_set:
            if stat.has_key(continent):
                stat[continent].append({'name':country, 'count':count})
            else:
                stat[continent] = [{'name':country, 'count':count}]

        keys = stat.keys()
        keys.sort()
        ordered = [{'name': k, 'countries': stat[k]} for k in keys]
        return ordered

    def list_countries(self):
        "Return a 2D list of all countries with continents"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT continent, etat FROM telemeta_item "
            "GROUP BY continent, etat ORDER BY REPLACE(etat, '\"', '')");
        return cursor.fetchall()

    def list_continents(self):
        "Return a list of all continents"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT DISTINCT(continent) FROM telemeta_item ORDER BY continent")
        result_set = cursor.fetchall()
        result = []
        for a, in result_set:
            if a != '' and a != 'N': # CREM fix
                result.append(a)

        return result

class MediaItemQuerySet(CoreQuerySet):

    def quick_search(self, pattern):
        "Perform a quick search on id and title"
        return self.filter(
            Q(id__icontains=pattern) |
            Q(_title__icontains=pattern) |
            Q(auteur__icontains=pattern)
        )

    def without_collection(self):
        "Find items which do not belong to any collection"
        return self.extra(
            where = ["collection_id NOT IN (SELECT id FROM telemeta_collection)"]);

    def by_recording_date(self, pattern):
        "Find items by recording date"
        return self.filter(Q(dates_enregistr__icontains=pattern)
            | Q(annee_enreg__icontains=pattern))

    def by_title(self, pattern):
        # to (sort of) sync with models.media.MediaItem.get_title()
        return self.filter(Q(_title__icontains=pattern)
          | Q(collection__title__icontains=pattern))

    def by_publish_date(self, pattern):
        return self.filter(collection__date_published__icontains=pattern)

class MediaItemManager(CoreManager):
    "Manage media items queries"

    def get_query_set(self):
        return MediaItemQuerySet(self.model)

    def quick_search(self, *args, **kwargs):
        return self.get_query_set().quick_search(*args, **kwargs)

    def without_collection(self, *args, **kwargs):
        return self.get_query_set().without_collection(*args, **kwargs)

    def by_recording_date(self, *args, **kwargs):
        return self.get_query_set().by_recording_date(*args, **kwargs)

    def by_title(self, *args, **kwargs):
        return self.get_query_set().by_title(*args, **kwargs)

    def by_publish_date(self, *args, **kwargs):
        return self.get_query_set().by_publish_date(*args, **kwargs)

    def list_ethnic_groups(self):
        "Return a list of all ethnic groups"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT DISTINCT(ethnie_grsocial) FROM telemeta_item "
            "ORDER BY REPLACE(ethnie_grsocial, '\\'', '')")
        result_set = cursor.fetchall()
        result = []
        for a, in result_set:
            if a != '/' and a != '': # CREM fix
                result.append(a)

        return result

<commit_msg>fix search combining country/continent and ethnic group<commit_after># Copyright (C) 2007 Samalyse SARL
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://svn.parisson.org/telemeta/TelemetaLicense.
#
# Author: Olivier Guilyardi <olivier@samalyse.com>

from django.db.models import Manager, Q
from django.db.models.query import QuerySet

class CoreQuerySet(QuerySet):
    "Base class for all query sets"

    def none(self): # redundant with none() in recent Django svn
        "Return an empty result set"
        return self.extra(where = ["0 = 1"])

class CoreManager(Manager):
    "Base class for all models managers"

    def none(self, *args, **kwargs):
        return self.get_query_set().none(*args, **kwargs)

class MediaCollectionQuerySet(CoreQuerySet):

    def quick_search(self, pattern):
        "Perform a quick search on id, title and creator name"
        return self.filter(
            Q(id__icontains=pattern) |
            Q(title__icontains=pattern) |
            Q(creator__icontains=pattern)
        )

    def by_country(self, country):
        "Find collections by country"
        return self.filter(items__etat=country).distinct()

    def by_continent(self, continent):
        "Find collections by continent"
        return self.filter(items__continent=continent).distinct()

    def by_recording_date(self, pattern):
        return self.filter(annee_enr__icontains=pattern)

    def by_publish_date(self, pattern):
        return self.filter(date_published__icontains=pattern)

    def by_ethnic_group(self, group):
        return self.filter(items__ethnie_grsocial=group).distinct()

class MediaCollectionManager(CoreManager):
    "Manage collection queries"

    def get_query_set(self):
        return MediaCollectionQuerySet(self.model)

    def quick_search(self, *args, **kwargs):
        return self.get_query_set().quick_search(*args, **kwargs)

    def by_country(self, *args, **kwargs):
        return self.get_query_set().by_country(*args, **kwargs)

    def by_continent(self, *args, **kwargs):
        return self.get_query_set().by_continent(*args, **kwargs)

    def by_recording_date(self, *args, **kwargs):
        return self.get_query_set().by_recording_date(*args, **kwargs)

    def by_publish_date(self, *args, **kwargs):
        return self.get_query_set().by_publish_date(*args, **kwargs)

    def by_ethnic_group(self, *args, **kwargs):
        return self.get_query_set().by_ethnic_group(*args, **kwargs)

    def stat_continents(self, order_by='num'):
        "Return the number of collections by continents and countries as a tree"
        from django.db import connection
        cursor = connection.cursor()
        if order_by == 'num':
            order_by = 'items_num DESC'
        else:
            order_by = 'etat'
        cursor.execute("SELECT continent, etat, count(*) AS items_num "
            "FROM telemeta_collection INNER JOIN telemeta_item "
            "ON telemeta_collection.id = telemeta_item.collection_id "
            "WHERE (continent IN "
            "  ('EUROPE', 'OCEANIE', 'ASIE', 'AMERIQUE', 'AFRIQUE')) "
            "AND etat <> '' "
            "GROUP BY etat ORDER BY continent, " + order_by)
        result_set = cursor.fetchall()
        stat = {}
        for continent, country, count in result_set:
            if stat.has_key(continent):
                stat[continent].append({'name':country, 'count':count})
            else:
                stat[continent] = [{'name':country, 'count':count}]

        keys = stat.keys()
        keys.sort()
        ordered = [{'name': k, 'countries': stat[k]} for k in keys]
        return ordered

    def list_countries(self):
        "Return a 2D list of all countries with continents"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT continent, etat FROM telemeta_item "
            "GROUP BY continent, etat ORDER BY REPLACE(etat, '\"', '')");
        return cursor.fetchall()

    def list_continents(self):
        "Return a list of all continents"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT DISTINCT(continent) FROM telemeta_item ORDER BY continent")
        result_set = cursor.fetchall()
        result = []
        for a, in result_set:
            if a != '' and a != 'N': # CREM fix
                result.append(a)

        return result

class MediaItemQuerySet(CoreQuerySet):

    def quick_search(self, pattern):
        "Perform a quick search on id and title"
        return self.filter(
            Q(id__icontains=pattern) |
            Q(_title__icontains=pattern) |
            Q(auteur__icontains=pattern)
        )

    def without_collection(self):
        "Find items which do not belong to any collection"
        return self.extra(
            where = ["collection_id NOT IN (SELECT id FROM telemeta_collection)"]);

    def by_recording_date(self, pattern):
        "Find items by recording date"
        return self.filter(Q(dates_enregistr__icontains=pattern)
            | Q(annee_enreg__icontains=pattern))

    def by_title(self, pattern):
        # to (sort of) sync with models.media.MediaItem.get_title()
        return self.filter(Q(_title__icontains=pattern)
          | Q(collection__title__icontains=pattern))

    def by_publish_date(self, pattern):
        return self.filter(collection__date_published__icontains=pattern)

class MediaItemManager(CoreManager):
    "Manage media items queries"

    def get_query_set(self):
        return MediaItemQuerySet(self.model)

    def quick_search(self, *args, **kwargs):
        return self.get_query_set().quick_search(*args, **kwargs)

    def without_collection(self, *args, **kwargs):
        return self.get_query_set().without_collection(*args, **kwargs)

    def by_recording_date(self, *args, **kwargs):
        return self.get_query_set().by_recording_date(*args, **kwargs)

    def by_title(self, *args, **kwargs):
        return self.get_query_set().by_title(*args, **kwargs)

    def by_publish_date(self, *args, **kwargs):
        return self.get_query_set().by_publish_date(*args, **kwargs)

    def list_ethnic_groups(self):
        "Return a list of all ethnic groups"

        from django.db import connection
        cursor = connection.cursor()

        cursor.execute("SELECT DISTINCT(ethnie_grsocial) FROM telemeta_item "
            "ORDER BY REPLACE(ethnie_grsocial, '\\'', '')")
        result_set = cursor.fetchall()
        result = []
        for a, in result_set:
            if a != '/' and a != '': # CREM fix
                result.append(a)

        return result

<|endoftext|>