import datetime import logging import os import typing from decimal import Decimal from django.http import HttpResponse from django.utils import formats, timezone from rest_framework.exceptions import APIException _log = logging.getLogger(__name__) def catch_exception(func): """ Catches any exceptions raised in the function. If the exception is a subclass of APIException, reraise it to be handled by DRF. If not a subclass of APIException, then raise an APIException so that DRF handles it anyway and shows human error """ def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except APIException as e: raise e except Exception as e: _log.error(f'Error: {str(e)}') raise APIException(detail={ 'status': 'error', 'code': 'unknown', 'detail': f'{str(e)}' }) return wrapper def dtnow(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0, tz: timezone = timezone.utc, local=False) -> datetime.datetime: """ returns current datetime or datetime in future/past by time units use positive/negative time units for future/past datetime :param tz: timezone or None to use datetime without timezone, can not be None if local parameter is True :param local: True to change returning value timezone to user local :return: datetime.datetime object """ if not tz: # make sure None is used for disabling and only for nonlocal dates tz = timezone.utc if local else None ret = datetime.datetime.now(tz=tz) + datetime.timedelta(days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks) return timezone.localtime(ret) if local else ret def fmt_input( val: typing.Union[str, bool, int, float, Decimal, datetime.datetime, datetime.date, datetime.time], format: str = None) -> str: """ Make localized string for specified input value using specified format :param val: value to format, may be of different types :param format: format for data or None to use default localized format :return: localized data as str """ return formats.localize_input(val, format) def store_file_response(file_name): with open(file_name, 'rb') as fh: return httpFileResponse(fh.read(), os.path.basename(file_name)) def httpFileResponse(content, fname='file', zip=None): response = HttpResponse(content, content_type="application/file") fname = fname.replace('"', '').replace("'", '') if zip: response['Content-Disposition'] = f'attachment; filename="{fname}.zip"' else: response['Content-Disposition'] = f'attachment; filename="{fname}"' return response