<commit_before># specifically use concurrent.futures for threadsafety
# asyncio Futures cannot be used across threads
import json
import threading
import time
from concurrent.futures import Future
from functools import partial

from kubernetes import config
from kubernetes import watch
from traitlets import Any
from traitlets import Bool
from traitlets import Dict
from traitlets import Int
from traitlets import Unicode
from traitlets.config import LoggingConfigurable
from urllib3.exceptions import ReadTimeoutError

from .clients import shared_client

# This is kubernetes client implementation specific, but we need to know
# whether it was a network or watch timeout.

class ResourceReflector(LoggingConfigurable):
    """Base class for keeping a local up-to-date copy of a set of
    kubernetes resources.

    Must be subclassed once per kind of resource that needs watching.
    """

    labels = Dict(
        {},
        config=True,
        help="""
        Labels to reflect onto local cache
        """,
    )

    fields = Dict(
        {},
        config=True,
        help="""
        Fields to restrict the reflected objects
        """,
    )

    resources = Dict(
        {},
        help="""
        Dictionary of resource names to the appropriate resource objects.

        This can be accessed across threads safely.
        """,
    )

    kind = Unicode(
        'resource',
        help="""
        Human readable name for kind of object we're watching for.

        Used for diagnostic messages.
        """,
    )

    omit_namespace = Bool(
        False,
        config=True,
        help="""
        Set this to true if the reflector is to operate across
        multiple namespaces.
        """,
    )

    namespace = Unicode(
        None,
        allow_none=True,
        help="""
        Namespace to watch for resources in; leave at 'None' for
        multi-namespace reflectors.
        """,
    )

    list_method_name = Unicode(
        "",
        help="""
        Name of function (on apigroup respresented by
        `api_group_name`) that is to be called to list resources.

        This will be passed a a label selector.

        If self.omit_namespace is False you want something of the form
        list_namespaced_<resource> - for example,
        `list_namespaced_pod` will give you a PodReflector.  It will
        take its namespace from self.namespace (which therefore should
        not be None).

        If self.omit_namespace is True, you want
        list_<resource>_for_all_namespaces.

        This must be set by a subclass.

        It is not necessary to set it for pod or event reflectors, because
        __init__ will figure it out.  If you create your own reflector
        subclass you probably want to add the logic to choose the method
        name to that class's __init__().
        """,
    )

    api_group_name = Unicode(
        'CoreV1Api',
        help="""
        Name of class that represents the apigroup on which
        `list_method_name` is to be found.

        Defaults to CoreV1Api, which has everything in the 'core' API group. If you want to watch Ingresses,
        for example, you would have to use ExtensionsV1beta1Api
        """,
    )

    request_timeout = Int(
        60,
        config=True,
        help="""
        Network timeout for kubernetes watch.

        Trigger watch reconnect when a given request is taking too long,
        which can indicate network issues.
        """,
    )

    timeout_seconds = Int(
        10,
        config=True,
        help="""
        Timeout for kubernetes watch.

        Trigger watch reconnect when no watch event has been received.
        This will cause a full reload of the currently existing resources
        from the API server.
        """,
    )

    restart_seconds = Int(
        30,
        config=True,
        help="""
        Maximum time before restarting a watch.

        The watch will be restarted at least this often,
        even if events are still arriving.
        Avoids trusting kubernetes watch to yield all events,
        which seems to not be a safe assumption.
        """,
    )

    on_failure = Any(help="""Function to be called when the reflector gives up.""")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # client configuration for kubernetes has already taken place
        self.api = shared_client(self.api_group_name)

        # FIXME: Protect against malicious labels?
        self.label_selector = ','.join(
            ['{}={}'.format(k, v) for k, v in self.labels.items()]
        )
        self.field_selector = ','.join(
            ['{}={}'.format(k, v) for k, v in self.fields.items()]
        )

        self.first_load_future = Future()
        self._stop_event = threading.Event()

        # Make sure that we know kind, whether we should omit the
        #  namespace, and what our list_method_name is.  For the things
        #  we already know about, we can derive list_method_name from
        #  those two things.  New reflector types should also update
        #  their __init__() methods to derive list_method_name, but you
        #  could just set it directly in the subclass.
        if not self.list_method_name:
            # This logic can be extended if we add other reflector types or
            #  it can be directly supplied or overridden in a subclass.
            if self.kind in ["pods", "events", "services", "ingresses", "endpoints"]:
                singular_kind = self.kind[:-1]
                if self.kind == "ingresses":
                    singular_kind = self.kind[:-2]

                if self.omit_namespace:
                    self.list_method_name = f"list_{singular_kind}_for_all_namespaces"
                else:
                    self.list_method_name = f"list_namespaced_{singular_kind}"

        # Make sure we have the required values.
        if not self.kind:
            raise RuntimeError("Reflector kind must be set!")
        if not self.list_method_name:
            raise RuntimeError("Reflector list_method_name must be set!")

        self.start()

    def __del__(self):
        self.stop()

    def _list_and_update(self):
        """
        Update current list of resources by doing a full fetch.

        Overwrites all current resource info.
        """
        initial_resources = None
        kwargs = dict(
            label_selector=self.label_selector,
            field_selector=self.field_selector,
            _request_timeout=self.request_timeout,
            _preload_content=False,
        )
        if not self.omit_namespace:
            kwargs["namespace"] = self.namespace

        initial_resources = getattr(self.api, self.list_method_name)(**kwargs)
        # This is an atomic operation on the dictionary!
        initial_resources = json.loads(initial_resources.read())
        self.resources = {
            f'{p["metadata"]["namespace"]}/{p["metadata"]["name"]}': p
            for p in initial_resources["items"]
        }
        # return the resource version so we can hook up a watch
        return initial_resources["metadata"]["resourceVersion"]

    def _watch_and_update(self):
        """
        Keeps the current list of resources up-to-date

        This method is to be run not on the main thread!

        We first fetch the list of current resources, and store that. Then we
        register to be notified of changes to those resources, and keep our
        local store up-to-date based on these notifications.

        We also perform exponential backoff, giving up after we hit 32s
        wait time. This should protect against network connections dropping
        and intermittent unavailability of the api-server. Every time we
        recover from an exception we also do a full fetch, to pick up
        changes that might've been missed in the time we were not doing
        a watch.

        Note that we're playing a bit with fire here, by updating a dictionary
        in this thread while it is probably being read in another thread
        without using locks! However, dictionary access itself is atomic,
        and as long as we don't try to mutate them (do a 'fetch / modify /
        update' cycle on them), we should be ok!
        """
        selectors = []
        log_name = ""
        if self.label_selector:
            selectors.append("label selector=%r" % self.label_selector)
        if self.field_selector:
            selectors.append("field selector=%r" % self.field_selector)
        log_selector = ', '.join(selectors)

        cur_delay = 0.1

        if self.omit_namespace:
            ns_str = "all namespaces"
        else:
            ns_str = "namespace {}".format(self.namespace)

        self.log.info(
            "watching for %s with %s in %s",
            self.kind,
            log_selector,
            ns_str,
        )
        while True:
            self.log.debug("Connecting %s watcher", self.kind)
            start = time.monotonic()
            w = watch.Watch()
            try:
                resource_version = self._list_and_update()
                if not self.first_load_future.done():
                    # signal that we've loaded our initial data
                    self.first_load_future.set_result(None)
                watch_args = {
                    "label_selector": self.label_selector,
                    "field_selector": self.field_selector,
                    "resource_version": resource_version,
                }
                if not self.omit_namespace:
                    watch_args["namespace"] = self.namespace
                if self.request_timeout:
                    # set network receive timeout
                    watch_args['_request_timeout'] = self.request_timeout
                if self.timeout_seconds:
                    # set watch timeout
                    watch_args['timeout_seconds'] = self.timeout_seconds
                method = partial(
                    getattr(self.api, self.list_method_name), _preload_content=False
                )
                # in case of timeout_seconds, the w.stream just exits (no exception thrown)
                # -> we stop the watcher and start a new one
                for watch_event in w.stream(method, **watch_args):
                    # Remember that these events are k8s api related WatchEvents
                    # objects, not k8s Event or Pod representations, they will
                    # reside in the WatchEvent's object field depending on what
                    # kind of resource is watched.
                    #
                    # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#watchevent-v1-meta
                    # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#event-v1-core
                    cur_delay = 0.1
                    resource = watch_event['object']
                    ref_key = "{}/{}".format(
                        resource["metadata"]["namespace"], resource["metadata"]["name"]
                    )
                    if watch_event['type'] == 'DELETED':
                        # This is an atomic delete operation on the dictionary!
                        self.resources.pop(ref_key, None)
                    else:
                        # This is an atomic operation on the dictionary!
                        self.resources[ref_key] = resource
                    if self._stop_event.is_set():
                        self.log.info("%s watcher stopped", self.kind)
                        break
                    watch_duration = time.monotonic() - start
                    if watch_duration >= self.restart_seconds:
                        self.log.debug(
                            "Restarting %s watcher after %i seconds",
                            self.kind,
                            watch_duration,
                        )
                        break
            except ReadTimeoutError:
                # network read time out, just continue and restart the watch
                # this could be due to a network problem or just low activity
                self.log.warning("Read timeout watching %s, reconnecting", self.kind)
                continue
            except Exception:
                cur_delay = cur_delay * 2
                if cur_delay > 30:
                    self.log.exception("Watching resources never recovered, giving up")
                    if self.on_failure:
                        self.on_failure()
                    return
                self.log.exception(
                    "Error when watching resources, retrying in %ss", cur_delay
                )
                time.sleep(cur_delay)
                continue
            else:
                # no events on watch, reconnect
                self.log.debug("%s watcher timeout", self.kind)
            finally:
                w.stop()
                if self._stop_event.is_set():
                    self.log.info("%s watcher stopped", self.kind)
                    break
        self.log.warning("%s watcher finished", self.kind)

    def start(self):
        """
        Start the reflection process!

        We'll do a blocking read of all resources first, so that we don't
        race with any operations that are checking the state of the pod
        store - such as polls. This should be called only once at the
        start of program initialization (when the singleton is being created),
        and not afterwards!
        """
        if hasattr(self, 'watch_thread'):
            raise ValueError('Thread watching for resources is already running')

        self._list_and_update()
        self.watch_thread = threading.Thread(target=self._watch_and_update)
        # If the watch_thread is only thread left alive, exit app
        self.watch_thread.daemon = True
        self.watch_thread.start()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

class NamespacedResourceReflector(ResourceReflector):
    """
    Watches for resources in a particular namespace.  The list_methods
    want both a method name and a namespace.
    """

    omit_namespace = False

class MultiNamespaceResourceReflector(ResourceReflector):
    """
    Watches for resources across all namespaces.  The list_methods
    want only a method name.  Note that this requires the service account
    to be significantly more powerful, since it must be bound to ClusterRoles
    rather than just Roles, and therefore this is inherently more
    dangerous.
    """

    omit_namespace = True
<commit_msg>Update kubespawner/reflector.py<commit_after># specifically use concurrent.futures for threadsafety
# asyncio Futures cannot be used across threads
import json
import threading
import time
from concurrent.futures import Future
from functools import partial

from kubernetes import config
from kubernetes import watch
from traitlets import Any
from traitlets import Bool
from traitlets import Dict
from traitlets import Int
from traitlets import Unicode
from traitlets.config import LoggingConfigurable
from urllib3.exceptions import ReadTimeoutError

from .clients import shared_client

# This is kubernetes client implementation specific, but we need to know
# whether it was a network or watch timeout.

class ResourceReflector(LoggingConfigurable):
    """Base class for keeping a local up-to-date copy of a set of
    kubernetes resources.

    Must be subclassed once per kind of resource that needs watching.
    """

    labels = Dict(
        {},
        config=True,
        help="""
        Labels to reflect onto local cache
        """,
    )

    fields = Dict(
        {},
        config=True,
        help="""
        Fields to restrict the reflected objects
        """,
    )

    resources = Dict(
        {},
        help="""
        Dictionary of resource names to the appropriate resource objects.

        This can be accessed across threads safely.
        """,
    )

    kind = Unicode(
        'resource',
        help="""
        Human readable name for kind of object we're watching for.

        Used for diagnostic messages.
        """,
    )

    omit_namespace = Bool(
        False,
        config=True,
        help="""
        Set this to true if the reflector is to operate across
        multiple namespaces.
        """,
    )

    namespace = Unicode(
        None,
        allow_none=True,
        help="""
        Namespace to watch for resources in; leave at 'None' for
        multi-namespace reflectors.
        """,
    )

    list_method_name = Unicode(
        "",
        help="""
        Name of function (on apigroup respresented by
        `api_group_name`) that is to be called to list resources.

        This will be passed a a label selector.

        If self.omit_namespace is False you want something of the form
        list_namespaced_<resource> - for example,
        `list_namespaced_pod` will give you a PodReflector.  It will
        take its namespace from self.namespace (which therefore should
        not be None).

        If self.omit_namespace is True, you want
        list_<resource>_for_all_namespaces.

        This must be set by a subclass.

        It is not necessary to set it for pod or event reflectors, because
        __init__ will figure it out.  If you create your own reflector
        subclass you probably want to add the logic to choose the method
        name to that class's __init__().
        """,
    )

    api_group_name = Unicode(
        'CoreV1Api',
        help="""
        Name of class that represents the apigroup on which
        `list_method_name` is to be found.

        Defaults to CoreV1Api, which has everything in the 'core' API group. If you want to watch Ingresses,
        for example, you would have to use ExtensionsV1beta1Api
        """,
    )

    request_timeout = Int(
        60,
        config=True,
        help="""
        Network timeout for kubernetes watch.

        Trigger watch reconnect when a given request is taking too long,
        which can indicate network issues.
        """,
    )

    timeout_seconds = Int(
        10,
        config=True,
        help="""
        Timeout for kubernetes watch.

        Trigger watch reconnect when no watch event has been received.
        This will cause a full reload of the currently existing resources
        from the API server.
        """,
    )

    restart_seconds = Int(
        30,
        config=True,
        help="""
        Maximum time before restarting a watch.

        The watch will be restarted at least this often,
        even if events are still arriving.
        Avoids trusting kubernetes watch to yield all events,
        which seems to not be a safe assumption.
        """,
    )

    on_failure = Any(help="""Function to be called when the reflector gives up.""")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # client configuration for kubernetes has already taken place
        self.api = shared_client(self.api_group_name)

        # FIXME: Protect against malicious labels?
        self.label_selector = ','.join(
            ['{}={}'.format(k, v) for k, v in self.labels.items()]
        )
        self.field_selector = ','.join(
            ['{}={}'.format(k, v) for k, v in self.fields.items()]
        )

        self.first_load_future = Future()
        self._stop_event = threading.Event()

        # Make sure that we know kind, whether we should omit the
        #  namespace, and what our list_method_name is.  For the things
        #  we already know about, we can derive list_method_name from
        #  those two things.  New reflector types should also update
        #  their __init__() methods to derive list_method_name, but you
        #  could just set it directly in the subclass.
        if not self.list_method_name:
            # This logic can be extended if we add other reflector types or
            #  it can be directly supplied or overridden in a subclass.
            if self.kind in ["pods", "events", "services", "ingresses", "endpoints"]:
                if self.kind == "ingresses":
                    singular_kind = "ingress"
                else:
                    singular_kind = self.kind[:-1]

                if self.omit_namespace:
                    self.list_method_name = f"list_{singular_kind}_for_all_namespaces"
                else:
                    self.list_method_name = f"list_namespaced_{singular_kind}"

        # Make sure we have the required values.
        if not self.kind:
            raise RuntimeError("Reflector kind must be set!")
        if not self.list_method_name:
            raise RuntimeError("Reflector list_method_name must be set!")

        self.start()

    def __del__(self):
        self.stop()

    def _list_and_update(self):
        """
        Update current list of resources by doing a full fetch.

        Overwrites all current resource info.
        """
        initial_resources = None
        kwargs = dict(
            label_selector=self.label_selector,
            field_selector=self.field_selector,
            _request_timeout=self.request_timeout,
            _preload_content=False,
        )
        if not self.omit_namespace:
            kwargs["namespace"] = self.namespace

        initial_resources = getattr(self.api, self.list_method_name)(**kwargs)
        # This is an atomic operation on the dictionary!
        initial_resources = json.loads(initial_resources.read())
        self.resources = {
            f'{p["metadata"]["namespace"]}/{p["metadata"]["name"]}': p
            for p in initial_resources["items"]
        }
        # return the resource version so we can hook up a watch
        return initial_resources["metadata"]["resourceVersion"]

    def _watch_and_update(self):
        """
        Keeps the current list of resources up-to-date

        This method is to be run not on the main thread!

        We first fetch the list of current resources, and store that. Then we
        register to be notified of changes to those resources, and keep our
        local store up-to-date based on these notifications.

        We also perform exponential backoff, giving up after we hit 32s
        wait time. This should protect against network connections dropping
        and intermittent unavailability of the api-server. Every time we
        recover from an exception we also do a full fetch, to pick up
        changes that might've been missed in the time we were not doing
        a watch.

        Note that we're playing a bit with fire here, by updating a dictionary
        in this thread while it is probably being read in another thread
        without using locks! However, dictionary access itself is atomic,
        and as long as we don't try to mutate them (do a 'fetch / modify /
        update' cycle on them), we should be ok!
        """
        selectors = []
        log_name = ""
        if self.label_selector:
            selectors.append("label selector=%r" % self.label_selector)
        if self.field_selector:
            selectors.append("field selector=%r" % self.field_selector)
        log_selector = ', '.join(selectors)

        cur_delay = 0.1

        if self.omit_namespace:
            ns_str = "all namespaces"
        else:
            ns_str = "namespace {}".format(self.namespace)

        self.log.info(
            "watching for %s with %s in %s",
            self.kind,
            log_selector,
            ns_str,
        )
        while True:
            self.log.debug("Connecting %s watcher", self.kind)
            start = time.monotonic()
            w = watch.Watch()
            try:
                resource_version = self._list_and_update()
                if not self.first_load_future.done():
                    # signal that we've loaded our initial data
                    self.first_load_future.set_result(None)
                watch_args = {
                    "label_selector": self.label_selector,
                    "field_selector": self.field_selector,
                    "resource_version": resource_version,
                }
                if not self.omit_namespace:
                    watch_args["namespace"] = self.namespace
                if self.request_timeout:
                    # set network receive timeout
                    watch_args['_request_timeout'] = self.request_timeout
                if self.timeout_seconds:
                    # set watch timeout
                    watch_args['timeout_seconds'] = self.timeout_seconds
                method = partial(
                    getattr(self.api, self.list_method_name), _preload_content=False
                )
                # in case of timeout_seconds, the w.stream just exits (no exception thrown)
                # -> we stop the watcher and start a new one
                for watch_event in w.stream(method, **watch_args):
                    # Remember that these events are k8s api related WatchEvents
                    # objects, not k8s Event or Pod representations, they will
                    # reside in the WatchEvent's object field depending on what
                    # kind of resource is watched.
                    #
                    # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#watchevent-v1-meta
                    # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#event-v1-core
                    cur_delay = 0.1
                    resource = watch_event['object']
                    ref_key = "{}/{}".format(
                        resource["metadata"]["namespace"], resource["metadata"]["name"]
                    )
                    if watch_event['type'] == 'DELETED':
                        # This is an atomic delete operation on the dictionary!
                        self.resources.pop(ref_key, None)
                    else:
                        # This is an atomic operation on the dictionary!
                        self.resources[ref_key] = resource
                    if self._stop_event.is_set():
                        self.log.info("%s watcher stopped", self.kind)
                        break
                    watch_duration = time.monotonic() - start
                    if watch_duration >= self.restart_seconds:
                        self.log.debug(
                            "Restarting %s watcher after %i seconds",
                            self.kind,
                            watch_duration,
                        )
                        break
            except ReadTimeoutError:
                # network read time out, just continue and restart the watch
                # this could be due to a network problem or just low activity
                self.log.warning("Read timeout watching %s, reconnecting", self.kind)
                continue
            except Exception:
                cur_delay = cur_delay * 2
                if cur_delay > 30:
                    self.log.exception("Watching resources never recovered, giving up")
                    if self.on_failure:
                        self.on_failure()
                    return
                self.log.exception(
                    "Error when watching resources, retrying in %ss", cur_delay
                )
                time.sleep(cur_delay)
                continue
            else:
                # no events on watch, reconnect
                self.log.debug("%s watcher timeout", self.kind)
            finally:
                w.stop()
                if self._stop_event.is_set():
                    self.log.info("%s watcher stopped", self.kind)
                    break
        self.log.warning("%s watcher finished", self.kind)

    def start(self):
        """
        Start the reflection process!

        We'll do a blocking read of all resources first, so that we don't
        race with any operations that are checking the state of the pod
        store - such as polls. This should be called only once at the
        start of program initialization (when the singleton is being created),
        and not afterwards!
        """
        if hasattr(self, 'watch_thread'):
            raise ValueError('Thread watching for resources is already running')

        self._list_and_update()
        self.watch_thread = threading.Thread(target=self._watch_and_update)
        # If the watch_thread is only thread left alive, exit app
        self.watch_thread.daemon = True
        self.watch_thread.start()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

class NamespacedResourceReflector(ResourceReflector):
    """
    Watches for resources in a particular namespace.  The list_methods
    want both a method name and a namespace.
    """

    omit_namespace = False

class MultiNamespaceResourceReflector(ResourceReflector):
    """
    Watches for resources across all namespaces.  The list_methods
    want only a method name.  Note that this requires the service account
    to be significantly more powerful, since it must be bound to ClusterRoles
    rather than just Roles, and therefore this is inherently more
    dangerous.
    """

    omit_namespace = True
<|endoftext|>


<commit_before>import numpy as np

from sisl import geom, Atom, Cuboid

__all__ = ['bilayer']

def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB',
            twist=(0, 0), separation=3.35, ret_angle=False, layer='both'):
    r""" Commensurate unit cell of a hexagonal bilayer structure, possibly with a twist angle.

    This routine follows the prescription of twisted bilayer graphene found in [1]_.

    Notes
    -----
    This routine may change in the future to force some of the arguments.

    Parameters
    ----------
    bond : float, optional
       bond length between atoms in the honeycomb lattice
    bottom_atom : Atom, optional
       atom (or atoms) in the bottom layer. Defaults to ``Atom(6)``
    top_atom : Atom, optional
       atom (or atoms) in the top layer, defaults to `bottom_atom`
    stacking : {'AB', 'AA'}
       stacking sequence of the bilayer
    twist : tuple of int, optional
       integer coordinates (m, n) defining a commensurate twist angle
    separation : float, optional
       distance between the two layers (in Angstrom)
    ret_angle : bool, optional
       return the twist angle (in degrees) in addition to the geometry instance
    layer : {'both', 'bottom', 'top'}
       control which layer(s) to return

    References
    ----------
    .. [1] G. Trambly de Laissardiere, D. Mayou, L. Magaud, "Localization of Dirac Electrons in Rotated Graphene Bilayers", Nano Letts. 10, 804-808 (2010)
    """
    if bottom_atom is None:
        bottom_atom = top_atom
    if bottom_atom is None:
        bottom_atom = Atom(Z=6, R=bond * 1.01)
    if top_atom is None:
        top_atom = bottom_atom

    # Construct two layers
    bottom = geom.honeycomb(bond, bottom_atom)
    top = geom.honeycomb(bond, top_atom)
    ref_cell = bottom.cell.copy()

    if stacking.lower() == 'aa':
        top = top.move([0, 0, separation])
    elif stacking.lower() == 'ab':
        top = top.move([bond, 0, separation])

    # Compute twist angle
    m, n = twist
    m, n = abs(m), abs(n)
    if m > n:
        # Set m as the smaller integer
        m, n = n, m

    if not (isinstance(n, int) and isinstance(m, int)):
        raise ValueError("bilayer: twist coordinates need to be integers!")

    if m == n:
        # No twisting
        theta = 0
        rep = 1
        natoms = 2
    else:
        # Twisting
        cos_theta = (n ** 2 + 4 * n * m + m ** 2) / (2 * (n ** 2 + n * m + m ** 2))
        theta = np.arccos(cos_theta) * 180 / np.pi
        rep = 4 * (n + m)
        natoms = 2 * (n ** 2 + n * m + m ** 2)

    if rep > 1:
        # Set origo through an A atom near the middle of the geometry
        bottom = bottom.tile(rep, axis=0).tile(rep, axis=1)
        top = top.tile(rep, axis=0).tile(rep, axis=1)
        tvec = rep * (ref_cell[0] + ref_cell[1]) / 2
        bottom = bottom.move(-tvec)
        top = top.move(-tvec)

        # Set new lattice vectors
        bottom.cell[0] = n * ref_cell[0] + m * ref_cell[1]
        bottom.cell[1] = -m * ref_cell[0] + (n + m) * ref_cell[1]

        # Rotate top layer around A atom in bottom layer
        top = top.rotate(theta, [0, 0, 1])

        top.cell[:] = bottom.cell[:]

    # Which layers to be returned
    if layer.lower() == 'bottom':
        bilayer = bottom
    elif layer.lower() == 'top':
        bilayer = top
    else:
        bilayer = bottom.add(top)
        natoms *= 2

    if rep > 1:
        # Remove atoms outside cell
        cell_box = Cuboid(bilayer.cell)
        cell_box.set_center([-0.0001] * 3)
        inside_idx = cell_box.within_index(bilayer.xyz)
        bilayer = bilayer.sub(inside_idx)

        # Rotate whole cell
        vec = bilayer.cell[0] + bilayer.cell[1]
        vec_costh = vec[0] / vec.dot(vec) ** 0.5
        vec_th = np.arccos(vec_costh) * 180 / np.pi
        bilayer = bilayer.rotate(vec_th, [0, 0, 1])

    # Sanity check
    assert len(bilayer) == natoms

    if ret_angle:
        return bilayer, theta
    else:
        return bilayer
<commit_msg>enh: include possibility of BA stacking (in addition to AB and AA)<commit_after>import numpy as np

from sisl import geom, Atom, Cuboid

__all__ = ['bilayer']

def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB',
            twist=(0, 0), separation=3.35, ret_angle=False, layer='both'):
    r""" Commensurate unit cell of a hexagonal bilayer structure, possibly with a twist angle.

    This routine follows the prescription of twisted bilayer graphene found in [1]_.

    Notes
    -----
    This routine may change in the future to force some of the arguments.

    Parameters
    ----------
    bond : float, optional
       bond length between atoms in the honeycomb lattice
    bottom_atom : Atom, optional
       atom (or atoms) in the bottom layer. Defaults to ``Atom(6)``
    top_atom : Atom, optional
       atom (or atoms) in the top layer, defaults to `bottom_atom`
    stacking : {'AB', 'AA', 'BA'}
       stacking sequence of the bilayer, where XY means that site X in bottom layer coincides with site Y in top layer
    twist : tuple of int, optional
       integer coordinates (m, n) defining a commensurate twist angle
    separation : float, optional
       distance between the two layers (in Angstrom)
    ret_angle : bool, optional
       return the twist angle (in degrees) in addition to the geometry instance
    layer : {'both', 'bottom', 'top'}
       control which layer(s) to return

    References
    ----------
    .. [1] G. Trambly de Laissardiere, D. Mayou, L. Magaud, "Localization of Dirac Electrons in Rotated Graphene Bilayers", Nano Letts. 10, 804-808 (2010)
    """
    if bottom_atom is None:
        bottom_atom = top_atom
    if bottom_atom is None:
        bottom_atom = Atom(Z=6, R=bond * 1.01)
    if top_atom is None:
        top_atom = bottom_atom

    # Construct two layers
    bottom = geom.honeycomb(bond, bottom_atom)
    top = geom.honeycomb(bond, top_atom)
    ref_cell = bottom.cell.copy()

    if stacking.lower() == 'aa':
        top = top.move([0, 0, separation])
    elif stacking.lower() == 'ab':
        top = top.move([bond, 0, separation])
    elif stacking.lower() == 'ba':
        top = top.move([-bond, 0, separation])

    # Compute twist angle
    m, n = twist
    m, n = abs(m), abs(n)
    if m > n:
        # Set m as the smaller integer
        m, n = n, m

    if not (isinstance(n, int) and isinstance(m, int)):
        raise ValueError("bilayer: twist coordinates need to be integers!")

    if m == n:
        # No twisting
        theta = 0
        rep = 1
        natoms = 2
    else:
        # Twisting
        cos_theta = (n ** 2 + 4 * n * m + m ** 2) / (2 * (n ** 2 + n * m + m ** 2))
        theta = np.arccos(cos_theta) * 180 / np.pi
        rep = 4 * (n + m)
        natoms = 2 * (n ** 2 + n * m + m ** 2)

    if rep > 1:
        # Set origo through an A atom near the middle of the geometry
        bottom = bottom.tile(rep, axis=0).tile(rep, axis=1)
        top = top.tile(rep, axis=0).tile(rep, axis=1)
        tvec = rep * (ref_cell[0] + ref_cell[1]) / 2
        bottom = bottom.move(-tvec)
        top = top.move(-tvec)

        # Set new lattice vectors
        bottom.cell[0] = n * ref_cell[0] + m * ref_cell[1]
        bottom.cell[1] = -m * ref_cell[0] + (n + m) * ref_cell[1]

        # Rotate top layer around A atom in bottom layer
        top = top.rotate(theta, [0, 0, 1])

        top.cell[:] = bottom.cell[:]

    # Which layers to be returned
    if layer.lower() == 'bottom':
        bilayer = bottom
    elif layer.lower() == 'top':
        bilayer = top
    else:
        bilayer = bottom.add(top)
        natoms *= 2

    if rep > 1:
        # Remove atoms outside cell
        cell_box = Cuboid(bilayer.cell)
        cell_box.set_center([-0.0001] * 3)
        inside_idx = cell_box.within_index(bilayer.xyz)
        bilayer = bilayer.sub(inside_idx)

        # Rotate whole cell
        vec = bilayer.cell[0] + bilayer.cell[1]
        vec_costh = vec[0] / vec.dot(vec) ** 0.5
        vec_th = np.arccos(vec_costh) * 180 / np.pi
        bilayer = bilayer.rotate(vec_th, [0, 0, 1])

    # Sanity check
    assert len(bilayer) == natoms

    if ret_angle:
        return bilayer, theta
    else:
        return bilayer
<|endoftext|>


<commit_before>#!/usr/bin/env python
import sys

USAGE="""
Usage: %s command [args]

Initialize or modify igor database.
Note: use only on host where igorServer is hosted, and not when it is running.

help - this message
initialize - create empty igor database
list - show all installed plugins
add pathname - add plugin (copy) from given pathname
addstd name - add standard plugin (linked) with given name
remove name - remove plugin
liststd - list all available standard plugins
runatboot - make igorServer run at system boot (Linux or OSX, requires sudo permission)
runatlogin - make igorServer run at user login (OSX only)
"""
def main():
    if len(sys.argv) < 2 or sys.argv[1] in ('help', '--help'):
        print >>sys.stderr, USAGE % sys.argv[0]
        sys.exit(1)
    if sys.argv[1] == 'initialize':
        assert 0
    elif sys.argv[1] == 'list':
        assert 0
    elif sys.argv[1] == 'add':
        assert 0
    elif sys.argv[1] == 'addstd':
        assert 0
    elif sys.argv[1] == 'remove':
        assert 0
    elif sys.argv[1] == 'liststd':
        assert 0
    elif sys.argv[1] == 'runatboot':
        assert 0
    elif sys.argv[1] == 'runatlogin':
        assert 0
    else:
        print >>sys.stderr, '%s: unknown command: %s. Use --help for help.' % sys.argv[0]
        sys.exit(1)
    sys.exit(0)

if __name__ == '__main__':
    main()
<commit_msg>Initialize implemented<commit_after>#!/usr/bin/env python
import sys
import igor
import os
import os.path
import shutil

USAGE="""
Usage: %s command [args]

Initialize or modify igor database.
Note: use only on host where igorServer is hosted, and not when it is running.

help - this message
initialize - create empty igor database
list - show all installed plugins
add pathname - add plugin (copy) from given pathname
addstd name - add standard plugin (linked) with given name
remove name - remove plugin
liststd - list all available standard plugins
runatboot - make igorServer run at system boot (Linux or OSX, requires sudo permission)
runatlogin - make igorServer run at user login (OSX only)
"""
def main():
    if len(sys.argv) < 2 or sys.argv[1] in ('help', '--help'):
        print >>sys.stderr, USAGE % sys.argv[0]
        sys.exit(1)
    if sys.argv[1] == 'initialize':
        igorDir = os.path.dirname(igor.__file__)
        src = os.path.join(igorDir, 'igorDatabase.empty')
        dst = os.path.join(os.path.expanduser('~'), '.igor')
        if os.path.exists(dst):
            print >>sys.stderr, '%s: %s already exists!' % (sys.argv[0], dst)
            sys.exit(1)
        shutil.copytree(src, dst)
    elif sys.argv[1] == 'list':
        assert 0
    elif sys.argv[1] == 'add':
        assert 0
    elif sys.argv[1] == 'addstd':
        assert 0
    elif sys.argv[1] == 'remove':
        assert 0
    elif sys.argv[1] == 'liststd':
        assert 0
    elif sys.argv[1] == 'runatboot':
        assert 0
    elif sys.argv[1] == 'runatlogin':
        assert 0
    else:
        print >>sys.stderr, '%s: unknown command: %s. Use --help for help.' % sys.argv[0]
        sys.exit(1)
    sys.exit(0)

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

import re
import rapidsms
from rapidsms.parsers import Matcher
from models import *

# this is a temporary hack for the nigeria
# project, to allow users to ask something
# along the lines of LLIN MY STATUS as an
# alternative to "who am i", with as much
# built-in flexibility as we can manage.
#
# it matches:
#  llin status blahh
#  llin my status
#  lin my status
#  lin status
#  my status
#  status
#  stat
#
LLIN_MY_STATUS = "(?:ll?in )?(?:my )?stat(?:us)?(?:.*)"

class App(rapidsms.app.App):
    MSG = {
        "en": {
            "bad-alias":   "Sorry, I don't know anyone by that name.",
            "first-login": "Hello, %(name)s! This is the first time I've met you.",
            "login":       "Hello, %(name)s! It has been %(days)d days since I last heard from you.",
            "reminder":    "I think you are %(name)s.",
            "dont-know":   "Please register your phone with RapidSMS.",
            "list":        "I have %(num)d %(noun)s: %(items)s",
            "empty-list":  "I don't have any %(noun)s.",
            "lang-set":    "I will now speak to you in English, where possible.",
            "denied":      "Sorry, you must identify yourself before you can do that." },

        # worst german translations _ever_
        # just an example. all of this stuff
        # should be moved to an i18n app!
        "de": {
            "bad-alias":   "Tut mir leit, ich weiss nicht diesen Namen",
            "first-login": "%(name)s hallo! Ich habe nicht gesehen, bevor Sie",
            "login":       "%(name)s hallo! Ich habe nicht gesehen, Sie sich fur %(days)d Tag",
            "reminder":    "Sie sind %(name)s.",
            "lang-set":    "Sie sind Deutsche." }}

    HELP = [
        ("identify", "To identify yourself to RapidSMS, reply: IDENTIFY <alias>")
    ]

    def __str(self, key, reporter=None, lang=None):

        # if no language was explicitly requested,
        # inherit it from the reporter, or fall
        # back to english. because everyone in the
        # world speaks english... right?
        if lang is None:
            if reporter is not None:
                lang = reporter.language

            # fall back
            if lang is None:
                lang = "en"

        # look for an exact match, in the language
        # that the reporter has chosen as preferred
        if lang is not None:
            if lang in self.MSG:
                if key in self.MSG[lang]:
                    return self.MSG[lang][key]

        # not found in localized language. try again in english
        # TODO: allow the default to be set in rapidsms.ini
        return self.__str(key, lang="en") if lang != "en" else None

    def __deny(self, msg):
        """Responds to an incoming message with a localizable
           error message to instruct the caller to identify."""
        return msg.respond(self.__str("denied", msg.reporter))

    def start(self):

        # fetch a list of all the backends
        # that we already have objects for
        known_backends = PersistantBackend.objects.values_list("slug", flat=True)

        # find any running backends which currently
        # don't have objects, and fill in the gaps
        for be in self.router.backends:
            if not be.slug in known_backends:
                self.info("Creating PersistantBackend object for %s" % be)
                PersistantBackend(slug=be.slug, title=be.title).save()

    def parse(self, msg):

        # fetch the persistantconnection object
        # for this message's sender (or create
        # one if this is the first time we've
        # seen the sender), and stuff the meta-
        # dta into the message for other apps
        conn = PersistantConnection.from_message(msg)
        msg.persistant_connection = conn
        msg.reporter = conn.reporter

        # store a handy dictionary, containing the most useful persistance
        # information that we have. this is useful when creating an object
        # linked to _something_, like so:
        #
        #   class SomeObject(models.Model):
        #     reporter   = models.ForeignKey(Reporter, null=True)
        #     connection = models.ForeignKey(PersistantConnection, null=True)
        #     stuff      = models.CharField()
        #
        #   # this object will be linked to a reporter,
        #   # if one exists - otherwise, a connection
        #   SomeObject(stuff="hello", **msg.persistance_dict)
        if msg.reporter: msg.persistance_dict = { "reporter": msg.reporter }
        else:            msg.persistance_dict = { "connection": msg.persistant_connection }

        # log, whether we know who the sender is or not
        if msg.reporter: self.info("Identified: %s as %r" % (conn, msg.reporter))
        else:            self.info("Unidentified: %s" % (conn))

        # update last_seen, which automatically
        # populates the same property
        conn.seen()

    def handle(self, msg):
        matcher = Matcher(msg)

        # TODO: this is sort of a lightweight implementation
        # of the keyworder. it wasn't supposed to be. maybe
        # replace it *with* the keyworder, or extract it
        # into a parser of its own
        map = {
            "identify":  ["identify (slug)", "this is (slug)", "i am (slug)"],
            "remind":    ["whoami", "who am i", LLIN_MY_STATUS],
            "reporters": ["list reporters", "reporters\\?"],
            "lang":      ["lang (slug)"]
        }

        # search the map for a match, dispatch
        # the message to it, and return/stop
        for method, patterns in map.items():
            if matcher(*patterns) and hasattr(self, method):
                getattr(self, method)(msg, *matcher.groups)
                return True

        # no matches, so this message is not
        # for us; allow processing to continue
        return False

    def identify(self, msg, alias):
        try:

            # give me reporter.
            # if no alias will match,
            # exception must raise
            rep = Reporter.objects.get(alias=alias)

        # no such alias, but we can be pretty sure that the message
        # was for us, since it matched a pretty specific pattern
        # TODO: levenshtein spell-checking from rapidsms/ethiopia
        except Reporter.DoesNotExist:
            msg.respond(self.__str("bad-alias"))
            return True

        # assign the reporter to this message's connection
        # (it may currently be assigned to someone else)
        msg.persistant_connection.reporter = rep
        msg.persistant_connection.save()
        msg.reporter = rep

        # send a welcome message back to the now-registered reporter,
        # depending on how long it's been since their last visit
        ls = rep.last_seen()
        if ls is not None:
            msg.respond(
                self.__str("login", rep) % {
                    "name": unicode(rep),
                    "days": (datetime.now() - ls).days })

        # or a slightly different welcome message
        else:
            msg.respond(
                self.__str("first-login", rep) % {
                    "name": unicode(rep) })

        # re-call this app's prepare, so other apps can
        # get hold of the reporter's info right away
        self.parse(msg)

    def remind(self, msg):

        # if a reporter object was attached to the
        # message by self.parse, respond with a reminder
        if msg.reporter is not None:
            msg.respond(
                self.__str("reminder", msg.reporter) % {
                    "name": unicode(msg.reporter) })

        # if not, we have no idea
        # who the message was from
        else:
            msg.respond(self.__str(
                "dont-know",
                msg.reporter))

    def reporters(self, msg):
        if msg.reporter is not None:

            # collate all reporters, with their full name,
            # username, and current connection. TODO: this
            # sucks, don't use __unicode__ and __repr__!
            items = [
                "%r (%s)" % (rep, rep.connection())
                for rep in Reporter.objects.all()
                if rep.connection()]

            if items:
                msg.respond(
                    self.__str("list", msg.reporter) % {
                        "items": ", ".join(items),
                        "noun":  "reporters",
                        "num":    len(items), })

            else:
                # there are no reportes to list!
                msg.respond(
                    self.__str("empty-list", msg.reporter) % {
                        "noun": "reporters" })

        # not identified yet; reject, so
        # we don't allow random people to
        # query our reporters list
        else:
            msg.respond(
                self.__str(
                    "denied", msg.reporter))

    def lang(self, msg, code):

        # reqiure identification to continue
        # TODO: make this check a decorator, so other apps
        #  can easily indicate that methods need a valid login
        if msg.reporter is not None:

            # if the language code was valid, save it
            # TODO: obviously, this is not cross-app
            if code in self.MSG:
                msg.reporter.language = code
                msg.reporter.save()
                resp = "lang-set"

            # invalid language code. don't do
            # anything, just send an error message
            else: resp = "bad-lang"

        # if the caller isn't logged in, send
        # an error message, and halt processing
        else: resp = "denied"

        # always send *some*
        # kind of response
        msg.respond(
            self.__str(
                resp, msg.reporter))
<commit_msg>added REGISTER handler to reporters app<commit_after>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

import re
import rapidsms
from rapidsms.parsers import Matcher
from models import *

class App(rapidsms.app.App):
    MSG = {
        "en": {
            "bad-alias":   "Sorry, I don't know anyone by that name.",
            "first-login": "Hello, %(name)s! This is the first time I've met you.",
            "login":       "Hello, %(name)s! It has been %(days)d days since I last heard from you.",
            "reminder":    "I think you are %(name)s.",
            "dont-know":   "Please register your phone with RapidSMS.",
            "list":        "I have %(num)d %(noun)s: %(items)s",
            "empty-list":  "I don't have any %(noun)s.",
            "lang-set":    "I will now speak to you in English, where possible.",
            "denied":      "Sorry, you must identify yourself before you can do that." },

        # worst german translations _ever_
        # just an example. all of this stuff
        # should be moved to an i18n app!
        "de": {
            "bad-alias":   "Tut mir leit, ich weiss nicht diesen Namen",
            "first-login": "%(name)s hallo! Ich habe nicht gesehen, bevor Sie",
            "login":       "%(name)s hallo! Ich habe nicht gesehen, Sie sich fur %(days)d Tag",
            "reminder":    "Sie sind %(name)s.",
            "lang-set":    "Sie sind Deutsche." }}

    HELP = [
        ("identify", "To identify yourself to RapidSMS, reply: IDENTIFY <alias>")
    ]

    def __str(self, key, reporter=None, lang=None):

        # if no language was explicitly requested,
        # inherit it from the reporter, or fall
        # back to english. because everyone in the
        # world speaks english... right?
        if lang is None:
            if reporter is not None:
                lang = reporter.language

            # fall back
            if lang is None:
                lang = "en"

        # look for an exact match, in the language
        # that the reporter has chosen as preferred
        if lang is not None:
            if lang in self.MSG:
                if key in self.MSG[lang]:
                    return self.MSG[lang][key]

        # not found in localized language. try again in english
        # TODO: allow the default to be set in rapidsms.ini
        return self.__str(key, lang="en") if lang != "en" else None

    def __deny(self, msg):
        """Responds to an incoming message with a localizable
           error message to instruct the caller to identify."""
        return msg.respond(self.__str("denied", msg.reporter))

    def start(self):

        # fetch a list of all the backends
        # that we already have objects for
        known_backends = PersistantBackend.objects.values_list("slug", flat=True)

        # find any running backends which currently
        # don't have objects, and fill in the gaps
        for be in self.router.backends:
            if not be.slug in known_backends:
                self.info("Creating PersistantBackend object for %s" % be)
                PersistantBackend(slug=be.slug, title=be.title).save()

    def parse(self, msg):

        # fetch the persistantconnection object
        # for this message's sender (or create
        # one if this is the first time we've
        # seen the sender), and stuff the meta-
        # dta into the message for other apps
        conn = PersistantConnection.from_message(msg)
        msg.persistant_connection = conn
        msg.reporter = conn.reporter

        # store a handy dictionary, containing the most useful persistance
        # information that we have. this is useful when creating an object
        # linked to _something_, like so:
        #
        #   class SomeObject(models.Model):
        #     reporter   = models.ForeignKey(Reporter, null=True)
        #     connection = models.ForeignKey(PersistantConnection, null=True)
        #     stuff      = models.CharField()
        #
        #   # this object will be linked to a reporter,
        #   # if one exists - otherwise, a connection
        #   SomeObject(stuff="hello", **msg.persistance_dict)
        if msg.reporter: msg.persistance_dict = { "reporter": msg.reporter }
        else:            msg.persistance_dict = { "connection": msg.persistant_connection }

        # log, whether we know who the sender is or not
        if msg.reporter: self.info("Identified: %s as %r" % (conn, msg.reporter))
        else:            self.info("Unidentified: %s" % (conn))

        # update last_seen, which automatically
        # populates the same property
        conn.seen()

    def handle(self, msg):
        matcher = Matcher(msg)

        # TODO: this is sort of a lightweight implementation
        # of the keyworder. it wasn't supposed to be. maybe
        # replace it *with* the keyworder, or extract it
        # into a parser of its own
        map = {
            "register":  ["register (whatever)"],
            "identify":  ["identify (slug)", "this is (slug)", "i am (slug)"],
            "remind":    ["whoami", "who am i"]
            "reporters": ["list reporters", "reporters\\?"],
            "lang":      ["lang (slug)"]
        }

        # search the map for a match, dispatch
        # the message to it, and return/stop
        for method, patterns in map.items():
            if matcher(*patterns) and hasattr(self, method):
                getattr(self, method)(msg, *matcher.groups)
                return True

        # no matches, so this message is not
        # for us; allow processing to continue
        return False

    def register(self, msg, name):
        try:
            # parse the name, and create a reporter
            alias, fn, ln = Reporter.parse_name(name)
            rep = Reporter(alias=alias, first_name=fn, last_name=ln)
            rep.save()

            # attach the reporter to the current connection
            msg.persistant_connection.reporter = rep
            msg.persistant_connection.save()

            msg.respond(
                self.__str("first-login", rep) % {
                 "name": rep.full_name() })

        # something went wrong - at the
        # moment, we don't care what
        except:
            msg.respond("Sorry, I couldn't register you.")

    def identify(self, msg, alias):
        try:

            # give me reporter.
            # if no alias will match,
            # exception must raise
            rep = Reporter.objects.get(alias=alias)

        # no such alias, but we can be pretty sure that the message
        # was for us, since it matched a pretty specific pattern
        # TODO: levenshtein spell-checking from rapidsms/ethiopia
        except Reporter.DoesNotExist:
            msg.respond(self.__str("bad-alias"))
            return True

        # assign the reporter to this message's connection
        # (it may currently be assigned to someone else)
        msg.persistant_connection.reporter = rep
        msg.persistant_connection.save()
        msg.reporter = rep

        # send a welcome message back to the now-registered reporter,
        # depending on how long it's been since their last visit
        ls = rep.last_seen()
        if ls is not None:
            msg.respond(
                self.__str("login", rep) % {
                    "name": unicode(rep),
                    "days": (datetime.now() - ls).days })

        # or a slightly different welcome message
        else:
            msg.respond(
                self.__str("first-login", rep) % {
                    "name": unicode(rep) })

        # re-call this app's prepare, so other apps can
        # get hold of the reporter's info right away
        self.parse(msg)

    def remind(self, msg):

        # if a reporter object was attached to the
        # message by self.parse, respond with a reminder
        if msg.reporter is not None:
            msg.respond(
                self.__str("reminder", msg.reporter) % {
                    "name": unicode(msg.reporter) })

        # if not, we have no idea
        # who the message was from
        else:
            msg.respond(self.__str(
                "dont-know",
                msg.reporter))

    def reporters(self, msg):
        if msg.reporter is not None:

            # collate all reporters, with their full name,
            # username, and current connection. TODO: this
            # sucks, don't use __unicode__ and __repr__!
            items = [
                "%r (%s)" % (rep, rep.connection())
                for rep in Reporter.objects.all()
                if rep.connection()]

            if items:
                msg.respond(
                    self.__str("list", msg.reporter) % {
                        "items": ", ".join(items),
                        "noun":  "reporters",
                        "num":    len(items), })

            else:
                # there are no reportes to list!
                msg.respond(
                    self.__str("empty-list", msg.reporter) % {
                        "noun": "reporters" })

        # not identified yet; reject, so
        # we don't allow random people to
        # query our reporters list
        else:
            msg.respond(
                self.__str(
                    "denied", msg.reporter))

    def lang(self, msg, code):

        # reqiure identification to continue
        # TODO: make this check a decorator, so other apps
        #  can easily indicate that methods need a valid login
        if msg.reporter is not None:

            # if the language code was valid, save it
            # TODO: obviously, this is not cross-app
            if code in self.MSG:
                msg.reporter.language = code
                msg.reporter.save()
                resp = "lang-set"

            # invalid language code. don't do
            # anything, just send an error message
            else: resp = "bad-lang"

        # if the caller isn't logged in, send
        # an error message, and halt processing
        else: resp = "denied"

        # always send *some*
        # kind of response
        msg.respond(
            self.__str(
                resp, msg.reporter))
<|endoftext|>


<commit_before>""" ImageKit image specifications

All imagekit specifications must inherit from the ImageSpec class. Models
inheriting from ImageModel will be modified with a descriptor/accessor for each
spec found.

"""
import os
from StringIO import StringIO
from imagekit import processors
from imagekit.lib import *
from imagekit.utils import img_to_fobj
from django.core.files.base import ContentFile

class ImageSpec(object):
    pre_cache = False
    quality = 70
    increment_count = False
    processors = []

    @classmethod
    def name(cls):
        return getattr(cls, 'access_as', cls.__name__.lower())

    @classmethod
    def process(cls, image, obj):
        fmt = image.format
        img = image.copy()
        for proc in cls.processors:
            img, fmt = proc.process(img, fmt, obj)
        img.format = fmt
        return img, fmt

class Accessor(object):
    def __init__(self, obj, spec):
        self._img = None
        self._fmt = None
        self._obj = obj
        self.spec = spec

    def _get_imgfile(self):
        format = self._img.format or 'JPEG'
        if format != 'JPEG':
            imgfile = img_to_fobj(self._img, format)
        else:
            imgfile = img_to_fobj(self._img, format,
                                  quality=int(self.spec.quality),
                                  optimize=True)
        return imgfile

    def _create(self):
        if self._obj._imgfield:
            if self._exists():
                return
            # process the original image file
            try:
                fp = self._obj._imgfield.storage.open(self._obj._imgfield.name)
            except IOError:
                return
            fp.seek(0)
            fp = StringIO(fp.read())
            self._img, self._fmt = self.spec.process(Image.open(fp), self._obj)
            # save the new image to the cache
            content = ContentFile(self._get_imgfile().read())
            self._obj._storage.save(self.name, content)

    def _delete(self):
        self._obj._storage.delete(self.name)

    def _exists(self):
        return self._obj._storage.exists(self.name)

    @property
    def name(self):
        if self._obj._imgfield.name:
            filepath, basename = os.path.split(self._obj._imgfield.name)
            filename, extension = os.path.splitext(basename)
            for processor in self.spec.processors:
                if issubclass(processor, processors.Format):
                    extension = processor.extension
            cache_filename = self._obj._ik.cache_filename_format % \
                {'filename': filename,
                 'specname': self.spec.name(),
                 'extension': extension.lstrip('.')}
            if callable(self._obj._ik.cache_dir):
                return self._obj._ik.cache_dir(self._obj, filepath,
                                               cache_filename)
            else:
                return os.path.join(self._obj._ik.cache_dir, filepath,
                                    cache_filename)

    @property
    def url(self):
        if not self.spec.pre_cache:
            self._create()
        if self.spec.increment_count:
            fieldname = self._obj._ik.save_count_as
            if fieldname is not None:
                current_count = getattr(self._obj, fieldname)
                setattr(self._obj, fieldname, current_count + 1)
                self._obj.save(clear_cache=False)
        return self._obj._storage.url(self.name)

    @property
    def file(self):
        self._create()
        return self._obj._storage.open(self.name)

    @property
    def image(self):
        if not self._img:
            self._create()
            if not self._img:
                self._img = Image.open(self.file)
        return self._img

    @property
    def width(self):
        return self.image.size[0]

    @property
    def height(self):
        return self.image.size[1]

class Descriptor(object):
    def __init__(self, spec):
        self._spec = spec

    def __get__(self, obj, type=None):
        return Accessor(obj, self._spec)
<commit_msg>Accessor._delete() and Accessor._exists() needed conditionalizing as well. ikflush was yelling at me.<commit_after>""" ImageKit image specifications

All imagekit specifications must inherit from the ImageSpec class. Models
inheriting from ImageModel will be modified with a descriptor/accessor for each
spec found.

"""
import os
from StringIO import StringIO
from imagekit import processors
from imagekit.lib import *
from imagekit.utils import img_to_fobj
from django.core.files.base import ContentFile

class ImageSpec(object):
    pre_cache = False
    quality = 70
    increment_count = False
    processors = []

    @classmethod
    def name(cls):
        return getattr(cls, 'access_as', cls.__name__.lower())

    @classmethod
    def process(cls, image, obj):
        fmt = image.format
        img = image.copy()
        for proc in cls.processors:
            img, fmt = proc.process(img, fmt, obj)
        img.format = fmt
        return img, fmt

class Accessor(object):
    def __init__(self, obj, spec):
        self._img = None
        self._fmt = None
        self._obj = obj
        self.spec = spec

    def _get_imgfile(self):
        format = self._img.format or 'JPEG'
        if format != 'JPEG':
            imgfile = img_to_fobj(self._img, format)
        else:
            imgfile = img_to_fobj(self._img, format,
                                  quality=int(self.spec.quality),
                                  optimize=True)
        return imgfile

    def _create(self):
        if self._obj._imgfield:
            if self._exists():
                return
            # process the original image file
            try:
                fp = self._obj._imgfield.storage.open(self._obj._imgfield.name)
            except IOError:
                return
            fp.seek(0)
            fp = StringIO(fp.read())
            self._img, self._fmt = self.spec.process(Image.open(fp), self._obj)
            # save the new image to the cache
            content = ContentFile(self._get_imgfile().read())
            self._obj._storage.save(self.name, content)

    def _delete(self):
        if self._obj._imgfield:
            self._obj._storage.delete(self.name)

    def _exists(self):
        if self._obj._imgfield:
            return self._obj._storage.exists(self.name)

    @property
    def name(self):
        if self._obj._imgfield.name:
            filepath, basename = os.path.split(self._obj._imgfield.name)
            filename, extension = os.path.splitext(basename)
            for processor in self.spec.processors:
                if issubclass(processor, processors.Format):
                    extension = processor.extension
            cache_filename = self._obj._ik.cache_filename_format % \
                {'filename': filename,
                 'specname': self.spec.name(),
                 'extension': extension.lstrip('.')}
            if callable(self._obj._ik.cache_dir):
                return self._obj._ik.cache_dir(self._obj, filepath,
                                               cache_filename)
            else:
                return os.path.join(self._obj._ik.cache_dir, filepath,
                                    cache_filename)

    @property
    def url(self):
        if not self.spec.pre_cache:
            self._create()
        if self.spec.increment_count:
            fieldname = self._obj._ik.save_count_as
            if fieldname is not None:
                current_count = getattr(self._obj, fieldname)
                setattr(self._obj, fieldname, current_count + 1)
                self._obj.save(clear_cache=False)
        return self._obj._storage.url(self.name)

    @property
    def file(self):
        self._create()
        return self._obj._storage.open(self.name)

    @property
    def image(self):
        if not self._img:
            self._create()
            if not self._img:
                self._img = Image.open(self.file)
        return self._img

    @property
    def width(self):
        return self.image.size[0]

    @property
    def height(self):
        return self.image.size[1]

class Descriptor(object):
    def __init__(self, spec):
        self._spec = spec

    def __get__(self, obj, type=None):
        return Accessor(obj, self._spec)
<|endoftext|>