from typing import Any, Callable, Dict, Optional, Type, Union

from fugue.execution.execution_engine import ExecutionEngine, SQLEngine
from fugue.execution.native_execution_engine import NativeExecutionEngine
from triad.utils.convert import to_instance
from triad import assert_or_throw, ParamDict

class _ExecutionEngineFactory(object):
    def __init__(self):
        self._funcs: Dict[str, Callable] = {}
        self._type_funcs: Dict[Type, Callable] = {}
        self._sql_funcs: Dict[str, Callable] = {}
        self.register_default(lambda conf, **kwargs: NativeExecutionEngine(conf=conf))
        self.register_default_sql_engine(lambda engine, **kwargs: engine.sql_engine)

    def register(
        self, name_or_type: Union[str, Type], func: Callable, on_dup="overwrite"
    ) -> None:
        if isinstance(name_or_type, str):
            self._register(self._funcs, name=name_or_type, func=func, on_dup=on_dup)
        else:
            self._register(
                self._type_funcs, name=name_or_type, func=func, on_dup=on_dup
            )

    def register_default(self, func: Callable, on_dup="overwrite") -> None:
        self.register("", func, on_dup)

    def register_sql_engine(
        self, name: str, func: Callable, on_dup="overwrite"
    ) -> None:
        self._register(self._sql_funcs, name=name, func=func, on_dup=on_dup)

    def register_default_sql_engine(self, func: Callable, on_dup="overwrite") -> None:
        self.register_sql_engine("", func, on_dup)

    def make(
        self, engine: Any = None, conf: Any = None, **kwargs: Any
    ) -> ExecutionEngine:
        if isinstance(engine, tuple):
            execution_engine = self.make_execution_engine(
                engine[0], conf=conf, **kwargs
            )
            sql_engine = self.make_sql_engine(engine[1], execution_engine)
            execution_engine.set_sql_engine(sql_engine)
            return execution_engine
        else:
            return self.make((engine, None), conf=conf, **kwargs)

    def make_execution_engine(
        self, engine: Any = None, conf: Any = None, **kwargs: Any
    ) -> ExecutionEngine:
        # Apply this function to an Execution Engine instance can
        # make sure the compile conf is a superset of conf
        # TODO: it's a mess here, can we make the logic more intuitive?

        def make_engine(engine: Any) -> ExecutionEngine:
            if isinstance(engine, str) and engine in self._funcs:
                return self._funcs[engine](conf, **kwargs)
            for k, f in self._type_funcs.items():
                if isinstance(engine, k):
                    return f(engine, conf, **kwargs)
            if isinstance(engine, ExecutionEngine):
                if conf is not None:
                    engine.compile_conf.update(conf)
                engine.compile_conf.update(kwargs)
                return engine
            return to_instance(
                engine, ExecutionEngine, kwargs=dict(conf=conf, **kwargs)
            )

        result = make_engine(engine or "")
        result.compile_conf.update(result.conf, on_dup=ParamDict.IGNORE)
        result.compile_conf.update(conf, on_dup=ParamDict.OVERWRITE)
        result.compile_conf.update(kwargs, on_dup=ParamDict.OVERWRITE)
        return result

    def make_sql_engine(
        self,
        engine: Any = None,
        execution_engine: Optional[ExecutionEngine] = None,
        **kwargs: Any,
    ) -> SQLEngine:
        if engine is None:
            engine = ""
        if isinstance(engine, str) and engine in self._sql_funcs:
            return self._sql_funcs[engine](execution_engine, **kwargs)
        if isinstance(engine, SQLEngine):
            assert_or_throw(
                execution_engine is None and len(kwargs) == 0,
                lambda: ValueError(
                    f"{engine} is an instance, can't take arguments "
                    f"execution_engine={execution_engine}, kwargs={kwargs}"
                ),
            )
            return engine
        return to_instance(
            engine, SQLEngine, kwargs=dict(execution_engine=execution_engine, **kwargs)
        )

    def _register(
        self,
        callables: Dict[Any, Callable],
        name: Any,
        func: Callable,
        on_dup="overwrite",
    ) -> None:
        if name not in callables:
            callables[name] = func
        if on_dup in ["raise", "throw"]:
            raise KeyError(f"{name} is already registered")
        if on_dup == "overwrite":
            callables[name] = func
            return
        if on_dup == "ignore":
            return
        raise ValueError(on_dup)

_EXECUTION_ENGINE_FACTORY = _ExecutionEngineFactory()

def register_execution_engine(
    name_or_type: Union[str, Type], func: Callable, on_dup="overwrite"
) -> None:
    """Register :class:`~fugue.execution.execution_engine.ExecutionEngine` with
    a given name.

    :param name_or_type: alias of the execution engine, or type of an object that
      can be converted to an execution engine
    :param func: a callable taking |ParamsLikeObject| and ``**kwargs`` and returning an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        Alias registration examples:

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_execution_engine("my", lambda conf: MyExecutionEngine(conf))

            # 0
            make_execution_engine("my")
            make_execution_engine("my", {"myconfig":"value})

            # 1
            with FugueWorkflow("my") as dag:
                dag.create([[0]],"a:int").show()

            # 2
            dag = FugueWorkflow()
            dag.create([[0]],"a:int").show()
            dag.run("my", {"myconfig":"value})

            # 3
            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run("my")

        Type registration examples:

        .. code-block:: python

            from pyspark.sql import SparkSession
            from fugue_spark import SparkExecutionEngine
            from fugue_sql import fsql

            register_execution_engine(
                SparkSession,
                lambda session, conf: SparkExecutionEngine(session, conf))

            spark_session = SparkSession.builder.getOrCreate()

            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run(spark_session)
    """
    _EXECUTION_ENGINE_FACTORY.register(name_or_type, func, on_dup)

def register_default_execution_engine(func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.ExecutionEngine` as the
    default engine.

    :param func: a callable taking |ParamsLikeObject| and ``**kwargs`` and returning an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_default_execution_engine(lambda conf: MyExecutionEngine(conf))

            # the following examples will use MyExecutionEngine

            # 0
            make_execution_engine()
            make_execution_engine(None, {"myconfig":"value})

            # 1
            with FugueWorkflow() as dag:
                dag.create([[0]],"a:int").show()

            # 2
            dag = FugueWorkflow()
            dag.create([[0]],"a:int").show()
            dag.run(None, {"myconfig":"value})

            # 3
            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run("", {"myconfig":"value})
    """
    _EXECUTION_ENGINE_FACTORY.register_default(func, on_dup)

def register_sql_engine(name: str, func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.SQLEngine` with
    a given name.

    :param name: name of the SQL engine
    :param func: a callable taking
      :class:`~fugue.execution.execution_engine.ExecutionEngine`
      and ``**kwargs`` and returning a
      :class:`~fugue.execution.execution_engine.SQLEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_sql_engine("mysql", lambda engine: MySQLEngine(engine))

            # create execution engine with MySQLEngine as the default
            make_execution_engine(("", "mysql"))

            # create DaskExecutionEngine with MySQLEngine as the default
            make_execution_engine(("dask", "mysql"))

            # default execution engine + MySQLEngine
            with FugueWorkflow(("","mysql")) as dag:
                dag.create([[0]],"a:int").show()
    """
    _EXECUTION_ENGINE_FACTORY.register_sql_engine(name, func, on_dup)

def register_default_sql_engine(func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.SQLEngine` as the
    default engine

    :param func: a callable taking
      :class:`~fugue.execution.execution_engine.ExecutionEngine`
      and ``**kwargs`` and returning a
      :class:`~fugue.execution.execution_engine.SQLEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. note::

        You should be careful to use this function, because when you set a custom
        SQL engine as default, all execution engines you create will use this SQL
        engine unless you are explicit. For example if you set the default SQL engine
        to be a Spark specific one, then if you start a NativeExecutionEngine, it will
        try to use it and will throw exceptions.

        So it's always a better idea to use ``register_sql_engine`` instead

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_default_sql_engine(lambda engine: MySQLEngine(engine))

            # create NativeExecutionEngine with MySQLEngine as the default
            make_execution_engine()

            # create SparkExecutionEngine with MySQLEngine instead of SparkSQLEngine
            make_execution_engine("spark")

            # NativeExecutionEngine with MySQLEngine
            with FugueWorkflow() as dag:
                dag.create([[0]],"a:int").show()
    """
    _EXECUTION_ENGINE_FACTORY.register_default_sql_engine(func, on_dup)

def make_execution_engine(
    engine: Any = None, conf: Any = None, **kwargs: Any
) -> ExecutionEngine:
    """Create :class:`~fugue.execution.execution_engine.ExecutionEngine`
    with specified ``engine``

    :param engine: it can be empty string or null (use the default execution
      engine), a string (use the registered execution engine), an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` type, or
      the :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
      , or a tuple of two values where the first value represents execution
      engine and the second value represents the sql engine (you can use ``None``
      for either of them to use the default one), defaults to None
    :param conf: |ParamsLikeObject|, defaults to None
    :param kwargs: additional parameters to initialize the execution engine

    :return: the :class:`~fugue.execution.execution_engine.ExecutionEngine`
      instance

    .. admonition:: Examples

        .. code-block:: python

            register_default_execution_engine(lambda conf: E1(conf))
            register_execution_engine("e2", lambda conf, **kwargs: E2(conf, **kwargs))

            register_sql_engine("s", lambda conf: S2(conf))

            # E1 + E1.default_sql_engine
            make_execution_engine()

            # E2 + E2.default_sql_engine
            make_execution_engine(e2)

            # E1 + S2
            make_execution_engine((None, "s"))

            # E2(conf, a=1, b=2) + S2
            make_execution_engine(("e2", "s"), conf, a=1, b=2)

            # SparkExecutionEngine + SparkSQLEngine
            make_execution_engine(SparkExecutionEngine)
            make_execution_engine(SparkExecutionEngine(spark_session, conf))

            # SparkExecutionEngine + S2
            make_execution_engine((SparkExecutionEngine, "s"))
    """
    import fugue._utils.register  # pylint: disable=W0611 # noqa: F401

    return _EXECUTION_ENGINE_FACTORY.make(engine, conf, **kwargs)

def make_sql_engine(
    engine: Any = None,
    execution_engine: Optional[ExecutionEngine] = None,
    **kwargs: Any,
) -> SQLEngine:
    """Create :class:`~fugue.execution.execution_engine.SQLEngine`
    with specified ``engine``

    :param engine: it can be empty string or null (use the default SQL
      engine), a string (use the registered SQL engine), an
      :class:`~fugue.execution.execution_engine.SQLEngine` type, or
      the :class:`~fugue.execution.execution_engine.SQLEngine` instance
      (you can use ``None`` to use the default one), defaults to None
    :param execution_engine: the
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
      to create
      the :class:`~fugue.execution.execution_engine.SQLEngine`. Normally you
      should always provide this value.
    :param kwargs: additional parameters to initialize the sql engine

    :return: the :class:`~fugue.execution.execution_engine.SQLEngine`
      instance

    .. note::

        For users, you normally don't need to call this function directly.
        Use ``make_execution_engine`` instead

    .. admonition:: Examples

        .. code-block:: python

            register_default_sql_engine(lambda conf: S1(conf))
            register_sql_engine("s2", lambda conf: S2(conf))

            engine = NativeExecutionEngine()

            # S1(engine)
            make_sql_engine(None, engine)

            # S1(engine, a=1)
            make_sql_engine(None, engine, a=1)

            # S2(engine)
            make_sql_engine("s2", engine)

            # SqliteEngine(engine)
            make_sql_engine(SqliteEngine)
    """
    import fugue._utils.register  # pylint: disable=W0611 # noqa: F401

    return _EXECUTION_ENGINE_FACTORY.make_sql_engine(engine, execution_engine, **kwargs)


# encoding: utf-8

import badgrlog
import datetime

from django.utils import timezone
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import status

from backpack.models import BackpackCollection, BackpackBadgeShare, BackpackCollectionShare
from backpack.serializers_v1 import CollectionSerializerV1, LocalBadgeInstanceUploadSerializerV1
from backpack.serializers_v2 import BackpackAssertionSerializerV2, BackpackCollectionSerializerV2, \
    BackpackImportSerializerV2, BackpackAssertionAcceptanceSerializerV2
from entity.api import BaseEntityListView, BaseEntityDetailView
from issuer.models import BadgeInstance
from issuer.permissions import AuditedModelOwner, VerifiedEmailMatchesRecipientIdentifier, BadgrOAuthTokenHasScope
from issuer.public_api import ImagePropertyDetailView
from apispec_drf.decorators import apispec_list_operation, apispec_post_operation, apispec_get_operation, \
    apispec_delete_operation, apispec_put_operation, apispec_operation
from mainsite.permissions import AuthenticatedWithVerifiedIdentifier

logger = badgrlog.BadgrLogger()

_TRUE_VALUES = ['true', 't', 'on', 'yes', 'y', '1', 1, 1.0, True]
_FALSE_VALUES = ['false', 'f', 'off', 'no', 'n', '0', 0, 0.0, False]

def _scrub_boolean(boolean_str, default=None):
    if boolean_str in _TRUE_VALUES:
        return True
    if boolean_str in _FALSE_VALUES:
        return False
    return default

class BackpackAssertionList(BaseEntityListView):
    model = BadgeInstance
    v1_serializer_class = LocalBadgeInstanceUploadSerializerV1
    v2_serializer_class = BackpackAssertionSerializerV2
    create_event = badgrlog.BadgeUploaded
    permission_classes = (AuthenticatedWithVerifiedIdentifier, VerifiedEmailMatchesRecipientIdentifier, BadgrOAuthTokenHasScope)
    http_method_names = ('get', 'post')
    valid_scopes = {
        'get': ['r:backpack', 'rw:backpack'],
        'post': ['rw:backpack'],
    }
    include_defaults = {
        'include_expired': {'v1': 'true', 'v2': 'false'},
        'include_revoked': {'v1': 'false', 'v2': 'false'},
        'include_pending': {'v1': 'false', 'v2': 'false'},
    }

    def get_objects(self, request, **kwargs):
        version = kwargs.get('version', 'v1')
        include_expired = request.query_params.get(
            'include_expired', self.include_defaults['include_expired'][version]
        ).lower() in ['1', 'true']
        include_revoked = request.query_params.get(
            'include_revoked', self.include_defaults['include_revoked'][version]
        ).lower() in ['1', 'true']
        include_pending = request.query_params.get(
            'include_pending', self.include_defaults['include_pending'][version]
        ).lower() in ['1', 'true']

        def badge_filter(b):
            if ((b.acceptance == BadgeInstance.ACCEPTANCE_REJECTED) or
                    (not include_expired and b.expires_at != None and b.expires_at < timezone.now()) or
                    (not include_revoked and b.revoked) or
                    (not include_pending and b.pending)):
                return False
            return True

        return list(filter(badge_filter, self.request.user.cached_badgeinstances()))

    @apispec_list_operation('Assertion',
        summary="Get a list of Assertions in authenticated user's backpack ",
        tags=['Backpack']
    )
    def get(self, request, **kwargs):
        mykwargs = kwargs.copy()
        mykwargs['expands'] = []
        expands = request.GET.getlist('expand', [])

        if 'badgeclass' in expands:
            mykwargs['expands'].append('badgeclass')
        if 'issuer' in expands:
            mykwargs['expands'].append('issuer')

        return super(BackpackAssertionList, self).get(request, **mykwargs)

    @apispec_post_operation('Assertion',
        summary="Upload a new Assertion to the backpack",
        tags=['Backpack']
    )
    def post(self, request, **kwargs):
        if kwargs.get('version', 'v1') == 'v1':
            try:
                return super(BackpackAssertionList, self).post(request, **kwargs)
            except serializers.ValidationError as e:
                self.log_not_created(e)
                raise e
        raise NotImplementedError("use BackpackImportBadge.post instead")

    def log_not_created(self, error):
        request = self.request
        user = request.user
        image_data = ''
        user_entity_id = ''
        error_name = ''
        error_result = ''

        if request.data.get('image', None) is not None:
            image_data = request.data.get('image', '')[:1024]

        if user is not None:
            user_entity_id = user.entity_id

        if len(error.detail) <= 1:
            #grab first error
            e = error.detail[0]
            error_name = e.get('name', '')
            error_result = e.get('result', '')

        invalid_badge_upload_report = badgrlog.InvalidBadgeUploadReport(image_data, user_entity_id, error_name, error_result)
        logger.event(badgrlog.InvalidBadgeUploaded(invalid_badge_upload_report))

    def get_context_data(self, **kwargs):
        context = super(BackpackAssertionList, self).get_context_data(**kwargs)
        context['format'] = self.request.query_params.get('json_format', 'v1')  # for /v1/earner/badges compat
        return context

class BackpackAssertionDetail(BaseEntityDetailView):
    model = BadgeInstance
    v1_serializer_class = LocalBadgeInstanceUploadSerializerV1
    v2_serializer_class = BackpackAssertionSerializerV2
    permission_classes = (AuthenticatedWithVerifiedIdentifier, VerifiedEmailMatchesRecipientIdentifier, BadgrOAuthTokenHasScope)
    http_method_names = ('get', 'delete', 'put')
    valid_scopes = {
        'get': ['r:backpack', 'rw:backpack'],
        'put': ['rw:backpack'],
        'delete': ['rw:backpack'],
    }

    def get_context_data(self, **kwargs):
        context = super(BackpackAssertionDetail, self).get_context_data(**kwargs)
        context['format'] = self.request.query_params.get('json_format', 'v1')  # for /v1/earner/badges compat
        return context

    @apispec_get_operation('BackpackAssertion',
                           summary="Get detail on an Assertion in the user's Backpack",
                           tags=['Backpack']
                           )
    def get(self, request, **kwargs):
        mykwargs = kwargs.copy()
        mykwargs['expands'] = []
        expands = request.GET.getlist('expand', [])

        if 'badgeclass' in expands:
            mykwargs['expands'].append('badgeclass')
        if 'issuer' in expands:
            mykwargs['expands'].append('issuer')

        return super(BackpackAssertionDetail, self).get(request, **mykwargs)

    @apispec_delete_operation('BackpackAssertion',
                              summary='Remove an assertion from the backpack',
                              tags=['Backpack']
                              )
    def delete(self, request, **kwargs):
        obj = self.get_object(request, **kwargs)
        related_collections = list(BackpackCollection.objects.filter(backpackcollectionbadgeinstance__badgeinstance=obj))

        if obj.source_url is None:
            obj.acceptance = BadgeInstance.ACCEPTANCE_REJECTED
            obj.save()
        else:
            obj.delete()

        for collection in related_collections:
            collection.save()

        request.user.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

    @apispec_put_operation('BackpackAssertion',
                           summary="Update acceptance of an Assertion in the user's Backpack",
                           tags=['Backpack']
                           )
    def put(self, request, **kwargs):
        fields_whitelist = ('acceptance',)
        data = {k: v for k, v in list(request.data.items()) if k in fields_whitelist}

        obj = self.get_object(request, **kwargs)
        if not self.has_object_permissions(request, obj):
            return Response(status=status.HTTP_404_NOT_FOUND)

        context = self.get_context_data(**kwargs)

        update_serializer = BackpackAssertionAcceptanceSerializerV2(obj, data, context=context)
        update_serializer.is_valid(raise_exception=True)
        update_serializer.save(updated_by=request.user)

        main_serializer_class = self.get_serializer_class()
        serializer = main_serializer_class(update_serializer.instance, context=context)

        return Response(serializer.data)

class BackpackAssertionDetailImage(ImagePropertyDetailView, BadgrOAuthTokenHasScope):
    model = BadgeInstance
    prop = 'image'
    valid_scopes = ['r:backpack', 'rw:backpack']

class BackpackCollectionList(BaseEntityListView):
    model = BackpackCollection
    v1_serializer_class = CollectionSerializerV1
    v2_serializer_class = BackpackCollectionSerializerV2
    permission_classes = (AuthenticatedWithVerifiedIdentifier, AuditedModelOwner, BadgrOAuthTokenHasScope)
    valid_scopes = {
        'get': ['r:backpack', 'rw:backpack'],
        'post': ['rw:backpack'],
    }

    def get_objects(self, request, **kwargs):
        return self.request.user.cached_backpackcollections()

    @apispec_get_operation('Collection',
                           summary='Get a list of Collections',
                           tags=['Backpack']
                           )
    def get(self, request, **kwargs):
        return super(BackpackCollectionList, self).get(request, **kwargs)

    @apispec_post_operation('Collection',
                            summary='Create a new Collection',
                            tags=['Backpack']
                            )
    def post(self, request, **kwargs):
        return super(BackpackCollectionList, self).post(request, **kwargs)

class BackpackCollectionDetail(BaseEntityDetailView):
    model = BackpackCollection
    v1_serializer_class = CollectionSerializerV1
    v2_serializer_class = BackpackCollectionSerializerV2
    permission_classes = (AuthenticatedWithVerifiedIdentifier, AuditedModelOwner, BadgrOAuthTokenHasScope)
    valid_scopes = {
        'get': ['r:backpack', 'rw:backpack'],
        'post': ['rw:backpack'],
        'put': ['rw:backpack'],
        'delete': ['rw:backpack']
    }

    @apispec_get_operation('Collection',
                           summary='Get a single Collection',
                           tags=['Backpack']
                           )
    def get(self, request, **kwargs):
        return super(BackpackCollectionDetail, self).get(request, **kwargs)

    @apispec_put_operation('Collection',
                           summary='Update a Collection',
                           tags=['Backpack']
                           )
    def put(self, request, **kwargs):
        return super(BackpackCollectionDetail, self).put(request, **kwargs)

    @apispec_delete_operation('Collection',
                              summary='Delete a collection',
                              tags=['Backpack']
                              )
    def delete(self, request, **kwargs):
        return super(BackpackCollectionDetail, self).delete(request, **kwargs)

class BackpackImportBadge(BaseEntityListView):
    v2_serializer_class = BackpackImportSerializerV2
    permission_classes = (AuthenticatedWithVerifiedIdentifier, BadgrOAuthTokenHasScope,)
    http_method_names = ('post',)
    valid_scopes = ['rw:backpack']

    @apispec_operation(
        summary="Import a new Assertion to the backpack",
        tags=['Backpack'],
        parameters=[
            {
                "in": "body",
                "name": "body",
                "required": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "url": {
                            "type": "string",
                            "format": "url",
                            "description": "URL to an OpenBadge compliant badge",
                            'required': False
                        },
                        "image": {
                            'type': "string",
                            'format': "data:image/png;base64",
                            'description': "base64 encoded Baked OpenBadge image",
                            'required': False
                        },
                        "assertion": {
                            'type': "json",
                            'description': "OpenBadge compliant json",
                            'required': False
                        },
                    }
                },
            }
        ]
    )
    def post(self, request, **kwargs):
        context = self.get_context_data(**kwargs)
        serializer_class = self.get_serializer_class()
        serializer = serializer_class(data=request.data, context=context)
        serializer.is_valid(raise_exception=True)
        new_instance = serializer.save(created_by=request.user)
        self.log_create(new_instance)

        response_serializer = BackpackAssertionSerializerV2(new_instance, context=context)
        return Response(response_serializer.data, status=status.HTTP_201_CREATED)

class ShareBackpackAssertion(BaseEntityDetailView):
    model = BadgeInstance
    permission_classes = (permissions.AllowAny,)  # this is AllowAny to support tracking sharing links in emails
    http_method_names = ('get',)
    allow_any_unauthenticated_access = True

    def get(self, request, **kwargs):
        """
        Share a single badge to a support share provider
        ---
        parameters:
            - name: provider
              description: The identifier of the provider to use. Supports 'facebook', 'linkedin'
              required: true
              type: string
              paramType: query
        """
        redirect = _scrub_boolean(request.query_params.get('redirect', "1"))

        provider = request.query_params.get('provider')
        if not provider:
            return Response({'error': "unspecified share provider"}, status=status.HTTP_400_BAD_REQUEST)
        provider = provider.lower()

        source = request.query_params.get('source', 'unknown')

        badge = self.get_object(request, **kwargs)
        if not badge:
            return Response(status=status.HTTP_404_NOT_FOUND)

        include_identifier = _scrub_boolean(request.query_params.get('include_identifier', False))

        share = BackpackBadgeShare(provider=provider, badgeinstance=badge, source=source)
        share_url = share.get_share_url(provider, include_identifier=include_identifier)
        if not share_url:
            return Response({'error': "invalid share provider"}, status=status.HTTP_400_BAD_REQUEST)

        share.save()
        logger.event(badgrlog.BadgeSharedEvent(badge, provider, datetime.datetime.now(), source))

        if redirect:
            headers = {'Location': share_url}
            return Response(status=status.HTTP_302_FOUND, headers=headers)
        else:
            return Response({'url': share_url})

class ShareBackpackCollection(BaseEntityDetailView):
    model = BackpackCollection
    permission_classes = (permissions.AllowAny,)  # this is AllowAny to support tracking sharing links in emails
    http_method_names = ('get',)

    def get(self, request, **kwargs):
        """
        Share a collection to a supported share provider
        ---
        parameters:
            - name: provider
              description: The identifier of the provider to use. Supports 'facebook', 'linkedin'
              required: true
              type: string
              paramType: query
        """
        redirect = _scrub_boolean(request.query_params.get('redirect', "1"))

        provider = request.query_params.get('provider')
        if not provider:
            return Response({'error': "unspecified share provider"}, status=status.HTTP_400_BAD_REQUEST)
        provider = provider.lower()

        source = request.query_params.get('source', 'unknown')

        collection = self.get_object(request, **kwargs)
        if not collection:
            return Response(status=status.HTTP_404_NOT_FOUND)

        share = BackpackCollectionShare(provider=provider, collection=collection, source=source)
        share_url = share.get_share_url(provider, title=collection.name, summary=collection.description)
        if not share_url:
            return Response({'error': "invalid share provider"}, status=status.HTTP_400_BAD_REQUEST)

        share.save()

        if redirect:
            headers = {'Location': share_url}
            return Response(status=status.HTTP_302_FOUND, headers=headers)
        else:
            return Response({'url': share_url})


from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

import numpy as np

from btk import btk
import os

import matplotlib

import matplotlib.pyplot as plt
from tkinter import *
from tkinter.messagebox import *
from tkinter import ttk

# Label = strike / off ; context = droite / gauche

def filtreExtremum(extrem, originalData):
    if 0 in extrem:
        extrem = extrem[1:]
    if len(originalData)-1 in extrem:
        extrem = extrem[:-1]
    return extrem
# But : trouvé tous les maximum locaux
# In : un vecteur de taille nx1
# Out : les positions x des max locaux (pas leur valeur y)
def maxLocal(a):
    TFarray = np.array(np.r_[True, a[1:] > a[:-1]] & np.r_[a[:-1] > a[1:], True])   # Rempli un vecteur avec que des False, sauf lorsqu'une donnée dans le vecteur est plus grande que son voisin de droite et de gauche (il met alors True)
    indMax = np.ravel( np.where( TFarray == True ) )    # On récupère les index où il y a les True
    indMax = filtreExtremum(indMax, a)
    return indMax

# Fonctions en cours, pas encore effective
def semiMaxLocal(a):
    TFarray = np.array(np.r_[True, a[1:] > a[:-1]] & np.r_[a[:-1] == a[1:], True])
    indSemiMax = np.where( TFarray == True )
    return indSemiMax

def findMinMin(data, Min):
    minMin = minLocal(data[Min])
    return Min[minMin]

# Pareil que maxLocal, mais pour trouver les minimum locaux
def minLocal(a):
    TFarray = np.array(np.r_[True, a[1:] < a[:-1]] & np.r_[a[:-1] < a[1:], True])
    indMin = np.ravel( np.where( TFarray == True ) )
    indMin = filtreExtremum(indMin, a)
    return indMin

#clean the arrays of all local extremum that are too close of eachother (extremums super local)
def cleanMinMax(indMin, indMax):
    for i in indMax:
        for j in np.flip(indMin,0):
            if(np.abs(i-j)<7):
                indMax = np.extract(indMax!=i,indMax)

                indMin = np.extract(indMin!=j,indMin)
                break
    return indMin, indMax

# Dico avec comme clé les labels, et comme valeur un entier
# e.g. : DicoLabels = {"LSHO" = 0, "RSHO" = 1, "RANK" = 2, ...}
# Fonction jamais utilisé pour l'instant
def dicLab(metadata):
    point_labels = metadata.FindChild("POINT").value().FindChild("LABELS").value().GetInfo().ToString()

    dicoLabels = {}
    index = 0
    for lab in point_labels:
        dicoLabels[lab] = index
        index += 1

    return dicoLabels

# Plot les events dans la figure "ax", lignes verticales
# In : acq, qui contient les events ; ax, où on va ploter les lignes verticales
# Out : la nouvelle figure, où on a ploté les lignes
def plotEvent(acq, ax):
    n_events = acq.GetEventNumber()             # On récupère le nombre d'évènements, pour les parcourirs
    for numevent in range(n_events):            # On parcours les indices des évènements
        event = acq.GetEvent(numevent)          # On récupère un évènement, grâce à son indice correspondant
        event_frame = event.GetFrame()          # On récupère la frame où se situe l'évènement
        context = event.GetContext()            # On récupère le context (e.g. Left ou Right)
        label = event.GetLabel()                # On récupère le label (e.g. : Foot_Strike_GS)
        if context == 'Left':                   # Test si c'est le pied gauche
            if label == 'Foot_Strike_GS':       # Test si c'est quand le pied touche le sol
                leftLineStrike = ax.axvline(x = event_frame, color='r', label='Left - Strike', linestyle='--')  # Plot en rouge, avec des tirets
#                ax.legend([leftLineStrike], 'Left - Strike')
            elif label == 'Foot_Off_GS':        # Test si c'est quand le pied ne touche plus le sol
                leftLineOff = ax.axvline(x = event_frame, color='r', label='Left - Off', linestyle='-.')        # Plot en rouge, avec des tirets et des points
        if context == 'Right':                  # Test si c'est le pied droit
            if label == 'Foot_Strike_GS':       # Test si c'est quand le pied touche le sol
                rightLineStrike = ax.axvline(x = event_frame, color='g', label='Righ - Strike', linestyle='--') # Plot en vert, avec des tirets
            elif label == 'Foot_Off_GS':        # Test si c'est quand le pied ne touche plus le sol
                rightLineOff = ax.axvline(x = event_frame, color='g', label='Right - Off', linestyle='-.')      # Plot en rouge, avec des tirets et des points

    # On rajoute la légende
    # S'IL Y A UNE ERREUR, ENLEVER CETTE LIGNE
#    ax.legend((leftLineOff, rightLineStrike, rightLineOff), ('Left - Off', 'Right - Strike', 'Right - Off'))

    return ax

# Selectionne les éléments de files ayant un event correspondant au label et au contexte
# Renvoie en training set (3/4) et un testing set (1/4) constitués de ces éléments.
def selectWithExistingEvent(files, lab, cont):
    eventfiles = []
    for acq in files:
        n_events = acq.GetEventNumber()             # On récupère le nombre d'évènements, pour les parcourirs
        for numevent in range(n_events):            # On parcours les indices des évènements
            event = acq.GetEvent(numevent)          # On récupère un évènement, grâce à son indice correspondant
            if event.GetLabel() == lab and event.GetContext()==cont:                        # Test si c'est le label recherché
                eventfiles.append(acq)
                break
    test = np.random.choice(eventfiles, (len(eventfiles)//4), replace = False).tolist()
    train = list(set(eventfiles)-set(test))
    return train, test

# But : Récupérer les données
# In : path des données (Attention : le chemin commence de là où est le fichier)
# Out : les données
def initial(pathFile):
    reader = btk.btkAcquisitionFileReader()
    reader.SetFilename(pathFile)
    reader.Update()
    acq = reader.GetOutput()
    return acq

def save(acq, pathFile):
    writer = btk.btkAcquisitionFileWriter()
    writer.SetInput(acq)
    writer.SetFilename(pathFile)
    writer.Update()

def allFiles(path):
    files = []

    # Pour trouver tous les fichiers .c3d
    for r, d, f in os.walk(path):
        for file in f:
            if '.c3d' in file:
                files.append(initial(os.path.join(r, file)))
    return files

# But : avoir des infos à propos des frames de "acq"
# In : les données acq
# Out : nombres de frames, numéro de la 1ère frame, numéro de la dernière frame
def frameData(acq):
# get some parameters
    n_frames = acq.GetPointFrameNumber()  # give the number of frames
    first_frame = acq.GetFirstFrame()
    last_frame = acq.GetLastFrame()
    return n_frames, first_frame, last_frame

# But : créer un nouvel évènement
# Un nouvel évènement est caractérisé par un label, un context, et un numéro de frame
# In : les données "acq", un label, un context, et une frame
def addEvent(acq, label, context, frameNumber):
    newEvent = btk.btkEvent()           # Créer un nouvel évènement vide
    newEvent.SetLabel(label)            # Met un label
    newEvent.SetContext(context)        # Met un context
    newEvent.SetFrame(frameNumber)      # Met la positoin, la frame
    acq.AppendEvent(newEvent)           # Rajoute l'évènement parmi tous les autres évènements

# But : équivalent à print('obj = ', obj)
# Pas nécessaire pour le projet
def printName(obj, namespace):
    nom = [name for name in namespace if namespace[name] is obj]
    print(nom[0],' = ', obj)

# But : Avoir toutes les infos d'un évènements
# In : les données "acq", et le numéro de l'évènement
# Out : l'évènement, le label, le context, et le num de la frame
def eventInfo(acq, numEvent):
    event = acq.GetEvent(0) # extract the first event of the aquisition
    label = event.GetLabel() # return a string representing the Label
    context = event.GetContext() # return a string representing the Context
    frame = event.GetFrame() # return the frame as an integer
    return event, label, context, frame

# But : trouver l'évènement le plus proche d'une position, frame donnée
# In : des données "data", l'ensemble des évènements (AllEvents), le label et le context recherché , et la position depuis laquel on recherche
# Out : l'évènement, et la frame cprrespondante
def closestEvent(data, AllEvents, label=0, context=0, start=1):
    if (label == 0) and (context == 0):
        return AllEvents.GetItem(0), AllEvents.GetItem(0).GetFrame()

    eventVIP = []   # Array qui contiendra tous les évènements correspondant au même label et même contexte que demandé
    numberEvent = AllEvents.GetItemNumber()     # Nombre d'évènements au total
    for num in range(numberEvent):              # On regarde tout les évènement
        event = AllEvents.GetItem(num)          # On récupère un évènement
        if (event.GetContext() == context) and (event.GetLabel() == label): # Test si on a les mêmes context et label
            eventVIP.append(event)              # On rajoute l'évènement

    if len(eventVIP) == 0:                      # Si on a trouvé aucun évènement recherché, on arrête
        return 0, 0

    dist = 1000                                 # On initialise une distance très grande, qui diminuera
    even = eventVIP[0]                          # On commence par le premier évènement
    for event in eventVIP:                      # On parcours les évènements
        if np.abs(event.GetFrame() - start) < dist:     # On test si la distance entre la position de départ et un évènement correspondant
            dist = np.abs(event.GetFrame() - start)     # On mémorise la nouvel distance
            even = event                                # On mémorise le nouvel évènement

    return even, even.GetFrame()

# But : trouver l'extremum le plus proche d'une position de départ
# In : position de départ "start", les indices (position x) d'extremum (les min ou les max)
# Out : position x de l'extremum, la distance par rapport au point de départ (start), et l'indice dans l'array des min ou max
def closestExtrem(start, indExtrem):  # Renvoie la position de l'extrem par rapport à la frame Start
    AllDistance = indExtrem - start         # Soustraction d'un vecteur par un scalaire, ici les positions des indices moins la position de départ (start)
    absDist = np.abs(AllDistance)           # On met en valeur absolue
    indexMinimDist = np.argmin(absDist)     # On récupère l'indice de la distance minimale
    positionExtrem = indExtrem[indexMinimDist]  # On récupère la position x de l'extremum
    distance = AllDistance[indexMinimDist]      # On récupère la distance (sans la valeur absolue)
    return positionExtrem, distance, indexMinimDist

def plotPosi(acq, position, axe, ax, event=0):

    dicoAxe = {"x" : 0, "y" : 1, "z" : 2}
    data = np.array(acq.GetPoint(position).GetValues()[:, dicoAxe[axe]])
    n_frames, first_frame, last_frame = frameData(acq)
    Min, Max = minLocal(data), maxLocal(data)
    Min, Max = cleanMinMax(Min, Max)  #used to clean some local extremums
    # Plot part
    ax.plot(np.array(range(first_frame, last_frame + 1)), data, 'k')
    ax.plot(Min, data[Min], 'o b')
    ax.plot(Max, data[Max], 'o', color='purple')
    ax = plotEvent(acq, ax)

    if (event != 0):
        print('Position de depart :', event.GetFrame())
        positionExtrem, distance, indexMinimDist = closestExtrem(event.GetFrame(), Max)
        ax.plot(positionExtrem, data[positionExtrem], 'o g')
        print('Position :', positionExtrem)
    plt.title(" Position = {} - axis = {}".format(position, axe))
    # ax.show(block = False)
    return ax

def simple(files, posiCombo, axeCombo , buttonCombo, fileCombo):
    posiCombo['values'] = ['LFHD', 'RFHD', 'LBHD', 'RBHD', 'C7', 'T10', 'STRN', 'CLAV', 'RBAK', 'LSHO', 'LELB', 'LWRA', 'LWRB', 'RSHO', 'RELB', 'RWRA', 'RWRB', 'LASI', 'RASI', 'LPSI', 'RPSI', 'LTHI', 'RTHI', 'LKNE', 'RKNE', 'LTIB', 'RTIB', 'LANK', 'RANK', 'LHEE', 'RHEE', 'RTOE', 'LTOE']
    posiCombo.current(0)
    buttonCombo["text"] = "PLOT"
    buttonCombo["command"] = lambda: onePlot(files, posiCombo, axeCombo, fileCombo )

def double(files, posiCombo, axeCombo , buttonCombo, fileCombo):
    posiCombo['values'] = ["FHD", "BHD", "SHO", "ELB", "WRA", "WRB", "ASI", "PSI", "THI", "KNE", "TIB", "ANK", "HEE", "TOE"]
    posiCombo.current(0)
    buttonCombo["text"] = "PLOT x2"
    buttonCombo["command"] = lambda: twoPlot(files, posiCombo, axeCombo, fileCombo )

def onePlot (files, posiCombo, axeCombo, fileCombo ):
    acq = files[int(fileCombo.get())]             # voir le chapitre sur les événements
    n_frames, first_frame, last_frame = frameData(acq)
    plt.figure(figsize=(9,7))
    guiPlot = plt.subplot()
    guiPlot = plotPosi(acq, posiCombo.get(), axeCombo.get(), guiPlot)
    plt.show(block=False)

def twoPlot(files, posiCombo, axeCombo, fileCombo ):               # voir le chapitre sur les événements
    acq = files[int(fileCombo.get())]
    n_frames, first_frame, last_frame = frameData(acq)
    dr = 'R' + posiCombo.get()
    ga = 'L' + posiCombo.get()
    plt.figure(figsize=(9,7))
    guiPlot = plt.subplot(2,1,1)
    guiPlot = plotPosi(acq, dr, axeCombo.get(), guiPlot)
    guiPlot = plt.subplot(2,1,2)
    guiPlot = plotPosi(acq, ga, axeCombo.get(), guiPlot)
    plt.show(block=False)

def GUIplot(files):
    acq = files[0]
    metadata = acq.GetMetaData()
    point_labels = list(metadata.FindChild("POINT").value().FindChild("LABELS").value().GetInfo().ToString())

    win = Tk()
    win.title("BTK Project")
    # win.geometry("500x100")

    ttk.Label(win, text="Choix du capteur").grid(column=1, row=0)
    posiCombo = ttk.Combobox(win, values=point_labels)
    posiCombo.grid(column=1, row=1)
    ttk.Label(win, text="Choix de l'axe").grid(column=2, row=0)
    axeCombo = ttk.Combobox(win, values=["x", "y", "z"])
    axeCombo.grid(column=2, row=1)
    ttk.Label(win, text="Choix du fichier").grid(column=0, row=0)
    fileCombo = ttk.Combobox(win, values=list(range(len(files))))
    fileCombo.grid(column=0, row=1)
    posiCombo.current(newindex=28)
    axeCombo.current(2)
    fileCombo.current(0)

    buttonCombo = Button (win, text="PLOT", command= lambda: onePlot(files, posiCombo, axeCombo, fileCombo ))
    buttonCombo.grid(column=3, row=1)

    v = IntVar()
#    v.set(1)
    R1 = Radiobutton(win, text="Plot unique", variable=v, value=1, command= lambda: simple(files, posiCombo, axeCombo , buttonCombo, fileCombo))
    R1.grid(column=0, row=2)
    R2 = Radiobutton(win, text="Double Plot", variable=v, value=2, command= lambda: double(files, posiCombo, axeCombo , buttonCombo, fileCombo))
    R2.grid(column=1, row=2)
    v.set(1)

    win.mainloop()


import torch
import torch.nn as nn
import numpy as np
from torch.nn.functional import upsample, interpolate
from Spa_downs import *
import torch.nn.functional as F
from torch.autograd import Variable
import argparse
from torch.nn import init
import scipy.io as sio
import os
import random

class ReshapeTo2D(nn.Module):

    def __init__(self):
        super(ReshapeTo2D, self).__init__()

    def forward(self,x):
        return torch.reshape(x, (x.shape[0], x.shape[1], x.shape[2]*x.shape[3]))

class ReshapeTo3D(nn.Module):
    def __init__(self):
        super(ReshapeTo3D, self).__init__()

    def forward(self,x):
        return  torch.reshape(x, (x.shape[0], x.shape[1], int(np.sqrt(x.shape[2])), int(np.sqrt(x.shape[2]))))

class TransDimen(nn.Module):
    def __init__(self):
        super(TransDimen, self).__init__()

    def forward(self,x):
        return torch.Tensor.permute(x,[0,2,1])

def channel_crop(data, position, length):
    assert data.size(1) >= position + length, 'the cropped channel out of size.'
    return data[:, position: position + length, :, :]

def ins (list_, data, index):
    list_start = list_[:index]
    list_start = [ Variable(i, requires_grad=False).type(torch.cuda.FloatTensor) for i in list_start]
    data = [Variable(data, requires_grad=False).type(torch.cuda.FloatTensor)]
    list_end = list_[index:]
    list_end = [ Variable(i, requires_grad=False).type(torch.cuda.FloatTensor) for i in list_end]

    return list_start + data + list_end

def to_gpu(data):
    return Variable(data, requires_grad=False).type(torch.cuda.FloatTensor)

class L_Dspec(nn.Module):
    def __init__(self,in_channel,out_channel,P_init):
        super(L_Dspec, self).__init__()
        self.in_channle = in_channel
        self.out_channel = out_channel
        self.P = nn.Parameter(P_init)

    def forward(self,input):
        S = input.shape
        out = torch.reshape(input,[S[0],S[1],S[2]*S[3]])
        out = torch.matmul(self.P,out)

        return torch.reshape(out,[S[0],self.out_channel,S[2],S[3]])

def add_wgn(x, snr):
    P_signal=torch.sum(x.abs()**2)
    P_noise = P_signal/10**(snr/10.0)
    sigma = torch.sqrt(P_noise/x.numel())
    noise = torch.randn(x.shape).type(torch.cuda.FloatTensor)
    return x + sigma * noise

def tensor_copy(x):
    return x.clone()

def parse_arg():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model'     , default='MSDANet',        help='MSDANet')
    parser.add_argument('--fusion'    , default='Concate',        help='Concate')
    parser.add_argument('--lr'        , default=1e-4, type=float, help='learning rate for optimizer')
    parser.add_argument('--batch_size', default=16, type=int,     help='batch size for training')
    parser.add_argument('--factor'    , default=8, type=int,      help='scale factor. 4/8/16')
    parser.add_argument('--dataset'   , default='Houston',        help='Houston/PaviaU/dc/PaviaC')
    parser.add_argument('--patch_size', default=64, type=int,     help='patch size of training')
    parser.add_argument('--stride'    , default=32, type=int,     help='stride of training')
    parser.add_argument('--pan'       , action='store_true',      help='pan_sharpening or MSI + HSI')
    parser.add_argument('--mem_load'  , action='store_true',      help='load the all dataset into memory or disk')
    parser.add_argument('--phase'     , default='train',          help='train/test')
    parser.add_argument('--noise'     , action='store_true',      help='wheater to add noise to LR_HSI and HR_MSI')

    return parser.parse_args()

def weights_init_kaiming(m):
    classname = m.__class__.__name__
    if classname.find('Conv2d') != -1:
        init.kaiming_normal(m.weight.data)

def split(full_list,shuffle=False,ratio=0.2):
    n_total = len(full_list)
    offset = int(n_total * ratio)
    if n_total==0 or offset<1:
        return [],full_list
    random.seed(4)
    if shuffle:
        random.shuffle(full_list)
    sublist_1 = full_list[:offset]
    sublist_2 = full_list[offset:]
    return sublist_1,sublist_2

def all_data_in(Path='Data/Houston/', datasets='Houston', Train_image_num=10):

    names = get_img_name(Path=Path, datasets=datasets)
    allData = []

    for i in range(Train_image_num):
        Data = sio.loadmat(os.path.join(Path, names[i])+'.mat')
        HSI = Data['hsi']
        HSI = HSI.transpose((2, 0, 1))
        allData.append(HSI)

    return allData

dataset_dict = dict(
    PaviaC = [10, 5, 300, 8000, 102, 1, (55, 41, 12)], ### [train_img_num, val_img_num, stop epoch, max_value, band_number, RGB]
    PaviaU = [10, 5, 300, 8000, 103, 1, (46, 27, 10)],
    Houston = [3, 2, 300, 65535, 144, 1, (65, 51, 22)],
    dc = [11, 5, 300, 65535, 191, 4, (51, 35, 21)],
    )

def get_img_name(Path='Data/Houston/', datasets='Houston'):

    names_PaviaC_list = [
    'PaviaC_01', 'PaviaC_02', 'PaviaC_03', 'PaviaC_04', 'PaviaC_05', 'PaviaC_06',
    'PaviaC_07', 'PaviaC_08', 'PaviaC_09', 'PaviaC_10', 'PaviaC_11', 'PaviaC_12',
    'PaviaC_13', 'PaviaC_14', 'PaviaC_15'
    ]

    names_Houston_list = [
    'Houston_01', 'Houston_02', 'Houston_03', 'Houston_04', 'Houston_05'
    ]

    names_dc_list = [
    'dc_01', 'dc_02', 'dc_03', 'dc_04',
    'dc_05', 'dc_06', 'dc_07', 'dc_08',
    'dc_09', 'dc_10', 'dc_11', 'dc_12',
    'dc_13', 'dc_14', 'dc_15', 'dc_16',
    ]

    names_PaviaU_list = [
    'PaviaU_01', 'PaviaU_02', 'PaviaU_03', 'PaviaU_04', 'PaviaU_05', 'PaviaU_06',
    'PaviaU_07', 'PaviaU_08', 'PaviaU_09', 'PaviaU_10', 'PaviaU_11', 'PaviaU_12',
    'PaviaU_13', 'PaviaU_14', 'PaviaU_15'
    ]

    names_Houston, names_Houston_valid = split(names_Houston_list, shuffle=True, ratio=0.6)
    names_dc, names_dc_valid = split(names_dc_list, shuffle=True, ratio=0.7)
    names_PaviaU, names_PaviaU_valid = split(names_PaviaU_list, shuffle=True, ratio=0.67)
    names_PaviaC, names_PaviaC_valid = split(names_PaviaC_list, shuffle=True, ratio=0.67)

    if datasets == 'PaviaC':
        names = names_PaviaC
    elif datasets == 'PaviaC_val':
        names = names_PaviaC_valid
    elif datasets == 'PaviaU':
        names = names_PaviaU
    elif datasets == 'PaviaU_val':
        names = names_PaviaU_valid
    elif datasets == 'Houston':
        names = names_Houston
    elif datasets == 'Houston_val':
        names = names_Houston_valid
    elif datasets == 'dc':
        names = names_dc
    elif datasets == 'dc_val':
        names = names_dc_valid

    else:
        assert 'wrong dataset name'
    return names


# -*- coding: utf-8 -*-
# Author：sen
# Date：9090/3/94 10:48

from typing import List
from heapq import *
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        from collections import Counter
        counter = Counter(nums)
        for item in counter.items(): # item: (元素, 数量)
            if item[1] > (len(nums) / 2.0):
                return item[0]

class Solution2:
    def majorityElement(self, nums: List[int]) -> int:
        # 不使用自带Counter
        counter = {}
        for num in nums:
            counter[num] = counter.get(num, 0) + 1
        for item in counter.items():
            if item[1] > (len(nums) / 2.0):
                return item[0]

if __name__ == '__main__':
    nums = [9,9,8,8,8,9,9]
    so = Solution()
    print(so.majorityElement(nums))
    so = Solution2()
    print(so.majorityElement(nums))


import cv2
import time
import os
import HandTrackingModule as htm
from dronekit import connect, VehicleMode, LocationGlobalRelative, APIException
import time
import socket
import math
import argparse
from pymavlink import mavutil
from time import sleep
import numpy as np

###################################################################################

def connectMyCopter():
    parser=argparse.ArgumentParser(description='commands')
    parser.add_argument('--connect', default='127.0.0.1:14550')
    args=parser.parse_args()

    connection_string=args.connect
    baud_rate=921600

    vehicle=connect(connection_string, baud=baud_rate, wait_ready=True)
    return vehicle

###################################################################################
# Function to arm and takeoff

def arm_and_takeoff(TargetAltitude):

    # Switch vehicle to Guided Mode
    vehicle.mode = VehicleMode("GUIDED")
    while vehicle.mode!="GUIDED":
        print("Waiting for guided mode")
        time.sleep(1)

    # Arming the Vehicle
    vehicle.armed = True
    while vehicle.armed == False:
        print("Waiting for the vehicle to be armed")
        time.sleep(1)

    vehicle.simple_takeoff(TargetAltitude)

    while True:
        print("Current Altitude: %d" , vehicle.location.global_relative_frame.alt)
        if vehicle.location.global_relative_frame.alt >= TargetAltitude*.95:
            break
        time.sleep(1)

    print("Target Altitude reached")
    return None

##################################################################
#-- Define the function for sending mavlink velocity command in body frame
def set_velocity_body(vehicle, vx, vy, vz):
    """ Remember: vz is positive downward!!!
    http://ardupilot.org/dev/docs/copter-commands-in-guided-mode.html

    Bitmask to indicate which dimensions should be ignored by the vehicle
    (a value of 0b0000000000000000 or 0b0000001000000000 indicates that
    none of the setpoint dimensions should be ignored). Mapping:
    bit 1: x,  bit 2: y,  bit 3: z,
    bit 4: vx, bit 5: vy, bit 6: vz,
    bit 7: ax, bit 8: ay, bit 9:

    """
    msg = vehicle.message_factory.set_position_target_local_ned_encode(
            0,
            0, 0,
            mavutil.mavlink.MAV_FRAME_BODY_NED,
            0b0000111111000111, #-- BITMASK -> Consider only the velocities
            0, 0, 0,        #-- POSITION
            vx, vy, vz,     #-- VELOCITY
            0, 0, 0,        #-- ACCELERATIONS
            0, 0)
    vehicle.send_mavlink(msg)
    vehicle.flush()
###################################################################
vehicle = connectMyCopter()

wCam, hCam = 640, 480
deadZone = 100
pTime = 0
cap = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
detector = htm.handDetector(detectionCon=0.8, maxHands=1)
x = [300, 245, 200, 170, 145, 130, 112, 103, 93, 87, 80, 75, 70, 67, 62, 59, 57]
y = [20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
coff = np.polyfit(x, y, 2)   # y = AX^2 + BX + C
c = []
i = 0
tipIds = [4, 8, 12, 16, 20]
while True:
    success, img = cap.read()
    img = detector.findHands(img)
    lmList = detector.findPosition(img, draw=False)
    #print(lmList)

    if len(lmList) !=0:
        fingers = []
        # Thumb . Here the x value of thumb tip is compared with the x value of mid thumb
        if lmList[tipIds[0]][1] > lmList[tipIds[0] - 1][1]:
            fingers.append(1)

        else:
            fingers.append(0)

        # Other Fingers
        for id in range(1,5):
            if lmList[tipIds[id]][2] < lmList[tipIds[id]-2][2]:
                fingers.append(1)
            else:
                fingers.append(0)

        #print(sum(fingers))

        x1, y1 = lmList[5][1], lmList[5][2]
        x2, y2 = lmList[17][1], lmList[17][2]
        cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
        ty = lmList[4][2]
        #print(cx, cy)
        cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED)
        #length = int(math.hypot(x2 - x1, y2 - y1))
        #A, B, C = coff
        #distanceCM = A*length**2 + B*length + C
        #print(distanceCM)

        if sum(fingers) == 0:
            print(" Arm and Takeoff ")
            arm_and_takeoff(2)

        if sum(fingers) == 5:

            if ((cx < int(wCam/2) + deadZone) and (cx > int(wCam/2) - deadZone)):
                print("Hold Position")
                set_velocity_body(vehicle, 0, 0, 0)

            if (cx < int(wCam/2) - deadZone):
                print("Moving Right")
                set_velocity_body(vehicle, 0, 0.5, 0)

            if (cx > int(wCam/2) + deadZone):
                print("Moving Left")
                set_velocity_body(vehicle, 0, -0.5, 0)

        if sum(fingers) == 1:

            if ((ty < int(hCam/2) + deadZone) and (ty > int(hCam/2) - deadZone)):
                print("Hold Position")
                set_velocity_body(vehicle, 0, 0, 0)

            if (ty < int(hCam/2) - deadZone):
                print("Moving Up")
                set_velocity_body(vehicle, 0, 0, -1)

            if (ty > int(hCam/2) + deadZone):
                print("Moving Down")
                set_velocity_body(vehicle, 0, 0, 1)

        #if sum(fingers) == 5:
         #   c.append(cx)
          #  if len(c)!=0:
           #     for i in range(len(c)):
            #        difference = c[i]-c[i-1]
                    #print(difference)
             #       if difference > 0:
              #          print("Moving Left")
               #         set_velocity_body(vehicle, 0, -3, 0)
                #    elif difference < 0:
                 #       print("Moving Right")
                  #      set_velocity_body(vehicle, 0, 3, 0)
                   # elif difference == 0:
                    #    print("Hold Position")
                     #   set_velocity_body(vehicle, 0, 0, 0)
            #
            #print(" Moving Right ")
            #set_velocity_body(vehicle, distanceCM*0.05, 0, 0)

    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime
    cv2.putText(img, f'FPS: {int(fps)}', (40, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 3)
    cv2.imshow("Image", img)
    cv2.waitKey(1)


"""
Very simple HTTP server in python for logging requests
Usage::
    ./server.py [<port>]
"""
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib import parse
import os
import logging

class S(BaseHTTPRequestHandler):
    def _set_response(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_POST(self):
        content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
        post_data = self.rfile.read(content_length) # <--- Gets the data itself
        # logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
                # str(self.path), str(self.headers), post_data.decode('utf-8'))

        standard = post_data.decode("utf-8")
        dump = parse.parse_qs(standard)

        if "type" in dump.keys():
            if dump["type"][0] == "vote":
                writeVoteToFile(dump["titleID"][0])
            if dump["type"][0] == "chat":
                writeChatToFile(dump)

        self._set_response()

def run(server_class=HTTPServer, handler_class=S, port=3000):
    # logging.basicConfig(level=logging.INFO)
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    # logging.info('Starting httpd...\n')
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    # logging.info('Stopping httpd...\n')

# Safety operations
def doesIDExist(titleID):
    with open("/resources/text/titleinfo.txt", "r") as f:
        for line in f:
            if line[8:16] == titleID[8:16]:
                return True
    return False

# Saving operations
def writeVoteToFile(titleID):
    if doesIDExist(titleID):
        with open("/resources/text/vote.txt", "a") as f:
            f.write(titleID + "\r")

        os.system("echo \"$(tail -n 200 /resources/text/vote.txt)\" > /resources/text/vote.txt")
    else:
        print("Could not write vote for: " + titleID)

def writeChatToFile(details):
    with open("/resources/text/msg.txt", "a") as f:
        f.write(details["author"][0] + ";;" + details["time"][0] + ";;" + details["message"][0] +"\r")

    os.system("echo \"$(tail -n 200 /resources/text/msg.txt)\" > /resources/text/msg.txt")

# Main function
if __name__ == '__main__':
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()


from strong.models import Project, Images
from rest_framework import serializers

class ImagesSerializers(serializers.HyperlinkedModelSerializer):
    project_id = serializers.PrimaryKeyRelatedField(queryset=Project.objects.all(),source='project.id')
    class Meta:
        model = Images
        fields = ('project_id', 'image')

    def create(self, validated_data):
        subject = Images.objects.create(parent=validated_data['project']['id'], child_name=validated_data['image'])
        return child

class ProjectSerializers(serializers.ModelSerializer):
    images = ImagesSerializers(many=True, read_only=True)
    class Meta:
        model = Project
        fields = ('type_of_project', 'description', 'images')
        # fields = "__all__"


from flask import Flask, request, redirect, url_for
from flask_jsonpify import jsonify
from flask import render_template
from flask import abort
from flask import Response
from flask_api import status
import json
from flaskext.mysql import MySQL
import pandas as pd
import requests
from datetime import datetime, timedelta
import matplotlib as plt
import base64
import io

app = Flask(__name__)
mysql = MySQL()

# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = ''
app.config['MYSQL_DATABASE_DB'] = 'cloud'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)

db = mysql.connect()
cursor = db.cursor()

@app.route('/')
def homepage():
    return  render_template('forecast.html')

@app.route('/historical/', methods=['GET','POST'])   #lists all the dates
def historical():
    if(request.method=='GET'):
        dates_list = []
        cursor.execute("select DATE from dailyweather")
        query=cursor.fetchall()
        my_hist = [i[0] for i in query]
        for item in my_hist:
            a = {"DATE":str(item)}
            dates_list.append(a)
        js = json.dumps(dates_list)
        return js, 200
    else:
        l=request.get_json()
        d=l['DATE']
        tmax=l['TMAX']
        tmin=l['TMIN']
        obj = {}
        cursor.execute("select DATE from dailyweather")
        q=cursor.fetchall()
        list=[i[0] for i in q]
        x=0
        for item in list:
            if(int(d)==item):
                x=1
        if(x==1):
            cursor.execute("update dailyweather set TMAX=%f, TMIN=%f where DATE=%d" %(float(tmax),float(tmin),int(d)))
        else:
            cursor.execute("insert into dailyweather values(%d,%f,%f)" % (int(d),float(tmax),float(tmin)))
        db.commit()
        obj={"DATE":str(d)}
        return jsonify(obj), 201

@app.route('/historical/<string:DATE>', methods=['GET'])    #gets the weather info of a particular day
def get_info(DATE):
    obj = {}
    l=[]
    cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE =%d" % int(DATE))
    q=cursor.fetchall()
    if(len(q)>0):
        for i in range(3):
            l.append(q[0][i])
        obj =  {
                "DATE": str(l[0]),
                "TMAX": l[1],
                "TMIN": l[2]
                }
        return jsonify(obj), 200
    else:
        return '', 404

@app.route('/historical/<int:DATE>', methods=['DELETE'])
def del_info(DATE):
    obj={}
    l=[]
    cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE=%d" % int(DATE))
    query=cursor.fetchall()
    cursor.execute("delete from dailyweather where DATE=%d" % int(DATE))
    db.commit()
    if(len(query)>0):
        for i in range(3):
            l.append(str(query[0][i]))
        obj =  {
                "DATE": l[0],
                "TMAX": l[1],
                "TMIN": l[2]
                }
        return jsonify(obj), 200
    else:
        return '', 204

@app.route('/forecast/<DATE>', methods=['GET'])   #forecasts weather info of the next 7days
def forecast(DATE):
    lst_dates = []
    lst_obj = []
    current_date = pd.to_datetime(DATE,format='%Y%m%d')
    stop_date = current_date+timedelta(days=7)
    while current_date<stop_date:
        lst_dates.append(str(pd.to_datetime(current_date)).split(' ')[0].replace("-",""))
        current_date = current_date+timedelta(days=1)
    for curr_date in lst_dates:
        cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE =%d" % int(curr_date))
        query=cursor.fetchall()
        if (len(query) > 0):
            obj = {
                    "DATE": curr_date,
                    "TMAX": query[0][1],
                    "TMIN": query[0][2]
                    }
            lst_obj.append(obj)
        else:
            cursor.execute("select ROUND(RAND()*(80-75+1),1)+75")
            q=cursor.fetchall()
            cursor.execute("select ROUND(RAND()*(50-45+1),1)+45")
            q1=cursor.fetchall()
            obj = {
                    "DATE": curr_date,
                    "TMAX": q[0][0],
                    "TMIN": q1[0][0]
                }
            lst_obj.append(obj)
    return jsonify(lst_obj), 200

if __name__ == '__main__':
     app.run(host='0.0.0.0',debug=True,port=80)


# -*- coding: utf-8 -*-
"""
Created on Wed Oct 28 07:07:43 2015

@author: RAHUL JAIN
"""

from xml.dom.minidom import parse
import xml.dom.minidom
DOMTree = xml.dom.minidom.parse("chemsep1.xml")
compounds = DOMTree.documentElement
compound = compounds.getElementsByTagName("compound")
i = 1
for comp in compound:
   compName = comp.getElementsByTagName("CompoundID")[0].getAttribute("value")
   CompName = compName.replace(" ","")
   CompName = CompName.replace("-","")
   CompName = CompName.replace(",","")
   CompName = CompName.replace("1","One")
   CompName = CompName.replace("2","Two")
   CompName = CompName.replace("3","Three")
   CompName = CompName.replace("4","Four")
   CompName = CompName.replace("5","Five")
   CriticalTemp = comp.getElementsByTagName("CriticalTemperature")[0].getAttribute("value")
   CriticalPres = comp.getElementsByTagName("CriticalPressure")[0].getAttribute("value")
   CriticalVol = comp.getElementsByTagName("CriticalVolume")[0].getAttribute("value")
   CriticalComp = comp.getElementsByTagName("CriticalCompressibility")[0].getAttribute("value")
   try:
     NormalBoilPoint = comp.getElementsByTagName("NormalBoilingPointTemperature")[0].getAttribute("value")
   except IndexError:
     NormalBoilPoint = "0"
   try:
     NormalMeltingPoint = comp.getElementsByTagName("NormalMeltingPointTemperature")[0].getAttribute("value")
   except IndexError:
     NormalMeltingPoint = "0"
   try:
     TripPntTemp = comp.getElementsByTagName("TriplePointTemperature")[0].getAttribute("value")
   except IndexError:
     TripPntTemp = "0"
   try:
     TripPntPres = comp.getElementsByTagName("TriplePointPressure")[0].getAttribute("value")
   except IndexError:
     TripPntPres = "0"
   MolWt =  comp.getElementsByTagName("MolecularWeight")[0].getAttribute("value")
   try:
     LiqVolAtBoilPnt = comp.getElementsByTagName("LiquidVolumeAtNormalBoilingPoint")[0].getAttribute("value")
   except IndexError:
     LiqVolAtBoilPnt = "0"
   try:
     AcenFactor = comp.getElementsByTagName("AcentricityFactor")[0].getAttribute("value")
   except IndexError:
     AcenFactor = "0"
   try:
     SolParam = comp.getElementsByTagName("SolubilityParameter")[0].getAttribute("value")
   except IndexError:
     SolParam = "0"
   try:
     DipoleMoment = comp.getElementsByTagName("DipoleMoment")[0].getAttribute("value")
   except IndexError:
     DipoleMoment = "0"
   try:
     IGHF = comp.getElementsByTagName("HeatOfFormation")[0].getAttribute("value")
   except IndexError:
     IGHF = "0"
   try:
     GEF = comp.getElementsByTagName("GibbsEnergyOfFormation")[0].getAttribute("value")
   except IndexError:
     GEF = "0"
   try:
     AbsEntropy = comp.getElementsByTagName("AbsEntropy")[0].getAttribute("value")
   except IndexError:
     AbsEntropy = "0"
   try:
     HeatFusionMeltPnt = comp.getElementsByTagName("HeatOfFusionAtMeltingPoint")[0].getAttribute("value")
   except IndexError:
     HeatFusionMeltPnt = "0"
   try:
     HOC = comp.getElementsByTagName("HeatOfCombustion")[0].getAttribute("value")
   except IndexError:
     HOC = "0"
   try:
     UniquacR = comp.getElementsByTagName("UniquacR")[0].getAttribute("value")
   except IndexError:
     UniquacR = "0"
   try:
     UniquacQ = comp.getElementsByTagName("UniquacQ")[0].getAttribute("value")
   except IndexError:
     UniquacQ = "0"
   try:
     RacketParam = comp.getElementsByTagName("RacketParameter")[0].getAttribute("value")
   except IndexError:
     RacketParam = "0"

   try:
     LiqDen = comp.getElementsByTagName("LiquidDensity")[0]
     LiqDenEqn = LiqDen.getElementsByTagName("eqno")[0].getAttribute("value")
     A=LiqDen.getElementsByTagName("A")[0].getAttribute("value")
     B=LiqDen.getElementsByTagName("B")[0].getAttribute("value")
     C=LiqDen.getElementsByTagName("C")[0].getAttribute("value")
     D=LiqDen.getElementsByTagName("D")[0].getAttribute("value")
     try:
          E=LiqDen.getElementsByTagName("E")[0].getAttribute("value")
     except IndexError:
          E = "0"
   except IndexError:
     LiqDenEqn = "0"
     A = "0"
     B = "0"
     C = "0"
     D = "0"
     E = "0"
   try:
      VapPres = comp.getElementsByTagName("VaporPressure")[0]
      VapPresEqn = VapPres.getElementsByTagName("eqno")[0].getAttribute("value")
      VA=VapPres.getElementsByTagName("A")[0].getAttribute("value")
      VB=VapPres.getElementsByTagName("B")[0].getAttribute("value")
      VC=VapPres.getElementsByTagName("C")[0].getAttribute("value")
      try:
          VD=VapPres.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          VD = "0"
      try:
          VE=VapPres.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          VE = "0"
   except IndexError:
     VapPresEqn = "0"
     VA = "0"
     VB = "0"
     VC = "0"
     VD = "0"
     VE = "0"
   try:
      LiqCp = comp.getElementsByTagName("LiquidHeatCapacityCp")[0]
      LiqCpEqn = LiqCp.getElementsByTagName("eqno")[0].getAttribute("value")
      LCpA=LiqCp.getElementsByTagName("A")[0].getAttribute("value")
      LCpB=LiqCp.getElementsByTagName("B")[0].getAttribute("value")
      LCpC=LiqCp.getElementsByTagName("C")[0].getAttribute("value")
      try:
          LCpD=LiqCp.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          LCpD = "0"
      try:
          LCpE=LiqCp.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          LCpE = "0"
   except IndexError:
     LiqCpEqn = "0"
     LCpA = "0"
     LCpB = "0"
     LCpC = "0"
     LCpD = "0"
     LCpE = "0"
   try:
      HOV = comp.getElementsByTagName("HeatOfVaporization")[0]
      HOVEqn = HOV.getElementsByTagName("eqno")[0].getAttribute("value")
      HOVA=HOV.getElementsByTagName("A")[0].getAttribute("value")
      HOVB=HOV.getElementsByTagName("B")[0].getAttribute("value")
      HOVC=HOV.getElementsByTagName("C")[0].getAttribute("value")
      try:
          HOVD=HOV.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          HOVD = "0"
      try:
          HOVE=HOV.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          HOVE = "0"
   except IndexError:
     HOVEqn = "0"
     HOVA = "0"
     HOVB = "0"
     HOVC = "0"
     HOVD = "0"
     HOVE = "0"
   if (float(NormalBoilPoint) >  298.15 ):
     HA = float(HOVA)
     HB = float(HOVB)
     HC = float(HOVC)
     HD = float(HOVD)
     HE = float(HOVE)
     Tr = 298.15/float(CriticalTemp)
     SHOV = HA*(pow((1-Tr),(HB + HC*Tr + HD*pow(Tr,2) + HE*pow(Tr,3))))
     AbsEnthalpy = float(IGHF) -  SHOV
   else:
     AbsEnthalpy = float(IGHF)
   SH = str(AbsEnthalpy)
   try:
      VapCp = comp.getElementsByTagName("IdealGasHeatCapacityCp")[0]
      VapCpEqn = VapCp.getElementsByTagName("eqno")[0].getAttribute("value")
      VCpA=VapCp.getElementsByTagName("A")[0].getAttribute("value")
      VCpB=VapCp.getElementsByTagName("B")[0].getAttribute("value")
      VCpC=VapCp.getElementsByTagName("C")[0].getAttribute("value")
      try:
          VCpD=VapCp.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          VCpD = "0"
      try:
          VCpE=VapCp.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          VCpE = "0"
   except IndexError:
     VapCpEqn = "0"
     VCpA = "0"
     VCpB = "0"
     VCpC = "0"
     VCpD = "0"
     VCpE = "0"

   try:
      LiqVis = comp.getElementsByTagName("LiquidViscosity")[0]
      LiqVisEqn = LiqVis.getElementsByTagName("eqno")[0].getAttribute("value")
      LiqVisA=LiqVis.getElementsByTagName("A")[0].getAttribute("value")
      LiqVisB=LiqVis.getElementsByTagName("B")[0].getAttribute("value")
      LiqVisC=LiqVis.getElementsByTagName("C")[0].getAttribute("value")
      try:
          LiqVisD=LiqVis.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          LiqVisD = "0"
      try:
          LiqVisE=LiqVis.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          LiqVisE = "0"
   except IndexError:
     LiqVisEqn = "0"
     LiqVisA = "0"
     LiqVisB = "0"
     LiqVisC = "0"
     LiqVisD = "0"
     LiqVisE = "0"

   try:
      VapVis = comp.getElementsByTagName("VaporViscosity")[0]
      VapVisEqn = VapVis.getElementsByTagName("eqno")[0].getAttribute("value")
      VapVisA=VapVis.getElementsByTagName("A")[0].getAttribute("value")
      VapVisB=VapVis.getElementsByTagName("B")[0].getAttribute("value")
      VapVisC=VapVis.getElementsByTagName("C")[0].getAttribute("value")
      try:
          VapVisD=VapVis.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          VapVisD = "0"
      try:
          VapVisE=VapVis.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          VapVisE = "0"
   except IndexError:
     VapVisEqn = "0"
     VapVisA = "0"
     VapVisB = "0"
     VapVisC = "0"
     VapVisD = "0"
     VapVisE = "0"

   try:
      LiqK = comp.getElementsByTagName("LiquidThermalConductivity")[0]
      LiqKEqn = LiqK.getElementsByTagName("eqno")[0].getAttribute("value")
      LiqKA=LiqK.getElementsByTagName("A")[0].getAttribute("value")
      LiqKB=LiqK.getElementsByTagName("B")[0].getAttribute("value")
      LiqKC=LiqK.getElementsByTagName("C")[0].getAttribute("value")
      try:
          LiqKD=LiqK.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          LiqKD = "0"
      try:
          LiqKE=LiqK.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          LiqKE = "0"
   except IndexError:
     LiqKEqn = "0"
     LiqKA = "0"
     LiqKB = "0"
     LiqKC = "0"
     LiqKD = "0"
     LiqKE = "0"

   try:
      VapK = comp.getElementsByTagName("VaporThermalConductivity")[0]
      VapKEqn = VapK.getElementsByTagName("eqno")[0].getAttribute("value")
      VapKA=VapK.getElementsByTagName("A")[0].getAttribute("value")
      VapKB=VapK.getElementsByTagName("B")[0].getAttribute("value")
      VapKC=VapK.getElementsByTagName("C")[0].getAttribute("value")
      try:
          VapKD=VapK.getElementsByTagName("D")[0].getAttribute("value")
      except IndexError:
          VapKD = "0"
      try:
          VapKE=VapK.getElementsByTagName("E")[0].getAttribute("value")
      except IndexError:
          VapKE = "0"
   except IndexError:
     VapKEqn = "0"
     VapKA = "0"
     VapKB = "0"
     VapKC = "0"
     VapKD = "0"
     VapKE = "0"

   f = open('File5.txt','a')
   f.write('model '+CompName)
   f.write('\n')
   f.write('extends General_Properties(')
   f.write('\n')
   f.write('SN ' + '=' + str(i) +',')
   f.write('\n')
   f.write('name' + '=' + '"'+ CompName + '",')
   f.write('\n')
   f.write('Tc ' + '=' + CriticalTemp + ',')
   f.write('\n')
   f.write('Pc ' + '=' + CriticalPres + ',')
   f.write('\n')
   f.write('Vc ' + '=' + CriticalVol + ',')
   f.write('\n')
   f.write('Cc ' + '=' + CriticalComp + ',')
   f.write('\n')
   f.write('Tb ' + '=' + NormalBoilPoint + ',')
   f.write('\n')
   f.write('Tm ' + '=' + NormalMeltingPoint + ',')
   f.write('\n')
   f.write('TT ' + '=' + TripPntTemp + ',')
   f.write('\n')
   f.write('TP ' + '=' + TripPntPres + ',')
   f.write('\n')
   f.write('MW ' + '=' + MolWt + ',')
   f.write('\n')
   f.write('LVB ' + '=' + LiqVolAtBoilPnt + ',')
   f.write('\n')
   f.write('AF ' + '=' + AcenFactor + ',')
   f.write('\n')
   f.write('SP ' + '=' + SolParam + ',')
   f.write('\n')
   f.write('DM ' + '=' + DipoleMoment + ',')
   f.write('\n')
   f.write('SH ' + '=' + SH + ',')
   f.write('\n')
   f.write('IGHF ' + '=' + IGHF + ',')
   f.write('\n')
   f.write('GEF ' + '=' + GEF + ',')
   f.write('\n')
   f.write('AS ' + '=' + AbsEntropy + ',')
   f.write('\n')
   f.write('HFMP ' + '=' + HeatFusionMeltPnt + ',')
   f.write('\n')
   f.write('HOC ' + '=' + HOC + ',')
   f.write('\n')
   f.write('LiqDen = {'+LiqDenEqn+","+A+","+B+","+C+","+D+","+E+'},')
   f.write('\n')
   f.write('VP = {'+VapPresEqn+","+VA+","+VB+","+VC+","+VD+","+VE+'},')
   f.write('\n')
   f.write('LiqCp = {'+LiqCpEqn+","+LCpA+","+LCpB+","+LCpC+","+LCpD+","+LCpE+'},')
   f.write('\n')
   f.write('HOV = {'+HOVEqn+","+HOVA+","+HOVB+","+HOVC+","+HOVD+","+HOVE+'},')
   f.write('\n')
   f.write('VapCp = {'+VapCpEqn+","+VCpA+","+VCpB+","+VCpC+","+VCpD+","+VCpE+'},')
   f.write('\n')
   f.write('LiqVis = {'+LiqVisEqn+","+LiqVisA+","+LiqVisB+","+LiqVisC+","+LiqVisD+","+LiqVisE+'},')
   f.write('\n')
   f.write('VapVis = {'+VapVisEqn+","+VapVisA+","+VapVisB+","+VapVisC+","+VapVisD+","+VapVisE+'},')
   f.write('\n')
   f.write('LiqK = {'+LiqKEqn+","+LiqKA+","+LiqKB+","+LiqKC+","+LiqKD+","+LiqKE+'},')
   f.write('\n')
   f.write('VapK = {'+VapKEqn+","+VapKA+","+VapKB+","+VapKC+","+VapKD+","+VapKE+'},')
   f.write('\n')
   f.write('Racketparam = '+RacketParam +',')
   f.write('\n')
   f.write('UniquacR = '+ UniquacR + ',')
   f.write('\n')
   f.write('UniquacQ = '+ UniquacQ + ');')
   f.write('\n')
   f.write('end '+CompName+';')
   f.write('\n')
   f.write('\n')
#   f.write('function Psat')
#   f.write('\n')
#   f.write('input Real T;')
#   f.write('\n')
#   f.write('output Real Vp;')
#   f.write('\n')
#   f.write('algorithm')
#   f.write('\n')
#   f.write('Vp := exp(VP[2] + VP[3] / T + VP[4] * log(T) + VP[5] * T ^ VP[6]);')
#   f.write('\n')
#   f.write('end Psat;')
#   f.write('\n')
#   f.write('\n')
#
#   f.write('function LCp')
#   f.write('\n')
#   f.write('input Real T;')
#   f.write('\n')
#   f.write('output Real Cp;')
#   f.write('\n')
#   f.write('algorithm')
#   f.write('\n')
#   f.write('Cp := (LiqCp[2] + exp(LiqCp[3] / T + LiqCp[4] + LiqCp[5] * T + LiqCp[6] * T ^ 2)) / 1000;')
#   f.write('\n')
#   f.write('end LCp;')
#   f.write('\n')
#   f.write('\n')
#
#   f.write('function HV')
#   f.write('\n')
#   f.write('input Real T;')
#   f.write('\n')
#   f.write('output Real Hv;')
#   f.write('\n')
#   f.write('protected')
#   f.write('\n')
#   f.write('Real Tr = T / Tc;')
#   f.write('\n')
#   f.write('algorithm')
#   f.write('\n')
#   f.write('Hv := HOV[2] * (1 - Tr) ^ (HOV[3] + HOV[4] * Tr + HOV[5] * Tr ^ 2 + HOV[6] * Tr ^ 3) / 1000;')
#   f.write('\n')
#   f.write('end HV;')
#   f.write('\n')
#   f.write('\n')
#
#   f.write('function HLiq')
#   f.write('\n')
#   f.write('input Real T;')
#   f.write('\n')
#   f.write('output Real Ent;')
#   f.write('\n')
#   f.write('protected')
#   f.write('\n')
#   f.write('Real Temp = 298.15;')
#   f.write('\n')
#   f.write('algorithm')
#   f.write('\n')
#   f.write('Ent := 0;')
#   f.write('\n')
#   f.write('while Temp < T loop')
#   f.write('\n')
#   f.write('Ent := Ent + LCp(Temp) * 1;')
#   f.write('\n')
#   f.write('Temp := Temp + 1;')
#   f.write('\n')
#   f.write('end while;')
#   f.write('\n')
#   f.write('Ent := SH / 1000 + Ent;')
#   f.write('\n')
#   f.write('end HLiq;')
#   f.write('\n')
#   f.write('\n')
#
#   f.write('function HVap')
#   f.write('\n')
#   f.write('input Real T;')
#   f.write('\n')
#   f.write('output Real Ent;')
#   f.write('\n')
#   f.write('algorithm')
#   f.write('\n')
#   f.write('Ent := HLiq(T) + HV(T);')
#   f.write('\n')
#   f.write('end HVap;')
#   f.write('\n')

   i = i + 1
f.close()


"""Original from https://github.com/yhenon/pytorch-retinanet"""
import torch
import torch.nn as nn
import numpy as np

import skimage.io
import skimage.transform
import skimage.color
import skimage

from PIL import Image

def comptue_dim(dim, padding, kernel_size, stride):
    return np.floor((dim + 2*padding - kernel_size) / stride) + 1

def conv3x3(in_planes, out_planes, stride=1):
    """3x3 convolution with padding"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
                     padding=1, bias=False)

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(Bottleneck, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                               padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

class BBoxTransform(nn.Module):

    def __init__(self, mean=None, std=None):
        super(BBoxTransform, self).__init__()
        if mean is None:
            self.mean = torch.from_numpy(np.array([0, 0, 0, 0]).astype(np.float32)).cuda()
        else:
            self.mean = mean
        if std is None:
            self.std = torch.from_numpy(np.array([0.1, 0.1, 0.2, 0.2]).astype(np.float32)).cuda()
        else:
            self.std = std

    def forward(self, boxes, deltas):

        widths = boxes[:, :, 2] - boxes[:, :, 0]
        heights = boxes[:, :, 3] - boxes[:, :, 1]
        ctr_x = boxes[:, :, 0] + 0.5 * widths
        ctr_y = boxes[:, :, 1] + 0.5 * heights

        dx = deltas[:, :, 0] * self.std[0] + self.mean[0]
        dy = deltas[:, :, 1] * self.std[1] + self.mean[1]
        dw = deltas[:, :, 2] * self.std[2] + self.mean[2]
        dh = deltas[:, :, 3] * self.std[3] + self.mean[3]

        pred_ctr_x = ctr_x + dx * widths
        pred_ctr_y = ctr_y + dy * heights
        pred_w = torch.exp(dw) * widths
        pred_h = torch.exp(dh) * heights

        pred_boxes_x1 = pred_ctr_x - 0.5 * pred_w
        pred_boxes_y1 = pred_ctr_y - 0.5 * pred_h
        pred_boxes_x2 = pred_ctr_x + 0.5 * pred_w
        pred_boxes_y2 = pred_ctr_y + 0.5 * pred_h

        pred_boxes = torch.stack([pred_boxes_x1, pred_boxes_y1, pred_boxes_x2, pred_boxes_y2], dim=2)

        return pred_boxes

class ClipBoxes(nn.Module):

    def __init__(self, width=None, height=None):
        super(ClipBoxes, self).__init__()

    def forward(self, boxes, img):
        batch_size, num_channels, height, width = img.shape

        boxes[:, :, 0] = torch.clamp(boxes[:, :, 0], min=0)
        boxes[:, :, 1] = torch.clamp(boxes[:, :, 1], min=0)

        boxes[:, :, 2] = torch.clamp(boxes[:, :, 2], max=width)
        boxes[:, :, 3] = torch.clamp(boxes[:, :, 3], max=height)

        return boxes

class Resizer(object):
    """Convert ndarrays in sample to Tensors."""

    def __call__(self, image, annots, min_side=608, max_side=1024):

        image = np.array(image)
        annots = np.array([[*annot['bbox'], annot['category_id']] for annot in annots])

        rows, cols, cns = image.shape

        smallest_side = min(rows, cols)

        # rescale the image so the smallest side is min_side
        scale = min_side / smallest_side

        # check if the largest side is now greater than max_side, which can happen
        # when images have a large aspect ratio
        largest_side = max(rows, cols)

        if largest_side * scale > max_side:
            scale = max_side / largest_side

        # resize the image with the computed scale
        image = skimage.transform.resize(image, (int(round(rows * scale)), int(round((cols * scale)))))
        rows, cols, cns = image.shape

        pad_w = 32 - rows % 32
        pad_h = 32 - cols % 32

        new_image = np.zeros((rows + pad_w, cols + pad_h, cns)).astype(np.float32)
        new_image[:rows, :cols, :] = image.astype(np.float32)

        annots[:, 4] = annots[:, 4] * scale

        return Image.fromarray(np.uint8(new_image)), torch.from_numpy(annots), scale

class Augmenter(object):
    """Convert ndarrays in sample to Tensors."""

    def __call__(self, sample, flip_x=0.5):
        if np.random.rand() < flip_x:
            image, annots = sample['img'], sample['annot']
            image = image[:, ::-1, :]

            rows, cols, channels = image.shape

            x1 = annots[:, 0].copy()
            x2 = annots[:, 2].copy()

            x_tmp = x1.copy()

            annots[:, 0] = cols - x2
            annots[:, 2] = cols - x_tmp

            sample = {'img': image, 'annot': annots}

        return sample

class Normalizer(object):

    def __init__(self):
        self.mean = np.array([[[0.485, 0.456, 0.406]]])
        self.std = np.array([[[0.229, 0.224, 0.225]]])

    def __call__(self, sample):
        image, annots = sample['img'], sample['annot']

        return {'img': ((image.astype(np.float32) - self.mean) / self.std), 'annot': annots}

class UnNormalizer(object):
    def __init__(self, mean=None, std=None):
        if mean == None:
            self.mean = [0.485, 0.456, 0.406]
        else:
            self.mean = mean
        if std == None:
            self.std = [0.229, 0.224, 0.225]
        else:
            self.std = std

    def __call__(self, tensor):
        """
        Args:
            tensor (Tensor): Tensor image of size (C, H, W) to be normalized.
        Returns:
            Tensor: Normalized image.
        """
        for t, m, s in zip(tensor, self.mean, self.std):
            t.mul_(s).add_(m)
        return tensor


from django.core.exceptions import ObjectDoesNotExist
from django.http import StreamingHttpResponse, HttpResponse
from rest_framework.response import Response
from .models import AudioSlice, Audio
from .serializers import AudioSerializer, AudioSliceSerializer
from rest_framework.decorators import api_view
from rest_framework.decorators import parser_classes
from rest_framework.parsers import MultiPartParser
from .utils import utils as ut
from audio.dbmanager.redis_dao import *
from audio.services.preprocessor import AudioPreprocessor
from audio.dbmanager.youtube_handler import *
import re
import time

range_re = re.compile(r'bytes\s*=\s*(\d+)\s*-\s*(\d*)', re.I)
file_count = 0

"""
timeline
t0 : page 1에서 사용자가 오디오 요청을 보낼 때
t1 : page 2에서 사용자가 오디오의 구간을 선택할 때
"""

"""
t0 > CELERY RESULT BACKEND
사용자의 오디오 요청에 대한 전처리 실행 후 기록 --> view 함수에
"""
# preprocessor = AudioPreprocessor()
# # task_id는 audio의 id
# # audio_id = uuid.uuid4()  # 처음 들어오는 경우, 그게 아니면 database에서 꺼내오기
# AudioPreprocessor().preprocess.apply_async((3, 56), task_id="hiek", expires=datetime.now() + timedelta(days=1))

"""
t1 > USER INFO RECORD : (audio <----> choreo <----> product) Inter-server communication
KEY "a30gk3" <-- uuid.uuid4()
VAL (HSET)
{ audio_id : e317fce <-- 클라이언트에게 받을 것
start : 13  <-- audio_handler가 계산하도록
end : 31 <-- audio_handler가 계산하도록
progress : 0.0 }  <-- 어느정도 진행되었는지 percentage
"""

"""
t1 > SIMILARITY : (audio <----> choreo) Inter-server communication
KEY e317fce-14 <-- 노래 구간 id
VAL [ "af3g0s39_13 : 89", "ldf9a8i_4 : 90", "fk02j3bu_9 : 99", ... ] <-- 노래구간 id 와 점수가 매핑된 요소들로 구성된 list
"""

"""
t1 > AMPLITUDE : (audio <----> choreo) Inter-server communication
KEY e317fce-14 <-- 노래 구간 id
VAL [ 7 2 9 8 6 ] <-- 점수 list
"""

"""
===================================================================================================================
"""

# def upload_file(request):
#     if request.method == 'POST':
#         form = UploadFileForm(request.POST, request.FILES)
#         if form.is_valid():
#             instance = ModelWithFileField(file_field=request.FILES['file'])
#             instance.save()
#             return HttpResponseRedirect('/success/url/')
#     else:
#         form = UploadFileForm()
#     return render(request, 'upload.html', {'form': form})

@api_view(['POST'])
@parser_classes([MultiPartParser])
def meta(request):
    data = MultiPartParser.parse(request)
    print(data)
    res = write_from_meta()
    return Response(AudioSerializer(Audio.objects.all(), many=True).data)

@api_view(['POST'])
async def youtube_url(request):
    download_url = request.data.get("download_url")
    try:
        # 이미 있는 경우

        return Response(AudioSerializer(Audio.objects.get(download_url=download_url)).data)
    except ObjectDoesNotExist:
        try:
            print(f"started at {time.strftime('%X')}")
            _id, _title, _duration = await write_from_link(download_url)
            audio = Audio(audio_id=_id, title=_title, download_url=download_url, duration=_duration)
            audio.save()
            serializer = AudioSerializer(audio)
            # 이게 tasks에 해당됨
            AudioPreprocessor(audio=audio).preprocess()
            # 파일 찾아서 정보와 함께 보내주기
            return Response(serializer.data)
        except:
            print("===========download failure=============")
            return Response("cannot open file.", status=400)

    # response = StreamingHttpResponse(streaming_content=request.FILES["audio_file"])
    # response['Content-Disposition'] = f'attachment; filename="{request.data["audio_file"]}"'
    # return response

@api_view(['POST'])
@parser_classes([MultiPartParser])
# @renderer_classes([MultiPartRenderer])
def file(request):
    """
    :param request:
    :return: audio_id 와 file을 streaming 형태로
    """
    ext = request.data.get("ext")
    global file_count
    filename = "up" + str(file_count)
    if ext != "wav":
        ut.get_console_output(
            'ffmpeg -n -i "{}/{}.{}" "{}/{}.wav"'.format("../../media/ORG", filename, ext, "../../media/WAV",
                                                         filename))

    # 바로 파일 저장 - store in the volume
    file_count += 1

    response = StreamingHttpResponse(streaming_content=request.data["audio_file"])
    response['Content-Disposition'] = f'attachment; filename="{request.data["audio_file"]}"'
    return response

@api_view(['POST'])
def skeletal_after_interval(request):
    """
    :param request: audio_id, start_sec, end_sec
    :return:
    """
    audio_id = request.data.get('audio_id')
    user_start_sec = request.data['start_sec']
    user_end_sec = request.data['end_sec']

    UserRedisHandler.set_user_info(audio_id, user_start_sec, user_end_sec)

    if bool(AudioSlice.objects.filter(audio_slice_id__contains=audio_id)):
        start_arr = AudioSlice.objects.values_list('start_sec', flat=True)
        start_audio_slice_id = AudioSlice.objects.get(
            start_sec=ut.find_nearest(start_arr, user_start_sec)).only('audio_slice_id')
        end_audio_slice_id = request.data.get('audio_id') + AudioSlice.objects.get(
            start_sec=ut.find_nearest(start_arr, user_end_sec)).only('audio_slice_id').split("_")[1]

    else:
        audio_handler = AudioPreprocessor(Audio.objects.get(audio_id=audio_id))
        audio_handler.preprocess()
        start_audio_slice_id = audio_handler.get_slice_id(ut.find_nearest(audio_handler.beat_track, user_start_sec))
        end_audio_slice_id = audio_handler.get_slice_id(ut.find_nearest(audio_handler.beat_track, user_end_sec))

    interval_number = int(end_audio_slice_id.split("ㅡ")[1]) - int(start_audio_slice_id.split("ㅡ")[1])

    # Task 1. Similarity process & get into redis
    # smlr_app = Celery('redis_dao', backend=cc.result_smlr_backend, broker=cc.broker_smlr_url)
    # smlr_app.config_from_object('celery_config') --꼭 안해도 될 듯
    # 여기에 혜린이가 한 부분을 어떻게 어떻게 만들어서..
    # cluster_smlr.apply_async(filter_kmeans_labels, filter_feat, 0, 6))

    # Task 2. Amplitude process & get into redis
    # ampl_app = Celery(backend=cc.result_ampl_backend, broker=cc.broker_ampl_url)
    # get_amplitude.apply_async((3, 56), task_id=audio_id, expires=datetime.now() + timedelta(days=1))

    return Response(
        AudioSliceSerializer(start_audio_slice_id=start_audio_slice_id, end_audio_slice_id=end_audio_slice_id,
                             interval_number=interval_number).data)

# app = Celery('redis_dao', backend=cc.result_backend, broker=cc.broker_url)
# app.config_from_object('celery_config')

# def youtube(request):
#     # task_id는 audio의 id
#     audio_id = uuid.uuid4()  # 처음 들어오는 경우, 그게 아니면 database에서 꺼내오기
#     preprocess.apply_async((3, 56), task_id=audio_id, expires=datetime.now() + timedelta(days=1))

# def serve(request):
#     return FileResponse(open(request.data.get('music'), 'rb'))

@api_view(['POST'])
def get_music(request):
    with open(request.data.get('music'), 'rb') as f:
        # 필요한 응답헤더 세팅
        return set_audio_response('오디오파일 경로, wav 확장자까지 꼭 입력할 것', "오디오 파일 id(youtube id)", "wav",
                                  "오디오파일 duration float 형태로")

def set_audio_response(audio_src, audio_id, ext, duration):
    response = HttpResponse(open(audio_src, "rb"))
    response["Access-Control-Allow-Origin"] = "*"
    response['Content-Type'] = "application/octet-stream"
    response['Content-Disposition'] = f'attachment; filename="{audio_id}.{ext}"'  # wav만 보내지 않아도 되도록
    response['audio_id'] = audio_id
    response['duration'] = duration
    return response

    # data = {
    #     "audio_id": "dfsdff",
    #     "interval_number": 14,
    #     "music": open(request.data.get('music'), 'rb')
    # }
    # return HttpResponse(data)
    # response = HttpResponse(content=open(request.data.get('music'), 'rb'))
    # response['Content-Type'] = 'application/json'
    # return FileResponse(open(request.data.get('music'), 'rb'))


from dataclasses import dataclass, replace
from typing import Any
from uuid import UUID

from topics.domain.repositories.topic_repository import TopicRepository
from topics.domain.usecases.base import Usecase

@dataclass(kw_only=True)
class UpdateTopicRequest:
    id: UUID
    content: str | None = None
    discussed: bool | None = None

@dataclass(kw_only=True)
class UpdateTopicUsecase(Usecase[UpdateTopicRequest, None]):
    topic_repository: TopicRepository

    def handle(self, request: UpdateTopicRequest) -> None:
        topic = self.topic_repository.get(request.id)
        updated_fields: dict[str, Any] = {}
        if request.content is not None:
            updated_fields["content"] = request.content
        if request.discussed is not None:
            updated_fields["discussed"] = request.discussed
        self.topic_repository.update(replace(topic, **updated_fields))