Skip to content
Snippets Groups Projects
utils.py 5.10 KiB
#
# steering, Programme/schedule management for AURA
#
# Copyright (C) 2011-2017, 2020, Ernesto Rico Schmidt
# Copyright (C) 2017-2019, Ingo Leindecker
#
# 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/>.
#

import json
import typing
from datetime import date, datetime, time
from typing import Dict, Optional, Tuple, Union

import requests
from rest_framework import exceptions

from django.utils import timezone
from steering.settings import CBA_AJAX_URL, CBA_API_KEY, DEBUG

if typing.TYPE_CHECKING:
    from program.models import Host, Note, Show


def parse_datetime(date_string: str) -> datetime:
    """
    parse a datetime string and return a timezone aware datetime object
    """
    try:
        parsed_datetime = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
    except ValueError:
        parsed_datetime = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S%z")

    return (
        timezone.make_aware(parsed_datetime)
        if timezone.is_naive(parsed_datetime)
        else parsed_datetime
    )


def parse_date(date_string: str) -> date:
    """
    parse a date string and return a date object
    """
    return datetime.strptime(date_string, "%Y-%m-%d").date()


def parse_time(date_string: str) -> time:
    """
    parse a time string and return a time object
    """

    if len(date_string) == 5:
        return datetime.strptime(date_string, "%H:%M").time()
    else:
        return datetime.strptime(date_string, "%H:%M:%S").time()


def get_audio_url(cba_id: Optional[int]) -> str:
    """
    Retrieve the direct URL to the mp3 in CBA
    In order to retrieve the URL, stations need
       - to be whitelisted by CBA
       - an API Key

    For these contact cba@fro.at
    """

    if not cba_id or CBA_API_KEY == "":
        return ""
    else:
        if DEBUG:
            url = (
                "https://cba.fro.at/wp-content/plugins/cba/ajax/cba-get-filename.php?post_id="
                + str(cba_id)
                + "&c=Ml3fASkfwR8"
            )
        else:
            url = (
                CBA_AJAX_URL
                + "?action=cba_ajax_get_filename&post_id="
                + str(cba_id)
                + "&api_key="
                + CBA_API_KEY
            )

        try:
            return requests.get(url).json()
        except (requests.RequestException, json.JSONDecodeError):
            # TODO: we might want to add some logging
            return ""


def get_values(
    kwargs: Dict[str, str], *keys: str
) -> Union[Tuple[Union[int, str, None], ...], int, str, None]:
    """Get the values of the keys from the kwargs."""

    def int_if_digit(value: Optional[str]) -> Optional[Union[int, str]]:
        return int(value) if value and value.isdigit() else value

    values = [kwargs.get(key) for key in keys]

    if len(values) > 1:
        return tuple(int_if_digit(value) for value in values)
    else:
        return int_if_digit(values[0])


def delete_links(instance: Union["Host", "Note", "Show"]) -> Union["Host", "Note", "Show"]:
    """Delete the links associated with the instance."""

    if instance.links.count() > 0:
        for link in instance.links.all():
            link.delete(keep_parents=True)

    return instance


class DisabledObjectPermissionCheckMixin:
    """
    At the time of writing permission checks were entirely circumvented by manual
    queries in viewsets. To make code refactoring easier and allow
    the paced introduction of .get_object() in viewsets, object permission checks
    need to be disabled until permission checks have been refactored as well.

    Object permissions checks should become mandatory once proper permission_classes
    are assigned to viewsets. This mixin should be removed afterwards.
    """

    # The text above becomes the viewset’s doc string otherwise and is displayed in
    # the generated OpenAPI schema.
    __doc__ = None

    def check_object_permissions(self, request, obj):
        pass


class NestedObjectFinderMixin:
    ROUTE_FILTER_LOOKUPS = {}

    def _get_route_filters(self) -> Dict[str, int]:
        filter_kwargs = {}
        for key, value in self.kwargs.items():
            if key in self.ROUTE_FILTER_LOOKUPS:
                try:
                    filter_kwargs[self.ROUTE_FILTER_LOOKUPS[key]] = int(value)
                except ValueError:
                    raise exceptions.ValidationError(
                        detail=f"{key} must map to an integer value.", code="invalid-pk"
                    )
        return filter_kwargs

    def get_queryset(self):
        return super().get_queryset().filter(**self._get_route_filters())