class StationWrongToken(APIException):
    status_code = 403
    default_detail = 'Wrong token, please provide proper token parameter.'
    default_code = 'station_wrong_token' class StationFilterSet(FilterSet):
    class Meta:
        model = Station
        fields = {
            'name': ['exact', 'icontains'],
            'is_in_test_mode': ['exact'],
            'country': ['exact', 'icontains'],
            'state': ['exact', 'icontains'],
            'county': ['exact', 'icontains'],
            'community': ['exact', 'icontains'],
            'city': ['exact', 'icontains'],
            'district': ['exact', 'icontains'],
            'owner': ['exact'],
            'project': ['exact'],
            'project__name': ['exact', 'icontains'],
            'created': ['lte', 'gte'],
            'updated': ['lte', 'gte'],
        } class MeteringFilterSet(FilterSet):
    class Meta:
        model = Metering
        fields = {
            'created': ['lte', 'gte'],
            'station': ['exact'],
        } class MeteringHistoryFilterSet(FilterSet):
    class Meta:
        model = MeteringHistory
        fields = {
            'created': ['lte', 'gte'],
            'station': ['exact'],
        } class ProjectFilterSet(FilterSet):
    class Meta:
        model = Project
        fields = {
            'name': ['exact', 'icontains'],
            'country': ['exact', 'icontains'],
            'state': ['exact', 'icontains'],
            'county': ['exact', 'icontains'],
            'community': ['exact', 'icontains'],
            'city': ['exact', 'icontains'],
            'district': ['exact', 'icontains'],
            'owner': ['exact'],
            'created': ['lte', 'gte'],
            'updated': ['lte', 'gte'],
        } class Station(AbstractTimeTrackable, AbstractLocation):
    """
    Model representing sensor station. Can be grouped using Project model.
    """

    TYPE_SMOGLY = 'smogly'
    TYPE_CUSTOM = 'custom'
    TYPE_AQICN = 'aqicn'
    TYPE_BASIC_SDS011 = 'basic-sds011'
    TYPE_CHOICES = (
        (TYPE_SMOGLY, 'SMOGLY'),
        (TYPE_CUSTOM, 'CUSTOM'),
        (TYPE_AQICN, 'AQICN'),
        (TYPE_BASIC_SDS011, 'BASIC-SDS011'),
    )

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=255)
    type = models.CharField(max_length=30, choices=TYPE_CHOICES, default=TYPE_SMOGLY)
    notes = models.CharField(max_length=255)
    is_in_test_mode = models.BooleanField(
        help_text='Whether sensor is in test mode or not (all data).',
        default=False
    )
    token = models.CharField(
        max_length=255,
        help_text='Token automatically generated while saving model, needed by Station to POST any data.',
        default=generate_token,
        unique=True
    )
    altitude = models.FloatField(help_text='Altitude of sensor location.', default=0)

    # Relationship Fields
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    project = models.ForeignKey('api.Project')

    @property
    def last_metering(self):
        """
        Return lastly created, serialized Metering object.
        We remove cache key while adding metering to given station.
        """
        if not cache.get(self.last_metering_cache_key):
            from api.serializers import MeteringSerializer
            last_metering = self.metering_set.first()
            cache.set(self.last_metering_cache_key, MeteringSerializer(last_metering).data)
        return cache.get(self.last_metering_cache_key)

    @property
    def last_metering_cache_key(self):
        return u'station-{}-last-metering'.format(self.pk)

    class Meta:
        ordering = ('-created',)

    def __unicode__(self):
        return u'%s' % self.id

    def get_absolute_url(self):
        return reverse('api_station_detail', args=(self.id,))

    def get_update_url(self):
        return reverse('api_station_update', args=(self.id,)) class Metering(AbstractMetering):
    """
    Model representing data submitted by sensor station.
    """

    is_test = models.BooleanField(
        help_text='Whether entry was created using Sensor test mode (Sensor.is_in_test_mode=True).',
        default=False,
        db_index=True
    )

    class Meta:
        ordering = ('-created',)

    def get_absolute_url(self):
        return reverse('api_metering_detail', args=(self.pk,)) class MeteringHistory(AbstractMetering):
    """
    Model representing history entries calculated from Metering entries, resolution 1 hour.

    Idea:
    * background job runs once per hour
    * job calculates avarage of readings from Metering entries older than 2 weeks (avarage
    should be calulated per hour basis, one MeteringHistory entry per hour)
    * entries that was used to calculate avarage should be cleaned from Metering database
    """

    class Meta:
        ordering = ('-created',)

    def get_absolute_url(self):
        return reverse('api_meteringhistory_detail', args=(self.pk,)) class Project(AbstractTimeTrackable, AbstractLocation):
    """
    Model used for grouping sensor stations. Eg. by local anti-smog groups.
    """

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=255)
    slug = AutoSlugField(populate_from='name', blank=True, unique=True)

    website = models.URLField()
    description = models.TextField()
    logo = models.ImageField(max_length=100, upload_to='project/', blank=True, null=True, default=None)

    owner = models.ForeignKey(settings.AUTH_USER_MODEL)

    class Meta:
        ordering = ('-created',)

    def __unicode__(self):
        return u'%s' % self.slug

    def get_absolute_url(self):
        return reverse('api_project_detail', args=(self.slug,))

    def get_update_url(self):
        return reverse('api_project_update', args=(self.slug,)) class StationSerializer(serializers.ModelSerializer):
    owner = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = Station
        fields = (
            'id',
            'name',
            'created',
            'updated',
            'type',
            'notes',
            'is_in_test_mode',
            'altitude',
            'position',
            'country',
            'state',
            'county',
            'community',
            'city',
            'district',
            'owner',
            'project',
            'last_metering',
        ) class MeteringSerializer(serializers.ModelSerializer):
    pm01 = serializers.FloatField(default=None, write_only=True)
    temp_out2 = serializers.FloatField(default=None, write_only=True)
    temp_out3 = serializers.FloatField(default=None, write_only=True)
    temp_int_air1 = serializers.FloatField(default=None, write_only=True)
    hum_out2 = serializers.FloatField(default=None, write_only=True)
    hum_out3 = serializers.FloatField(default=None, write_only=True)
    hum_int_air1 = serializers.FloatField(default=None, write_only=True)
    rssi = serializers.FloatField(default=None, write_only=True)
    bpress_out1 = serializers.FloatField(default=None, write_only=True)

    class Meta:
        model = Metering
        fields = (
            'station',
            'created',
            'pm01',
            'pm25',
            'pm10',
            'temp_out1',
            'temp_out2',
            'temp_out3',
            'temp_int_air1',
            'hum_out1',
            'hum_out2',
            'hum_out3',
            'hum_int_air1',
            'rssi',
            'bpress_out1',
            'hw_id',
        ) class MeteringHistorySerializer(serializers.ModelSerializer):
    class Meta(MeteringSerializer.Meta):
        model = MeteringHistory class ProjectSerializer(serializers.ModelSerializer):
    owner = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = Project
        fields = (
            'name',
            'slug',
            'created',
            'updated',
            'website',
            'description',
            'logo',
            'position',
            'country',
            'state',
            'county',
            'community',
            'city',
            'district',
            'owner',
        )
from django.core.cache import cache
from rest_framework import response, schemas
from rest_framework.authentication import BasicAuthentication
from rest_framework.decorators import api_view, renderer_classes, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.status import HTTP_201_CREATED
from rest_framework.viewsets import ModelViewSet
from rest_framework_gis.filters import InBBoxFilter
from rest_framework_jwt.views import ObtainJSONWebToken
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
from .exceptions import StationWrongToken
from .filters import StationFilterSet, MeteringFilterSet, MeteringHistoryFilterSet, ProjectFilterSet
from .models import Station, Metering, MeteringHistory, Project
from .serializers import StationSerializer, MeteringSerializer, MeteringHistorySerializer, ProjectSerializer



class ObtainJWT(ObtainJSONWebToken):
    authentication_classes = (BasicAuthentication,)


class StationViewSet(ModelViewSet):
    """ViewSet for the Station class"""

    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS + [InBBoxFilter]
    queryset = Station.objects.all()
    serializer_class = StationSerializer
    filter_class = StationFilterSet
    ordering_fields = ('updated', 'created', 'name')
    bbox_filter_field = 'position'


class MeteringViewSet(ModelViewSet):
    """ViewSet for the Metering class"""


    queryset = Metering.objects.all()