<commit_before>import math
import requests
import logging
import sys
from datetime import timedelta
from setproctitle import setproctitle
from time import sleep
from django.db import Error, IntegrityError, InterfaceError, transaction
from django.core.management.base import BaseCommand
from django.utils import timezone
from busstops.models import DataSource, ServiceCode
from ..models import Vehicle, VehicleLocation

logger = logging.getLogger(__name__)

def calculate_bearing(a, b):
    if a.equals_exact(b, 0.001):
        return

    a_lat = math.radians(a.y)
    a_lon = math.radians(a.x)
    b_lat = math.radians(b.y)
    b_lon = math.radians(b.x)

    y = math.sin(b_lon - a_lon) * math.cos(b_lat)
    x = math.cos(a_lat) * math.sin(b_lat) - math.sin(a_lat) * math.cos(b_lat) * math.cos(b_lon - b_lon)

    bearing_radians = math.atan2(y, x)
    bearing_degrees = math.degrees(bearing_radians)

    if bearing_degrees < 0:
        bearing_degrees += 360

    return bearing_degrees

def calculate_speed(a, b):
    time = b.datetime - a.datetime
    if time:
        distance = a.latlong.distance(b.latlong) * 69  # approximate miles
        return distance / time.total_seconds() * 60 * 60
    return 0

def same_journey(latest_location, journey, when):
    if not latest_location:
        return False
    if latest_location.journey.route_name and journey.route_name:
        same_route = latest_location.journey.route_name == journey.route_name
    else:
        same_route = latest_location.journey.service_id == journey.service_id
    if same_route:
        if latest_location.journey.code and journey.code:
            return str(latest_location.journey.code) == str(journey.code)
        elif latest_location.journey.direction and journey.direction:
            return latest_location.journey.direction == journey.direction
        elif latest_location.journey.trip_id:
            if journey.trip_id:
                return latest_location.journey.trip_id == journey.trip_id
            return latest_location.journey.trip.end > timedelta(hours=when.hour, minutes=when.minute,
                                                                seconds=when.second)
        elif journey.trip_id:
            return False
        return when - latest_location.datetime < timedelta(minutes=15)
    return False

class ImportLiveVehiclesCommand(BaseCommand):
    session = requests.Session()
    current_location_ids = set()
    vehicles = Vehicle.objects.select_related('latest_location__journey__service')

    @staticmethod
    def get_datetime(self):
        return

    def get_items(self):
        response = self.session.get(self.url, timeout=40)
        if response.ok:
            return response.json()

    def get_old_locations(self):
        return VehicleLocation.objects.filter(
            current=True, journey__source=self.source, latest_vehicle__isnull=False
        ).exclude(id__in=self.current_location_ids)

    @staticmethod
    def get_service(queryset, latlong):
        for filtered_queryset in (
            queryset,
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.1)),
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.05)),
            queryset.filter(geometry__bboverlaps=latlong)
        ):
            try:
                return filtered_queryset.get()
            except queryset.model.MultipleObjectsReturned:
                continue

    def handle_item(self, item, now, service_code=None):
        datetime = self.get_datetime(item)
        location = None
        vehicle, vehicle_created = self.get_vehicle(item)
        if not vehicle:
            return
        if vehicle_created:
            latest = None
        else:
            latest = vehicle.latest_location
            if latest:
                if datetime:
                    if latest.datetime >= datetime:
                        self.current_location_ids.add(latest.id)
                        return
                else:
                    location = self.create_vehicle_location(item)
                    if location.latlong.equals_exact(latest.latlong, 0.001):
                        self.current_location_ids.add(latest.id)
                        return
        journey = self.get_journey(item, vehicle)
        journey.vehicle = vehicle
        if not journey:
            return
        if latest and latest.current and latest.journey.source_id != self.source.id:
            if ((datetime or now) - latest.datetime).total_seconds() < 300:  # less than 5 minutes old
                if latest.journey.service_id or not journey.service:
                    return  # defer to other source
        if not location:
            location = self.create_vehicle_location(item)
        location.datetime = datetime
        if latest:
            if location.datetime:
                if location.datetime == latest.datetime:
                    self.current_location_ids.add(latest.id)
                    return
            elif location.latlong == latest.latlong and location.heading == latest.heading:
                self.current_location_ids.add(latest.id)
                return
        if not location.datetime:
            location.datetime = now
        if same_journey(latest, journey, location.datetime):
            changed = False
            if latest.journey.source_id != self.source.id:
                latest.journey.source = self.source
                changed = True
            if journey.service and not latest.journey.service:
                latest.journey.service = journey.service
                changed = True
            if journey.destination and not latest.journey.destination:
                latest.journey.destination = journey.destination
                changed = True
            if changed:
                latest.journey.save()
            location.journey = latest.journey
            if location.heading is None:
                location.heading = calculate_bearing(latest.latlong, location.latlong)
        else:
            journey.source = self.source
            if not journey.datetime:
                journey.datetime = location.datetime
            journey.save()
            if journey.service and not journey.service.tracking:
                journey.service.tracking = True
                journey.service.save(update_fields=['tracking'])
            if journey.service_id:
                if service_code and journey.service_id != service_code.service_id or self.source.name.endswith(' SIRI'):
                    if not journey.service.servicecode_set.filter(scheme__endswith=' SIRI').exists():
                        if service_code:
                            # doppelgänger
                            ServiceCode.objects.create(scheme=service_code.scheme, service=journey.service,
                                                       code=service_code.code)
                        else:
                            ServiceCode.objects.create(scheme=self.source.name, service=journey.service,
                                                       code=journey.route_name)
            location.journey = journey
        with transaction.atomic():
            # save new location
            location.current = True
            location.save()
            journey.vehicle.latest_location = location
            journey.vehicle.save(update_fields=['latest_location'])
            if latest:
                # mark old location as not current
                latest.current = False
                latest.save(update_fields=['current'])
        self.current_location_ids.add(location.id)
        vehicle.update_last_modified()

        if latest:
            speed = calculate_speed(latest, location)
            if speed > 90:
                print('{} mph\t{}'.format(speed, journey.vehicle.get_absolute_url()))
            elif speed < 1.5:
                last_three = list(location.journey.vehiclelocation_set.order_by('-id')[:3])
                if len(last_three) == 3:
                    speed = calculate_speed(last_three[2], last_three[0])
                    if speed < 1.5:
                        last_three[1].delete()

    def update(self):
        now = timezone.localtime()
        self.source, source_created = DataSource.objects.update_or_create(
            {'url': self.url, 'datetime': now},
            name=self.source_name
        )

        self.current_location_ids = set()

        try:
            items = self.get_items()
            if items:
                for item in items:
                    self.handle_item(item, now)
                # mark any vehicles that have gone offline as not current
                print(self.get_old_locations().update(current=False), end='\t', flush=True)
            else:
                return 300  # no items - wait five minutes
        except (requests.exceptions.RequestException, IntegrityError, TypeError, ValueError) as e:
            print(e)
            logger.error(e, exc_info=True)
            return 80

        time_taken = (timezone.now() - now).total_seconds()
        print(time_taken)
        if time_taken < 60:
            return 60 - time_taken
        return 0

    def handle(self, *args, **options):
        setproctitle(sys.argv[1].replace('import_', '', 1).replace('live_', '', 1))
        while True:
            try:
                wait = self.update()
            except Error as e:
                logger.error(e, exc_info=True)
                if type(e) is InterfaceError:
                    sys.exit()
                wait = 30
            sleep(wait)
<commit_msg>give vehicles management command logger a more useful name<commit_after>import math
import requests
import logging
import sys
from datetime import timedelta
from setproctitle import setproctitle
from time import sleep
from django.db import Error, IntegrityError, InterfaceError, transaction
from django.core.management.base import BaseCommand
from django.utils import timezone
from busstops.models import DataSource, ServiceCode
from ..models import Vehicle, VehicleLocation

logger = logging.getLogger(sys.argv[1])

def calculate_bearing(a, b):
    if a.equals_exact(b, 0.001):
        return

    a_lat = math.radians(a.y)
    a_lon = math.radians(a.x)
    b_lat = math.radians(b.y)
    b_lon = math.radians(b.x)

    y = math.sin(b_lon - a_lon) * math.cos(b_lat)
    x = math.cos(a_lat) * math.sin(b_lat) - math.sin(a_lat) * math.cos(b_lat) * math.cos(b_lon - b_lon)

    bearing_radians = math.atan2(y, x)
    bearing_degrees = math.degrees(bearing_radians)

    if bearing_degrees < 0:
        bearing_degrees += 360

    return bearing_degrees

def calculate_speed(a, b):
    time = b.datetime - a.datetime
    if time:
        distance = a.latlong.distance(b.latlong) * 69  # approximate miles
        return distance / time.total_seconds() * 60 * 60
    return 0

def same_journey(latest_location, journey, when):
    if not latest_location:
        return False
    if latest_location.journey.route_name and journey.route_name:
        same_route = latest_location.journey.route_name == journey.route_name
    else:
        same_route = latest_location.journey.service_id == journey.service_id
    if same_route:
        if latest_location.journey.code and journey.code:
            return str(latest_location.journey.code) == str(journey.code)
        elif latest_location.journey.direction and journey.direction:
            return latest_location.journey.direction == journey.direction
        elif latest_location.journey.trip_id:
            if journey.trip_id:
                return latest_location.journey.trip_id == journey.trip_id
            return latest_location.journey.trip.end > timedelta(hours=when.hour, minutes=when.minute,
                                                                seconds=when.second)
        elif journey.trip_id:
            return False
        return when - latest_location.datetime < timedelta(minutes=15)
    return False

class ImportLiveVehiclesCommand(BaseCommand):
    session = requests.Session()
    current_location_ids = set()
    vehicles = Vehicle.objects.select_related('latest_location__journey__service')

    @staticmethod
    def get_datetime(self):
        return

    def get_items(self):
        response = self.session.get(self.url, timeout=40)
        if response.ok:
            return response.json()

    def get_old_locations(self):
        return VehicleLocation.objects.filter(
            current=True, journey__source=self.source, latest_vehicle__isnull=False
        ).exclude(id__in=self.current_location_ids)

    @staticmethod
    def get_service(queryset, latlong):
        for filtered_queryset in (
            queryset,
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.1)),
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.05)),
            queryset.filter(geometry__bboverlaps=latlong)
        ):
            try:
                return filtered_queryset.get()
            except queryset.model.MultipleObjectsReturned:
                continue

    def handle_item(self, item, now, service_code=None):
        datetime = self.get_datetime(item)
        location = None
        vehicle, vehicle_created = self.get_vehicle(item)
        if not vehicle:
            return
        if vehicle_created:
            latest = None
        else:
            latest = vehicle.latest_location
            if latest:
                if datetime:
                    if latest.datetime >= datetime:
                        self.current_location_ids.add(latest.id)
                        return
                else:
                    location = self.create_vehicle_location(item)
                    if location.latlong.equals_exact(latest.latlong, 0.001):
                        self.current_location_ids.add(latest.id)
                        return
        journey = self.get_journey(item, vehicle)
        journey.vehicle = vehicle
        if not journey:
            return
        if latest and latest.current and latest.journey.source_id != self.source.id:
            if ((datetime or now) - latest.datetime).total_seconds() < 300:  # less than 5 minutes old
                if latest.journey.service_id or not journey.service:
                    return  # defer to other source
        if not location:
            location = self.create_vehicle_location(item)
        location.datetime = datetime
        if latest:
            if location.datetime:
                if location.datetime == latest.datetime:
                    self.current_location_ids.add(latest.id)
                    return
            elif location.latlong == latest.latlong and location.heading == latest.heading:
                self.current_location_ids.add(latest.id)
                return
        if not location.datetime:
            location.datetime = now
        if same_journey(latest, journey, location.datetime):
            changed = False
            if latest.journey.source_id != self.source.id:
                latest.journey.source = self.source
                changed = True
            if journey.service and not latest.journey.service:
                latest.journey.service = journey.service
                changed = True
            if journey.destination and not latest.journey.destination:
                latest.journey.destination = journey.destination
                changed = True
            if changed:
                latest.journey.save()
            location.journey = latest.journey
            if location.heading is None:
                location.heading = calculate_bearing(latest.latlong, location.latlong)
        else:
            journey.source = self.source
            if not journey.datetime:
                journey.datetime = location.datetime
            journey.save()
            if journey.service and not journey.service.tracking:
                journey.service.tracking = True
                journey.service.save(update_fields=['tracking'])
            if journey.service_id:
                if service_code and journey.service_id != service_code.service_id or self.source.name.endswith(' SIRI'):
                    if not journey.service.servicecode_set.filter(scheme__endswith=' SIRI').exists():
                        if service_code:
                            # doppelgänger
                            ServiceCode.objects.create(scheme=service_code.scheme, service=journey.service,
                                                       code=service_code.code)
                        else:
                            ServiceCode.objects.create(scheme=self.source.name, service=journey.service,
                                                       code=journey.route_name)
            location.journey = journey
        with transaction.atomic():
            # save new location
            location.current = True
            location.save()
            journey.vehicle.latest_location = location
            journey.vehicle.save(update_fields=['latest_location'])
            if latest:
                # mark old location as not current
                latest.current = False
                latest.save(update_fields=['current'])
        self.current_location_ids.add(location.id)
        vehicle.update_last_modified()

        if latest:
            speed = calculate_speed(latest, location)
            if speed > 90:
                print('{} mph\t{}'.format(speed, journey.vehicle.get_absolute_url()))
            elif speed < 1.5:
                last_three = list(location.journey.vehiclelocation_set.order_by('-id')[:3])
                if len(last_three) == 3:
                    speed = calculate_speed(last_three[2], last_three[0])
                    if speed < 1.5:
                        last_three[1].delete()

    def update(self):
        now = timezone.localtime()
        self.source, source_created = DataSource.objects.update_or_create(
            {'url': self.url, 'datetime': now},
            name=self.source_name
        )

        self.current_location_ids = set()

        try:
            items = self.get_items()
            if items:
                for item in items:
                    self.handle_item(item, now)
                # mark any vehicles that have gone offline as not current
                print(self.get_old_locations().update(current=False), end='\t', flush=True)
            else:
                return 300  # no items - wait five minutes
        except (requests.exceptions.RequestException, IntegrityError, TypeError, ValueError) as e:
            print(e)
            logger.error(e, exc_info=True)
            return 80

        time_taken = (timezone.now() - now).total_seconds()
        print(time_taken)
        if time_taken < 60:
            return 60 - time_taken
        return 0

    def handle(self, *args, **options):
        setproctitle(sys.argv[1].replace('import_', '', 1).replace('live_', '', 1))
        while True:
            try:
                wait = self.update()
            except Error as e:
                logger.error(e, exc_info=True)
                if type(e) is InterfaceError:
                    sys.exit()
                wait = 30
            sleep(wait)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

"""
The MIT License (MIT)

Copyright (c) 2015-2019 Rapptz

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from .asset import Asset
from . import utils
from .user import User

class PartialEmoji:
    """Represents a "partial" emoji.

    This model will be given in two scenarios:

    - "Raw" data events such as :func:`on_raw_reaction_add`
    - Custom emoji that the bot cannot see from e.g. :attr:`Message.reactions`

    .. container:: operations

        .. describe:: x == y

            Checks if two emoji are the same.

        .. describe:: x != y

            Checks if two emoji are not the same.

        .. describe:: hash(x)

            Return the emoji's hash.

        .. describe:: str(x)

            Returns the emoji rendered for discord.

    Attributes
    -----------
    name: :class:`str`
        The custom emoji name, if applicable, or the unicode codepoint
        of the non-custom emoji.
    animated: :class:`bool`
        Whether the emoji is animated or not.
    id: Optional[:class:`int`]
        The ID of the custom emoji, if applicable.
    """

    __slots__ = ('animated', 'name', 'id', '_state')

    def __init__(self, *, animated, name, id=None):
        self.animated = animated
        self.name = name
        self.id = id
        self._state = None

    @classmethod
    def with_state(cls, state, *, animated, name, id=None):
        self = cls(animated=animated, name=name, id=id)
        self._state = state
        return self

    def __str__(self):
        if self.id is None:
            return self.name
        if self.animated:
            return '<a:%s:%s>' % (self.name, self.id)
        return '<:%s:%s>' % (self.name, self.id)

    def __repr__(self):
        return '<{0.__class__.__name__} animated={0.animated} name={0.name!r} id={0.id}>'.format(self)

    def __eq__(self, other):
        if self.is_unicode_emoji():
            return isinstance(other, PartialEmoji) and self.name == other.name

        if isinstance(other, (PartialEmoji, Emoji)):
            return self.id == other.id

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash((self.id, self.name))

    def is_custom_emoji(self):
        """Checks if this is a custom non-Unicode emoji."""
        return self.id is not None

    def is_unicode_emoji(self):
        """Checks if this is a Unicode emoji."""
        return self.id is None

    def _as_reaction(self):
        if self.id is None:
            return self.name
        return '%s:%s' % (self.name, self.id)

    @property
    def url(self):
        """:class:`Asset`:Returns an asset of the emoji, if it is custom."""
        if self.is_unicode_emoji():
            return Asset(self._state)

        _format = 'gif' if self.animated else 'png'
        url = "https://cdn.discordapp.com/emojis/{0.id}.{1}".format(self, _format)
        return Asset(self._state, url)

class Emoji:
    """Represents a custom emoji.

    Depending on the way this object was created, some of the attributes can
    have a value of ``None``.

    .. container:: operations

        .. describe:: x == y

            Checks if two emoji are the same.

        .. describe:: x != y

            Checks if two emoji are not the same.

        .. describe:: hash(x)

            Return the emoji's hash.

        .. describe:: iter(x)

            Returns an iterator of ``(field, value)`` pairs. This allows this class
            to be used as an iterable in list/dict/etc constructions.

        .. describe:: str(x)

            Returns the emoji rendered for discord.

    Attributes
    -----------
    name: :class:`str`
        The name of the emoji.
    id: :class:`int`
        The emoji's ID.
    require_colons: :class:`bool`
        If colons are required to use this emoji in the client (:PJSalt: vs PJSalt).
    animated: :class:`bool`
        Whether an emoji is animated or not.
    managed: :class:`bool`
        If this emoji is managed by a Twitch integration.
    guild_id: :class:`int`
        The guild ID the emoji belongs to.
    available: :class:`bool`
        Whether the emoji is available for use.
    user: Optional[:class:`User`]
        The user that created the emoji. This can only be retrieved using :meth:`Guild.fetch_emoji` and
        having the :attr:`~Permissions.manage_emojis` permission.
    """
    __slots__ = ('require_colons', 'animated', 'managed', 'id', 'name', '_roles', 'guild_id',
                 '_state', 'user', 'available')

    def __init__(self, *, guild, state, data):
        self.guild_id = guild.id
        self._state = state
        self._from_data(data)

    def _from_data(self, emoji):
        self.require_colons = emoji['require_colons']
        self.managed = emoji['managed']
        self.id = int(emoji['id'])
        self.name = emoji['name']
        self.animated = emoji.get('animated', False)
        self.available = emoji.get('available', True)
        self._roles = utils.SnowflakeList(map(int, emoji.get('roles', [])))
        user = emoji.get('user')
        self.user = User(state=self._state, data=user) if user else None

    def _iterator(self):
        for attr in self.__slots__:
            if attr[0] != '_':
                value = getattr(self, attr, None)
                if value is not None:
                    yield (attr, value)

    def __iter__(self):
        return self._iterator()

    def __str__(self):
        if self.animated:
            return '<a:{0.name}:{0.id}>'.format(self)
        return "<:{0.name}:{0.id}>".format(self)

    def __repr__(self):
        return '<Emoji id={0.id} name={0.name!r} animated={0.animated} managed={0.managed}>'.format(self)

    def __eq__(self, other):
        return isinstance(other, (PartialEmoji, Emoji)) and self.id == other.id

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return self.id >> 22

    @property
    def created_at(self):
        """:class:`datetime.datetime`: Returns the emoji's creation time in UTC."""
        return utils.snowflake_time(self.id)

    @property
    def url(self):
        """:class:`Asset`: Returns the asset of the emoji."""
        _format = 'gif' if self.animated else 'png'
        url = "https://cdn.discordapp.com/emojis/{0.id}.{1}".format(self, _format)
        return Asset(self._state, url)

    @property
    def roles(self):
        """List[:class:`Role`]: A :class:`list` of roles that is allowed to use this emoji.

        If roles is empty, the emoji is unrestricted.
        """
        guild = self.guild
        if guild is None:
            return []

        return [role for role in guild.roles if self._roles.has(role.id)]

    @property
    def guild(self):
        """:class:`Guild`: The guild this emoji belongs to."""
        return self._state._get_guild(self.guild_id)

    async def delete(self, *, reason=None):
        """|coro|

        Deletes the custom emoji.

        You must have :attr:`~Permissions.manage_emojis` permission to
        do this.

        Parameters
        -----------
        reason: Optional[:class:`str`]
            The reason for deleting this emoji. Shows up on the audit log.

        Raises
        -------
        Forbidden
            You are not allowed to delete emojis.
        HTTPException
            An error occurred deleting the emoji.
        """

        await self._state.http.delete_custom_emoji(self.guild.id, self.id, reason=reason)

    async def edit(self, *, name, roles=None, reason=None):
        r"""|coro|

        Edits the custom emoji.

        You must have :attr:`~Permissions.manage_emojis` permission to
        do this.

        Parameters
        -----------
        name: :class:`str`
            The new emoji name.
        roles: Optional[list[:class:`Role`]]
            A :class:`list` of :class:`Role`\s that can use this emoji. Leave empty to make it available to everyone.
        reason: Optional[:class:`str`]
            The reason for editing this emoji. Shows up on the audit log.

        Raises
        -------
        Forbidden
            You are not allowed to edit emojis.
        HTTPException
            An error occurred editing the emoji.
        """

        if roles:
            roles = [role.id for role in roles]
        await self._state.http.edit_custom_emoji(self.guild.id, self.id, name=name, roles=roles, reason=reason)
<commit_msg>Add Emoji.is_usable()<commit_after># -*- coding: utf-8 -*-

"""
The MIT License (MIT)

Copyright (c) 2015-2019 Rapptz

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from .asset import Asset
from . import utils
from .user import User

class PartialEmoji:
    """Represents a "partial" emoji.

    This model will be given in two scenarios:

    - "Raw" data events such as :func:`on_raw_reaction_add`
    - Custom emoji that the bot cannot see from e.g. :attr:`Message.reactions`

    .. container:: operations

        .. describe:: x == y

            Checks if two emoji are the same.

        .. describe:: x != y

            Checks if two emoji are not the same.

        .. describe:: hash(x)

            Return the emoji's hash.

        .. describe:: str(x)

            Returns the emoji rendered for discord.

    Attributes
    -----------
    name: :class:`str`
        The custom emoji name, if applicable, or the unicode codepoint
        of the non-custom emoji.
    animated: :class:`bool`
        Whether the emoji is animated or not.
    id: Optional[:class:`int`]
        The ID of the custom emoji, if applicable.
    """

    __slots__ = ('animated', 'name', 'id', '_state')

    def __init__(self, *, animated, name, id=None):
        self.animated = animated
        self.name = name
        self.id = id
        self._state = None

    @classmethod
    def with_state(cls, state, *, animated, name, id=None):
        self = cls(animated=animated, name=name, id=id)
        self._state = state
        return self

    def __str__(self):
        if self.id is None:
            return self.name
        if self.animated:
            return '<a:%s:%s>' % (self.name, self.id)
        return '<:%s:%s>' % (self.name, self.id)

    def __repr__(self):
        return '<{0.__class__.__name__} animated={0.animated} name={0.name!r} id={0.id}>'.format(self)

    def __eq__(self, other):
        if self.is_unicode_emoji():
            return isinstance(other, PartialEmoji) and self.name == other.name

        if isinstance(other, (PartialEmoji, Emoji)):
            return self.id == other.id

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash((self.id, self.name))

    def is_custom_emoji(self):
        """Checks if this is a custom non-Unicode emoji."""
        return self.id is not None

    def is_unicode_emoji(self):
        """Checks if this is a Unicode emoji."""
        return self.id is None

    def _as_reaction(self):
        if self.id is None:
            return self.name
        return '%s:%s' % (self.name, self.id)

    @property
    def url(self):
        """:class:`Asset`:Returns an asset of the emoji, if it is custom."""
        if self.is_unicode_emoji():
            return Asset(self._state)

        _format = 'gif' if self.animated else 'png'
        url = "https://cdn.discordapp.com/emojis/{0.id}.{1}".format(self, _format)
        return Asset(self._state, url)

class Emoji:
    """Represents a custom emoji.

    Depending on the way this object was created, some of the attributes can
    have a value of ``None``.

    .. container:: operations

        .. describe:: x == y

            Checks if two emoji are the same.

        .. describe:: x != y

            Checks if two emoji are not the same.

        .. describe:: hash(x)

            Return the emoji's hash.

        .. describe:: iter(x)

            Returns an iterator of ``(field, value)`` pairs. This allows this class
            to be used as an iterable in list/dict/etc constructions.

        .. describe:: str(x)

            Returns the emoji rendered for discord.

    Attributes
    -----------
    name: :class:`str`
        The name of the emoji.
    id: :class:`int`
        The emoji's ID.
    require_colons: :class:`bool`
        If colons are required to use this emoji in the client (:PJSalt: vs PJSalt).
    animated: :class:`bool`
        Whether an emoji is animated or not.
    managed: :class:`bool`
        If this emoji is managed by a Twitch integration.
    guild_id: :class:`int`
        The guild ID the emoji belongs to.
    available: :class:`bool`
        Whether the emoji is available for use.
    user: Optional[:class:`User`]
        The user that created the emoji. This can only be retrieved using :meth:`Guild.fetch_emoji` and
        having the :attr:`~Permissions.manage_emojis` permission.
    """
    __slots__ = ('require_colons', 'animated', 'managed', 'id', 'name', '_roles', 'guild_id',
                 '_state', 'user', 'available')

    def __init__(self, *, guild, state, data):
        self.guild_id = guild.id
        self._state = state
        self._from_data(data)

    def _from_data(self, emoji):
        self.require_colons = emoji['require_colons']
        self.managed = emoji['managed']
        self.id = int(emoji['id'])
        self.name = emoji['name']
        self.animated = emoji.get('animated', False)
        self.available = emoji.get('available', True)
        self._roles = utils.SnowflakeList(map(int, emoji.get('roles', [])))
        user = emoji.get('user')
        self.user = User(state=self._state, data=user) if user else None

    def _iterator(self):
        for attr in self.__slots__:
            if attr[0] != '_':
                value = getattr(self, attr, None)
                if value is not None:
                    yield (attr, value)

    def __iter__(self):
        return self._iterator()

    def __str__(self):
        if self.animated:
            return '<a:{0.name}:{0.id}>'.format(self)
        return "<:{0.name}:{0.id}>".format(self)

    def __repr__(self):
        return '<Emoji id={0.id} name={0.name!r} animated={0.animated} managed={0.managed}>'.format(self)

    def __eq__(self, other):
        return isinstance(other, (PartialEmoji, Emoji)) and self.id == other.id

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return self.id >> 22

    @property
    def created_at(self):
        """:class:`datetime.datetime`: Returns the emoji's creation time in UTC."""
        return utils.snowflake_time(self.id)

    @property
    def url(self):
        """:class:`Asset`: Returns the asset of the emoji."""
        _format = 'gif' if self.animated else 'png'
        url = "https://cdn.discordapp.com/emojis/{0.id}.{1}".format(self, _format)
        return Asset(self._state, url)

    @property
    def roles(self):
        """List[:class:`Role`]: A :class:`list` of roles that is allowed to use this emoji.

        If roles is empty, the emoji is unrestricted.
        """
        guild = self.guild
        if guild is None:
            return []

        return [role for role in guild.roles if self._roles.has(role.id)]

    @property
    def guild(self):
        """:class:`Guild`: The guild this emoji belongs to."""
        return self._state._get_guild(self.guild_id)

    def is_usable(self):
        """:class:`bool`: Whether the bot can use this emoji."""
        if not self.available:
            return False
        if not self._roles:
            return True
        emoji_roles, my_roles = self._roles, self.guild.me._roles
        return any(my_roles.has(role_id) for role_id in emoji_roles)

    async def delete(self, *, reason=None):
        """|coro|

        Deletes the custom emoji.

        You must have :attr:`~Permissions.manage_emojis` permission to
        do this.

        Parameters
        -----------
        reason: Optional[:class:`str`]
            The reason for deleting this emoji. Shows up on the audit log.

        Raises
        -------
        Forbidden
            You are not allowed to delete emojis.
        HTTPException
            An error occurred deleting the emoji.
        """

        await self._state.http.delete_custom_emoji(self.guild.id, self.id, reason=reason)

    async def edit(self, *, name, roles=None, reason=None):
        r"""|coro|

        Edits the custom emoji.

        You must have :attr:`~Permissions.manage_emojis` permission to
        do this.

        Parameters
        -----------
        name: :class:`str`
            The new emoji name.
        roles: Optional[list[:class:`Role`]]
            A :class:`list` of :class:`Role`\s that can use this emoji. Leave empty to make it available to everyone.
        reason: Optional[:class:`str`]
            The reason for editing this emoji. Shows up on the audit log.

        Raises
        -------
        Forbidden
            You are not allowed to edit emojis.
        HTTPException
            An error occurred editing the emoji.
        """

        if roles:
            roles = [role.id for role in roles]
        await self._state.http.edit_custom_emoji(self.guild.id, self.id, name=name, roles=roles, reason=reason)
<|endoftext|>


<commit_before>import datetime
import json
import webapp2

from operator import itemgetter

from google.appengine.ext import db
from google.appengine.api.logservice import logservice

from .models import TaskState, QueueState

def _serializer(obj):
    if isinstance(obj, datetime.datetime):
        return obj.isoformat()

    if hasattr(obj, '__dict__'):
        return obj.__dict__

    raise ValueError(obj)

def serialize_model(obj):
    return json.dumps(db.to_dict(obj), default=_serializer)

def dump(obj):
    return json.dumps(obj, default=_serializer)

class QueueListHandler(webapp2.RequestHandler):
    def get(self):
        ctx = {
            "queues": map(db.to_dict, QueueState.all())
        }

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

class QueueHandler(webapp2.RequestHandler):
    def get(self, queue_name):
        queue_state = QueueState.get_by_key_name(queue_name)

        if not queue_state:
            self.response.set_status(404)
            return

        tasks = TaskState.all().ancestor(queue_state).order("-deferred_at").fetch(limit=int(self.request.GET.get('limit', 1000)))

        ctx = db.to_dict(queue_state)

        stats = queue_state.get_queue_statistics()
        ctx['stats'] = {k: getattr(stats, k) for k in ("tasks", "executed_last_minute", "in_flight", "enforced_rate",)}
        if stats.oldest_eta_usec:
            ctx['stats']['oldest_eta'] = datetime.datetime.utcfromtimestamp(stats.oldest_eta_usec / 1e6)

        ctx['tasks'] = map(db.to_dict, tasks)

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

    def delete(self, queue_name):
        # DELETE == purge in this case
        queue_state = QueueState.get_by_key_name(queue_name)

        if not queue_state:
            self.response.set_status(404)
            return

        queue_state.get_queue_statistics().queue.purge()

        rpcs = []

        for task in TaskState.all().ancestor(queue_state).filter('is_complete', False).filter('is_running', False).run():
            task.is_complete = task.is_permanently_failed = True
            task.was_purged = True
            rpcs.append(db.put_async(task))

        for rpc in rpcs:
            rpc.get_result()

        self.response.content_type = "application/json"
        self.response.write(dump({
            "message": "Purging " + queue_name
        }))

class TaskInfoHandler(webapp2.RequestHandler):
    def get(self, queue_name, task_name):
        queue_state = QueueState.get_by_key_name(queue_name)
        task_state = TaskState.get_by_key_name(task_name, parent=queue_state)

        if not (queue_state and task_state):
            self.response.set_status(404)
            return

        ctx = {
            'task': db.to_dict(task_state),
        }
        if task_state.request_log_ids:
            ctx['logs'] = sorted(get_logs(task_state.request_log_ids, logservice.LOG_LEVEL_INFO), key=itemgetter('start_time'), reverse=True)

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

class LogHandler(webapp2.RequestHandler):
    def get(self, log_id):
        log_level = int(self.request.GET.get('level', logservice.LOG_LEVEL_INFO))
        ctx = {
            'log': next(get_logs([log_id], log_level), None)
        }

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

def get_logs(log_ids, log_level):
    for request_log in logservice.fetch(minimum_log_level=log_level,
                                        include_incomplete=True,
                                        include_app_logs=True,
                                        request_ids=log_ids):

        d = {name: getattr(request_log, name)
            for name, val in request_log.__class__.__dict__.iteritems()
            if isinstance(val, property) and not name.startswith('_')
        }
        d['start_time'] = datetime.datetime.fromtimestamp(request_log.start_time)
        if request_log.end_time:
            d['end_time'] = datetime.datetime.fromtimestamp(request_log.end_time)
            d['duration'] = (d['end_time'] - d['start_time']).total_seconds()

        d['app_logs'] = [{
                'time': datetime.datetime.fromtimestamp(app_log.time),
                'level': app_log.level,
                'message': app_log.message
            }
            for app_log in d['app_logs']
            if app_log.level >= log_level
        ]
        yield d
<commit_msg>Add cursoring to tasks API<commit_after>import datetime
import json
import webapp2

from operator import itemgetter

from google.appengine.ext import db
from google.appengine.api.logservice import logservice

from .models import TaskState, QueueState

def _serializer(obj):
    if isinstance(obj, datetime.datetime):
        return obj.isoformat()

    if hasattr(obj, '__dict__'):
        return obj.__dict__

    raise ValueError(obj)

def serialize_model(obj):
    return json.dumps(db.to_dict(obj), default=_serializer)

def dump(obj):
    return json.dumps(obj, default=_serializer)

class QueueListHandler(webapp2.RequestHandler):
    def get(self):
        ctx = {
            "queues": map(db.to_dict, QueueState.all())
        }

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

class QueueHandler(webapp2.RequestHandler):
    def get(self, queue_name):
        queue_state = QueueState.get_by_key_name(queue_name)

        if not queue_state:
            self.response.set_status(404)
            return

        tasks = (
            TaskState.all()
            .ancestor(queue_state)
            .order("-deferred_at")
            .with_cursor(start_cursor=self.request.GET.get('cursor'))
        )

        ctx = db.to_dict(queue_state)

        stats = queue_state.get_queue_statistics()
        ctx['stats'] = {k: getattr(stats, k) for k in ("tasks", "executed_last_minute", "in_flight", "enforced_rate",)}
        if stats.oldest_eta_usec:
            ctx['stats']['oldest_eta'] = datetime.datetime.utcfromtimestamp(stats.oldest_eta_usec / 1e6)

        ctx['tasks'] = map(db.to_dict, tasks[:int(self.request.GET.get('limit', 1000))])
        ctx['cursor'] = tasks.cursor()

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

    def delete(self, queue_name):
        # DELETE == purge in this case
        queue_state = QueueState.get_by_key_name(queue_name)

        if not queue_state:
            self.response.set_status(404)
            return

        queue_state.get_queue_statistics().queue.purge()

        rpcs = []

        for task in TaskState.all().ancestor(queue_state).filter('is_complete', False).filter('is_running', False).run():
            task.is_complete = task.is_permanently_failed = True
            task.was_purged = True
            rpcs.append(db.put_async(task))

        for rpc in rpcs:
            rpc.get_result()

        self.response.content_type = "application/json"
        self.response.write(dump({
            "message": "Purging " + queue_name
        }))

class TaskInfoHandler(webapp2.RequestHandler):
    def get(self, queue_name, task_name):
        queue_state = QueueState.get_by_key_name(queue_name)
        task_state = TaskState.get_by_key_name(task_name, parent=queue_state)

        if not (queue_state and task_state):
            self.response.set_status(404)
            return

        ctx = {
            'task': db.to_dict(task_state),
        }
        if task_state.request_log_ids:
            ctx['logs'] = sorted(get_logs(task_state.request_log_ids, logservice.LOG_LEVEL_INFO), key=itemgetter('start_time'), reverse=True)

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

class LogHandler(webapp2.RequestHandler):
    def get(self, log_id):
        log_level = int(self.request.GET.get('level', logservice.LOG_LEVEL_INFO))
        ctx = {
            'log': next(get_logs([log_id], log_level), None)
        }

        self.response.content_type = "application/json"
        self.response.write(dump(ctx))

def get_logs(log_ids, log_level):
    for request_log in logservice.fetch(minimum_log_level=log_level,
                                        include_incomplete=True,
                                        include_app_logs=True,
                                        request_ids=log_ids):

        d = {name: getattr(request_log, name)
            for name, val in request_log.__class__.__dict__.iteritems()
            if isinstance(val, property) and not name.startswith('_')
        }
        d['start_time'] = datetime.datetime.fromtimestamp(request_log.start_time)
        if request_log.end_time:
            d['end_time'] = datetime.datetime.fromtimestamp(request_log.end_time)
            d['duration'] = (d['end_time'] - d['start_time']).total_seconds()

        d['app_logs'] = [{
                'time': datetime.datetime.fromtimestamp(app_log.time),
                'level': app_log.level,
                'message': app_log.message
            }
            for app_log in d['app_logs']
            if app_log.level >= log_level
        ]
        yield d
<|endoftext|>


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

"""Ros node for playing audio chirps and recording the returns along
with data from a depth camera.

This requires that the depth camera and sound_play nodes have been
started (in separate terminals.):

roslaunch openni2_launch openni2.launch
roslaunch sound_play soundplay_node.launch

"""
import sys
import numpy as np

import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

from sound_play.libsoundplay import SoundClient

from pyaudio_utils import AudioPlayer, AudioRecorder

CHIRP_FILE = '/home/spragunr/echolocation/data/chirp.wav'

class Recorder(object):

    CHIRP_RATE = 6 # in hz
    RECORD_DURATION = .06 # in seconds
    CHANNELS = 1

    def __init__(self, file_name):
        rospy.init_node('ros_record')

        self.file_name = file_name

        self.bridge = CvBridge()
        self.latest_depth = None

        self.soundhandle = SoundClient(blocking=False)
        self.audio_player = AudioPlayer(CHIRP_FILE)
        self.audio_recorder = AudioRecorder(channels=self.CHANNELS)

        rospy.Subscriber('/camera/depth/image_raw', Image, self.depth_callback)

        while self.latest_depth is None and not rospy.is_shutdown():
            rospy.loginfo("WAITING FOR CAMERA DATA.")
            rospy.sleep(.1)

        wavs = []
        images = []

        rate = rospy.Rate(self.CHIRP_RATE)
        while not rospy.is_shutdown():
            image = self.bridge.imgmsg_to_cv2(self.latest_depth)
            images.append(image)

            self.soundhandle.playWave(CHIRP_FILE)
            #self.audio_player.play() # takes a while to actually play. (about .015 seconds)
            self.audio_recorder.record(self.RECORD_DURATION)
            while not self.audio_recorder.done_recording():
                rospy.sleep(.005)
            audio = self.audio_recorder.get_data()
            wavs.append(audio[1])
            rate.sleep()

        audio = np.array(wavs)
        depth = np.array(images)

        rospy.loginfo("Saving data to disk...")
        np.savez_compressed(self.file_name, audio=audio, depth=depth)

        #self.audio_player.shutdown()
        self.audio_recorder.shutdown()

    def depth_callback(self, depth_image):
        self.latest_depth = depth_image

if __name__ == "__main__":
    Recorder(sys.argv[1])

<commit_msg>change capture time<commit_after>#!/usr/bin/env python

"""Ros node for playing audio chirps and recording the returns along
with data from a depth camera.

This requires that the depth camera and sound_play nodes have been
started (in separate terminals.):

source ./ros_config_account.sh

roslaunch openni2_launch openni2.launch
roslaunch sound_play soundplay_node.launch

"""
#import h5py
import sys
import numpy as np

import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

from sound_play.libsoundplay import SoundClient

from pyaudio_utils import AudioPlayer, AudioRecorder

CHIRP_FILE = '/home/hoangnt/echolocation/data/16000to8000.02s.wav'

class Recorder(object):

    CHIRP_RATE = 6 # in hz
    RECORD_DURATION = .06 # in seconds
    CHANNELS = 2

    def __init__(self, file_name):
        rospy.init_node('ros_record')

        self.file_name = file_name

        self.bridge = CvBridge()
        self.latest_depth = None

        self.soundhandle = SoundClient(blocking=False)
        self.audio_player = AudioPlayer(CHIRP_FILE)
        self.audio_recorder = AudioRecorder(channels=self.CHANNELS)

        rospy.Subscriber('/camera/depth/image_raw', Image, self.depth_callback)

        while self.latest_depth is None and not rospy.is_shutdown():
            rospy.loginfo("WAITING FOR CAMERA DATA.")
            rospy.sleep(.1)

        wavs = []
        images = []

        rate = rospy.Rate(self.CHIRP_RATE)
        while not rospy.is_shutdown():
            image = self.bridge.imgmsg_to_cv2(self.latest_depth)
            images.append(image)

            self.soundhandle.playWave(CHIRP_FILE)
            #self.audio_player.play() # takes a while to actually play. (about .015 seconds)
            rospy.sleep(.04)
            self.audio_recorder.record(self.RECORD_DURATION)
            while not self.audio_recorder.done_recording():
                rospy.sleep(.005)
            audio = self.audio_recorder.get_data()
            wavs.append(audio[1])
            rate.sleep()

        audio = np.array(wavs)
        depth = np.array(images)

        rospy.loginfo("Saving data to disk...")
        np.savez_compressed(self.file_name, audio=audio, depth=depth)
	#with h5py.File(self.file_name, 'w') as h5:
	#	h5.create_dataset('audio', data=audio)
	#	h5.create_dataset('depth', data=depth)

        #self.audio_player.shutdown()
        self.audio_recorder.shutdown()

    def depth_callback(self, depth_image):
        self.latest_depth = depth_image

if __name__ == "__main__":
    Recorder(sys.argv[1])

<|endoftext|>


<commit_before># The Hazard Library
# Copyright (C) 2013-2019 GEM Foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from openquake.hazardlib.mfd import YoungsCoppersmith1985MFD

from openquake.hazardlib.tests.mfd.base_test import BaseMFDTestCase

import numpy

class YoungsCoppersmith1985MFDConstraintsTestCase(BaseMFDTestCase):
    def test_negative_or_zero_min_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=-4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'minimum magnitude must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=0.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'minimum magnitude must be positive')

    def test_negative_or_zero_b_val(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=-1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'b value must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=0.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'b value must be positive')

    def test_negative_or_zero_char_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=-6.0, char_rate=0.001,
            bin_width=0.1
        )
        error = 'characteristic magnitude must be positive'
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=0.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), error)

    def test_negative_or_zero_char_rate(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=-0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'characteristic rate must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.0,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'characteristic rate must be positive')

    def test_bin_width_out_of_valid_range(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=-0.1
        )
        error = 'bin width must be in the range (0, 0.5] to allow for at ' \
                'least one magnitude bin representing the characteristic ' \
                'distribution'
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.0
        )
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.6
        )
        self.assertEqual(str(exc), error)

    def test_max_mag_GR_too_close_to_min_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=4.3, char_rate=0.001,
            bin_width=0.1
        )
        error = ('Maximum magnitude of the G-R distribution (char_mag - 0.25) '
                 'must be greater than the minimum magnitude by at least one '
                 'magnitude bin.')
        self.assertEqual(str(exc), error)

    def test_from_total_moment_rate(self):
        total_moment_rate = 6.38119198365e15
        mfd = YoungsCoppersmith1985MFD.from_total_moment_rate(
            min_mag=5.0, b_val=0.85, char_mag=6.75,
            total_moment_rate=total_moment_rate, bin_width=0.1)
        computed_rates = mfd.get_annual_occurrence_rates()
        expected_rates = [(5.05, 0.00017054956240777723),
                          (5.15, 0.00014023312414148282),
                          (5.25, 0.00011530565560445073),
                          (5.35, 9.480922781808791e-05),
                          (5.45, 7.795619072057948e-05),
                          (5.55, 6.409890483786924e-05),
                          (5.65, 5.27048533725948e-05),
                          (5.75, 4.333617830215377e-05),
                          (5.85, 3.5632854085742086e-05),
                          (5.95, 2.9298852368637887e-05),
                          (6.05, 2.4090766011996856e-05),
                          (6.15, 1.9808455284958928e-05),
                          (6.25, 1.6287356764862893e-05),
                          (6.35, 1.3392159386974224e-05),
                          (6.45, 1.1011604622859124e-05),
                          (6.55, 7.057610011964502e-05),
                          (6.65, 7.057610011964502e-05),
                          (6.75, 7.057610011964502e-05),
                          (6.85, 7.057610011964502e-05),
                          (6.95, 7.057610011964502e-05)]

        computed_total_moment_rate = sum([rate * 10.**(1.5 * mag + 9.05)
                                          for (mag, rate) in computed_rates])

        #numpy.testing.assert_allclose(computed_rates, expected_rates)
        numpy.testing.assert_almost_equal(computed_total_moment_rate,
                                          total_moment_rate)

        self.assertEqual((5.05, 6.95), mfd.get_min_max_mag())

    def test_from_characteristic_rate(self):
        mfd = YoungsCoppersmith1985MFD.from_characteristic_rate(
            min_mag=5.0, b_val=0.85, char_mag=6.75,
            char_rate=0.000125, bin_width=0.1
        )
        computed_rates = mfd.get_annual_occurrence_rates()
        expected_rates = [(5.05, 6.041335597980433e-05),
                          (5.15, 4.967443791302958e-05),
                          (5.25, 4.084444146424117e-05),
                          (5.35, 3.358404178516568e-05),
                          (5.45, 2.7614231513367556e-05),
                          (5.55, 2.270559889580352e-05),
                          (5.65, 1.8669511804720695e-05),
                          (5.75, 1.535086885953161e-05),
                          (5.85, 1.2622139090051389e-05),
                          (5.95, 1.0378461093404372e-05),
                          (6.05, 8.533613351813384e-06),
                          (6.15, 7.016700856018683e-06),
                          (6.25, 5.7694307057387526e-06),
                          (6.35, 4.74387199217263e-06),
                          (6.45, 3.900613877853681e-06),
                          (6.55, 2.5e-05),
                          (6.65, 2.5e-05),
                          (6.75, 2.5e-05),
                          (6.85, 2.5e-05),
                          (6.95, 2.5e-05)]
        numpy.testing.assert_allclose(computed_rates, expected_rates)

        self.assertEqual((5.05, 6.95), mfd.get_min_max_mag())
<commit_msg>yc1985_fix test updates<commit_after>
# Copyright (C) 2013-2019 GEM Foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from openquake.hazardlib.mfd import YoungsCoppersmith1985MFD

from openquake.hazardlib.tests.mfd.base_test import BaseMFDTestCase

import numpy

class YoungsCoppersmith1985MFDConstraintsTestCase(BaseMFDTestCase):
    def test_negative_or_zero_min_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=-4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'minimum magnitude must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=0.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'minimum magnitude must be positive')

    def test_negative_or_zero_b_val(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=-1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'b value must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=0.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'b value must be positive')

    def test_negative_or_zero_char_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=-6.0, char_rate=0.001,
            bin_width=0.1
        )
        error = 'characteristic magnitude must be positive'
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=0.0, char_rate=0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), error)

    def test_negative_or_zero_char_rate(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=-0.001,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'characteristic rate must be positive')

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.0,
            bin_width=0.1
        )
        self.assertEqual(str(exc), 'characteristic rate must be positive')

    def test_bin_width_out_of_valid_range(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=-0.1
        )
        error = 'bin width must be in the range (0, 0.5] to allow for at ' \
                'least one magnitude bin representing the characteristic ' \
                'distribution'
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.0
        )
        self.assertEqual(str(exc), error)

        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=6.0, char_rate=0.001,
            bin_width=0.6
        )
        self.assertEqual(str(exc), error)

    def test_max_mag_GR_too_close_to_min_mag(self):
        exc = self.assert_mfd_error(
            YoungsCoppersmith1985MFD,
            min_mag=4.0, b_val=1.0, char_mag=4.3, char_rate=0.001,
            bin_width=0.1
        )
        error = ('Maximum magnitude of the G-R distribution (char_mag - 0.25) '
                 'must be greater than the minimum magnitude by at least one '
                 'magnitude bin.')
        self.assertEqual(str(exc), error)

    def test_from_total_moment_rate(self):
        total_moment_rate = 6.38119198365e15
        mfd = YoungsCoppersmith1985MFD.from_total_moment_rate(
            min_mag=5.0, b_val=0.85, char_mag=6.75,
            total_moment_rate=total_moment_rate, bin_width=0.1)
        computed_rates = mfd.get_annual_occurrence_rates()

        expected_rates = [(5.05, 0.00017254493595137864),
                          (5.15, 0.000141873805371608),
                          (5.25, 0.00011665469368682727),
                          (5.35, 9.591846446582074e-05),
                          (5.45, 7.88682524012297e-05),
                          (5.55, 6.484884085108228e-05),
                          (5.65, 5.3321482747389806e-05),
                          (5.75, 4.384319727332041e-05),
                          (5.85, 3.6049746708167595e-05),
                          (5.95, 2.9641639263244427e-05),
                          (6.05, 2.4372620016585263e-05),
                          (6.15, 2.004020766858989e-05),
                          (6.25, 1.647791345891117e-05),
                          (6.35, 1.3548843228053625e-05),
                          (6.45, 1.1140436759552141e-05),
                          (6.55, 7.140181717808345e-05),
                          (6.65, 7.140181717808345e-05),
                          (6.75, 7.140181717808345e-05),
                          (6.85, 7.140181717808345e-05),
                          (6.95, 7.140181717808345e-05)]

        computed_total_moment_rate = sum([rate * 10.**(1.5 * mag + 9.05)
                                          for (mag, rate) in computed_rates])

        numpy.testing.assert_allclose(computed_rates, expected_rates)
        numpy.testing.assert_almost_equal(computed_total_moment_rate,
                                          total_moment_rate)

        self.assertEqual((5.05, 6.95), mfd.get_min_max_mag())

    def test_from_characteristic_rate(self):
        mfd = YoungsCoppersmith1985MFD.from_characteristic_rate(
            min_mag=5.0, b_val=0.85, char_mag=6.75,
            char_rate=0.000125, bin_width=0.1
        )
        computed_rates = mfd.get_annual_occurrence_rates()
        expected_rates = [(5.05, 6.041335597980433e-05),
                          (5.15, 4.967443791302958e-05),
                          (5.25, 4.084444146424117e-05),
                          (5.35, 3.358404178516568e-05),
                          (5.45, 2.7614231513367556e-05),
                          (5.55, 2.270559889580352e-05),
                          (5.65, 1.8669511804720695e-05),
                          (5.75, 1.535086885953161e-05),
                          (5.85, 1.2622139090051389e-05),
                          (5.95, 1.0378461093404372e-05),
                          (6.05, 8.533613351813384e-06),
                          (6.15, 7.016700856018683e-06),
                          (6.25, 5.7694307057387526e-06),
                          (6.35, 4.74387199217263e-06),
                          (6.45, 3.900613877853681e-06),
                          (6.55, 2.5e-05),
                          (6.65, 2.5e-05),
                          (6.75, 2.5e-05),
                          (6.85, 2.5e-05),
                          (6.95, 2.5e-05)]
        numpy.testing.assert_allclose(computed_rates, expected_rates)

        self.assertEqual((5.05, 6.95), mfd.get_min_max_mag())
<|endoftext|>


<commit_before>import math
import requests
import logging
import sys
from setproctitle import setproctitle
from time import sleep
from django.db import OperationalError, IntegrityError, transaction
from django.core.management.base import BaseCommand
from django.utils import timezone
from busstops.models import DataSource, ServiceCode
from ..models import VehicleLocation

logger = logging.getLogger(__name__)

def calculate_bearing(a, b):
    if a == b:
        return

    a_lat = math.radians(a.y)
    a_lon = math.radians(a.x)
    b_lat = math.radians(b.y)
    b_lon = math.radians(b.x)

    y = math.sin(b_lon - a_lon) * math.cos(b_lat)
    x = math.cos(a_lat) * math.sin(b_lat) - math.sin(a_lat) * math.cos(b_lat) * math.cos(b_lon - b_lon)

    bearing_radians = math.atan2(y, x)
    bearing_degrees = math.degrees(bearing_radians)

    if bearing_degrees < 0:
        bearing_degrees += 360

    return bearing_degrees

def same_journey(latest_location, journey):
    if not latest_location:
        return False
    if journey.code and latest_location.journey.code != str(journey.code):
        return False
    if latest_location.current:
        return latest_location.journey.service_id == journey.service_id
    if journey.code and latest_location.journey.code == str(journey.code):
        return True

class ImportLiveVehiclesCommand(BaseCommand):
    session = requests.Session()
    current_location_ids = set()

    def get_items(self):
        response = self.session.get(self.url, timeout=40)
        if response.ok:
            return response.json()

    @staticmethod
    def get_service(queryset, latlong):
        try:
            return queryset.get()
        except queryset.model.MultipleObjectsReturned:
            try:
                return queryset.get(geometry__bboverlaps=latlong.buffer(0.1))
            except queryset.model.MultipleObjectsReturned:
                try:
                    return queryset.get(geometry__bboverlaps=latlong.buffer(0.05))
                except queryset.model.MultipleObjectsReturned:
                    return queryset.get(geometry__bboverlaps=latlong)

    @transaction.atomic
    def handle_item(self, item, now, service_code=None):
        journey, vehicle_created = self.get_journey(item)
        if not journey:
            return
        if vehicle_created:
            latest = None
        else:
            latest = journey.vehicle.latest_location
            if latest and latest.current and latest.journey.source != self.source:
                if latest.journey.service or not journey.service:
                    return  # defer to other source
        location = self.create_vehicle_location(item)
        if latest:
            if location.datetime:
                if location.datetime == latest.datetime:
                    self.current_location_ids.add(latest.id)
                    return
            elif location.latlong == latest.latlong and location.heading == latest.heading:
                self.current_location_ids.add(latest.id)
                return
        if not location.datetime:
            location.datetime = now
        if same_journey(latest, journey):
            if journey.service and not latest.journey.service:
                latest.journey.service = journey.service
                latest.journey.save()
            if journey.destination and not latest.journey.destination:
                latest.journey.destination = journey.destination
                latest.journey.save()
            location.journey = latest.journey
            if location.heading is None or journey.vehicle.operator_id == 'ARBB' and location.heading == 0:
                location.heading = calculate_bearing(latest.latlong, location.latlong)
        else:
            journey.source = self.source
            if not journey.datetime:
                journey.datetime = location.datetime
            journey.save()
            if service_code and journey.service_id and journey.service_id != service_code.service_id:
                # doppelgänger
                if not journey.service.servicecode_set.filter(scheme__endswith=' SIRI').exists():
                    ServiceCode.objects.create(scheme=service_code.scheme, service=journey.service,
                                               code=service_code.code)
            location.journey = journey
        # save new location
        location.current = True
        location.save()
        journey.vehicle.latest_location = location
        journey.vehicle.save()
        self.current_location_ids.add(location.id)
        if latest:
            # mark old location as not current
            latest.current = False
            latest.save()

    def update(self):
        now = timezone.now()
        self.source, source_created = DataSource.objects.update_or_create(
            {'url': self.url, 'datetime': now},
            name=self.source_name
        )

        self.current_location_ids = set()

        current_locations = VehicleLocation.objects.filter(journey__source=self.source, current=True,
                                                           latest_vehicle__isnull=False)

        try:
            items = self.get_items()
            if items:
                for item in items:
                    self.handle_item(item, now)
            # mark any vehicles that have gone offline as not current
            old_locations = current_locations.exclude(id__in=self.current_location_ids)
            print(old_locations.update(current=False), end='\t', flush=True)
        except (requests.exceptions.RequestException, IntegrityError, TypeError, ValueError) as e:
            print(e)
            logger.error(e, exc_info=True)
            # current_locations.update(current=False)
            return 80

        return 60

    def handle(self, *args, **options):
        setproctitle(sys.argv[1])
        while True:
            try:
                wait = self.update()
            except OperationalError as e:
                wait = 0
                print(e)
                logger.error(e, exc_info=True)
            sleep(wait)
<commit_msg>refine get_service, use dates as a last resort<commit_after>import math
import requests
import logging
import sys
from setproctitle import setproctitle
from time import sleep
from django.db import OperationalError, IntegrityError, transaction
from django.core.management.base import BaseCommand
from django.utils import timezone
from busstops.models import DataSource, ServiceCode
from ..models import VehicleLocation

logger = logging.getLogger(__name__)

def calculate_bearing(a, b):
    if a == b:
        return

    a_lat = math.radians(a.y)
    a_lon = math.radians(a.x)
    b_lat = math.radians(b.y)
    b_lon = math.radians(b.x)

    y = math.sin(b_lon - a_lon) * math.cos(b_lat)
    x = math.cos(a_lat) * math.sin(b_lat) - math.sin(a_lat) * math.cos(b_lat) * math.cos(b_lon - b_lon)

    bearing_radians = math.atan2(y, x)
    bearing_degrees = math.degrees(bearing_radians)

    if bearing_degrees < 0:
        bearing_degrees += 360

    return bearing_degrees

def same_journey(latest_location, journey):
    if not latest_location:
        return False
    if journey.code and latest_location.journey.code != str(journey.code):
        return False
    if latest_location.current:
        return latest_location.journey.service_id == journey.service_id
    if journey.code and latest_location.journey.code == str(journey.code):
        return True

class ImportLiveVehiclesCommand(BaseCommand):
    session = requests.Session()
    current_location_ids = set()

    def get_items(self):
        response = self.session.get(self.url, timeout=40)
        if response.ok:
            return response.json()

    @staticmethod
    def get_service(queryset, latlong):
        for filtered_queryset in (
            queryset,
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.1)),
            queryset.filter(geometry__bboverlaps=latlong.buffer(0.05)),
            queryset.filter(geometry__bboverlaps=latlong)
        ):
            try:
                return filtered_queryset.get()
            except queryset.model.MultipleObjectsReturned:
                continue
            except queryset.model.DoesNotExist:
                break

        now = timezone.now()
        return queryset.filter(journey__datetime__lte=now,
                               journey__stopusageusage__datetime__gte=now).distinct().get()

    @transaction.atomic
    def handle_item(self, item, now, service_code=None):
        journey, vehicle_created = self.get_journey(item)
        if not journey:
            return
        if vehicle_created:
            latest = None
        else:
            latest = journey.vehicle.latest_location
            if latest and latest.current and latest.journey.source != self.source:
                if latest.journey.service or not journey.service:
                    return  # defer to other source
        location = self.create_vehicle_location(item)
        if latest:
            if location.datetime:
                if location.datetime == latest.datetime:
                    self.current_location_ids.add(latest.id)
                    return
            elif location.latlong == latest.latlong and location.heading == latest.heading:
                self.current_location_ids.add(latest.id)
                return
        if not location.datetime:
            location.datetime = now
        if same_journey(latest, journey):
            if journey.service and not latest.journey.service:
                latest.journey.service = journey.service
                latest.journey.save()
            if journey.destination and not latest.journey.destination:
                latest.journey.destination = journey.destination
                latest.journey.save()
            location.journey = latest.journey
            if location.heading is None or journey.vehicle.operator_id == 'ARBB' and location.heading == 0:
                location.heading = calculate_bearing(latest.latlong, location.latlong)
        else:
            journey.source = self.source
            if not journey.datetime:
                journey.datetime = location.datetime
            journey.save()
            if service_code and journey.service_id and journey.service_id != service_code.service_id:
                # doppelgänger
                if not journey.service.servicecode_set.filter(scheme__endswith=' SIRI').exists():
                    ServiceCode.objects.create(scheme=service_code.scheme, service=journey.service,
                                               code=service_code.code)
            location.journey = journey
        # save new location
        location.current = True
        location.save()
        journey.vehicle.latest_location = location
        journey.vehicle.save()
        self.current_location_ids.add(location.id)
        if latest:
            # mark old location as not current
            latest.current = False
            latest.save()

    def update(self):
        now = timezone.now()
        self.source, source_created = DataSource.objects.update_or_create(
            {'url': self.url, 'datetime': now},
            name=self.source_name
        )

        self.current_location_ids = set()

        current_locations = VehicleLocation.objects.filter(journey__source=self.source, current=True,
                                                           latest_vehicle__isnull=False)

        try:
            items = self.get_items()
            if items:
                for item in items:
                    self.handle_item(item, now)
            # mark any vehicles that have gone offline as not current
            old_locations = current_locations.exclude(id__in=self.current_location_ids)
            print(old_locations.update(current=False), end='\t', flush=True)
        except (requests.exceptions.RequestException, IntegrityError, TypeError, ValueError) as e:
            print(e)
            logger.error(e, exc_info=True)
            # current_locations.update(current=False)
            return 80

        return 60

    def handle(self, *args, **options):
        setproctitle(sys.argv[1])
        while True:
            try:
                wait = self.update()
            except OperationalError as e:
                wait = 0
                print(e)
                logger.error(e, exc_info=True)
            sleep(wait)
<|endoftext|>


<commit_before>import datetime
import sys

from daphne.server import Server
from daphne.endpoints import build_endpoint_description_strings
from django.conf import settings
from django.core.management.commands.runserver import Command as RunserverCommand
from django.utils import six
from django.utils.encoding import get_system_encoding

from channels.routing import get_default_application
from channels.log import setup_logger

class Command(RunserverCommand):
    protocol = 'http'
    server_cls = Server

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument('--noasgi', action='store_false', dest='use_asgi', default=True,
            help='Run the old WSGI-based runserver rather than the ASGI-based one')
        parser.add_argument('--http_timeout', action='store', dest='http_timeout', type=int, default=60,
            help='Specify the daphne http_timeout interval in seconds (default: 60)')
        parser.add_argument('--websocket_handshake_timeout', action='store', dest='websocket_handshake_timeout',
            type=int, default=5,
            help='Specify the daphne websocket_handshake_timeout interval in seconds (default: 5)')

    def handle(self, *args, **options):
        self.verbosity = options.get("verbosity", 1)
        self.logger = setup_logger('django.channels', self.verbosity)
        self.http_timeout = options.get("http_timeout", 60)
        self.websocket_handshake_timeout = options.get("websocket_handshake_timeout", 5)
        super(Command, self).handle(*args, **options)

    def inner_run(self, *args, **options):
        # Maybe they want the wsgi one?
        if not options.get("use_asgi", True):
            if hasattr(RunserverCommand, "server_cls"):
                self.server_cls = RunserverCommand.server_cls
            return RunserverCommand.inner_run(self, *args, **options)
        # Run checks
        self.stdout.write("Performing system checks...\n\n")
        self.check(display_num_errors=True)
        self.check_migrations()
        # Print helpful text
        quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'
        now = datetime.datetime.now().strftime('%B %d, %Y - %X')
        if six.PY2:
            now = now.decode(get_system_encoding())
        self.stdout.write(now)
        self.stdout.write((
            "Django version %(version)s, using settings %(settings)r\n"
            "Starting ASGI/Channels development server at %(protocol)s://%(addr)s:%(port)s/\n"
            "Quit the server with %(quit_command)s.\n"
        ) % {
            "version": self.get_version(),
            "settings": settings.SETTINGS_MODULE,
            "protocol": self.protocol,
            "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
            "port": self.port,
            "quit_command": quit_command,
        })

        # Launch server in 'main' thread. Signals are disabled as it's still
        # actually a subthread under the autoreloader.
        self.logger.debug("Daphne running, listening on %s:%s", self.addr, self.port)

        # build the endpoint description string from host/port options
        endpoints = build_endpoint_description_strings(host=self.addr, port=self.port)
        try:
            self.server_cls(
                application=get_default_application(),
                endpoints=endpoints,
                signal_handlers=not options['use_reloader'],
                action_logger=self.log_action,
                http_timeout=self.http_timeout,
                ws_protocols=getattr(settings, 'CHANNELS_WS_PROTOCOLS', None),
                root_path=getattr(settings, 'FORCE_SCRIPT_NAME', '') or '',
                websocket_handshake_timeout=self.websocket_handshake_timeout,
            ).run()
            self.logger.debug("Daphne exited")
        except KeyboardInterrupt:
            shutdown_message = options.get('shutdown_message', '')
            if shutdown_message:
                self.stdout.write(shutdown_message)
            return

    def log_action(self, protocol, action, details):
        """
        Logs various different kinds of requests to the console.
        """
        # All start with timestamp
        msg = "[%s] " % datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
        # HTTP requests
        if protocol == "http" and action == "complete":
            msg += "HTTP %(method)s %(path)s %(status)s [%(time_taken).2f, %(client)s]\n" % details
            # Utilize terminal colors, if available
            if 200 <= details['status'] < 300:
                # Put 2XX first, since it should be the common case
                msg = self.style.HTTP_SUCCESS(msg)
            elif 100 <= details['status'] < 200:
                msg = self.style.HTTP_INFO(msg)
            elif details['status'] == 304:
                msg = self.style.HTTP_NOT_MODIFIED(msg)
            elif 300 <= details['status'] < 400:
                msg = self.style.HTTP_REDIRECT(msg)
            elif details['status'] == 404:
                msg = self.style.HTTP_NOT_FOUND(msg)
            elif 400 <= details['status'] < 500:
                msg = self.style.HTTP_BAD_REQUEST(msg)
            else:
                # Any 5XX, or any other response
                msg = self.style.HTTP_SERVER_ERROR(msg)
        # Websocket requests
        elif protocol == "websocket" and action == "connected":
            msg += "WebSocket CONNECT %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "disconnected":
            msg += "WebSocket DISCONNECT %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "connecting":
            msg += "WebSocket HANDSHAKING %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "rejected":
            msg += "WebSocket REJECT %(path)s [%(client)s]\n" % details

        sys.stderr.write(msg)
<commit_msg>Nicer handling if you forget to set ASGI_APPLICATION<commit_after>import datetime
import sys

from daphne.server import Server
from daphne.endpoints import build_endpoint_description_strings
from django.conf import settings
from django.core.management import CommandError
from django.core.management.commands.runserver import Command as RunserverCommand
from django.utils import six
from django.utils.encoding import get_system_encoding

from channels.routing import get_default_application
from channels.log import setup_logger

class Command(RunserverCommand):
    protocol = 'http'
    server_cls = Server

    def add_arguments(self, parser):
        super().add_arguments(parser)
        parser.add_argument('--noasgi', action='store_false', dest='use_asgi', default=True,
            help='Run the old WSGI-based runserver rather than the ASGI-based one')
        parser.add_argument('--http_timeout', action='store', dest='http_timeout', type=int, default=60,
            help='Specify the daphne http_timeout interval in seconds (default: 60)')
        parser.add_argument('--websocket_handshake_timeout', action='store', dest='websocket_handshake_timeout',
            type=int, default=5,
            help='Specify the daphne websocket_handshake_timeout interval in seconds (default: 5)')

    def handle(self, *args, **options):
        self.verbosity = options.get("verbosity", 1)
        self.logger = setup_logger('django.channels', self.verbosity)
        self.http_timeout = options.get("http_timeout", 60)
        self.websocket_handshake_timeout = options.get("websocket_handshake_timeout", 5)
        # Check Channels is installed right
        if not hasattr(settings, "ASGI_APPLICATION"):
            raise CommandError("You have not set ASGI_APPLICATION, which is needed to run the server.")
        # Dispatch upward
        super().handle(*args, **options)

    def inner_run(self, *args, **options):
        # Maybe they want the wsgi one?
        if not options.get("use_asgi", True):
            if hasattr(RunserverCommand, "server_cls"):
                self.server_cls = RunserverCommand.server_cls
            return RunserverCommand.inner_run(self, *args, **options)
        # Run checks
        self.stdout.write("Performing system checks...\n\n")
        self.check(display_num_errors=True)
        self.check_migrations()
        # Print helpful text
        quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'
        now = datetime.datetime.now().strftime('%B %d, %Y - %X')
        if six.PY2:
            now = now.decode(get_system_encoding())
        self.stdout.write(now)
        self.stdout.write((
            "Django version %(version)s, using settings %(settings)r\n"
            "Starting ASGI/Channels development server at %(protocol)s://%(addr)s:%(port)s/\n"
            "Quit the server with %(quit_command)s.\n"
        ) % {
            "version": self.get_version(),
            "settings": settings.SETTINGS_MODULE,
            "protocol": self.protocol,
            "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
            "port": self.port,
            "quit_command": quit_command,
        })

        # Launch server in 'main' thread. Signals are disabled as it's still
        # actually a subthread under the autoreloader.
        self.logger.debug("Daphne running, listening on %s:%s", self.addr, self.port)

        # build the endpoint description string from host/port options
        endpoints = build_endpoint_description_strings(host=self.addr, port=self.port)
        try:
            self.server_cls(
                application=get_default_application(),
                endpoints=endpoints,
                signal_handlers=not options['use_reloader'],
                action_logger=self.log_action,
                http_timeout=self.http_timeout,
                ws_protocols=getattr(settings, 'CHANNELS_WS_PROTOCOLS', None),
                root_path=getattr(settings, 'FORCE_SCRIPT_NAME', '') or '',
                websocket_handshake_timeout=self.websocket_handshake_timeout,
            ).run()
            self.logger.debug("Daphne exited")
        except KeyboardInterrupt:
            shutdown_message = options.get('shutdown_message', '')
            if shutdown_message:
                self.stdout.write(shutdown_message)
            return

    def log_action(self, protocol, action, details):
        """
        Logs various different kinds of requests to the console.
        """
        # All start with timestamp
        msg = "[%s] " % datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
        # HTTP requests
        if protocol == "http" and action == "complete":
            msg += "HTTP %(method)s %(path)s %(status)s [%(time_taken).2f, %(client)s]\n" % details
            # Utilize terminal colors, if available
            if 200 <= details['status'] < 300:
                # Put 2XX first, since it should be the common case
                msg = self.style.HTTP_SUCCESS(msg)
            elif 100 <= details['status'] < 200:
                msg = self.style.HTTP_INFO(msg)
            elif details['status'] == 304:
                msg = self.style.HTTP_NOT_MODIFIED(msg)
            elif 300 <= details['status'] < 400:
                msg = self.style.HTTP_REDIRECT(msg)
            elif details['status'] == 404:
                msg = self.style.HTTP_NOT_FOUND(msg)
            elif 400 <= details['status'] < 500:
                msg = self.style.HTTP_BAD_REQUEST(msg)
            else:
                # Any 5XX, or any other response
                msg = self.style.HTTP_SERVER_ERROR(msg)
        # Websocket requests
        elif protocol == "websocket" and action == "connected":
            msg += "WebSocket CONNECT %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "disconnected":
            msg += "WebSocket DISCONNECT %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "connecting":
            msg += "WebSocket HANDSHAKING %(path)s [%(client)s]\n" % details
        elif protocol == "websocket" and action == "rejected":
            msg += "WebSocket REJECT %(path)s [%(client)s]\n" % details

        sys.stderr.write(msg)
<|endoftext|>