import os
import shutil
import traceback
import django_rq
import os.path as osp

from datetime import datetime
from tempfile import mkstemp
from distutils.util import strtobool

from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.db import IntegrityError
from django.forms import model_to_dict
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.decorators import method_decorator
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from drf_yasg import openapi
from drf_yasg.inspectors import CoreAPICompatInspector, NotHandled
from drf_yasg.utils import swagger_auto_schema
from rest_framework import mixins, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import APIException, NotFound
from rest_framework.permissions import SAFE_METHODS, IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from sendfile import sendfile

import cvat.apps.dataset_manager as dm
import cvat.apps.dataset_manager.views
from cvat.apps.authentication import auth
from cvat.apps.dataset_manager.serializers import DatasetFormatsSerializer
from cvat.apps.engine.frame_provider import FrameProvider
from cvat.apps.engine.models import Job, StatusChoice, Task, Log, Segment, Project, StorageMethodChoice, StorageChoice, \
    Review, ReviewStatus, Issue, Comment
from cvat.apps.engine.serializers import (
    AnnotationFileSerializer, BasicUserSerializer,
    DataMetaSerializer, DataSerializer, ExceptionSerializer, FileInfoSerializer, JobSerializer, LabeledDataSerializer,
    LogEventSerializer, ProjectSerializer, RqStatusSerializer, TaskSerializer, UserSerializer, LogSerializer,
    ReviewSerializer, CombinedIssueSerializer, PluginsSerializer, ProjectSearchSerializer, CombinedReviewSerializer,
    IssueSerializer, CommentSerializer)

from . import models, task
from .log import clogger, slogger
from .utils import av_scan_paths

class ServerViewSet(viewsets.ViewSet):
    serializer_class = None

    # 要获得有关ServerViewSet操作的良好文档，必须实现该方法。默认情况下，ViewSet不提供它。
    def get_serializer(self, *args, **kwargs):
        pass

    @staticmethod
    @swagger_auto_schema(method='post', request_body=ExceptionSerializer)
    @action(detail=False, methods=['POST'], serializer_class=ExceptionSerializer)
    def exception(request):
        """从服务器上的客户端保存异常，向ELK发送日志（如果它已连接）"""
        serializer = ExceptionSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            additional_info = {
                "username": request.user.username,
                "name": "Send exception",
            }
            message = JSONRenderer().render({**serializer.data, **additional_info}).decode('UTF-8')
            jid = serializer.data.get("job_id")
            tid = serializer.data.get("task_id")
            if jid:
                clogger.job[jid].error(message)
            elif tid:
                clogger.task[tid].error(message)
            else:
                clogger.glob.error(message)

            return Response(serializer.data, status=status.HTTP_201_CREATED)

    @staticmethod
    @swagger_auto_schema(method='post', request_body=LogEventSerializer(many=True))
    @action(detail=False, methods=['POST'], serializer_class=LogEventSerializer)
    def logs(request):
        """将来自客户端的日志保存到服务器上，向 ELK 发送日志（如果它已连接）"""
        serializer = LogEventSerializer(many=True, data=request.data)
        serializer.is_valid(raise_exception=True)
        user = {"username": request.user.username}
        for event in serializer.data:
            message = JSONRenderer().render({**event, **user}).decode('UTF-8')
            jobId = event.get("job_id")
            taskId = event.get("task_id")
            if jobId:
                clogger.job[jobId].info(message)
            elif taskId:
                clogger.task[taskId].info(message)
            else:
                clogger.glob.info(message)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @staticmethod
    @swagger_auto_schema(
        method='get',
        operation_summary='返回服务器上沿指定路径的所有文件和文件夹',
        manual_parameters=[
            openapi.Parameter('directory', openapi.IN_QUERY, type=openapi.TYPE_STRING, description='要浏览的目录')],
        responses={'200': FileInfoSerializer(many=True)}
    )
    @action(detail=False, methods=['GET'], serializer_class=FileInfoSerializer)
    def share(request):
        param = request.query_params.get('directory', '/')
        if param.startswith("/"):
            param = param[1:]
        directory = os.path.abspath(os.path.join(settings.SHARE_ROOT, param))

        if directory.startswith(settings.SHARE_ROOT) and os.path.isdir(directory):
            data = []
            content = os.scandir(directory)
            for entry in content:
                entry_type = None
                if entry.is_file():
                    entry_type = "REG"
                elif entry.is_dir():
                    entry_type = "DIR"
                if entry_type:
                    data.append({"name": entry.name, "type": entry_type})
            serializer = FileInfoSerializer(many=True, data=data)
            if serializer.is_valid(raise_exception=True):
                return Response(serializer.data)
        else:
            return Response("{} 为非法访问路径！".format(param), status=status.HTTP_400_BAD_REQUEST)

    @staticmethod
    @swagger_auto_schema(method='get',
                         operation_summary='方法提供支持的标注格式的列表',
                         responses={'200': DatasetFormatsSerializer()})
    @action(detail=False, methods=['GET'], url_path='annotation/formats')
    def annotation_formats(request):
        data = dm.views.get_all_formats()
        return Response(DatasetFormatsSerializer(data).data)

    @staticmethod
    @swagger_auto_schema(method='get', operation_summary='Method provides allowed plugins.',
                         responses={'200': PluginsSerializer()})
    @action(detail=False, methods=['GET'], url_path='plugins', serializer_class=PluginsSerializer)
    def plugins(request):
        response = {
            'GIT_INTEGRATION': apps.is_installed('cvat.apps.dataset_repo'),
            'ANALYTICS': False,
            'MODELS': False,
        }
        if strtobool(os.environ.get("CVAT_ANALYTICS", '0')):
            response['ANALYTICS'] = True
        if strtobool(os.environ.get("CVAT_SERVERLESS", '0')):
            response['MODELS'] = True
        return Response(response)

class ProjectFilter(filters.FilterSet):
    name = filters.CharFilter(field_name="name", lookup_expr="icontains")
    owner = filters.CharFilter(field_name="owner__username", lookup_expr="icontains")
    status = filters.CharFilter(field_name="status", lookup_expr="icontains")

    class Meta:
        model = models.Project
        fields = ("id", "name", "owner", "status")

@method_decorator(name='list', decorator=swagger_auto_schema(
    operation_summary='Returns a paginated list of projects according to query parameters (12 projects per page)',
    manual_parameters=[
        openapi.Parameter('id', openapi.IN_QUERY, description="A unique number value identifying this project",
                          type=openapi.TYPE_NUMBER),
        openapi.Parameter('name', openapi.IN_QUERY,
                          description="Find all projects where name contains a parameter value",
                          type=openapi.TYPE_STRING),
        openapi.Parameter('owner', openapi.IN_QUERY,
                          description="Find all project where owner name contains a parameter value",
                          type=openapi.TYPE_STRING),
        openapi.Parameter('status', openapi.IN_QUERY, description="Find all projects with a specific status",
                          type=openapi.TYPE_STRING, enum=[str(i) for i in StatusChoice])]))
@method_decorator(name='create', decorator=swagger_auto_schema(operation_summary='Method creates a new project'))
@method_decorator(name='retrieve',
                  decorator=swagger_auto_schema(operation_summary='Method returns details of a specific project'))
@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_summary='Method deletes a specific project'))
@method_decorator(name='partial_update', decorator=swagger_auto_schema(
    operation_summary='Methods does a partial update of chosen fields in a project'))
class ProjectViewSet(auth.ProjectGetQuerySetMixin, viewsets.ModelViewSet):
    queryset = models.Project.objects.all().order_by('-id')
    search_fields = ("name", "owner__username", "status")
    filterset_class = ProjectFilter
    ordering_fields = ("id", "name", "owner", "status", "assignee")
    http_method_names = ['get', 'post', 'head', 'patch', 'delete']

    def get_serializer_class(self):
        if self.request.query_params and self.request.query_params.get("names_only") == "true":
            return ProjectSearchSerializer
        else:
            return ProjectSerializer

    def get_permissions(self):
        http_method = self.request.method
        permissions = [IsAuthenticated]

        if http_method in SAFE_METHODS:
            permissions.append(auth.ProjectAccessPermission)
        elif http_method in ["POST"]:
            permissions.append(auth.ProjectCreatePermission)
        elif http_method in ["PATCH"]:
            permissions.append(auth.ProjectChangePermission)
        elif http_method in ["DELETE"]:
            permissions.append(auth.ProjectDeletePermission)
        else:
            permissions.append(auth.AdminRolePermission)

        return [perm() for perm in permissions]

    def perform_create(self, serializer):
        def validate_project_limit(owner):
            admin_perm = auth.AdminRolePermission()
            is_admin = admin_perm.has_permission(self.request, self)
            if not is_admin and settings.RESTRICTIONS['project_limit'] is not None and \
                Project.objects.filter(owner=owner).count() >= settings.RESTRICTIONS['project_limit']:
                raise serializers.ValidationError('The user has the maximum number of projects')

        owner = self.request.data.get('owner', None)
        if owner:
            validate_project_limit(owner)
            serializer.save()
        else:
            validate_project_limit(self.request.user)
            serializer.save(owner=self.request.user)

    @swagger_auto_schema(method='get',
                         operation_summary='Returns information of the tasks of the project with the selected id',
                         responses={'200': TaskSerializer(many=True)})
    @action(detail=True, methods=['GET'], serializer_class=TaskSerializer)
    def tasks(self, request, pk):
        self.get_object()  # force to call check_object_permissions
        queryset = Task.objects.filter(project_id=pk).order_by('-id')
        queryset = auth.filter_task_queryset(queryset, request.user)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True,
                                             context={"request": request})
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True,
                                         context={"request": request})
        return Response(serializer.data)

class TaskFilter(filters.FilterSet):
    project = filters.CharFilter(field_name="project__name", lookup_expr="icontains")
    name = filters.CharFilter(field_name="name", lookup_expr="icontains")
    owner = filters.CharFilter(field_name="owner__username", lookup_expr="icontains")
    mode = filters.CharFilter(field_name="mode", lookup_expr="icontains")
    status = filters.CharFilter(field_name="status", lookup_expr="icontains")
    assignee = filters.CharFilter(field_name="assignee__username", lookup_expr="icontains")

    class Meta:
        model = Task
        fields = ("id", "project_id", "project", "name", "owner", "mode", "status", "assignee")

class DjangoFilterInspector(CoreAPICompatInspector):
    def get_filter_parameters(self, filter_backend):
        if isinstance(filter_backend, DjangoFilterBackend):
            result = super(DjangoFilterInspector, self).get_filter_parameters(filter_backend)
            res = result.copy()
            for param in result:
                if param.get('name') == 'project_id' or param.get('name') == 'project':
                    res.remove(param)
            return res
        return NotHandled

@method_decorator(name='list', decorator=swagger_auto_schema(
    operation_summary='根据查询参数返回分页的任务列表（每页10个任务）',
    manual_parameters=[
        openapi.Parameter('id', openapi.IN_QUERY, description="标识此任务的唯一数值", type=openapi.TYPE_NUMBER),
        openapi.Parameter('name', openapi.IN_QUERY, description="查找名称中包含参数值的所有任务", type=openapi.TYPE_STRING),
        openapi.Parameter('owner', openapi.IN_QUERY,
                          description="Find all tasks where owner name contains a parameter value",
                          type=openapi.TYPE_STRING),
        openapi.Parameter('mode', openapi.IN_QUERY, description="Find all tasks with a specific mode",
                          type=openapi.TYPE_STRING, enum=['annotation', 'interpolation']),
        openapi.Parameter('status', openapi.IN_QUERY, description="Find all tasks with a specific status",
                          type=openapi.TYPE_STRING, enum=['annotation', 'validation', 'completed']),
        openapi.Parameter('assignee', openapi.IN_QUERY,
                          description="Find all tasks where assignee name contains a parameter value",
                          type=openapi.TYPE_STRING)
    ],
    filter_inspectors=[DjangoFilterInspector]))
@method_decorator(name='create',
                  decorator=swagger_auto_schema(operation_summary='在没有任何附加的方法的情况下创建一个新的视频数据库和任务'))
@method_decorator(name='retrieve',
                  decorator=swagger_auto_schema(operation_summary='方法返回特定任务的详细信息'))
@method_decorator(name='update',
                  decorator=swagger_auto_schema(operation_summary='方法按id更新任务'))
@method_decorator(name='destroy',
                  decorator=swagger_auto_schema(operation_summary='方法删除特定任务、所有附加的作业、批注和数据'))
@method_decorator(name='partial_update',
                  decorator=swagger_auto_schema(operation_summary='方法对任务中选定的字段执行部分更新'))
class TaskViewSet(auth.TaskGetQuerySetMixin, viewsets.ModelViewSet):
    queryset = Task.objects.all().prefetch_related(
        "label_set__attributespec_set",
        "segment_set__job_set",
    ).order_by('-id')
    serializer_class = TaskSerializer
    search_fields = ("name", "owner__username", "mode", "status")
    filterset_class = TaskFilter
    ordering_fields = ("id", "name", "owner", "status", "assignee")

    def get_permissions(self):
        http_method = self.request.method
        permissions = [IsAuthenticated]

        if http_method in SAFE_METHODS:
            permissions.append(auth.TaskAccessPermission)
        elif http_method in ["POST"]:
            permissions.append(auth.TaskCreatePermission)
        elif self.action == 'annotations' or http_method in ["PATCH", "PUT"]:
            permissions.append(auth.TaskChangePermission)
        elif http_method in ["DELETE"]:
            permissions.append(auth.TaskDeletePermission)
        else:
            permissions.append(auth.AdminRolePermission)

        return [perm() for perm in permissions]

    def perform_create(self, serializer):
        def validateTaskLimit(master):
            admin_perm = auth.AdminRolePermission()
            isAdmin = admin_perm.has_permission(self.request, self)
            if not isAdmin and settings.RESTRICTIONS['task_limit'] is not None:
                if Task.objects.filter(owner=master).count() >= settings.RESTRICTIONS['task_limit']:
                    raise serializers.ValidationError('用户拥有最大数量的任务')

        owner = self.request.data.get('owner', None)
        if owner:
            validateTaskLimit(owner)
            serializer.save()
        else:
            validateTaskLimit(self.request.user)
            serializer.save(owner=self.request.user)

    def perform_destroy(self, instance):
        task_dirname = instance.get_task_dirname()
        super().perform_destroy(instance)
        shutil.rmtree(task_dirname, ignore_errors=True)
        if instance.data and not instance.data.tasks.all():
            shutil.rmtree(instance.data.get_data_dirname(), ignore_errors=True)
            instance.data.delete()

    @swagger_auto_schema(method='get', operation_summary='返回特定任务的作业列表', responses={'200': JobSerializer(many=True)})
    @action(detail=True, methods=['GET'], serializer_class=JobSerializer)
    def jobs(self, request, pk):
        self.get_object()  # force to call check_object_permissions
        queryset = Job.objects.filter(segment__task_id=pk)
        serializer = JobSerializer(queryset, many=True, context={"request": request})
        return Response(serializer.data)

    @swagger_auto_schema(method='post',
                         operation_summary='方法将图像或视频永久附加到任务')
    @swagger_auto_schema(method='get',
                         operation_summary='方法返回特定任务的数据',
                         manual_parameters=[
                             openapi.Parameter('type',
                                               in_=openapi.IN_QUERY,
                                               required=True,
                                               type=openapi.TYPE_STRING,
                                               enum=['chunk', 'frame', 'preview'],
                                               description="指定请求数据的类型"),
                             openapi.Parameter('quality',
                                               in_=openapi.IN_QUERY,
                                               required=True,
                                               type=openapi.TYPE_STRING,
                                               enum=['·', 'original'],
                                               description="指定所请求数据的质量级别，对于“预览”类型无所谓"),
                             openapi.Parameter('number',
                                               in_=openapi.IN_QUERY,
                                               required=True,
                                               type=openapi.TYPE_NUMBER,
                                               description="标识块或帧的唯一数值，对于“预览”类型无关紧要"),
                         ]
                         )
    @action(detail=True, methods=['POST', 'GET'])
    def data(self, request, pk):
        if request.method == 'POST':
            db_task = self.get_object()  # call check_object_permissions as well
            serializer = DataSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            db_data = serializer.save()
            db_task.data = db_data
            db_task.save()
            data = {k: v for k, v in serializer.data.items()}
            data['use_zip_chunks'] = serializer.validated_data['use_zip_chunks']
            data['use_cache'] = serializer.validated_data['use_cache']
            data['copy_data'] = serializer.validated_data['copy_data']
            if data['use_cache']:
                db_task.data.storage_method = StorageMethodChoice.CACHE
                db_task.data.save(update_fields=['storage_method'])
            if data['server_files'] and data.get('copy_data') == False:
                db_task.data.storage = StorageChoice.SHARE
                db_task.data.save(update_fields=['storage'])
            # if the value of stop_frame is 0, then inside the function we cannot know
            # the value specified by the user or it's default value from the database
            if 'stop_frame' not in serializer.validated_data:
                data['stop_frame'] = None
            task.create(db_task.id, data)
            return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
        else:
            LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Load task data")

            data_type = request.query_params.get('type', None)
            data_id = request.query_params.get('number', None)
            data_quality = request.query_params.get('quality', 'compressed')

            # 管理员或项目创建者调用时没有 jobId 参数则可以拿到所有的图片
            jobId = request.query_params.get('jobId', None)
            if jobId:
                segment = Segment.objects.filter(job__pk=jobId).first()
                segment = model_to_dict(segment)

                # 判断用户请求的图片 index 在所请求 job 的范围内
                if not int(segment["start_frame"]) <= int(data_id) <= int(segment["stop_frame"]):
                    return Response(data="不是你的终究不是你的", status=status.HTTP_403_FORBIDDEN)

            possible_data_type_values = ('chunk', 'frame', 'preview')
            possible_quality_values = ('compressed', 'original')

            try:
                if not data_type or data_type not in possible_data_type_values:
                    return Response(data='数据类型未指定或值错误', status=status.HTTP_200_OK)
                elif data_type == 'chunk' or data_type == 'frame':
                    if not data_id:
                        return Response(data='未指定编号', status=status.HTTP_200_OK)
                    elif data_quality not in possible_quality_values:
                        return Response(data='错误的质量值', status=status.HTTP_200_OK)

                db_task = self.get_object()
                db_data = db_task.data
                if not db_data:
                    raise NotFound(detail='找不到任务的请求数据')

                frame_provider = FrameProvider(db_task.data)
                if data_type == 'chunk':
                    data_id = int(data_id)
                    data_quality = FrameProvider.Quality.COMPRESSED \
                        if data_quality == 'compressed' else FrameProvider.Quality.ORIGINAL

                    # TODO: av.FFmpegError processing
                    if settings.USE_CACHE and db_data.storage_method == StorageMethodChoice.CACHE:
                        buff, mime_type = frame_provider.get_chunk(data_id, data_quality)
                        return HttpResponse(buff.getvalue(), content_type=mime_type)

                    # 获取当前执行脚本的绝对路径
                    path = os.path.realpath(frame_provider.get_chunk(data_id, data_quality))
                    # 如果块是真实图像上的链接，请遵循符号链接，否则sendfile中的mimetype检测将无法正常工作。
                    return sendfile(request, path)
                elif data_type == 'frame':
                    data_id = int(data_id)
                    data_quality = FrameProvider.Quality.COMPRESSED \
                        if data_quality == 'compressed' else FrameProvider.Quality.ORIGINAL
                    buf, mime = frame_provider.get_frame(data_id, data_quality)
                    return HttpResponse(buf.getvalue(), content_type=mime)
                elif data_type == 'preview':
                    return sendfile(request, frame_provider.get_preview())
                else:
                    return Response(data='unknown data type {}.'.format(data_type), status=status.HTTP_200_OK)
            except APIException as e:
                return Response(data=e.get_full_details(), status=e.status_code)
            except FileNotFoundError as ex:
                msg = f"{ex.strerror} {ex.filename}"
                slogger.task[pk].error(msg, exc_info=True)
                return Response(data=msg, status=status.HTTP_404_NOT_FOUND)
            except Exception as e:
                msg = 'cannot get requested data type: {}, number: {}, quality: {}'.format(data_type, data_id,
                                                                                data_quality)
                slogger.task[pk].error(msg, exc_info=True)
                return Response(data=msg + '\n' + str(e), status=status.HTTP_400_BAD_REQUEST)

    @swagger_auto_schema(method='get',
                         operation_summary='方法允许下载任务标注',
                         manual_parameters=[
                             openapi.Parameter('format',
                                               openapi.IN_QUERY,
                                               description="所需的输出格式名称，您可以在以下位置获取支持格式的列表：/server/annotation/formats",
                                               type=openapi.TYPE_STRING,
                                               required=False),
                             openapi.Parameter('filename',
                                               openapi.IN_QUERY,
                                               description="所需的输出文件名",
                                               type=openapi.TYPE_STRING,
                                               required=False),
                             openapi.Parameter('action',
                                               in_=openapi.IN_QUERY,
                                               description='用于在创建批注文件后开始下载进程',
                                               type=openapi.TYPE_STRING,
                                               required=False,
                                               enum=['download'])],
                         responses={
                             '202': openapi.Response(description='开始转储标注数据集'),
                             '201': openapi.Response(description='标注数据集准备下载'),
                             '200': openapi.Response(description='开始下载标注数据集'),
                             '405': openapi.Response(description='Format is not available')})
    @swagger_auto_schema(method='put', operation_summary='方法允许上载任务批注',
                         manual_parameters=[
                             openapi.Parameter('format', openapi.IN_QUERY,
                                               description="输入格式名称\n您可以在/server/annotation/formats获取支持格式的列表。",
                                               type=openapi.TYPE_STRING, required=False)],
                         responses={
                             '202': openapi.Response(description='Uploading has been started'),
                             '201': openapi.Response(description='Uploading has finished'),
                             '405': openapi.Response(description='Format is not available'), })
    @swagger_auto_schema(method='patch',
                         operation_summary='方法对特定任务中的批注执行部分更新',
                         manual_parameters=[
                             openapi.Parameter('action',
                                               in_=openapi.IN_QUERY,
                                               required=True,
                                               type=openapi.TYPE_STRING,
                                               enum=['create', 'update', 'delete'])])
    @swagger_auto_schema(method='delete', operation_summary='方法删除特定任务的所有批注')
    @action(detail=True,
            methods=['GET', 'DELETE', 'PUT', 'PATCH'],
            serializer_class=LabeledDataSerializer)
    def annotations(self, request, pk):
        db_task = self.get_object()  # force to call check_object_permissions
        if request.method == "GET":
            LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Download task annotation data")
            format_name = request.query_params.get('format')
            if format_name:
                return _export_annotations(db_task=db_task,
                                           rq_id=f"/api/v1/tasks/{pk}/annotations/{format_name}",
                                           request=request,
                                           action=request.query_params.get("action", "").lower(),
                                           callback=dm.views.export_task_annotations,
                                           format_name=format_name)
            else:
                data = dm.task.get_task_data(pk)
                serializer = LabeledDataSerializer(data=data)
                if serializer.is_valid(raise_exception=True):
                    return Response(serializer.data)
        elif request.method == 'PUT':
            LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Upload task annotation data")
            format_name = request.query_params.get('format')
            if format_name:
                return _import_annotations(
                    request=request,
                    rq_id="{}@/api/v1/tasks/{}/annotations/upload".format(request.user, pk),
                    rq_func=dm.task.import_task_annotations,
                    pk=pk,
                    format_name=format_name)
            else:
                serializer = LabeledDataSerializer(data=request.data)
                if serializer.is_valid(raise_exception=True):
                    data = dm.task.put_task_data(pk, serializer.data)
                    return Response(data)
        elif request.method == 'DELETE':
            LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Delete task annotation data")
            dm.task.delete_task_data(pk)
            return Response(status=status.HTTP_204_NO_CONTENT)
        elif request.method == 'PATCH':
            # 获取要执行的操作
            action = self.request.query_params.get("action", None)
            LogViewSet.createLog(taskId=pk, userId=request.user.id, message=action + " task annotation")
            # 判断操作是否允许
            if action not in dm.task.PatchAction.values():
                raise serializers.ValidationError("请为请求指定正确的“操作”")
            serializer = LabeledDataSerializer(data=request.data)
            if serializer.is_valid(raise_exception=True):
                try:
                    data = dm.task.patch_task_data(pk, serializer.data, action)
                except (AttributeError, IntegrityError) as e:
                    return Response(data=str(e), status=status.HTTP_400_BAD_REQUEST)
                return Response(data)

    @swagger_auto_schema(method='get', operation_summary='创建任务时，该方法返回有关创建进程状态的信息')
    @action(detail=True, methods=['GET'], serializer_class=RqStatusSerializer)
    def status(self, request, pk):
        self.get_object()  # force to call check_object_permissions
        response = self._get_rq_response(queue="default", job_id="/api/{}/tasks/{}".format(request.version, pk))
        serializer = RqStatusSerializer(data=response)
        if serializer.is_valid(raise_exception=True):
            return Response(serializer.data)

    @staticmethod
    def _get_rq_response(queue, job_id):
        queue = django_rq.get_queue(queue)
        job = queue.fetch_job(job_id)
        if job is None or job.is_finished:  # job 还未创建 or job 以完成
            response = {"state": "Finished"}
        elif job.is_queued:  # job 正在队列中
            response = {"state": "Queued"}
        elif job.is_failed:  # job 失败，返回错误信息
            response = {"state": "Failed", "message": job.exc_info}
        else:  # job 已开始，返回状态信息
            response = {"state": "Started"}
            if 'status' in job.meta:
                response['message'] = job.meta['status']
        return response

    @staticmethod
    @swagger_auto_schema(method='get', operation_summary='元任务提供了与媒体相关的信息',
                         responses={'200': DataMetaSerializer()})
    @action(detail=True, methods=['GET'], serializer_class=DataMetaSerializer, url_path='data/meta')
    def data_info(request, pk):
        LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Load task data info")

        db_task = models.Task.objects.prefetch_related('data__images').select_related('data__video').get(pk=pk)
        media = [db_task.data.video] if hasattr(db_task.data, 'video') else list(db_task.data.images.order_by('frame'))

        frame_meta = [{
            'width': item.width,
            'height': item.height,
            'name': item.path} for item in media]

        db_data = db_task.data
        db_data.frames = frame_meta

        serializer = DataMetaSerializer(db_data)
        return Response(serializer.data)

    @swagger_auto_schema(method='get', operation_summary='将任务导出为特定格式的数据集',
                         manual_parameters=[
                             openapi.Parameter('format',
                                               openapi.IN_QUERY,
                                               description="所需的输出格式名称\n您可以在以下位置获取支持格式的列表：\n/server/annotation/formats",
                                               type=openapi.TYPE_STRING, required=True),
                             openapi.Parameter('filename',
                                               openapi.IN_QUERY,
                                               description="所需的输出文件名",
                                               type=openapi.TYPE_STRING,
                                               required=False),
                             openapi.Parameter('action',
                                               in_=openapi.IN_QUERY,
                                               description='用于在创建批注文件后开始下载进程',
                                               type=openapi.TYPE_STRING,
                                               required=False,
                                               enum=['download'])
                         ],
                         responses={'202': openapi.Response(description='已开始导出'),
                                    '201': openapi.Response(description='输出文件已准备好下载'),
                                    '200': openapi.Response(description='已开始下载文件'),
                                    '405': openapi.Response(description='Format is not available'), }
                         )
    @action(detail=True, methods=['GET'], serializer_class=None, url_path='dataset')
    def dataset_export(self, request, pk):
        LogViewSet.createLog(taskId=pk, userId=request.user.id, message="Download dataset data")
        db_task = self.get_object()  # force to call check_object_permissions

        format_name = request.query_params.get("format", "")
        return _export_annotations(db_task=db_task,
                                   rq_id="/api/v1/tasks/{}/dataset/{}".format(pk, format_name),
                                   request=request,
                                   action=request.query_params.get("action", "").lower(),
                                   callback=dm.views.export_task_as_dataset,
                                   format_name=format_name)

@method_decorator(name='retrieve', decorator=swagger_auto_schema(operation_summary='方法返回作业的详细信息'))
@method_decorator(name='update', decorator=swagger_auto_schema(operation_summary='方法按id更新作业'))
@method_decorator(name='partial_update', decorator=swagger_auto_schema(operation_summary='方法对作业中的选定字段执行部分更新'))
class JobViewSet(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
    queryset = Job.objects.all().order_by('id')
    serializer_class = JobSerializer

    def get_permissions(self):
        http_method = self.request.method
        permissions = [IsAuthenticated]
        if http_method in SAFE_METHODS:
            permissions.append(auth.JobAccessPermission)
        elif http_method in ["PATCH", "PUT", "DELETE"]:
            permissions.append(auth.JobChangePermission)
        else:
            permissions.append(auth.AdminRolePermission)
        return [perm() for perm in permissions]

    @swagger_auto_schema(method='get', operation_summary='方法返回特定作业的批注')
    @swagger_auto_schema(method='put', operation_summary='方法对特定作业中的所有批注执行更新')
    @swagger_auto_schema(method='patch', manual_parameters=[openapi.Parameter('action',
                                                                              in_=openapi.IN_QUERY,
                                                                              type=openapi.TYPE_STRING,
                                                                              required=True,
                                                                              enum=['create', 'update', 'delete'])],
                         operation_summary='方法对特定作业中的批注执行部分更新')
    @swagger_auto_schema(method='delete', operation_summary='方法删除特定作业的所有批注')
    @action(detail=True,
            methods=['GET', 'DELETE', 'PUT', 'PATCH'],
            serializer_class=LabeledDataSerializer)
    def annotations(self, request, pk):
        self.get_object()  # force to call check_object_permissions
        segment = Segment.objects.filter(job__pk=pk).first()
        if request.method == 'GET':
            LogViewSet.createLog(taskId=segment.task_id, userId=request.user.id, message="Download job annotation data")
            data = dm.task.get_job_data(pk)
            return Response(data)
        elif request.method == 'PUT':
            LogViewSet.createLog(taskId=segment.task_id, userId=request.user.id, message="Upload job annotation data")
            format_name = request.query_params.get("format", "")
            if format_name:
                return _import_annotations(
                    request=request,
                    rq_id="{}@/api/v1/jobs/{}/annotations/upload".format(request.user, pk),
                    rq_func=dm.task.import_job_annotations,
                    pk=pk,
                    format_name=format_name
                )
            else:
                serializer = LabeledDataSerializer(data=request.data)
                if serializer.is_valid(raise_exception=True):
                    try:
                        data = dm.task.put_job_data(pk, serializer.data)
                    except (AttributeError, IntegrityError) as e:
                        return Response(data=str(e), status=status.HTTP_200_OK)
                    return Response(data)
        elif request.method == 'DELETE':
            LogViewSet.createLog(taskId=segment.task_id, userId=request.user.id, message="Delete job annotation data")
            dm.task.delete_job_data(pk)
            return Response(status=status.HTTP_204_NO_CONTENT)
        elif request.method == 'PATCH':
            action = self.request.query_params.get("action", None)
            LogViewSet.createLog(taskId=segment.task_id, userId=request.user.id, message=action + " job annotation")
            if action not in dm.task.PatchAction.values():
                raise serializers.ValidationError("请为请求指定正确的“操作”")
            serializer = LabeledDataSerializer(data=request.data)
            if serializer.is_valid(raise_exception=True):
                try:
                    data = dm.task.patch_job_data(pk, serializer.data, action)
                except (AttributeError, IntegrityError) as e:
                    return Response(data=str(e), status=status.HTTP_200_OK)
                return Response(data)

    @swagger_auto_schema(method='get', operation_summary='方法返回 job 的质检列表',
                         responses={'200': ReviewSerializer(many=True)})
    @action(detail=True, methods=['GET'], serializer_class=ReviewSerializer)
    def reviews(self, request, pk):
        db_job = self.get_object()
        queryset = db_job.review_set
        serializer = ReviewSerializer(queryset, context={'request': request}, many=True)
        return Response(serializer.data)

    @swagger_auto_schema(method='get', operation_summary='方法返回 job 的 issues 列表',
                         responses={'200': CombinedIssueSerializer(many=True)})
    @action(detail=True, methods=['GET'], serializer_class=CombinedIssueSerializer)
    def issues(self, request, pk):
        db_job = self.get_object()
        queryset = db_job.issue_set
        serializer = CombinedIssueSerializer(queryset, context={'request': request}, many=True)
        return Response(serializer.data)

@method_decorator(name='create', decorator=swagger_auto_schema(operation_summary='Submit a review for a job'))
@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_summary='Method removes a review from a job'))
class ReviewViewSet(viewsets.GenericViewSet, mixins.DestroyModelMixin, mixins.CreateModelMixin):
    queryset = Review.objects.all().order_by('id')

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return CombinedReviewSerializer
        else:
            return ReviewSerializer

    def get_permissions(self):
        permissions = [IsAuthenticated]
        if self.request.method == 'POST':
            permissions.append(auth.JobReviewPermission)
        else:
            permissions.append(auth.AdminRolePermission)
        return [perm() for perm in permissions]

    def create(self, request, *args, **kwargs):
        job_id = request.data['job']
        db_job = get_object_or_404(Job, pk=job_id)
        self.check_object_permissions(self.request, db_job)

        if request.data['status'] == ReviewStatus.REVIEW_FURTHER:
            if 'reviewer_id' not in request.data:
                return Response('Must provide a new reviewer', status=status.HTTP_400_BAD_REQUEST)
            reviewer_id = request.data['reviewer_id']
            reviewer = get_object_or_404(User, pk=reviewer_id)

        request.data.update({'reviewer_id': request.user.id})
        if db_job.assignee:
            request.data.update({'assignee_id': db_job.assignee.id})

        issue_set = request.data['issue_set']
        for issue in issue_set:
            issue['job'] = db_job.id
            issue['owner_id'] = request.user.id
            comment_set = issue['comment_set']
            for comment in comment_set:
                comment['author_id'] = request.user.id

        serializer = self.get_serializer(data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        if serializer.data['status'] == ReviewStatus.ACCEPTED:
            db_job.status = StatusChoice.COMPLETED
            db_job.save()
        elif serializer.data['status'] == ReviewStatus.REJECTED:
            db_job.status = StatusChoice.ANNOTATION
            db_job.save()
        else:
            db_job.reviewer = reviewer
            db_job.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_summary='Method removes an issue from a job'))
@method_decorator(name='partial_update', decorator=swagger_auto_schema(
    operation_summary='Method updates an issue. It is used to resolve/reopen an issue'))
class IssueViewSet(viewsets.GenericViewSet, mixins.DestroyModelMixin, mixins.UpdateModelMixin):
    queryset = Issue.objects.all().order_by('id')
    http_method_names = ['get', 'patch', 'delete', 'options']

    def get_serializer_class(self):
        return IssueSerializer

    def partial_update(self, request, *args, **kwargs):
        db_issue = self.get_object()
        if 'resolver_id' in request.data and request.data['resolver_id'] and db_issue.resolver is None:
            # resolve
            db_issue.resolver = request.user
            db_issue.resolved_date = datetime.now()
            db_issue.save(update_fields=['resolver', 'resolved_date'])
        elif 'resolver_id' in request.data and not request.data['resolver_id'] and db_issue.resolver is not None:
            # reopen
            db_issue.resolver = None
            db_issue.resolved_date = None
            db_issue.save(update_fields=['resolver', 'resolved_date'])
        serializer = self.get_serializer(db_issue)
        return Response(serializer.data)

    def get_permissions(self):
        http_method = self.request.method
        permissions = [IsAuthenticated]

        if http_method in SAFE_METHODS:
            permissions.append(auth.IssueAccessPermission)
        elif http_method in ['DELETE']:
            permissions.append(auth.IssueDestroyPermission)
        elif http_method in ['PATCH']:
            permissions.append(auth.IssueChangePermission)
        else:
            permissions.append(auth.AdminRolePermission)
        return [perm() for perm in permissions]

    @swagger_auto_schema(method='get', operation_summary='The action returns all comments of a specific issue',
                         responses={'200': CommentSerializer(many=True)})
    @action(detail=True, methods=['GET'], serializer_class=CommentSerializer)
    def comments(self, request, pk):
        db_issue = self.get_object()
        queryset = db_issue.comment_set
        serializer = CommentSerializer(queryset, context={'request': request}, many=True)
        return Response(serializer.data)

@method_decorator(name='partial_update',
                  decorator=swagger_auto_schema(operation_summary='Method updates comment in an issue'))
@method_decorator(name='destroy',
                  decorator=swagger_auto_schema(operation_summary='Method removes a comment from an issue'))
class CommentViewSet(viewsets.GenericViewSet, mixins.DestroyModelMixin, mixins.UpdateModelMixin,
                     mixins.CreateModelMixin):
    queryset = Comment.objects.all().order_by('id')
    serializer_class = CommentSerializer
    http_method_names = ['get', 'post', 'patch', 'delete', 'options']

    def create(self, request, *args, **kwargs):
        request.data.update({
            'author_id': request.user.id,
        })
        issue_id = request.data['issue']
        db_issue = get_object_or_404(Issue, pk=issue_id)
        self.check_object_permissions(self.request, db_issue.job)
        return super().create(request, args, kwargs)

    def get_permissions(self):
        http_method = self.request.method
        permissions = [IsAuthenticated]
        if http_method in ['PATCH', 'DELETE']:
            permissions.append(auth.CommentChangePermission)
        elif http_method in ['POST']:
            permissions.append(auth.CommentCreatePermission)
        else:
            permissions.append(auth.AdminRolePermission)
        return [perm() for perm in permissions]

class UserFilter(filters.FilterSet):
    class Meta:
        model = User
        fields = ("id",)

@method_decorator(name='list', decorator=swagger_auto_schema(operation_summary='方法提供在服务器上注册的用户的分页列表'))
@method_decorator(name='retrieve', decorator=swagger_auto_schema(operation_summary='方法提供特定用户的信息'))
@method_decorator(name='partial_update', decorator=swagger_auto_schema(operation_summary='更新用户选择的方法字段'))
@method_decorator(name='destroy', decorator=swagger_auto_schema(operation_summary='方法从服务器中删除特定用户'))
class UserViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin, mixins.DestroyModelMixin):
    queryset = User.objects.prefetch_related('groups').all().order_by('id')
    http_method_names = ['get', 'post', 'head', 'patch', 'delete']
    search_fields = ('username', 'first_name', 'last_name')
    filterset_class = UserFilter

    def get_serializer_class(self):
        user = self.request.user
        if user.is_staff:
            return UserSerializer
        else:
            is_self = int(self.kwargs.get("pk", 0)) == user.id or self.action == "self"
            return UserSerializer if is_self and self.request.method in SAFE_METHODS else BasicUserSerializer

    def get_permissions(self):
        permissions = [IsAuthenticated]
        user = self.request.user
        if not (self.request.method in SAFE_METHODS):
            is_self = int(self.kwargs.get("pk", 0)) == user.id
            if not is_self:
                permissions.append(auth.AdminRolePermission)
        return [perm() for perm in permissions]

    @swagger_auto_schema(method='get', operation_summary='方法返回当前已被授权的用户的实例')
    @action(detail=False, methods=['GET'])
    def self(self, request):
        serializer_class = self.get_serializer_class()
        serializer = serializer_class(request.user, context={"request": request})
        return Response(serializer.data)

def rq_handler(job, exc_type, exc_value, tb):
    job.exc_info = "".join(
        traceback.format_exception_only(exc_type, exc_value))
    job.save()
    if "tasks" in job.id.split("/"):
        return task.rq_handler(job, exc_type, exc_value, tb)
    return True

# TODO: Method should be reimplemented as a separated view
# @swagger_auto_schema(method='put', manual_parameters=[openapi.Parameter('format', in_=openapi.IN_QUERY,
#         description='A name of a loader\nYou can get annotation loaders from this API:\n/server/annotation/formats',
#         required=True, type=openapi.TYPE_STRING)],
#     operation_summary='Method allows to upload annotations',
#     responses={'202': openapi.Response(description='Load of annotations has been started'),
#         '201': openapi.Response(description='Annotations have been uploaded')},
#     tags=['tasks'])
# @api_view(['PUT'])
def _import_annotations(request, rq_id, rq_func, pk, format_name):
    format_desc = {f.DISPLAY_NAME: f
                   for f in dm.views.get_import_formats()}.get(format_name)
    if format_desc is None:
        raise serializers.ValidationError(
            "Unknown input format '{}'".format(format_name))
    elif not format_desc.ENABLED:
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

    queue = django_rq.get_queue("default")
    rq_job = queue.fetch_job(rq_id)

    if not rq_job:
        serializer = AnnotationFileSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            anno_file = serializer.validated_data['annotation_file']
            fd, filename = mkstemp(prefix='cvat_{}'.format(pk))
            with open(filename, 'wb+') as f:
                for chunk in anno_file.chunks():
                    f.write(chunk)

            av_scan_paths(filename)
            rq_job = queue.enqueue_call(
                func=rq_func,
                args=(pk, filename, format_name),
                job_id=rq_id
            )
            rq_job.meta['tmp_file'] = filename
            rq_job.meta['tmp_file_descriptor'] = fd
            rq_job.save_meta()
    else:
        if rq_job.is_finished:
            os.close(rq_job.meta['tmp_file_descriptor'])
            os.remove(rq_job.meta['tmp_file'])
            rq_job.delete()
            return Response(status=status.HTTP_201_CREATED)
        elif rq_job.is_failed:
            os.close(rq_job.meta['tmp_file_descriptor'])
            os.remove(rq_job.meta['tmp_file'])
            exc_info = str(rq_job.exc_info)
            rq_job.delete()
            return Response(data=exc_info, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return Response(status=status.HTTP_202_ACCEPTED)

def _export_annotations(db_task, rq_id, request, format_name, action, callback):
    # 如果 action 提供了意外的参数，返回错误结果
    if action not in {"", "download"}:
        raise serializers.ValidationError("为请求指定了意外的操作")

    # 如果 format_name 不是允许的格式，返回错误结果
    format_desc = {f.DISPLAY_NAME: f for f in dm.views.get_export_formats()}.get(format_name)
    if format_desc is None:
        raise serializers.ValidationError("为请求指定的格式未知！")
    elif not format_desc.ENABLED:
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

    queue = django_rq.get_queue("default")
    rqJob = queue.fetch_job(rq_id)
    if rqJob:  # 队列中任务存在
        lastTaskUpdateTime = timezone.localtime(db_task.updated_date)
        request_time = rqJob.meta.get('request_time', None)
        if request_time is None or request_time < lastTaskUpdateTime:
            rqJob.cancel()
            rqJob.delete()
        else:
            if rqJob.is_finished:  # 标注数据集生成成功
                file_path = rqJob.return_value
                if action == "download" and osp.exists(file_path):
                    rqJob.delete()
                    timestamp = datetime.strftime(lastTaskUpdateTime, "%Y_%m_%d_%H_%M_%S")
                    filename = f"task_{db_task.name}-{timestamp}-{format_name}{osp.splitext(file_path)[1]}"
                    return sendfile(request, file_path, attachment=True, attachment_filename=filename.lower())
                else:
                    if osp.exists(file_path):
                        return Response(status=status.HTTP_201_CREATED)
            elif rqJob.is_failed:  # 标注数据集生成失败
                exc_info = str(rqJob.exc_info)
                rqJob.delete()
                return Response(exc_info, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            else:  # 标注数据集正在生成
                return Response(status=status.HTTP_202_ACCEPTED)
    try:
        if request.scheme:
            server_address = request.scheme + '://'
        server_address += request.get_host()
    except Exception:
        server_address = None

    ttl = dm.views.CACHE_TTL.total_seconds()
    # 加入任务队列
    queue.enqueue_call(func=callback, args=(db_task.id, format_name, server_address), job_id=rq_id,
                       meta={'request_time': timezone.localtime()}, result_ttl=ttl, failure_ttl=ttl)
    return Response(status=status.HTTP_202_ACCEPTED)

class LogFilter(filters.FilterSet):
    task = filters.CharFilter(field_name="task__name", lookup_expr="icontains")
    user = filters.CharFilter(field_name="user__username", lookup_expr="icontains")
    time = filters.DateTimeFilter(field_name="time", lookup_expr="icontains")
    message = filters.CharFilter(field_name="message", lookup_expr="icontains")

    class Meta:
        model = Log
        fields = ("id", "task_id", "task", "user", "time", "message")

class LogViewSet(ModelViewSet):
    filterset_class = LogFilter
    queryset = Log.objects.all().order_by('time')
    permission_classes = [IsAuthenticated]
    serializer_class = LogSerializer

    @classmethod
    def createLog(cls, taskId, userId, message):
        log = Log(task_id=taskId, user_id=userId, time=timezone.now(), message=message)
        log.save()


import sys
_module = sys.modules[__name__]
del sys
CamVid = _module
dataset = _module
demo = _module
eval = _module
loss = _module
model = _module
build_BiSeNet = _module
build_contextpath = _module
train = _module
utils = _module

from _paritybench_helpers import _mock_config, patch_functional
from unittest.mock import mock_open, MagicMock
from torch.autograd import Function
from torch.nn import Module
import abc, collections, copy, enum, functools, inspect, itertools, logging, math, matplotlib, numbers, numpy, pandas, queue, random, re, scipy, sklearn, string, tensorflow, time, torch, torchaudio, torchtext, torchvision, types, typing, uuid, warnings
import numpy as np
from torch import Tensor
patch_functional()
open = mock_open()
yaml = logging = sys = argparse = MagicMock()
ArgumentParser = argparse.ArgumentParser
_global_config = args = argv = cfg = config = params = _mock_config()
argparse.ArgumentParser.return_value.parse_args.return_value = _global_config
yaml.load.return_value = _global_config
sys.argv = _global_config
__version__ = '1.0.0'
xrange = range
wraps = functools.wraps

import torch

from torchvision import transforms

import pandas as pd

import numpy as np

import random

from torch.utils.data import DataLoader

import torch.nn as nn

import torch.nn.functional as F

from torch import nn

import warnings

from torchvision import models

from torch.utils.data import Dataset

from torch.nn import functional as F

import numbers

import torchvision

def flatten(tensor):
    """Flattens a given tensor such that the channel axis is first.
    The shapes are transformed as follows:
       (N, C, D, H, W) -> (C, N * D * H * W)
    """
    C = tensor.size(1)
    axis_order = (1, 0) + tuple(range(2, tensor.dim()))
    transposed = tensor.permute(axis_order)
    return transposed.contiguous().view(C, -1)

class DiceLoss(nn.Module):

    def __init__(self):
        super().__init__()
        self.epsilon = 1e-05

    def forward(self, output, target):
        assert output.size() == target.size(), "'input' and 'target' must have the same shape"
        output = F.softmax(output, dim=1)
        output = flatten(output)
        target = flatten(target)
        intersect = (output * target).sum(-1)
        denominator = (output + target).sum(-1)
        dice = intersect / denominator
        dice = torch.mean(dice)
        return 1 - dice

class ConvBlock(torch.nn.Module):

    def __init__(self, in_channels, out_channels, kernel_size=3, stride=2, padding=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU()

    def forward(self, input):
        x = self.conv1(input)
        return self.relu(self.bn(x))

class Spatial_path(torch.nn.Module):

    def __init__(self):
        super().__init__()
        self.convblock1 = ConvBlock(in_channels=3, out_channels=64)
        self.convblock2 = ConvBlock(in_channels=64, out_channels=128)
        self.convblock3 = ConvBlock(in_channels=128, out_channels=256)

    def forward(self, input):
        x = self.convblock1(input)
        x = self.convblock2(x)
        x = self.convblock3(x)
        return x

class AttentionRefinementModule(torch.nn.Module):

    def __init__(self, in_channels, out_channels):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
        self.bn = nn.BatchNorm2d(out_channels)
        self.sigmoid = nn.Sigmoid()
        self.in_channels = in_channels
        self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))

    def forward(self, input):
        x = self.avgpool(input)
        assert self.in_channels == x.size(1), 'in_channels and out_channels should all be {}'.format(x.size(1))
        x = self.conv(x)
        x = self.sigmoid(x)
        x = torch.mul(input, x)
        return x

class FeatureFusionModule(torch.nn.Module):

    def __init__(self, num_classes, in_channels):
        super().__init__()
        self.in_channels = in_channels
        self.convblock = ConvBlock(in_channels=self.in_channels, out_channels=num_classes, stride=1)
        self.conv1 = nn.Conv2d(num_classes, num_classes, kernel_size=1)
        self.relu = nn.ReLU()
        self.conv2 = nn.Conv2d(num_classes, num_classes, kernel_size=1)
        self.sigmoid = nn.Sigmoid()
        self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))

    def forward(self, input_1, input_2):
        x = torch.cat((input_1, input_2), dim=1)
        assert self.in_channels == x.size(1), 'in_channels of ConvBlock should be {}'.format(x.size(1))
        feature = self.convblock(x)
        x = self.avgpool(feature)
        x = self.relu(self.conv1(x))
        x = self.sigmoid(self.conv2(x))
        x = torch.mul(feature, x)
        x = torch.add(x, feature)
        return x

class resnet101(torch.nn.Module):

    def __init__(self, pretrained=True):
        super().__init__()
        self.features = models.resnet101(pretrained=pretrained)
        self.conv1 = self.features.conv1
        self.bn1 = self.features.bn1
        self.relu = self.features.relu
        self.maxpool1 = self.features.maxpool
        self.layer1 = self.features.layer1
        self.layer2 = self.features.layer2
        self.layer3 = self.features.layer3
        self.layer4 = self.features.layer4

    def forward(self, input):
        x = self.conv1(input)
        x = self.relu(self.bn1(x))
        x = self.maxpool1(x)
        feature1 = self.layer1(x)
        feature2 = self.layer2(feature1)
        feature3 = self.layer3(feature2)
        feature4 = self.layer4(feature3)
        tail = torch.mean(feature4, 3, keepdim=True)
        tail = torch.mean(tail, 2, keepdim=True)
        return feature3, feature4, tail

class resnet18(torch.nn.Module):

    def __init__(self, pretrained=True):
        super().__init__()
        self.features = models.resnet18(pretrained=pretrained)
        self.conv1 = self.features.conv1
        self.bn1 = self.features.bn1
        self.relu = self.features.relu
        self.maxpool1 = self.features.maxpool
        self.layer1 = self.features.layer1
        self.layer2 = self.features.layer2
        self.layer3 = self.features.layer3
        self.layer4 = self.features.layer4

    def forward(self, input):
        x = self.conv1(input)
        x = self.relu(self.bn1(x))
        x = self.maxpool1(x)
        feature1 = self.layer1(x)
        feature2 = self.layer2(feature1)
        feature3 = self.layer3(feature2)
        feature4 = self.layer4(feature3)
        tail = torch.mean(feature4, 3, keepdim=True)
        tail = torch.mean(tail, 2, keepdim=True)
        return feature3, feature4, tail

def build_contextpath(name):
    model = {'resnet18': resnet18(pretrained=True), 'resnet101': resnet101(pretrained=True)}
    return model[name]

class BiSeNet(torch.nn.Module):

    def __init__(self, num_classes, context_path):
        super().__init__()
        self.saptial_path = Spatial_path()
        self.context_path = build_contextpath(name=context_path)
        if context_path == 'resnet101':
            self.attention_refinement_module1 = AttentionRefinementModule(1024, 1024)
            self.attention_refinement_module2 = AttentionRefinementModule(2048, 2048)
            self.supervision1 = nn.Conv2d(in_channels=1024, out_channels=num_classes, kernel_size=1)
            self.supervision2 = nn.Conv2d(in_channels=2048, out_channels=num_classes, kernel_size=1)
            self.feature_fusion_module = FeatureFusionModule(num_classes, 3328)
        elif context_path == 'resnet18':
            self.attention_refinement_module1 = AttentionRefinementModule(256, 256)
            self.attention_refinement_module2 = AttentionRefinementModule(512, 512)
            self.supervision1 = nn.Conv2d(in_channels=256, out_channels=num_classes, kernel_size=1)
            self.supervision2 = nn.Conv2d(in_channels=512, out_channels=num_classes, kernel_size=1)
            self.feature_fusion_module = FeatureFusionModule(num_classes, 1024)
        else:
            None
        self.conv = nn.Conv2d(in_channels=num_classes, out_channels=num_classes, kernel_size=1)
        self.init_weight()
        self.mul_lr = []
        self.mul_lr.append(self.saptial_path)
        self.mul_lr.append(self.attention_refinement_module1)
        self.mul_lr.append(self.attention_refinement_module2)
        self.mul_lr.append(self.supervision1)
        self.mul_lr.append(self.supervision2)
        self.mul_lr.append(self.feature_fusion_module)
        self.mul_lr.append(self.conv)

    def init_weight(self):
        for name, m in self.named_modules():
            if 'context_path' not in name:
                if isinstance(m, nn.Conv2d):
                    nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
                elif isinstance(m, nn.BatchNorm2d):
                    m.eps = 1e-05
                    m.momentum = 0.1
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)

    def forward(self, input):
        sx = self.saptial_path(input)
        cx1, cx2, tail = self.context_path(input)
        cx1 = self.attention_refinement_module1(cx1)
        cx2 = self.attention_refinement_module2(cx2)
        cx2 = torch.mul(cx2, tail)
        cx1 = torch.nn.functional.interpolate(cx1, size=sx.size()[-2:], mode='bilinear')
        cx2 = torch.nn.functional.interpolate(cx2, size=sx.size()[-2:], mode='bilinear')
        cx = torch.cat((cx1, cx2), dim=1)
        if self.training == True:
            cx1_sup = self.supervision1(cx1)
            cx2_sup = self.supervision2(cx2)
            cx1_sup = torch.nn.functional.interpolate(cx1_sup, size=input.size()[-2:], mode='bilinear')
            cx2_sup = torch.nn.functional.interpolate(cx2_sup, size=input.size()[-2:], mode='bilinear')
        result = self.feature_fusion_module(sx, cx)
        result = torch.nn.functional.interpolate(result, scale_factor=8, mode='bilinear')
        result = self.conv(result)
        if self.training == True:
            return result, cx1_sup, cx2_sup
        return result

class OHEM_CrossEntroy_Loss(nn.Module):

    def __init__(self, threshold, keep_num):
        super(OHEM_CrossEntroy_Loss, self).__init__()
        self.threshold = threshold
        self.keep_num = keep_num
        self.loss_function = nn.CrossEntropyLoss(reduction='none')

    def forward(self, output, target):
        loss = self.loss_function(output, target).view(-1)
        loss, loss_index = torch.sort(loss, descending=True)
        threshold_in_keep_num = loss[self.keep_num]
        if threshold_in_keep_num > self.threshold:
            loss = loss[loss > self.threshold]
        else:
            loss = loss[:self.keep_num]
        return torch.mean(loss)

import torch
from torch.nn import MSELoss, ReLU
from _paritybench_helpers import _mock_config, _mock_layer, _paritybench_base, _fails_compile

TESTCASES = [
    # (nn.Module, init_args, forward_args, jit_compiles)
    (AttentionRefinementModule,
     lambda: ([], {'in_channels': 4, 'out_channels': 4}),
     lambda: ([torch.rand([4, 4, 4, 4])], {}),
     True),
    (ConvBlock,
     lambda: ([], {'in_channels': 4, 'out_channels': 4}),
     lambda: ([torch.rand([4, 4, 4, 4])], {}),
     True),
    (DiceLoss,
     lambda: ([], {}),
     lambda: ([torch.rand([4, 4, 4, 4]), torch.rand([4, 4, 4, 4])], {}),
     False),
    (Spatial_path,
     lambda: ([], {}),
     lambda: ([torch.rand([4, 3, 64, 64])], {}),
     True),
    (resnet101,
     lambda: ([], {}),
     lambda: ([torch.rand([4, 3, 64, 64])], {}),
     True),
    (resnet18,
     lambda: ([], {}),
     lambda: ([torch.rand([4, 3, 64, 64])], {}),
     True),
]

class Test_ooooverflow_BiSeNet(_paritybench_base):
    def test_000(self):
        self._check(*TESTCASES[0])

    def test_001(self):
        self._check(*TESTCASES[1])

    def test_002(self):
        self._check(*TESTCASES[2])

    def test_003(self):
        self._check(*TESTCASES[3])

    def test_004(self):
        self._check(*TESTCASES[4])

    def test_005(self):
        self._check(*TESTCASES[5])


from testtools import matchers

from tempest.api.volume import base
from tempest.common import waiters
from tempest import config
from tempest import test

CONF = config.CONF

class VolumesV2TransfersTest(base.BaseVolumeTest):

    credentials = ['primary', 'alt', 'admin']

    @classmethod
    def setup_clients(cls):
        super(VolumesV2TransfersTest, cls).setup_clients()

        cls.client = cls.volumes_client
        cls.alt_client = cls.os_alt.volumes_client
        cls.alt_tenant_id = cls.alt_client.tenant_id
        cls.adm_client = cls.os_adm.volumes_client

    def _delete_volume(self, volume_id):
        # Delete the specified volume using admin creds
        self.adm_client.delete_volume(volume_id)
        self.adm_client.wait_for_resource_deletion(volume_id)

    @test.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
    def test_create_get_list_accept_volume_transfer(self):
        # Create a volume first
        volume = self.create_volume()
        self.addCleanup(self._delete_volume, volume['id'])

        # Create a volume transfer
        transfer = self.client.create_volume_transfer(
            volume_id=volume['id'])['transfer']
        transfer_id = transfer['id']
        auth_key = transfer['auth_key']
        waiters.wait_for_volume_status(self.client,
                                       volume['id'], 'awaiting-transfer')

        # Get a volume transfer
        body = self.client.show_volume_transfer(transfer_id)['transfer']
        self.assertEqual(volume['id'], body['volume_id'])

        # List volume transfers, the result should be greater than
        # or equal to 1
        body = self.client.list_volume_transfers()['transfers']
        self.assertThat(len(body), matchers.GreaterThan(0))

        # Accept a volume transfer by alt_tenant
        body = self.alt_client.accept_volume_transfer(
            transfer_id, auth_key=auth_key)['transfer']
        waiters.wait_for_volume_status(self.alt_client,
                                       volume['id'], 'available')

    @test.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
    def test_create_list_delete_volume_transfer(self):
        # Create a volume first
        volume = self.create_volume()
        self.addCleanup(self._delete_volume, volume['id'])

        # Create a volume transfer
        body = self.client.create_volume_transfer(
            volume_id=volume['id'])['transfer']
        transfer_id = body['id']
        waiters.wait_for_volume_status(self.client,
                                       volume['id'], 'awaiting-transfer')

        # List all volume transfers (looking for the one we created)
        body = self.client.list_volume_transfers()['transfers']
        for transfer in body:
            if volume['id'] == transfer['volume_id']:
                break
        else:
            self.fail('Transfer not found for volume %s' % volume['id'])

        # Delete a volume transfer
        self.client.delete_volume_transfer(transfer_id)
        waiters.wait_for_volume_status(self.client, volume['id'], 'available')

class VolumesV1TransfersTest(VolumesV2TransfersTest):
    _api_version = 1


#!/usr/bin/python3
'''Parser for the main building blocks of the phenopacket schema, that are
Phenopacket, Family, Cohort and Interpretation'''
import json

import datetime
import logging

from google.protobuf.json_format import Parse, MessageToJson
from google.protobuf import message
from google.protobuf.timestamp_pb2 import Timestamp

from base_pb2 import MetaData,Individual,Disease,Biosample,Pedigree,Gene,PhenotypicFeature, \
     Variant,HtsFile,OntologyClass,Age,Evidence,ExternalReference

from phenopackets_pb2 import Phenopacket,Family,Cohort
from interpretation_pb2 import Interpretation,Diagnosis,GenomicInterpretation

from typing import List
import copy

def ParsePheno(jsonpheno:json)->Phenopacket:
    phenopacket_input={}
    ##first required fields
    #id
    phenopacket_input['id']=jsonpheno['id']
    #metadata
    phenopacket_input['meta_data']=ParseMeta(jsonpheno['meta_data'])
    ##optional and recommended fields
    #subject
    if 'subject' in jsonpheno:
        phenopacket_input['subject']= ParseSubject(jsonpheno['subject'])
    #diseases
    if 'diseases' in jsonpheno:
        phenopacket_input['diseases']=ParseDiseases(jsonpheno['diseases'])
    #biosamples
    if 'biosamples' in jsonpheno:
        phenopacket_input['biosamples']=ParseBiosamples(jsonpheno['biosamples'])
    #genes
    if 'genes' in jsonpheno:
        phenopacket_input['genes']=ParseGenes(jsonpheno['genes'])
    #phenotypic_features
    if 'phenotypic_features' in jsonpheno:
        phenopacket_input['phenotypic_features']=ParsePhenotypic(jsonpheno['phenotypic_features'])
    #variants
    if 'variants' in jsonpheno:
        phenopacket_input['variants']=ParseVariants(jsonpheno['variants'])
    #hts_files
    if 'hts_files' in jsonpheno:
        phenopacket_input['hts_files']= Parsehts(jsonpheno['hts_files'])
    return Phenopacket(**phenopacket_input)

def ParseFamily(jsonfamily:json)->Family:
    family_input={}
    ##first required fields
    #id
    family_input['id']=jsonfamily['id']
    #proband
    family_input['proband']=ParsePheno(jsonfamily['proband'])
    #pedigree
    family_input['pedigree']=ParsePedigree(jsonfamily['pedigree'])
    #metadata
    family_input['meta_data']= ParseMeta(jsonfamily['meta_data'])
    ##optional and recommended fields
    #relatives
    if 'relatives' in jsonfamily:
        family_input['relatives']=ParseRelatives(jsonfamily['relatives'])
    #hts_files
    if 'hts_files' in jsonfamily:
        family_input['hts_files']= Parsehts(jsonfamily['hts_files'])
    return Family(**family_input)

def ParseCohort(jsoncohort:json)->Cohort:
    cohort_input={}
    ##first required fields
    #id
    cohort_input['id']=jsoncohort['id']
    #members
    cohort_input['members']=ParseRelatives(jsoncohort['members'])
    #metadata
    cohort_input['meta_data']=ParseMeta(jsoncohort['meta_data'])
    ##optional and recommended fields
    #description
    if 'description' in jsoncohort:
        cohort_input['description']=jsoncohort['description']
    #hts_files
    if 'hts_files' in jsoncohort:
        cohort_input['hts_files']=Parsehts(jsoncohort['hts_files'])

    return Cohort(**cohort_input)

def ParseInterpretation(jsoninterpretation:json)->Interpretation:
    interpretation_input={}
    ##first required fields
    #id
    interpretation_input['id']=jsoninterpretation['id']
    #resolution_status
    interpretation_input['resolution_status']=jsoninterpretation['resolution_status']
    #phenopacket or family
    if 'phenopacket' in jsoninterpretation:
        interpretation_input['phenopacket']=ParsePheno(jsoninterpretation['phenopacket'])
    if 'family' in jsoninterpretation:
        interpretation_input['family']=ParseFamily(jsoninterpretation['family'])
    #metadata
    interpretation_input['meta_data']=ParseMeta(jsoninterpretation['meta_data'])
    #diagnosis
    if 'diagnosis' in jsoninterpretation:
        interpretation_input['diagnosis']=ParseDiagnosis(jsoninterpretation['diagnosis'])
    return Interpretation(**interpretation_input)

def ToOnto(jsononto:json)->OntologyClass:
#    print (jsononto)
    return OntologyClass(id=jsononto['id'],label=jsononto['label'])

def ParseMeta(meta_data:json)->MetaData:
    if 'created' in meta_data:
        ts=Timestamp()
        oldvalue=meta_data['created']
        dt= datetime.datetime.strptime(oldvalue,'%Y-%m-%dT%H:%M:%S.%fZ')
        ts.FromDatetime(dt)
        meta_data['created']=ts
    if ('updates' in meta_data):
        for up in meta_data['updates']:
            oldvalue=up['timestamp']
            dt= datetime.datetime.strptime(oldvalue,'%Y-%m-%dT%H:%M:%S.%fZ')
            ts.FromDatetime(dt)
            up['timestamp']=ts
 #   for res in meta_data['resources']:
 #       print("llllllllllllllllll")
 #       print(res)
 #       oldvalue=res['version']
 #       oldvalue='2019-10-10'
 #       res['version']=oldvalue
#        dt=datetime.datetime.strptime(oldvalue,'%Y-%m-%d')
#        ts.FromDatetime(dt)
#        res['version']=ts
#        del res['version']
        # timestamp = Timestamp(seconds=1570695783)
        # res['version']=timestamp
    return MetaData(**meta_data)

def Parsehts(jsonhts:json)->List[HtsFile]:
    hts=[]
    for ht in jsonhts:
        if ht['hts_format'].startswith('at00'):
            ht['hts_format']='VCF'
        hts.append(HtsFile(**ht))
    return hts

def ParseSubject(subjectP:json)->Individual:
    ts=Timestamp()
    if ('date_of_birth' in subjectP):
        oldvalue=subjectP['date_of_birth']
        dt= datetime.datetime.strptime(oldvalue,'%Y-%m-%dT%H:%M:%SZ')
        ts.FromDatetime(dt)
        subjectP['date_of_birth']=ts
    return Individual(**subjectP)

def ParseDiseases(jsondis:list)->List[Disease]:
    diseases=[]
    for dis in jsondis:
        dis['term']=ToOnto(dis['term'])
        if 'tumor_stage' in dis:
            for tum in dis['tumor_stage']:
                tum=ToOnto(tum)
        if 'onset' in dis:
            dis['age_of_onset']=Age(age=dis.pop('onset'))
        diseases.append(Disease(**dis))
    return diseases

def ParseBiosamples(jsonbio:json)->List[Biosample]:
    biosamples=[]
    for bio in jsonbio:
        bio['sampled_tissue']=ToOnto(bio['sampled_tissue'])
        if 'individual_age_at_collection' in bio:
            bio['age_of_individual_at_collection']=Age(age=bio.pop('individual_age_at_collection'))
        if 'taxonomy' in bio:
            bio['taxonomy']=ToOnto(bio['taxonomy'])
        if 'histological_diagnosis' in bio:
            bio['histological_diagnosis']=ToOnto(bio['histological_diagnosis'])
        if 'tumor_progression' in bio:
            bio['tumor_progression']=ToOnto(bio['tumor_progression'])
        if 'tumor_grade' in bio:
            bio['tumor_grade']=ToOnto(bio['tumor_grade'])
        if 'diagnostic_markers' in bio:
            for dm in bio['diagnostic_markers']:
                dm=ToOnto(dm)
        biosamples.append(Biosample(**bio))
    return biosamples

def ParseGenes(jsongenes:json)->List[Gene]:
    genes=[]
    for gen in jsongenes:
        genes.append(Gene(**gen))
    return genes

def ParsePhenotypic(jsonpheno:json)->List[PhenotypicFeature]:
    phefe=[]
    for phe in jsonpheno:
        phe['type']=ToOnto(phe['type'])
        if 'severity' in phe:
            phe['severity']=ToOnto(phe['severity'])
        if 'modifiers' in phe:
            for modi in phe['modifiers']:
                modi=ToOnto(modi)
        if 'onset' in phe:
            phe['class_of_onset']=ToOnto(phe.pop('onset'))
        if 'evidence' in phe:
            for evid in phe['evidence']:
                evid['evidence_code']=ToOnto(evid['evidence_code'])
                if 'reference' in evid:
                    evid['reference']=ExternalReference(**evid['reference'])
        phefe.append(PhenotypicFeature(**phe))
    return phefe

def ParseVariants(jsonvar:json)->List[Variant]:
    variants=[]
    for vari in jsonvar:
#        print('bbbb')
#        print (vari)
        variants.append(Variant(**vari))
    return variants

def ParsePedigree(jsonped:json)->Pedigree:
    for p in jsonped['persons']:
        if p['affected_status'].startswith('at00'):
            p['affected_status']='AFFECTED'
#    print (jsonped)
    return Pedigree(**jsonped)

def ParseRelatives(jsonrel:json)->List[Phenopacket]:
    relatives=[]
    for rel in jsonrel:
        relatives.append(ParsePheno(rel))
    return relatives

def ParseDiagnosis(dia:json)->Diagnosis:
#    print ('dia')
#    print (dia)
#    print ('----------')
    dia['disease']=ParseDiseases([dia['disease']])[0]
    genomic_interpretations=[]
    for gi in dia['genomic_interpretations']:
        if 'gene' in gi:
#            print ('gene')
#            print (gi)
            genes=ParseGenes([gi['gene']])
            genomic_interpretation={}
            genomic_interpretation['status']=gi['status']
            genomic_interpretation['gene']=genes[0]
#            print ('genomic_interpretation')
#            print (genomic_interpretation)
            genomic_interpretations.append(GenomicInterpretation(**genomic_interpretation))

        if 'variant' in gi:
            variants=ParseVariants([gi['variant']])
            genomic_interpretation={}
            genomic_interpretation['status']=gi['status']
            genomic_interpretation['variant']=variants[0]
            genomic_interpretations.append(GenomicInterpretation(**genomic_interpretation))
    dia['genomic_interpretations']=genomic_interpretations
    return [Diagnosis(**dia)]


# Martí Gelabert Gómez
# Tennis
import numpy as np
import cv2
import os
from time import sleep
import argparse
import random

class Enitity(object):
    def __init__(self, bbox, player = 0, is_ball = 0):
        # Initialization of the Kalman filte
        self.kf = cv2.KalmanFilter(4, 2)
        self.kf.measurementMatrix = np.array([[1, 0, 0, 0], [0, 1, 0, 0]], np.float32)
        self.kf.transitionMatrix = np.array([[1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]], np.float32)
        self.kf.processNoiseCov = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], np.float32) * 0.03

        self.player = player
        self.hit = 0
        self.is_ball = is_ball

        self.current = bbox
        self.anterior = bbox
        self.last_aparience = bbox

        (x,y,w,h) = bbox

        self.time = 0

        self.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        # we will guess that the objects will mantein proportions
        self.h = h
        self.w = w

    def score_hit(self):
        self.hit  += 1

    def missed(self):
        self.time += 1

    def found(self):
        self.time = 0

    def predicted_state(self):
        return self.kf.predict()

    def predict_bbox(self):
        predicted_state = self.kf.predict()
        x, y = int(predicted_state[0]), int(predicted_state[1])
        return (x, y, self.w, self.h)

    def correct(self, box):
        self.kf.correct(np.array([[np.float32(box[0])], [np.float32(box[1])]]))

    def update_bbox(self, bbox):

        self.anterior = self.current
        self.current = bbox

        (x,y,w,h) = bbox

        # we will guess that the objects will mantein proportions
        self.h = h
        self.w = w

def center(bbox):
    (x,y,w,h) = bbox
    return(x+w//2, y+h//2)

def find_score_player(entities, ball_object, first=0):
    distance = []
    for index,i in enumerate(entities):
        (xe,ye) = center(i.current)
        (xb,yb) = center(ball_object.current)

        #(xe,ye,we,he) = i.current
        #(xb,yb,wb,hb) = ball_object.current

        distance.append(((xe - xb)**2 + (ye - yb)**2)**0.5)
        # if its the first shot we are gona just check which one is  nearest the dude
        if first:
            pass
        else:
            if distance[index] > 150:
                distance[index] = 10000

    return np.argmin(np.array(distance)), distance[np.argmin(np.array(distance))]

def get_rois(frame, cal, backSub):

    fgMask = frame

    # original
    fgMask = cv2.blur(fgMask, (8, 8))
    #fgMask = cv2.blur(fgMask, (15, 15))

    fgMask = backSub.apply(fgMask)  # real

    fgMask = cv2.bitwise_and(fgMask, fgMask, mask = cal)

    #origina
    fgMask = cv2.erode(fgMask, np.ones((2, 1), np.uint8), iterations=3)
    fgMask = cv2.dilate(fgMask, np.ones((7, 7), np.uint8), iterations=2)

    fgMask = cv2.dilate(fgMask, np.ones((10, 10), np.uint8), iterations=2)

    # this is new, seems interesting
    fgMask = cv2.erode(fgMask, np.ones((5,5), np.uint8), iterations=3)
    fgMask = cv2.dilate(fgMask, np.ones((7, 7), np.uint8), iterations=2)

    #fgMask = cv2.erode(fgMask, np.ones((5,7), np.uint8), iterations=3)

    ret, fgMask = cv2.threshold(fgMask, 170, 200, cv2.THRESH_BINARY)
    #fgMask = cv2.bitwise_and(fgMask, fgMask, mask = cal)

    #ball = fgMask.copy()
    #ball = cv2.bitwise_and(fgMask, ball, mask = b_mask)
    #cv2.imshow('Blue Detector', ball) # to display the blue object output

    contours, _ = cv2.findContours(fgMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    height, width = fgMask.shape
    min_x, min_y = width, height
    max_x = max_y = 0

    rois = []
    for contour in contours:
        (x,y,w,h) = cv2.boundingRect(contour)
        min_x, max_x = min(x, min_x), max(x+w, max_x)
        min_y, max_y = min(y, min_y), max(y+h, max_y)
        #if w > 45 and h > 45 or 25 < w < 35 and 25 < h < 35:
        #cv2.rectangle(frame, (x,y), (x+w,y+h), (255, 0, 0), 2)
        rois.append(cv2.boundingRect(contour))

    return rois, fgMask

# Code extracted from
# https://pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/
def iou(boxA, boxB):
    (x,y,w,h) = boxA
    boxA = np.array([x,y,x+w,y+h])

    (x,y,w,h) = boxB
    boxB = np.array([x,y,x+w,y+h])

    # determine the (x, y)-coordinates of the intersection rectangle
    xA = max(boxA[0], boxB[0])
    yA = max(boxA[1], boxB[1])
    xB = min(boxA[2], boxB[2])
    yB = min(boxA[3], boxB[3])
    # compute the area of intersection rectangle
    interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
    # compute the area of both the prediction and ground-truth
    # rectangles
    boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
    boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
    # compute the intersection over union by taking the intersection
    # area and dividing it by the sum of prediction + ground-truth
    # areas - the interesection area
    if float(boxAArea + boxBArea - interArea) == 0:
        return -1
    iou = interArea / float(boxAArea + boxBArea - interArea)
    # return the intersection over union value
    return iou

def temporal_coherence(box, rois, ignore=[], frame = [], debug = 0, th = 0.3):
    """Return the index of which the box is more suitable to be"""

    scores = []
    for r in rois:
        scores.append(iou(box, r))
    scores = np.array(scores)

    ideal = np.argmax(scores)

    found = False
    i = 0
    while(not(found)) and i < len(rois):

        if ideal in ignore or scores[ideal]<th:
            scores[ideal] = -42 # we will not check it
            scores
            i+=1
            ideal = np.argmax(scores)

        else:
            return np.argmax(scores), rois[np.argmax(scores)]
    # return -1 if we dont find our thing
    return -1, box

def print_rois(rois, frame):
    tmp = frame.copy()

    for (x,y,w,h) in rois:
        cv2.rectangle(frame, (x,y), (x+w,y+h), (255, 0, 0), 2)
    return frame

def calibration_mask(vid):
    if vid=='vid2.mp4':
        return cv2.imread('vid2_mask_bin.jpg', cv2.IMREAD_GRAYSCALE)
    else:
        img = cv2.imread('video_cut_mask_bin.jpg', cv2.IMREAD_GRAYSCALE)
        img[:90][:] = 0

        #img[img.shape[0]-250:][:] = 0
        return img

# ahora que tengo a los wachos, los borro de la imagen
def get_rest_of_rois(rois_matched, image_th):

    ball_image = image_th.copy()

    for (x,y,w,h) in rois_matched:
        cv2.rectangle(ball_image, (x,y), (x+w,y+h), (0, 0, 0), -1)

    #fgMask = cv2.erode(ball_image, np.ones((5,5), np.uint8), iterations=3)

    contours, _ = cv2.findContours(ball_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    r = []
    for contour in contours:
        #if w > 45 and h > 45 or 25 < w < 35 and 25 < h < 35:
        #cv2.rectangle(frame, (x,y), (x+w,y+h), (255, 0, 0), 2)
        r.append(cv2.boundingRect(contour))

    return r, ball_image

def main():

    tracking_video = []
    general_video = []
    ball_video = []

    # The players
    entities = []

    # The ball (entity that will contain the possible ball)
    ball = []

    parser = argparse.ArgumentParser(description='This program shows how to use background subtraction and kalman filters  \
                                                  methods provided by \
                                                  OpenCV. You can process videos and images.')
    parser.add_argument('--input', type=str, help='Path to a video or a sequence of image.', default='video_cut.mp4')
    parser.add_argument('--substractor', type=str, help='Background subtraction method (KNN, MOG2).', default='KNN')
    parser.add_argument('--mode', type=int, help='Background subtraction method (KNN, MOG2).', default=0)

    args = parser.parse_args()
    filename  = args.input
    mode = args.mode

    if args.substractor == 'MOG2':
        print("%s selected" % args.substractor)
        backSub = cv2.createBackgroundSubtractorMOG2()
    else:
        print("%s selected" % args.substractor)
        backSub = cv2.createBackgroundSubtractorKNN()

    cap = cv2.VideoCapture(filename)

    cal = calibration_mask(filename)

    # Read the first frame of the video
    ret, frame = cap.read()

    _ = get_rois(frame, cal, backSub)

    for _ in range(5):
        # We will ignore the second and third frame
        ret, frame = cap.read()
        _ = get_rois(frame, cal, backSub)

    # Get the bounding box coordinates for all objects in the first frame
    bounding_boxes, _ = get_rois(frame, cal, backSub)

    #mot_tracker = sort.Sort(min_hits=10)

    # We will start assignating when the ball and all things are on the field
    # Create a Kalman filter for each object and add it to the list

    for i,box in enumerate(bounding_boxes):
        i+=1
        entities.append(Enitity(box, player=i))

    # las player that hitted the ball
    last = 0
    while(cap.isOpened()):
        # Capture frame-by-frame
        ret, frame = cap.read()

        if not ret:
            break

        new_bboxes, image_th = get_rois(frame, cal, backSub)

        matched = []
        assigned = []
        delete = []

        clean = frame.copy()

        # assign first players
        for j in range(len(entities)):
            (x,y,w,h) = entities[j].current
            # I will just ignore tiny predictions
            if w > 45 and h > 45:
                th=0.5
                # Search for a bounding box i want to use with the entity we are checking
                index, box_current = temporal_coherence(entities[j].current, new_bboxes, assigned, th=th)

                # If we do not found it, we will search using a predicted bounding box
                if index== -1:
                    break
                    pred_bbox = entities[j].predict_bbox()
                    index, box_current = temporal_coherence(pred_bbox, new_bboxes, assigned, th=th)

                # If we still do not find it, we will try to use the las position we have seen it
                if index== -1:
                    index, box_current = temporal_coherence(entities[j].anterior, new_bboxes, assigned, th=th)

                else:
                    matched.append(index)
                    entities[j].found()
                    pred_bbox = entities[j].predict_bbox()
                    assigned.append(index)
                    entities[j].update_bbox(box_current)
                    entities[j].correct(box_current)
                    ps = entities[j].predicted_state() # our predict stage
                    #pred_bbox = entities[j].predict_bbox()
                    #entities[j].correct(box_current)

                    # Draw the bounding box on the frame
                    x, y, w, h = box_current
                    cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
                    cv2.circle(frame, center(box_current), 2, (255,255,0), -1)
                    cv2.putText(frame, "Player %i" % entities[j].player , (x, y-10), cv2.FONT_HERSHEY_DUPLEX , 1, 2)

                    # Draw the Kalman filter's predicted position on the frame
                    x, y, w, h = pred_bbox
                    #x, y = int(predicted_state[0]), int(predicted_state[1])
                    cv2.rectangle(frame, (x, y), (x+w, y+h), entities[j].color, 2)

        # these are probably players
        rois_matched = [i for j, i in enumerate(new_bboxes) if j in matched]

        rest, ball_image = get_rest_of_rois(rois_matched, image_th)

        #print_rois(rest, frame=frame)
        #cv2.imshow("balota", ball_image)

        if not(ball):
            if rest:
                for i in rest:
                    print(i)
                    ball.append(Enitity(i, is_ball=1))
                # Here we supose that there is only one ball
                # and no artifacts for the first detection
                index, d = find_score_player(entities, ball[0], first=0)
                entities[index].score_hit()
                last = entities[index].player

        else :
            match_ball = []
            ass_ball   = []
            delete_ball= []

            # Assign first players
            for j in range(len(ball)):
                (x,y,w,h) = ball[j].current
                # I will just ignore tiny predictions
                if rest:

                    # We don't need to apply a treshhold here because we are just checking with the bounding box of the ball
                    # and if we have other, we will still checking a good IOU to be assigned.
                    th = 0.0

                    # Search for a bounding box i want to use with the entity we are checking
                    index, box_current = temporal_coherence(ball[j].current, rest, ass_ball, th=th)

                    # If we do not found it, we will search using a predicted bounding box
                    if index== -1:
                        pred_bbox = ball[j].predict_bbox()
                        index, box_current = temporal_coherence(pred_bbox, rest, ass_ball, th=th)

                    # If we still do not find it, we will try to use the las position we have seen it
                    if index== -1:
                        index, box_current = temporal_coherence(ball[j].anterior, rest, ass_ball, th=th)

                    if index == -1:

                        ball[j].missed()
                        if ball[j].time > 25:

                            # if the object has disapeared for more than 5 frames we will delete it
                            delete_ball.append(j)
                            pass
                        else:
                            #entities[j].update_bbox(entities[j].predict_bbox())
                            #ps = entities[j].predicted_state() # our predict stage
                            #pred_bbox = entities[j].predict_bbox()
                            #entities[j].correct(pred_bbox)
                            pass
                            #entities.append(Enitity(box_current))

                    else:
                        #print("aaaaaaaaaaaaaaaaa")
                        # si ha esta más que un poco tiempo y esta muy cerca de un jugador
                        # entonces es que es un hit
                        if True:
                            socore_player,distance = find_score_player(entities, ball[j])
                            if distance >= 1000:
                                pass
                            else:
                                if last == entities[socore_player].player:
                                    pass
                                else:
                                    entities[socore_player].score_hit()
                                    (x1, y1, w, h)=ball[j].current
                                    cv2.putText(frame, "HITAZO!!!!!", (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
                                    #print("TOCADA")
                                    last = entities[socore_player].player

                        match_ball.append(index)
                        ball[j].found()
                        pred_bbox = ball[j].predict_bbox()
                        ass_ball.append(index)
                        ball[j].update_bbox(box_current)
                        ps = ball[j].predicted_state() # our predict stage
                        ball[j].correct(box_current)

                        # Draw the bounding box on the frame
                        x, y, w, h = box_current
                        cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)

                        cv2.putText(frame, "Ball", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, 2)

                        # draw the kalman filter prediction
                        x, y, w, h = pred_bbox
                        cv2.rectangle(frame, (x, y), (x+w, y+h), ball[j].color, 2)
                else:
                    pass
            # In case of having more than one posible ball, we will get rid of the
            # inconsistent noise
            ball = [i for j, i in enumerate(ball) if j not in delete_ball]

        # Drawing of the Board
        x = 0
        y = 0
        w = 420
        h = 120
        cv2.rectangle(frame, (x,y), (x+w,y+h), (255,255, 255),-1)
        for i in entities:
            y+=40
            cv2.putText(frame, "Player %i scores -> %i" % (i.player, i.hit), (x, y+10), cv2.FONT_HERSHEY_SIMPLEX, 1, 4)

        if mode == 0:
            cv2.imshow("Tracking", frame)
        elif mode == 1:
            cv2.imshow("General", image_th)
        elif mode == 2:
            cv2.imshow("Ball", ball_image)
        elif mode == 3: # no display
            pass

        # Uncoment the one you want to save
        #tracking_video.append(frame)
        #general_video.append(image_th)
        #ball_video.append(ball_image)

        entities = [i for j, i in enumerate(entities) if j not in delete]

        # IDEA ; para el video dos meterle un poco de mascara a los catchers
        #if len(entities) > 2:
        #    print("Player overflow")

        if cv2.waitKey(10) & 0xFF == ord('q'):
            break

    print("The Winner is player %i" % last)

    # If you need to save the video enable this, also enable
    # and change the array to use and also the name
    if False:
        height,width =general_video[0].shape

        video=cv2.VideoWriter('videos_gen/video_binary.mp4',cv2.VideoWriter_fourcc(*'mp4v'),30,(width,height))
        for i in general_video:
            video.write(i)

    # release video capture
    cap.release()
    # Close
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()


import tensorflow as tf

class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
    def __init__(self, d_model, warmup_steps=4000):
        super(CustomSchedule, self).__init__()
        self.d_model = d_model
        self.d_model = tf.cast(self.d_model, tf.float32)
        self.warmup_steps = warmup_steps

    def __call__(self, step):
        arg1 = tf.math.rsqrt(step)
        arg2 = step * (self.warmup_steps ** -1.5)
        return tf.math.rsqrt(self.d_model) * tf.math.minimum(arg1, arg2)

def loss_func_mask(real, pred, weights=None):
    """ 屏蔽填充的SparseCategoricalCrossentropy损失

    真实标签real中有0填充部分，这部分不记入预测损失

    :param weights: 样本权重
    :param real: 真实标签张量
    :param pred: logits张量
    :return: 损失平均值
    """
    loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
        from_logits=True, reduction='none')
    mask = tf.math.logical_not(tf.math.equal(real, 0))  # 填充位为0，掩蔽
    loss_ = loss_object(real, pred, sample_weight=weights)
    mask = tf.cast(mask, dtype=loss_.dtype)
    loss_ *= mask
    return tf.reduce_mean(loss_)


import re
from google.appengine.api import users
from google.appengine.ext import db
import hashlib
import sys

class cloudUtils:

	def __init__(self):
		self.ver=1

	def checkAuthorised(self,apikey="",apirequest="",apiuser="",apidevice=""):
		sys.stderr.write("cloudUtils: checking if authorised or not\n")
		sys.stderr.write("SELECT * FROM gAuthorisedUsers WHERE authEmail = %s AND deviceId = %s\n" %( apiuser,apidevice))
		ourAuthKey = db.GqlQuery("SELECT * FROM gAuthorisedUsers WHERE authEmail = :1 AND deviceId = :2", apiuser,apidevice)
		results = ourAuthKey.fetch(1)
		if len(results) < 1:
			sys.stderr.write("cloudUtils: user not present in the database\n")
			return False	# not in the database
		sys.stderr.write("Hash Elements: cloudRequest=%s\n" %(apirequest))
		sys.stderr.write("             : cloudSalt=Dolphin\n")
		sys.stderr.write("             : cloudKey=%s\n" %(results[0].authHash))
		tmp = "cloudRequest=%s_cloudSalt=%s_cloudKey=%s" %(apirequest,"Dolphin",results[0].authHash)
		tmp = hashlib.md5(tmp).hexdigest()
		#tmp = re.compile("0").sub('',hashlib.md5(tmp).hexdigest())
		sys.stderr.write("Expected RequestHash: %s\n" %( re.compile("0").sub('',tmp ) ))
		sys.stderr.write("Received RequestHash: %s\n" %( re.compile("0").sub('',apikey )) )
		if not re.compile("0").sub('',tmp) == re.compile("0").sub('',apikey):
			sys.stderr.write("cloudUtils: request hash does not match\n")
			return False		# not correct

		return True