import os
import shutil
import uuid
from pprint import pprint

from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.files import File
from django.core.files.base import ContentFile
from django.db import utils
from rest_framework import status
from rest_framework.exceptions import ValidationError

import settings
from accounts.models import User
from common.models import UserCell
from document.models import Document
from projects import models
from projects.models import Project
from rest_framework.response import Response
from storyboards.models import Frame
from storyboards.serializers import ChronoframeSerializer
from table.models import DefaultTableModel
from timing.models import TimingGroup, Timing
from folders.models import Folder

def get_model(model_name):
    model_app_name = ContentType.objects.get(model=model_name).app_label
    return apps.get_model(model_app_name, model_name.capitalize())

def unpack_nested_kwargs(kwargs, self_base):
    print('vvvvvvvvvvvvvvvvv')
    print(23, 'unpack_nested_kwargs')
    print(kwargs)

    _, root_name, super_name, parent_name, host_name = [None] * 5
    _pk, root_pk, super_pk, parent_pk, host_pk, base_pk = [None] * 6

    is_pk = None
    if 'pk' in kwargs:
        print(31)
        is_pk = True
        base_pk = kwargs.pop('pk')

    kw_len = len(kwargs)
    kw_keys = [None] * (5 - kw_len) + list(kwargs.keys())
    kw_vals = [None] * (5 - kw_len) + list(kwargs.values())
    _, root_name, super_name, parent_name, host_name = kw_keys
    _pk, root_pk, super_pk, parent_pk, host_pk = kw_vals

    if is_pk:
        print(42)
        kwargs['pk'] = base_pk
    ret = {
        'su': _[:-3] if _ else None,
        'su_pk': _pk,
        'root': root_name[:-3] if root_name else None,
        'root_pk': root_pk,
        'super': super_name[:-3] if super_name else None,
        'super_pk': super_pk,
        'parent': parent_name[:-3] if parent_name else None,
        'parent_pk': parent_pk,
        'host': host_name[:-3] if host_name else None,
        'host_pk': host_pk,
        'base': self_base,
        'base_pk': base_pk
    }
    pprint(ret)
    print('^^^^^^^^^^^^^^^^^^^^')
    return ret

def get_object_or_404(model, pk):
    try:
        return get_model(model).objects.get(pk=pk)
    except (ObjectDoesNotExist, utils.IntegrityError):
        raise ValidationError(
            {"error": {"msg": f"{model.capitalize()}[{pk}] not found"}})

def organize_data_row_sort_arrays(instance):
    instance_has_userfields = False
    instance_data_row_attribute = None
    for attr in dir(instance):
        if attr.endswith('frames') or attr == 'locations':
            instance_data_row_attribute = getattr(instance, attr)
        if attr == 'userfields':
            instance_has_userfields = True
    instance_data_row = list(instance_data_row_attribute.values('id'))
    instance_data_row_ids = [data_row['id'] for data_row in instance_data_row]
    if set(instance_data_row_ids) != set(instance.data_row_order):
        diff = list(set(instance_data_row_ids)
                    .difference(set(instance.data_row_order)))
        instance.data_row_order += diff

    if instance.data_row_order:
        for idx in reversed(instance.data_row_order):
            if idx not in instance_data_row_ids:
                instance.data_row_order.remove(idx)
    else:
        for ident in instance_data_row_ids:
            instance.data_row_order.append(ident)

    if instance_has_userfields:
        if Frame.objects.first():
            frame = instance_data_row_attribute.first()
            column_ids = list(frame.userfields.values('field_id'))
            for idx in reversed(instance.col_order):
                if {'field_id': idx} not in column_ids:
                    instance.col_order.remove(idx)
        else:
            instance.col_order = []

    # отключаем изменение last_modified_date
    for field in instance._meta.local_fields:
        if field.name == "last_modified_date":
            field.auto_now = False

    instance.save()

    # включаем изменение last_modified_date
    for field in instance._meta.local_fields:
        if field.name == "last_modified_date":
            field.auto_now = True

def add_userfields(user, host_obj, new_obj):
    """
    Получение существующих юзер.колонок для модели и их добавление
    ко всем участникам или добавление к строкам этого-же документа
    """
    print(139, 'common.add_userfields')
    new_obj_name = new_obj.__class__.__name__
    columns = []
    if new_obj_name in ['Frame']:
        columns = host_obj.frame_columns.all()
    for usercolumn in columns:
        usercell = UserCell.objects.create(
            host_usercolumn=usercolumn, owner=user)
        if new_obj_name == 'Frame':
            new_obj.userfields.add(usercell)

def insert_frames(user, base, instance, frames_ids: list):
    print(165, 'utils insert_frames')
    for frame_id in frames_ids:
        frame = get_object_or_404('frame', frame_id)
        frame_data = {f'host_{base}': instance.id,
                      'sbdframe_id': frame.id}
        serializer = None
        if base == 'chrono':
            serializer = ChronoframeSerializer
        print(150, 'pass context')
        serializer = serializer(data=frame_data, context={'user': user})
        print(152, serializer.__class__.__name__)
        serializer.is_valid(raise_exception=True)
        new_frame = serializer.create(serializer.validated_data)
        instance.data_row_order.append(new_frame.id)
        instance.save()
        add_userfields(user, instance, new_frame)

def check_file_size(request, user):
    """
    Проверяет превышает ли размер загружаемого файла в совокупности с
    размером уже загруженных пользователем файлов установленный лимит.
    Если лимит не превышен, считает общий размер файлов на диске.
    """
    user = User.objects.filter(pkid=user.pkid).first()
    disk_space = user.disk_space if user.disk_space else 0
    for filename, file in request.FILES.items():
        file_size = file.size
        if file_size + disk_space < settings.DISK_SIZE:
            disk_space += file_size
        else:
            return False
    return disk_space

def change_disk_space(user, disk_space):
    """
    При загрузке файла обновляет занятое место на диске у пользователя.
    """
    user = User.objects.filter(pkid=user.pkid).first()
    user.disk_space = disk_space
    user.save()

def recount_disk_space(user):
    """
    Пересчитывает при обновлении файла общий вес файлов на диске
    у пользователя.
    """
    folder = os.path.join(os.getcwd(), settings.MEDIA_ROOT, str(user.pkid))
    size = 0
    for path, dirs, files in os.walk(folder):
        for f in files:
            fp = os.path.join(path, f)
            size += os.path.getsize(fp)
    user = User.objects.filter(pkid=user.pkid).first()
    user.disk_space = size
    user.save()
    return size

def find_host_project(kw):
    """
    Ищет PKID проекта по id документа.
    """
    for key, value in kw.items():
        if value == 'project':
            pk = int(kw.get(f'{key}_pk'))
            return Project.objects.filter(pk=pk).first()

def update_host_modified_date(kw, user_email):
    """
    Обновляет время и юзера последнего изменения сториборда
    """
    for key, value in kw.items():
        if value in ["storyboard"]:
            pk = int(kw.get(f'{key}_pk'))
            instance = get_model(value).objects.filter(
                id=pk).first()
            instance.last_modified_user = user_email
            instance.save()

def count_space_per_object(user, obj, doc_type):
    """
    Получает папку, в которой лежат файлы конкретного документа и
    считает вес каждого файла.
    """
    if isinstance(obj, File):
        folder = os.path.join(os.getcwd(),
                              settings.MEDIA_ROOT,
                              str(user.pkid),
                              doc_type)
    else:
        folder = os.path.join(os.getcwd(),
                              settings.MEDIA_ROOT,
                              str(user.pkid),
                              doc_type,
                              str(obj.id))
    exist_folder = os.path.exists(folder)
    size = 0
    if not exist_folder:
        return size
    for path, dirs, files in os.walk(folder):
        for f in files:
            fp = os.path.join(path, f)
            size += os.path.getsize(fp)
    return size

def like_or_dislike_object(user, object_type):
    """
    Ставит или убирает лайк. Затем сохраняет информацию
    о наличии или отсутствии лайка в БД.
    """
    user = get_model('user').objects.filter(pk=user.pk).first()
    if object_type == 'bordomatic':
        user.like = False if user.like else True
        user.save()
        return user.like
    elif object_type == 'document':
        user.document_like = False if user.document_like else True
        user.save()
        return user.document_like

def copy_file(parent_object, related_object=None):
    """
    Функция, отвечающая за копирование объекта с файлом.
    """
    if related_object:
        file_field_instance = getattr(related_object, 'file')
        owner_pk = str(related_object.owner.pk)
    else:
        file_field_instance = getattr(parent_object, 'file')
        owner_pk = str(parent_object.owner.pk)
    if file_field_instance:
        file_path = os.path.join(os.getcwd(),
                                 settings.MEDIA_ROOT,
                                 file_field_instance.name)
        if isinstance(parent_object, models.File):
            pk = str(parent_object.pk)
            model = 'file'
        new_path = os.path.join(os.getcwd(),
                                settings.MEDIA_ROOT,
                                owner_pk,
                                model,
                                pk)
        if not os.path.isdir(new_path):
            os.mkdir(new_path)
        new_path = shutil.copy(file_path, new_path)
        return new_path

def construct_new_instance_with_file(object, new_path, project_id=None):
    """
    Создает объект класса с файлом.
    """
    instance = get_model(str(object.__class__.__name__).lower())(
            name=object.name,
            owner=object.owner,
            col_order=object.col_order,
            created_at=object.created_at,
            data_row_order=object.data_row_order,
            description=object.description,
            last_modified_date=object.last_modified_date,
            last_modified_user=object.last_modified_user,
            tag_color=object.tag_color,
            file=File(open(new_path, 'rb'), name=os.path.split(object.file.name)[-1])
        )
    if isinstance(object, models.File):
        instance.host_project = models.Project.objects.filter(pk=project_id).first()
        instance.document_logo = None
    return instance

def duplicate_object(parent_object, project_id, host=None):
    """
    Копирует инстанс класса со всеми child объектами, в том числе, связанными
    foreign keys:
        1.  Собирает объекты, связанные с родительским объектов с помощью foreign keys и m2m relations.
        2.  Копирует родительский объект
        3a. Копирует child объекты вместе с их child объектами.
        3b. Создает новые m2m relations.
    """
    related_objects_to_copy = []
    relations_to_set = {}
    # Iterate through all the fields in the parent object looking for related fields
    for field in parent_object._meta.get_fields():
        if field.one_to_many:
            # One to many fields are backward relationships where many child
            # objects are related to the parent. Enumerate them and save a list
            # so we can copy them after duplicating our parent object.
            print(f'Found a one-to-many field: {field.name}')

            # 'field' is a ManyToOneRel which is not iterable, we need to get
            # the object attribute itself.
            related_object_manager = getattr(parent_object, field.name)
            related_objects = list(related_object_manager.all())
            if related_objects:
                print(f' - {len(related_objects)} related objects to copy')
                related_objects_to_copy += related_objects

        elif field.many_to_one:
            # In testing, these relationships are preserved when the parent
            # object is copied, so they don't need to be copied separately.
            print(f'Found a many-to-one field: {field.name}')

        elif field.many_to_many:
            # Many to many fields are relationships where many parent objects
            # can be related to many child objects. Because of this the child
            # objects don't need to be copied when we copy the parent, we just
            # need to re-create the relationship to them on the copied parent.
            print(f'Found a many-to-many field: {field.name}')
            related_object_manager = getattr(parent_object, field.name)
            relations = list(related_object_manager.all())
            if relations:
                print(f' - {len(relations)} relations to set')
                relations_to_set[field.name] = relations
    # Duplicate the parent object
    if isinstance(parent_object, Timing):

        # Keep the original pk of the copied object
        old_parent_obj_pk = parent_object.pk

        parent_object.pk = None
        parent_object.name += " копия"
        parent_object.host_project = models.Project.objects.filter(pk=project_id).first()
        parent_object.order_id = uuid.uuid4()
        parent_object.doc_uuid = None

        # Copying the document logo
        copy_document_logo(parent_object, old_parent_obj_pk)

        parent_object.save()

    elif isinstance(parent_object, DefaultTableModel):
        # Keep the original pk of the copied object
        old_parent_obj_pk = parent_object.pk

        parent_object.pk = None
        parent_object.name += " копия"
        if parent_object.host_folder:
            parent_object.host_folder = Folder.objects.filter(pk=project_id).first()
        else:
            parent_object.host_document = Document.objects.filter(pk=project_id).first()
        parent_object.order_id = uuid.uuid4()
        parent_object.doc_uuid = None

        # Copying the document logo
        copy_document_logo(parent_object, old_parent_obj_pk)

        parent_object.save()

    elif isinstance(parent_object, TimingGroup):
        parent_object.pk = None
        parent_object.host_timing = host
        parent_object.save()
    elif isinstance(parent_object, models.File):
        new_path = copy_file(parent_object)
        instance = construct_new_instance_with_file(parent_object, new_path, project_id)
        instance.name += " копия"
        instance.save()
    else:
        parent_object.pk = None
        parent_object.name += " копия"
        parent_object.order_id = uuid.uuid4()
        parent_object.document_logo = None
        parent_object.save()
    print(f'Copied parent object ({str(parent_object)})')
    # Copy the one-to-many child objects and relate them to the copied parent
    for related_object in related_objects_to_copy:
        # Iterate through the fields in the related object to find the one that
        # relates to the parent model.
        for related_object_field in related_object._meta.fields:
            if related_object_field.related_model == parent_object.__class__:
                # If the related_model on this field matches the parent
                # object's class, perform the copy of the child object and set
                # this field to the parent object, creating the new
                # child -> parent relationship. if object is Location or Callsheetmember
                # duplicates it as parent obj.
                if isinstance(related_object, TimingGroup):
                    duplicate_object(related_object, project_id, host=parent_object)
                else:
                    related_object.pk = None
                    setattr(related_object, related_object_field.name, parent_object)
                    related_object.save()

                    text = str(related_object)
                    text = (text[:40] + '..') if len(text) > 40 else text
                    print(f'|- Copied child object ({text})')

    # Set the many-to-many relations on the copied parent
    for field_name, relations in relations_to_set.items():
        # Get the field by name and set the relations, creating the new
        # relationships.
        field = getattr(parent_object, field_name)
        field.set(relations)
        text_relations = []
        for relation in relations:
            text_relations.append(str(relation))
        print(f'|- Set {len(relations)} many-to-many relations on {field_name} {text_relations}')

    if isinstance(parent_object, models.File):
        return instance
    else:
        return parent_object

def copy_document_logo(copied_instance, original_instance_pk):
    """
    The function is necessary to copy the document_logo file for a new instance
    (without deleting this file from the original instance).

    Retrieves the document_logo file and its original name.
    Adds a 'copy_' to the name. The new instance will have this file with a new name, for example:
    'orig.png' -> 'copy_orig.png'

    Gets the type (model) of the original object and the original object itself.
    Saves the document_logo file to the copied instance and to the original one.
    Without such saving, the post_save signal on the ShootingPlan model
    will transfer the image from the original instance to the copied one.

    This system is implemented because when creating a new object,
    its document_logo must have a path like:
        '{owner_id}/{model}/{model instance.id}/{filename}'
    The new instance does not have an id yet and therefore all images are placed in the None folder.
        '{owner_id}/{model}/None/{filename}'
    Then the signal transfers them from there to the appropriate folder:
        '10/shootingplan/None/orig.png' -> '10/shootingplan/1/orig.png'

    Thus, if you do not save the document_logo for both copies, the original and the copied one,
    that document_logo will simply be transferred from the original instance to the copied one.
    """
    if copied_instance.document_logo:
        picture_copy = ContentFile(copied_instance.document_logo.read())
        old_picture_name = copied_instance.document_logo.name.split("/")[-1]
        new_picture_name = 'copy_' + old_picture_name

        model = type(copied_instance)
        old_parent_obj = model.objects.get(pk=original_instance_pk)
        copied_instance.document_logo.save(new_picture_name, picture_copy)
        old_parent_obj.document_logo.save(old_picture_name, picture_copy)

def check_folder_exist(request):
    for folder in ['folder', 'parent_polder']:
        if folder in request.data:
            folder = Folder.objects.filter(
                id=request.data[folder],
                deleted_id__isnull=True
            )
            if not folder:
                raise ValidationError({'folder': 'does not exist'})


# coding: utf-8

import time
import datetime
from functools import partial

try:
    from django.utils import timezone
except ImportError:
    raise RuntimeError('Django is required for django simple serializer.')

class TimeFormatFactory(object):
    def __init__(self):
        super(TimeFormatFactory, self).__init__()

    @staticmethod
    def datetime_to_string(datetime_time, time_format='%Y-%m-%d %H:%M:%S'):
        if isinstance(datetime_time, datetime.datetime):
            if datetime_time.tzinfo:
                datetime_time = datetime_time.astimezone(timezone.get_current_timezone())
            return datetime_time.strftime(time_format)
        elif isinstance(datetime_time, datetime.time):
            time_format = '%H:%M:%S'
        elif isinstance(datetime_time, datetime.date):
            time_format = '%Y-%m-%d'
        return datetime_time.strftime(time_format)

    @staticmethod
    def datetime_to_timestamp(datetime_time, time_format=None):
        if isinstance(datetime_time, datetime.datetime):
            if datetime_time.tzinfo:
                datetime_time = datetime_time.astimezone(timezone.get_current_timezone())
            return time.mktime(datetime_time.timetuple())
        return time.mktime(datetime_time.timetuple())

    @staticmethod
    def get_time_func(func_type='string'):
        if func_type == 'string':
            return TimeFormatFactory.datetime_to_string
        elif func_type == 'timestamp':
            return TimeFormatFactory.datetime_to_timestamp
        else:
            return TimeFormatFactory.datetime_to_string


from django.contrib import admin

from projects.models import (
    Dependency, Project, ProjectBuild, ProjectBuildDependency)

class ProjectDependencyInline(admin.TabularInline):
    model = Project.dependencies.through

class ProjectAdmin(admin.ModelAdmin):
    inlines = [ProjectDependencyInline]

class ProjectBuildDependencyInline(admin.TabularInline):
    model = ProjectBuild.build_dependencies.through

class ProjectBuildAdmin(admin.ModelAdmin):
    inlines = [ProjectBuildDependencyInline]

admin.site.register(Dependency)
admin.site.register(Project, ProjectAdmin)

admin.site.register(ProjectBuildDependency)
admin.site.register(ProjectBuild, ProjectBuildAdmin)


from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path("<int:aluno_id>", views.aluno, name="aluno"),
    path('tempo/<int:id>', views.add_tempo, name='add_tempo'),
    path('busca/', views.busca, name='busca'),
    path('deletar/<int:id>', views.deletar, name='deletar'),
]


import math

from WebInfoHandle import WebInfo
from db import Session, table, SessionBase
from itertools import product

_D = 2140479  # 假装文档数是这么多
_weightIncrease = 1 / 20000  # 当收集到两万份文档时，idf = (其他文章idf+招聘网站idf)/2
session: SessionBase = Session()
_documentNum = session.query(table.Pages).count()

def getIDF(keyword: table.Keyword):
    # 计算权重
    if keyword.document_frequency == 0:
        general_idf = 12
    else:
        general_idf = math.log(_D / keyword.document_frequency)
    this_idf = math.log(_documentNum / keyword.frequency)
    weight_of_this_idf = _weightIncrease * _documentNum
    idf = (general_idf + weight_of_this_idf * this_idf) / (1 + weight_of_this_idf)
    return idf

def updateDB(webInfo: WebInfo):
    global _documentNum, _D, _weightIncrease
    print(webInfo.station + '开始')
    _documentNum += 1
    session: SessionBase = Session()
    # 创建page
    page = table.Pages()
    page.json = webInfo.getDict()
    page.pubdate = webInfo.pubdate

    # 获得station
    station: table.Station = session.query(table.Station). \
        filter(table.Station.station == webInfo.station).first()

    # 查不到则创建station
    if not station:
        station = table.Station()
        station.station = webInfo.station
        station.frequency = 0
        session.add(station)
    station.frequency += 1
    session.flush()

    # 获得专业
    specialityList = []
    for name in webInfo.speciality:
        speciality: table.Speciality = session.query(table.Speciality). \
            filter(table.Speciality.speciality == name).first()
        if not speciality:
            speciality = table.Speciality(speciality=name, frequency=0)
            session.add(speciality)
        speciality.frequency += 1
        specialityList.append(speciality)
    session.flush()

    # 获得关键字
    keywordList = []
    for name in webInfo.words:
        keyword: table.Keyword = session.query(table.Keyword). \
            filter(table.Keyword.keyword == name).first()
        if not keyword:
            keyword = table.Keyword(keyword=name, frequency=0)
            session.add(keyword)
        keyword.frequency += 1
        keywordList.append(keyword)
    session.flush()

    print(station.station + '统计wordFrequency')
    # 统计词频
    for speciality, keyword in product(specialityList, keywordList):
        wordFrequency: table.WordFrequency = session.query(table.WordFrequency). \
            filter(table.WordFrequency.r_keyword == keyword,
                   table.WordFrequency.r_speciality == speciality).first()
        if not wordFrequency:
            wordFrequency = table.WordFrequency()
            wordFrequency.r_keyword = keyword
            wordFrequency.r_speciality = speciality
            session.add(wordFrequency)
        if not wordFrequency.frequency:
            wordFrequency.frequency = 0
        wordFrequency.frequency += 1
        # 计算权重
        idf = getIDF(keyword)
        wordFrequency.weight = wordFrequency.frequency * idf

    print(station.station + '统计wordFrequencyStation')
    # 统计词频
    for keyword in keywordList:
        wordFrequencyStation: table.WordFrequencyStation = session.query(table.WordFrequencyStation). \
            filter(table.WordFrequencyStation.r_keyword == keyword,
                   table.WordFrequencyStation.r_station == station).first()
        if not wordFrequencyStation:
            wordFrequencyStation = table.WordFrequencyStation()
            wordFrequencyStation.r_keyword = keyword
            wordFrequencyStation.r_station = station
            session.add(wordFrequencyStation)
        if not wordFrequencyStation.frequency:
            wordFrequencyStation.frequency = 0
        wordFrequencyStation.frequency += 1
        # 计算权重
        idf = getIDF(keyword)
        wordFrequencyStation.weight = wordFrequencyStation.frequency * idf

    print(station.station + '统计specialityHasStation')
    # 统计specialityHasStation
    for speciality in specialityList:
        specialityHasStation: table.SpecialityHasStation = session.query(table.SpecialityHasStation). \
            filter(table.SpecialityHasStation.r_speciality == speciality,
                   table.SpecialityHasStation.r_station == station).first()
        if not specialityHasStation:
            specialityHasStation = table.SpecialityHasStation()
            specialityHasStation.r_speciality = speciality
            specialityHasStation.r_station = station
            session.add(specialityHasStation)
        if not specialityHasStation.frequency:
            specialityHasStation.frequency = 0
        specialityHasStation.frequency += 1

    page.keyword_collection = keywordList
    page.r_station = station
    page.speciality_collection = specialityList
    print(station.station+'开始commit')
    session.commit()
    print(station.station + 'commit完成')


import torch
from torch.autograd import Function
from eaconv._ext import eaconv2d

def _conv_output_dim(inputDim, pad, filterDim, dilation, stride):
    tmp = 1 + (inputDim + 2 * pad - (((filterDim - 1) * dilation) + 1))
    if tmp % stride == 0\
            and pad >= 0\
            and stride >= 1\
            and dilation >= 1\
            and tmp > 0:
        return tmp // stride
    else:
        raise ValueError('Parameters of the kernel must be compatible '
                         'with the dimensions of the input')

class EAConv2dFunction(Function):

    def __init__(self, stride, padding, dilation, groups):
        super(EAConv2dFunction, self).__init__()
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.groups = groups

    def forward(self, *bias_inp_and_weight):
        num_inp = len(bias_inp_and_weight) // 2
        inp = bias_inp_and_weight[:num_inp]
        if len(bias_inp_and_weight) % 2 == 1:
            weight = bias_inp_and_weight[num_inp:-1]
            bias = bias_inp_and_weight[-1]
        else:
            weight = bias_inp_and_weight[num_inp:]
            bias = None

        self.saved_for_later = inp, weight, bias
        stride = self.stride
        padding = self.padding
        dilation = self.dilation
        groups = self.groups

        h = _conv_output_dim(inp[0].size(2), padding[0],
                             weight[0].size(2), dilation[0], stride[0])
        w = _conv_output_dim(inp[0].size(3), padding[1],
                             weight[0].size(3), dilation[1], stride[1])
        output = inp[0].new(inp[0].size(0), weight[0].size(0), h, w).zero_()
        if bias is not None:
            eaconv2d.EAConv2d_cuda_forward_bias(bias, output)
        for _inp, _weight in zip(inp, weight):
            if not isinstance(_inp, torch.cuda.FloatTensor):
                raise NotImplementedError
            eaconv2d.EAConv2d_cuda_forward(_inp, _weight, output,
                                           stride[0], stride[1],
                                           padding[0], padding[1],
                                           dilation[0], dilation[1],
                                           groups)
        return output

    def backward(self, gradOutput):
        gradOutput = gradOutput.contiguous()

        inp, weight, bias = self.saved_for_later

        stride = self.stride
        padding = self.padding
        dilation = self.dilation
        groups = self.groups

        if bias is not None:
            grad_bias = bias.new(*bias.size()).zero_()
        else:
            grad_bias = None

        grad_inp = []
        grad_weight = []
        for _inp in inp:
            grad_inp.append(_inp.new(*_inp.size()).zero_())
        for _weight in weight:
            grad_weight.append(_weight.new(*_weight.size()).zero_())

        if bias is not None:
            eaconv2d.EAConv2d_cuda_backward_bias(grad_bias, gradOutput)
        for _inp, _weight, _grad_inp, _grad_weight in zip(inp, weight,
                                                          grad_inp,
                                                          grad_weight):
            eaconv2d.EAConv2d_cuda_backward(_grad_inp, _grad_weight,
                                            gradOutput,
                                            _inp, _weight,
                                            stride[0], stride[1],
                                            padding[0], padding[1],
                                            dilation[0], dilation[1],
                                            groups)
        return_val = tuple(grad_inp) + tuple(grad_weight)
        if bias is not None:
            return_val = return_val + (grad_bias,)
        return return_val


import numpy as np
import torch
import torch.nn as nn
from .utils import register_model, get_model
from . import cos_norm_classifier

@register_model('MannNet')
class MannNet(nn.Module):

    """Defines a Dynamic Meta-Embedding Network."""

    def __init__(self, num_cls=10, model='LeNet', src_weights_init=None,
                 weights_init=None, use_domain_factor_selector=False, centroids_path=None, feat_dim=512):

        super(MannNet, self).__init__()

        self.name = 'MannNet'
        self.base_model = model
        self.num_cls = num_cls
        self.feat_dim = feat_dim
        self.use_domain_factor_selector = use_domain_factor_selector
        self.cls_criterion = nn.CrossEntropyLoss()
        self.gan_criterion = nn.CrossEntropyLoss()

        self.centroids = torch.from_numpy(np.load(centroids_path)).float().cuda()
        assert self.centroids is not None
        self.centroids.requires_grad = False

        self.setup_net()

        if weights_init is not None:
            self.load(weights_init)
        elif src_weights_init is not None:
            self.load_src_net(src_weights_init)
        else:
            raise Exception('MannNet must be initialized with weights.')

    def forward(self, x_s, x_t):

        """Pass source and target images through their respective networks."""

        score_s, x_s = self.src_net(x_s, with_ft=True)
        score_t, x_t = self.tgt_net(x_t, with_ft=True)

        if self.discrim_feat:
            d_s = self.discriminator(x_s.clone())
            d_t = self.discriminator(x_t.clone())
        else:
            d_s = self.discriminator(score_s.clone())
            d_t = self.discriminator(score_t.clone())

        return score_s, score_t, d_s, d_t

    def setup_net(self):

        """Setup source, target and discriminator networks."""
        self.src_net = get_model(self.base_model, num_cls=self.num_cls, feat_dim=self.feat_dim)
        self.tgt_net = get_model(self.base_model, num_cls=self.num_cls, feat_dim=self.feat_dim)

        input_dim = self.num_cls

        self.discriminator = nn.Sequential(
            nn.Linear(input_dim, 500),
            nn.ReLU(),
            nn.Linear(500, 500),
            nn.ReLU(),
            nn.Linear(500, 2),
            )

        self.fc_selector = nn.Linear(self.feat_dim, self.feat_dim)

        if self.use_domain_factor_selector:
            self.domain_factor_selector = nn.Linear(self.feat_dim, self.feat_dim)

        self.classifier = cos_norm_classifier.create_model(self.feat_dim, self.num_cls)

        self.image_size = self.src_net.image_size
        self.num_channels = self.src_net.num_channels

    def load(self, init_path):
        """Loads full src and tgt models."""
        net_init_dict = torch.load(init_path)
        self.load_state_dict(net_init_dict)

    def load_src_net(self, init_path):
        """Initialize source and target with source weights."""
        self.src_net.load(init_path)
        self.tgt_net.load(init_path)

        net_init_dict = torch.load(init_path)
        classifier_weights = net_init_dict['classifier.weight']

        self.classifier.weight.data = classifier_weights.data.clone()

    def save(self, out_path):
        torch.save(self.state_dict(), out_path)

    def save_tgt_net(self, out_path):
        torch.save(self.tgt_net.state_dict(), out_path)


import torch
import torch.nn as nn
import torch.nn.functional as F
import math

def l2_norm(input, axis=1):
    norm = torch.norm(input, 2, axis, True)
    output = torch.div(input, norm)
    return output

class ArcFaceHead(nn.Module):
    def __init__(self, classnum, embedding_size=512, s=64.0, m=0.5):
        super(ArcFaceHead, self).__init__()
        self.weight = nn.Parameter(torch.FloatTensor(classnum, embedding_size))
        nn.init.xavier_uniform_(self.weight)

        self.m = m  # the margin value, default is 0.5
        self.s = s  # scalar value default is 64, see normface https://arxiv.org/abs/1704.06369
        self.eps = 1e-6

    def forward(self, embbedings, labels=None):
        emb_norm = l2_norm(embbedings)
        kernel_norm = l2_norm(self.weight)
        cosine = F.linear(emb_norm, kernel_norm)
        if labels is None:
            return cosine

        m_hot = torch.zeros(labels.size()[0], cosine.size()[1], device=cosine.device)
        m_hot.scatter_(1, labels.reshape(-1, 1), self.m)

        cosine = cosine.clamp(-1 + self.eps, 1 - self.eps)  # for stability
        theta = cosine.acos()
        theta_m = torch.clip(theta + m_hot, min=self.eps, max=math.pi - self.eps)
        cosine_m = theta_m.cos()
        scaled_cosine_m = cosine_m * self.s
        return scaled_cosine_m


import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
onehot_encoder = OneHotEncoder(sparse=False)
from numpy import linalg as la

np.random.seed(42)

class MultiClassLogesticRegression:
    def __init__(self, lr=0.01, epochs=100):
        self.lr = lr
        self.epochs = epochs

    def of_grad(self, X, Y, W, nu):
        Z = -X @ W
        P = self.Softmax(Z, ax=1)
        N = X.shape[0]
        gd = (1 / N) * (X.T @ (Y - P)) + 2 * nu * W
        return gd

    def Softmax(self, x, ax=1):
        max = np.max(x, axis=ax, keepdims=True)  # returns max of each row and keeps same dims
        e_x = np.exp(x - max)  # subtracts each row with its max value
        sum = np.sum(e_x, axis=ax, keepdims=True)  # returns sum of each row and keeps same dims
        f_x = e_x / sum
        return f_x

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def gradient_descent(self, X, Y, max_itr=1000, alpha=0.1, nu=0):
        self.Y_onehot = onehot_encoder.fit_transform(Y.reshape(-1, 1))
        self.W = np.zeros((X.shape[1], self.Y_onehot.shape[1]))
        i = 0
        ilist = []
        losslist = []
        while i < max_itr:
            i = i + 1
            self.W -= alpha * self.of_grad(X, self.Y_onehot, self.W, nu)
            ilist.append(i)
            losslist.append(self.loss(X, self.Y_onehot, self.W, 0))
            # print(W)

            df = pd.DataFrame({'i': ilist, 'loss': losslist})

        return self.W, df

    def loss(self, X, Y, W, nu):
        Z = - X @ W
        N = X.shape[0]
        loss = 1 / N * (np.trace((X @ W) @ Y.T) + np.sum(np.log(np.sum(np.exp(Z), axis=1)))) + (nu * la.norm(W, 2))
        return loss

    def fit(self, X, Y):
        self.n_samples = X.shape[0]
        self.n_features = X.shape[1]
        self.W = np.random.randn(self.n_features)
        self.b = np.random.randn()
        self.W, self.lossplotinfo = self.gradient_descent(X, Y)

    def predict(self, A):
        Z = -A @ self.W
        P = self.Softmax(Z)
        return np.argmax(P, axis=1)

    def loss_v_i_plot(self):
        return self.lossplotinfo.plot(x='i',
                                      y='loss',
                                      xlabel='i',
                                      ylabel='loss')


import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class EncapsulatedModel:
    def __init__(self, model, ID, seed, debug=False):
        print("[+] Encapsulate Model in Swag Model")
        self.ID = ID
        self.seed = seed
        self.model = model
        self.counter = 1
        weights = np.array(list(model.parameters()))

        self.orig_shape = self.save_orig_shape(weights)
        if debug:
            print("Orig_Shape " + str(self.orig_shape))
            print("Orig_Type" + str(type(weights)))
        weights = self.local_get_weights_array(weights)
        self.new_shape = len(weights)
        if debug:
            print("New Shape of Array: " + str(self.new_shape))

        self.theta_swa = weights
        if debug:
            print("Init Shape Theta swa: " + str(self.theta_swa.shape))

        self.theta_line_squared = self.square_weights(weights)
        if debug:
            print("Init Shape Theta Line squared: " + str(self.theta_line_squared))

        self.swag_diag = None

    def get_params(self):
        return self.model.get_params()

    def save_model(self, debug=False):
            if debug:
                print("Try to save model parameters")
            torch.save(self.theta_swa, 'models/' + str(self.seed) + '/swag/theta_swa' + self.ID + '.pt')
            torch.save(self.theta_line_squared, 'models/'+ str(self.seed) + '/swag/theta_line_squared' + self.ID + '.pt')
            torch.save(self.swag_diag, 'models/' + str(self.seed) + '/swag/swag_diag' + self.ID)
            torch.save(self.model.state_dict(), 'models/'+ str(self.seed) + '/swag/swag_model' + self.ID + '.bin')
            torch.save(self.counter, 'models/' + str(self.seed) + '/swag/counter' + self.ID + '.pt')
            torch.save(self.orig_shape, 'models/' + str(self.seed) + '/swag/orig_shape' + self.ID + '.pt')
            torch.save(self.new_shape, 'models/' + str(self.seed) + '/swag/new_shape' + self.ID + '.pt')

    def load_model(self, debug=True):
            if debug:
                print("Try to load model parameters")
            self.theta_swa = torch.load('models/' + str(self.seed) + '/swag/theta_swa' + self.ID + '.pt')
            self.theta_line_squared = torch.load('models/' + str(self.seed) + '/swag/theta_line_squared' + self.ID + '.pt')
            self.swag_diag = torch.load('models/' + str(self.seed) + '/swag/swag_diag' + self.ID)
            self.model.load_state_dict(torch.load('models/' + str(self.seed) + '/swag/swag_model' + self.ID + '.bin'))
            self.counter = torch.load('models/' + str(self.seed) + '/swag/counter' + self.ID + '.pt')
            self.orig_shape = torch.load('models/' + str(self.seed) + '/swag/orig_shape' + self.ID + '.pt')
            self.new_shape = torch.load('models/' + str(self.seed) + '/swag/new_shape' + self.ID + '.pt')

    def save_orig_shape(self, weights, debug=False):
            if debug:
                print("Type of Weights Array is: " + str(type(weights)))
                print("Shape of Weights Array is: " + str(weights.shape))
            shapes = []
            for entry in weights:
                if debug:
                    print("Type of Entry" + str(type(entry)))
                shapes.append(entry.shape)
            return shapes

    def restore_orig_shape(self, weights, debug=False):
            output = []
            for entry in self.orig_shape:
                if debug:
                    print("For entry : " + str(entry))
                    print("Size of weight Array: " + str(len(weights)))
                if len(entry) == 2:
                    i = entry[0]
                    j = entry[1]
                    product = i*j
                    if debug:
                        print("Will take first X elements out of weights. X=" + str(product))
                    used_weights = weights[:product]
                    weights = weights[product:]
                    result = np.reshape(used_weights, (i, j))
                    result = torch.tensor(np.array(result))
                    if debug:
                        print("Resulting Shape " + str(result.shape))
                    result = torch.nn.Parameter(result)
                    output.append(result)
                if len(entry) == 1:
                    i = entry[0]
                    if debug:
                        print("Will take first X elements out of weights. X=" + str(i))
                    used_weights = weights[:i]
                    weights = weights[i:]
                    result = torch.tensor(np.array(used_weights))
                    if debug:
                        print("Resulting Shape " + str(result.shape))
                    result = torch.nn.Parameter(result)
                    output.append(result)
            output = np.asarray(output)
            if debug:
                print("Type of Output: " + str(type(output)))
                print("Shape of Output: " + str(output.shape))
                print("Type of Each Element: " + str(type(output[0])))
            return output

    def forward(self, x):
        y = self.model.forward(x)
        return y

    def divide(self):
        self.theta_swa = np.true_divide(self.theta_swa, self.counter)
        self.theta_line_squared = np.true_divide(self.theta_line_squared, self.counter)

    def compute_swag_diagonal(self, debug=False):
        self.divide()
        theta_swa_squared = self.square_weights(self.theta_swa)
        sub = np.subtract(self.theta_line_squared, theta_swa_squared)
        super_threshold_indices = sub < 0
        sub[super_threshold_indices] = 0
        self.swag_diag = sub

        if debug:
            print("Shape Theta swa: " + str(self.theta_swa.shape))
            print("Shape Theta swa squared: " + str(theta_swa_squared.shape))
            print("Shape Swag_Diag: " + str(sub.shape))
            min_element = np.amin(self.swag_diag)
            max_element = np.amax(self.swag_diag)
            print(str(min_element)+ " at " + str(np.argmin(self.swag_diag)))
            print(str(max_element)+ " at " +str(np.argmax(self.swag_diag)))

    def add_to_swa(self, new_weights):
        self.theta_swa = np.add(self.theta_swa, new_weights)

    def add_to_line(self, new_weights):
        new_weights = self.square_weights(new_weights)
        self.theta_line_squared = np.add(self.theta_line_squared, new_weights)

    def square_weights(self, weights):
        return np.multiply(weights, weights)

    def get_weights_array(self, weights):
        array = weights[0].data
        array = array.flatten()
        try:
            array = torch.from_numpy(array)
        except:
            x = 0
        array = array.cpu()
        for dim in range(1, len(weights)):
            tensor = weights[dim].data
            tensor = tensor.flatten().cpu()
            array = np.concatenate((array, tensor), 0)
        return array

    def local_get_weights_array(self, weights):
        weights = weights
        array = weights[0].data
        array = array.flatten()
        for dim in range(1, len(weights)):
            tensor = weights[dim].data
            tensor = tensor.flatten()
            array = np.concatenate((array, tensor), 0)
        return array

    def train_model(self):
        #Cuda
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        self.model = self.model.to(device)

        #Params
        MODEL_PARAMETER = self.get_params()
        lr = MODEL_PARAMETER['lr']  # learning rate
        lam = MODEL_PARAMETER['lam']  # weight decay
        epochs = MODEL_PARAMETER['epochs_per_episode'] #epochs per communication episode

        #Optimizer and Loss
        opt = optim.SGD(self.model.parameters(), lr=lr, weight_decay=lam)
        CrEnt = nn.CrossEntropyLoss()

        for epoch in range(epochs):  # loop over the dataset multiple times
            for _, data in enumerate(self.model.trainloader, 0):
                # get the inputs; data is a list of [inputs, labels]
                inputs, labels = data
                inputs, labels = inputs.to(device), labels.to(device)
                # zero the parameter gradients
                opt.zero_grad()
                # forward + backward + optimize
                outputs = self.model.forward(inputs)
                loss = CrEnt(outputs, labels)
                loss.backward()
                opt.step()
                # Here Swag Magic
                self.counter = self.counter + 1
                new_weights = np.array(list(self.model.parameters()))
                new_weights = self.get_weights_array(new_weights)
                self.add_to_swa(new_weights)
                self.add_to_line(new_weights)
            print(loss.item())
        self.compute_swag_diagonal()
        return self


from stream import Stream, StreamArray, run

class feed_streams(object):
    pass

class single_item(object):
    def __init__(self, in_stream, func, **kwargs):
        self.in_stream = in_stream
        self.func = func
        self.kwargs = kwargs
        self.in_stream.subscribe(self.callback)
    def callback(self):
        for v in self.in_stream.recent[self.in_stream.start[self.callback] : self.in_stream.stop]:
            self.func(v, **self.kwargs)
        self.in_stream.start[self.callback] = self.in_stream.stop

class join_synch(object):
    def __init__(self, in_streams, func, **kwargs):
        self.in_streams = in_streams
        self.func = func
        self.kwargs = kwargs
        for in_stream in self.in_streams:
            in_stream.subscribe(self.callback)
    def callback(self):
        slices = [in_stream.recent[in_stream.start[self.callback] : in_stream.stop]
                  for in_stream in self.in_streams]
        zipped_slices = list(zip(*slices))
        for v in zipped_slices: self.func(v, **self.kwargs)
        for in_stream in self.in_streams:
            in_stream.start[self.callback] += len(zipped_slices)

class join_asynch(object):
    def __init__(self, in_streams, func, **kwargs):
        self.in_streams = in_streams
        self.func = func
        self.kwargs = kwargs
        for in_stream in self.in_streams:
            in_stream.subscribe(self.callback)
    def callback(self):
        for in_stream in self.in_streams:
            for v in in_stream.recent[in_stream.start[self.callback]: in_stream.stop]:
                self.func(v, **self.kwargs)
            in_stream.start[self.callback] = in_stream.stop

class join_timed(object):
    def __init__(self, in_streams, get_time, func, **kwargs):
        self.in_streams = in_streams
        self.get_time = get_time
        self.func = func
        self.kwargs = kwargs
        for in_stream in self.in_streams:
            in_stream.subscribe(self.callback)
    def callback(self):
        while all([in_stream.start[self.callback] < in_stream.stop
                   for in_stream in self.in_streams]):
            items = [in_stream.recent[in_stream.start[self.callback]]
                     for in_stream in self.in_streams]
            times = [self.get_time(item) for item in items]
            min_time = min(times)
            operand = [None for in_stream in self.in_streams]
            for i, in_stream in enumerate(self.in_streams):
                if times[i] == min_time:
                    operand[i] = items[i]
                    in_stream.start[self.callback] += 1
            self.func((min_time, operand), **self.kwargs)

class sliding_window(object):
    def __init__(self, in_stream, window_size, step_size, func, **kwargs):
        self.in_stream = in_stream
        self.window_size = window_size
        self.step_size = step_size
        self.func = func
        self.kwargs = kwargs
        self.in_stream.subscribe(self.callback)
    def callback(self):
        while self.in_stream.start[self.callback] + self.window_size <= self.in_stream.stop:
            start = self.in_stream.start[self.callback]
            window = self.in_stream.recent[start : start + self.window_size]
            self.func(window, **self.kwargs)
            self.in_stream.start[self.callback] += self.step_size

class subtract_mean_from_stream(object):
    def __init__(self, in_stream, window_size, func, **kwargs):
        self.in_stream = in_stream
        self.window_size = window_size
        self.func = func
        self.kwargs = kwargs
        self.in_stream.subscribe(self.callback)
    def callback(self):
        while self.in_stream.start[self.callback] + self.window_size <= self.in_stream.stop:
            start = self.in_stream.start[self.callback]
            window = self.in_stream.recent[start : start + self.window_size]
            item = window[-1] - np.mean(window)
            self.func(item, **self.kwargs)
            self.in_stream.start[self.callback] += 1

class subtract_mean_from_StreamArray(object):
    def __init__(self, in_stream, window_size, func, **kwargs):
        self.in_stream = in_stream
        self.window_size = window_size
        self.func = func
        self.kwargs = kwargs
        self.in_stream.subscribe(self.callback)
    def callback(self):
        while self.in_stream.start[self.callback] + self.window_size <= self.in_stream.stop:
            start = self.in_stream.start[self.callback]
            window = self.in_stream.recent[start : start + self.window_size]
            item = window[-1] - np.mean(window, axis=0)
            self.func(item, **self.kwargs)
            self.in_stream.start[self.callback] += 1

import numpy as np
class detect_anomaly(object):
    def __init__(self, in_stream, window_size, anomaly_size,
                     anomaly_factor, cloud_data_size,
                     cloud_func, **kwargs):
        self.in_stream = in_stream
        self.W = window_size
        self.A = anomaly_size
        self.F = anomaly_factor
        self.C = cloud_data_size
        self.cloud_func = cloud_func
        self.kwargs = kwargs
        self.in_stream.subscribe(self.callback)
        self.anomaly = False
    def callback(self):
        start = self.in_stream.start[self.callback]
        stop = self.in_stream.stop
        R = self.in_stream.recent

        while ((self.anomaly and stop - start >= self.C) or
                   (not self.anomaly and start + self.W <= stop)):
            if self.anomaly:
                data_to_cloud = R[start : start+self.C]
                self.anomaly = False
                start += self.C
                self.in_stream.set_start(self.callback, start)
                self.cloud_func(data_to_cloud, **self.kwargs)
            else:
                window = R[start : start + self.W]
                window_mean = np.mean(window[:self.A])
                window_std = np.std(window[:self.A])
                anomaly_mean = np.mean(window[-self.A: ])
                # if (np.abs(np.mean(window[-self.A: ])) >
                #         np.abs(np.mean(window)*self.F)):
                if (np.abs(anomaly_mean - window_mean) > self.F * window_std):
                    # print(window[-self.A: ].shape)
                    # print(window.shape)

                    # print(window[-self.A: ])
                    # print(window)

                    # print(window_mean, window_std, anomaly_mean, np.abs(anomaly_mean - window_mean))
                    # print()
                    self.anomaly = True
                    # print(window)
                    print('window_mean:', window_mean)
                    print('window_std: ', window_std)
                    print('anomaly_mean: ', anomaly_mean)
                    start += self.W - self.A
                    self.in_stream.set_start(self.callback, start)
                else:
                    start += 1
                    self.in_stream.set_start(self.callback, start)

def append_item_to_stream(v, out_stream):
    out_stream.append(v)

def append_item_to_StreamArray(v, out_stream):
    out_stream.append(np.stack(v, axis=0))

#------------------------------------------------------------------------
# Tests
#------------------------------------------------------------------------
def example_single_item():

    x, y, z = Stream(name='x'), Stream(name='y'), Stream(name='z')

    # Examples of functions passed to map_stream objects below
    # Function f has two parameters: an item v of the input stream
    # and a keyword parameter, out_stream, which is the output stream.
    def f(v, out_stream):
        out_stream.append(2*v)

    # This function has three parameters: the first is an item v of the
    # input stream, the second is a keyword argument (see kwargs) out_stream,
    # which is the output stream, and another keyword argument, multiplier.
    def h(v, out_stream, multiplier):
        out_stream.append(multiplier*v)

    # Set up the agent with input stream x and output stream y.
    single_item(in_stream=x, out_stream=y, func=f)
    # Set up the agent with input stream x and output stream z.
    single_item(in_stream=x, out_stream=z, func=h, multiplier=3)

    # Put values into input stream x.
    x.extend([0, 1, 2])
    # Run agents until all streams have been processed.
    run()
    # Functions f and h are called when stream x is extended.

    # Print the streams.
    x.print_recent()
    y.print_recent()
    z.print_recent()
    # y[i] = 2*x[i]), and z[i] = multiplier*x[i])

    # Put more values into input stream x.
    x.extend([3, 4])
    run()
    # Functions f and h are called when stream x is extended.

    # Print the output streams.
    x.print_recent()
    y.print_recent()
    z.print_recent()

def example_join_synch():

    def f(v, out_stream, multiplier):
        out_stream.append(sum(v) * multiplier)
        return

    x, y, z = Stream(name='x'), Stream(name='y'), Stream(name='z')
    join_synch(in_streams=[x, y], out_stream=z, func=f, multiplier=3)

    x.extend([0, 1, 2])
    y.extend([10, 11, 12, 13])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

    x.extend([3, 4, 5, 6])
    y.extend([10, 11, 12, 13, 14])
    run()
    x.print_recent()
    y.print_recent()

def example_join_asynch():
    def f(v, out_stream, multiplier):
        out_stream.append(v * multiplier)
        return

    x, y, z = Stream(name='x'), Stream(name='y'), Stream(name='z')
    join_asynch(in_streams=[x, y], out_stream=z, func=f, multiplier=3)

    x.extend([0, 1])
    y.extend([10])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

    x.extend([2])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

    y.extend([11])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

def example_join_timed():
    def get_time(v):
        return v[0]

    def f(v, out_stream):
        out_stream.append(v)
        return

    x, y, z = Stream(name='x'), Stream(name='y'), Stream(name='z')
    join_timed(in_streams=[x, y], func=f, get_time=get_time, out_stream=z)

    x.extend([(1, 0), (10, 1)])
    y.extend([(2, 'A'), (4, 'B')])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

    y.extend([(12, 'C')])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

    x.extend([(12, 2)])
    run()
    x.print_recent()
    y.print_recent()
    z.print_recent()

def example_sliding_window():
    def g(window, out_stream):
        out_stream.append(sum(window))
        return

    x, y = Stream(name='x'), Stream(name='y')
    sliding_window(in_stream=x, window_size=3, step_size=2, func=g, out_stream=y)

    x.extend(list(range(10)))
    run()
    x.print_recent()
    y.print_recent()

# EXAMPLE OF subtract_mean_from_StreamArray

def append_item_to_StreamArray(v, out_stream):
    out_stream.append(np.stack(v, axis=0))

from stream import StreamArray

def example_subtract_mean_from_StreamArray():
    xx = StreamArray(name='xx', dtype=float, dimension=2)
    yy = StreamArray(name='yy', dtype=float, dimension=2)
    subtract_mean_from_StreamArray(
        in_stream=xx, out_stream=yy, window_size=3,
        func=append_item_to_StreamArray)

    xx.extend([np.array([0., 1.]), np.array([2., 1.]),
               np.array([1., 1.]), np.array([2., 2.]),
               np.array([2., 1.])])
    run()
    xx.print_recent()
    yy.print_recent()

# EXAMPLE ILLUSTRATING example_subtract_mean_from_stream

import numpy as np

def append_item_to_stream(v, out_stream): out_stream.append(v)

def example_subtract_mean_from_stream():
    x, y = Stream(name='x'), Stream(name='y')

    subtract_mean_from_stream(
        in_stream=x, window_size=3,
        func=append_item_to_stream, out_stream=y)

    x.extend([0, 1, 2, 1, 1, 1, 2, 2, 2, 1])
    run()
    x.print_recent()
    y.print_recent()

def example_detect_anomaly():
    def cloud_func(window, ):
        print ('window ', window)
        return

    x = Stream(name='x')
    detect_anomaly(in_stream=x, window_size=4, anomaly_size=2,
                       anomaly_factor=1.1, cloud_data_size=2,
                       cloud_func=cloud_func)
    x.extend([1, 1, 2, 2, 3, 4, 7, 6, 11, 0, 3, 5, 5, 11, 11, 19, 19, 31])
    run()
    x.print_recent()

def example_detect_anomaly_with_StreamArray():
    def cloud_func(window, ):
        print ('window ', window)
        return

    x = StreamArray(name='x', dtype='float', dimension=2)

    detect_anomaly(in_stream=x, window_size=3, anomaly_size=1,
                       anomaly_factor=1.1, cloud_data_size=2,
                       cloud_func=cloud_func)

    x.extend([[1.0, 1.0], [2.0, 2.0], [3.0, 3.0], [7.0, 6.0], [11.0, 6.0], [2.0, 1.0], [18.0, 16.0], [2.0, 4.0]])
    run()

    x.extend([[0.0, 1.0], [8.0, 9.0], [12.0, 15.0], [1.0, 2.0], [21.0,
    31.0], [0.0, 0.0], [0., 0.]])
    run()

if __name__ =='__main__':
    print('example_single_item')
    example_single_item()
    print('')
    print('example_join_synch')
    example_join_synch()
    print('')
    print('example_join_asynch')
    example_join_asynch()
    print('')
    print('example_join_timed')
    example_join_timed()
    print('')
    print('example_sliding_window')
    example_sliding_window()
    print('')
    print('example_subtract_mean_from_stream')
    example_subtract_mean_from_stream()
    print('')
    print('example_subtract_mean_from_StreamArray')
    example_subtract_mean_from_StreamArray()
    print('')
    print('example_detect_anomaly')
    example_detect_anomaly()
    print('')
    print('example_detect_anomaly_with_StreamArray')
    example_detect_anomaly_with_StreamArray()


# -*- coding: utf-8 -*-
###############################################################################
#    License, author and contributors information in:                         #
#    __manifest__.py file at the root folder of this module.                  #
###############################################################################

from odoo import http
from odoo.http import request
from odoo.addons.payment.controllers.portal import PaymentProcessing

import logging

_logger = logging.getLogger(__name__)

class WebsiteSale(http.Controller):

    @http.route('/shop/payment/validate', type='http', auth="public", website=True, sitemap=False)
    def payment_validate(self, transaction_id=None, sale_order_id=None, **post):
        """ Method that should be called by the server when receiving an update
        for a transaction. State at this point :

         - UDPATE ME
        """
        # Customs Additions
        sale_order_id = int(sale_order_id) if sale_order_id else None
        transaction_id = int(transaction_id) if transaction_id else None

        if sale_order_id is None:
            order = request.website.sale_get_order()
        else:
            order = request.env['sale.order'].sudo().browse(sale_order_id)
            assert order.id == request.session.get('sale_last_order_id')

        print("========================")
        print("/shop/payment/validate")
        print("post------->", post)
        print("transaction_id", transaction_id)
        print("sale_order_id", sale_order_id)
        print("order", order)
        print("========================")

        try:
            transaction_ids = order.transaction_ids()
        except Exception as e:
            transaction_ids = order.transaction_ids

        if transaction_id:
            tx = request.env['payment.transaction'].sudo().browse(transaction_id)
            assert tx in transaction_ids
        elif order:
            tx = order.get_portal_last_transaction()
        else:
            tx = None

        if not order or (order.amount_total and not tx):
            return request.redirect('/shop')

        if order and not order.amount_total and not tx:
            order.with_context(send_email=True).action_confirm()
            return request.redirect(order.get_portal_url())

        # clean context and session, then redirect to the confirmation page
        request.website.sale_reset()
        if tx and tx.state == 'draft':
            return request.redirect('/shop')

        PaymentProcessing.remove_payment_transaction(tx)
        return request.redirect('/shop/confirmation')


from django.template import loader
import pdfkit
import os
from xhtml2pdf import pisa
from django.template.loader import get_template
from io import BytesIO
from urllib import request, response
from django.shortcuts import redirect, render
from .models import *
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from django.contrib.admin.views.decorators import staff_member_required

from django.contrib.auth import logout
from django.contrib import messages
from django.conf import settings
from django.contrib.sites.models import Site
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.views.decorators.cache import cache_control
from django.conf import settings
from django.core.mail import send_mail
from django.core import mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from datetime import datetime

from paytm import Checksum

MERCHANT_KEY = 'f63ilt9f9!374rCZ'

# Create your views here.

def index(requests):

    return render(requests, 'landing/index.html')

def about(requests):

    return render(requests, 'about/about.html')

def contact(requests):

    return render(requests, 'contact/contact.html')

def faq(requests):

    return render(requests, 'faq/faq.html')

def terms(requests):

    return render(requests, 'terms/terms.html')

def policy(requests):

    return render(requests, 'terms/terms.html')

def blogs(requests):

    blogs = blog.objects.all()

    return render(requests, 'blog/blog.html',{'blogs':blogs})

@staff_member_required(login_url='/')
def delete_blogs(requests,id):

    blog.objects.all().filter(id=id).delete()

    return redirect("/create_blogs")

@staff_member_required(login_url='/')
def create_blogs(request):

    blogs = blog.objects.all()

    if request.method == "POST":

        heading1 = request.POST.get('heading1', None)
        description = request.POST.get('description', None)
        image = request.FILES.get('image', None)

        blog.objects.create(heading1=heading1,description=description,image=image)

        return redirect("/create_blogs")

    return render(request, 'blog/create_blogs.html',{'blogs':blogs})

@cache_control(no_cache=True, must_revalidate=True)
@login_required(login_url='/')  # redirect when user is not logged in
def form_view(request):

    username = request.user.username

    user_id = request.user.id

    num = phone_numbers.objects.filter(user_name=username)

    all_states=states.objects.all()

    pri=price.objects.all()

    if request.method == "POST":

        full_name = request.POST.get('full_name', None)
        company_name = request.POST.get('company_name', None)
        company_type = request.POST.get('company_type', None)
        company_address = request.POST.get('company_address', None)
        gst_number = request.POST.get('gst_number', None)
        p_number = request.POST.get('p_number', None)
        email = request.POST.get('email', None)
        amount_input = request.POST.get('amount', None)
        total_states = request.POST.get('total_states', None)
        state = request.POST.get('state', None)
        # aadhar_card = request.FILES.get('aadhar_card')
        # photograph = request.FILES.get('photograph', None)
        # proof_of_address = request.FILES.get('proof_of_address', None)
        # pan_card = request.FILES.get('pan_card', None)
        # aadhar_card = request.FILES.get('aadhar_card',None)
        # photograph = request.FILES.get('photograph', None)
        # proof_of_address = request.FILES.get('proof_of_address', None)
        # pan_card = request.FILES.get('pan_card', None)

        # print(state)

        # state_list = []

        # Maharashtra = request.POST.get('Maharashtra', None)
        # if Maharashtra == "Maharashtra":
        #     state_list.append('Maharashtra')

        # Gujrat = request.POST.get('Gujrat', None)
        # if Gujrat == "Gujrat":
        #     state_list.append('Gujrat')

        # Karnataka = request.POST.get('Karnataka', None)
        # if Karnataka == "Karnataka":
        #     state_list.append('Karnataka')

        # West_Bengal = request.POST.get('West_Bengal', None)
        # if West_Bengal == "West_Bengal":
        #     state_list.append('West Bengal')

        # Telangana = request.POST.get('Telangana', None)
        # if Telangana == "Telangana":
        #     state_list.append('Telangana')

        # Delhi = request.POST.get('Delhi', None)
        # if Delhi == "Delhi":
        #     state_list.append('Delhi')

        # Rajasthan = request.POST.get('Rajasthan', None)
        # if Rajasthan == "Rajasthan":
        #     state_list.append('Rajasthan')

        # Bihar = request.POST.get('Bihar', None)
        # if Bihar == "Bihar":
        #     state_list.append('Bihar')

        # AndhraPradesh = request.POST.get('AndhraPradesh', None)
        # if AndhraPradesh == "AndhraPradesh":
        #     state_list.append('Andhra Pradesh')

        # listToStr = ' '.join(map(str, state_list))

        # docs = documents.objects.create(aadhar_card=aadhar_card, photograph=photograph, proof_of_address=proof_of_address, pan_card=pan_card)

        username = request.user.username

        obj, created = phone_numbers.objects.get_or_create(user_name=username)
        obj.phone_number = request.POST.get('p_number', None)
        obj.user_name = username
        obj.save()

        # aadhar_card=aadhar_card, photograph=photograph, proof_of_address=proof_of_address, pan_card=pan_card,

        done = orders.objects.create(gst_number=gst_number,company_addess=company_address,user_id=user_id, user_name=username, full_name=full_name, company_name=company_name, company_type=company_type, p_number=p_number, email=email, total_amount=amount_input, total_states=total_states, state=state)

        return redirect("/order_page/"+str(done.id))

    else:
        return render(request, 'form/form.html', {'numbers': num,'all_states':all_states,'pri':pri})

@csrf_exempt
@login_required(login_url='/')
def order_page(request, id):

    username = request.user.username

        # orders_placed1 = orders.objects.all().filter(user_name=username, id=(list_1[i]))

    orders_placed = orders.objects.get(id=id)

    pri = price.objects.all()

    param_dict = {

        'MID': 'XJailZ92874680327655',
        'ORDER_ID': str(id),
        'TXN_AMOUNT': str(orders_placed.total_amount),
        'CUST_ID': str(request.user.id),
        'INDUSTRY_TYPE_ID': 'Retail',
        'WEBSITE': 'DEFAULT',
        'CHANNEL_ID': 'WEB',
        'CALLBACK_URL': 'https://www.thegstco.com/handlerequest/' + str(orders_placed.id) + str("/") + str(request.user.id)+str("/"),
        # 'CALLBACK_URL': 'http://127.0.0.1:8000/handlerequest/' + str(orders_placed.id) + str("/") + str(request.user.id)+str("/"),

    }

    param_dict['CHECKSUMHASH'] = Checksum.generate_checksum(param_dict, MERCHANT_KEY)

    total_amount = str(orders_placed.total_amount),

    return render(request, 'order_page/order_page.html', {'param_dict': param_dict,'pri':pri,'state_str':orders_placed.state,'total_amount':orders_placed.total_amount,'total_states':orders_placed.total_states})

    # ===========================================================================================================================================================

@csrf_exempt
def handlerequest(request, id, user_id):

    user = User.objects.get(id=user_id)

    orders_placed = orders.objects.get(id=id)

    form = request.POST
    response_dict = {}
    for i in form.keys():
        response_dict[i] = form[i]
        if i == 'CHECKSUMHASH':
            checksum = form[i]

    verify = Checksum.verify_checksum(response_dict, MERCHANT_KEY, checksum)

    if verify:
        if response_dict['RESPCODE'] == '01':

            orders.objects.filter(id=id).update(payment="paid")

            return render(request, 'order/order_successful.html', {'id': id})
        else:
            return render(request, 'order/order_successful.html', {'id': id})
    return render(request, 'order/order_unsuccessful.html', {'response': response_dict})

# for generating pdf invoice
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
import os

def fetch_resources(uri, rel):
    path = os.path.join(uri.replace(settings.STATIC_URL, ""))
    return path

def render_to_pdf(template_src, context_dict={}):
    template = get_template(template_src)
    html  = template.render(context_dict)
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)#, link_callback=fetch_resources)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')
    return None

from django.views.generic import  View

class GenerateInvoice(View):

    def get(self, request, pk, *args, **kwargs):
        try:
            order_db = orders.objects.get(id = pk, user_name = request.user.username)
            pri=price.objects.all()  #you can filter using order_id as well
        except:
            return HttpResponse("505 Not Found")
        data = {
            'order_id': order_db.id,
            'user_email': order_db.email,
            'date': str(order_db.date),
            'name': order_db.full_name,
            'order': order_db,
            'amount': order_db.total_amount,
            'state':order_db.state,
            'total_states': order_db.total_states,
            'pri':pri
        }
        pdf = render_to_pdf('invoice/invoice.html',data )
        # return HttpResponse(pdf, content_type='application/pdf')

        # force download
        if pdf:
            response = HttpResponse(pdf, content_type='application/pdf')
            filename = "Invoice_%s.pdf" %(['order_id'])
            content = "inline; filename='%s'" %(filename)
            #download = request.GET.get("download")
            #if download:
            content = "attachment; filename=%s" %(filename)
            response['Content-Disposition'] = content
            return response
        return HttpResponse("Not found")

def invoice (request,id):
        try:
            order_db = orders.objects.get(id = id, user_name = request.user.username)     #you can filter using order_id as well
        except:
            return HttpResponse("505 Not Found")
        data = {
            'order_id': order_db.id,
            'user_email': order_db.email,
            'date': str(order_db.date),
            'name': order_db.full_name,
            'order': order_db,
            'amount': order_db.total_amount,
            'state':order_db.state,
            'total_states': order_db.total_states
        }
        # pdf = render_to_pdf('invoice/invoice.html',data )
        # # return HttpResponse(pdf, content_type='application/pdf')

        # # force download
        # if pdf:
        #     response = HttpResponse(pdf, content_type='application/pdf')
        #     filename = "Invoice_%s.pdf" %(['order_id'])
        #     content = "inline; filename='%s'" %(filename)
        #     #download = request.GET.get("download")
        #     #if download:
        #     content = "attachment; filename=%s" %(filename)
        #     response['Content-Disposition'] = content
        #     return response
        # return HttpResponse("Not found")

        return render(request, 'invoice/invoice.html',data)

@login_required(login_url='/')
def upload_docments(request, id):

    username = request.user.username

    orders_placed1 = orders.objects.get(id=id, user_name=username)

    if orders_placed1.proof_of_address and orders_placed1.aadhar_card and orders_placed1.photograph and orders_placed1.pan_card :

        return redirect("/")

    if orders_placed1.proof_of_address:
        proof_of_address1=True
    else:
        proof_of_address1=False

    if orders_placed1.aadhar_card:
        aadhar_card1=True
    else:
        aadhar_card1=False

    if orders_placed1.photograph:
        photograph1=True
    else:
        photograph1=False

    if orders_placed1.pan_card:
        pan_card1=True
    else:
        pan_card1=False

    if request.method == "POST":

        orders_done = orders.objects.get(id=id, user_name=username)

        aadhar_card = request.FILES.get('aadhar_card',None)
        if aadhar_card != None:
            orders_done.aadhar_card = aadhar_card

        photograph = request.FILES.get('photograph', None)
        if photograph != None:
            orders_done.photograph = photograph

        proof_of_address = request.FILES.get('proof_of_address', None)
        if proof_of_address != None:
          orders_done.proof_of_address = proof_of_address

        pan_card = request.FILES.get('pan_card', None)
        if pan_card != None:
            orders_done.pan_card = pan_card

        orders_done.save()

        return redirect('/order')

        # orders.objects.create(aadhar_card=aadhar_card,photograph=photograph,proof_of_address=proof_of_address,pan_card=pan_card)

    return render(request, 'form/upload_docments.html',{'proof_of_address':proof_of_address1,'aadhar_card':aadhar_card1,'photograph':photograph1,'pan_card':pan_card1})

@login_required(login_url='/')  # redirect when user is not logged in
def order(requests):

    pri = price.objects.all()

    orders_placed = orders.objects.all().filter(user_name=requests.user.username, user_id=requests.user.id,payment="paid").order_by("-id")

    return render(requests, 'order/order_details.html', {'orders_placed': orders_placed,'pri':pri})

@login_required(login_url='/')  # redirect when user is not logged in
def profile(request):

    # user_id = request.user.user_id

    username = request.user.username

    numbers = phone_numbers.objects.filter(user_name=username)

    if request.method == "POST":

        phone_number = request.POST.get('phone_number', None)
        user_name = request.POST.get('user_name', None)

        obj, created = phone_numbers.objects.get_or_create(user_name=user_name)
        obj.phone_number = request.POST.get('phone_number', None)
        obj.user_name = request.POST.get('user_name', None)
        obj.save()

        # obj1,created=orders.objects.get_or_create(user_name=user_name)
        # obj1.p_number=request.POST.get('phone_number', None)
        # obj1.save()

        messages.info(request, 'Phone Number Saved successfully!')
        return render(request, 'profile/profile.html', {"numbers": numbers})

    return render(request, 'profile/profile.html', {"numbers": numbers})

@login_required(login_url='/')  # redirect when user is not logged in
@staff_member_required(login_url='/')
def admin_panel(requests):

    orders_list = orders.objects.all().filter(payment="paid").order_by("-id")

    Recieved=orders.objects.filter(payment="paid",order_status="Order Recieved").count()
    Processing=orders.objects.filter(payment="paid",order_status="Processing").count()
    Submitted=orders.objects.filter(payment="paid",order_status="Submitted For Approval").count()
    Completed=orders.objects.filter(order_status="Order Completed").count()
    total=orders.objects.filter(payment="paid").count()

    pri = price.objects.all()

    if requests.method == "POST":
        state_price = requests.POST.get('price', None)

        percentage_price = requests.POST.get('percentage_price', None)

        percentage = requests.POST.get('percentage', None)

        price.objects.all().delete()

        price.objects.create(state_price=state_price,percentage_price=percentage_price,percentage=percentage)

        return(redirect("/admin_panel"))

    return render(requests, 'admin_panel/index.html', {"orders": orders_list,'pri':pri,'Recieved':Recieved,'Processing':Processing,'Submitted':Submitted,'Completed':Completed,'total':total})

@login_required(login_url='/')  # redirect when user is not logged in
@staff_member_required(login_url='/')
def admin_panel_upaid(requests):

         orders_list = orders.objects.all().filter(payment="Unpaid").order_by("-id")

         Recieved=orders.objects.filter(payment="paid",order_status="Order Recieved").count()
         Processing=orders.objects.filter(payment="paid",order_status="Processing").count()
         Submitted=orders.objects.filter(payment="paid",order_status="Submitted For Approval").count()
         Completed=orders.objects.filter(order_status="Order Completed").count()
         total=orders.objects.filter(payment="paid").count()

         pri = price.objects.all()

         return render(requests, 'admin_panel/index_copy.html', {"orders": orders_list,'pri':pri,'Recieved':Recieved,'Processing':Processing,'Submitted':Submitted,'Completed':Completed,'total':total})

@login_required(login_url='/')
@staff_member_required(login_url='/')
def order_information(requests,id):

    order_db1 = orders.objects.get(id=id)

    order_db = orders.objects.all().filter(id=id)

    return render(requests, 'order_information/order_information.html',{'order_db':order_db})

@login_required(login_url='/')  # redirect when user is not logged in
@staff_member_required(login_url='/')
def users_database(requests):

    numbers = phone_numbers.objects.all()
    users = User.objects.all()

    return render(requests, 'admin_panel/users_database.html', {"numbers": numbers, "users": users})

@login_required(login_url='/')  # redirect when user is not logged in
@staff_member_required(login_url='/')
def change_order_status(self, pk,status):

    try:
        status = status.replace("-"," ")
    except:
        pass

    if  orders.objects.all().filter(id=pk).exists():
        orders.objects.all().filter(id=pk).update(order_status=status)
        orders_placed=orders.objects.get(id=pk)

        return redirect('/order_information/'+str(pk))

    else:
        return HttpResponseBadRequest

@login_required(login_url='/')  # redirect when user is not logged in
def logout_view(request):

    logout(request)
    return redirect('/')

@staff_member_required(login_url='/')
def all_states(request):

    states_db=states.objects.all()

    if "State_name_form" in request.POST:

        state_name = request.POST.get('state_name', None)
        state_id = request.POST.get('state_id', None)

        states.objects.create(state_name=state_name,state_id=state_id)

        return redirect('/all_states')

    if "State_price_form" in request.POST:

        state_price = request.POST.get('state_price', None)
        percentage = request.POST.get('percentage', None)

        price.objects.all().delete()

        price.objects.create(state_price=state_price,price_with_percentage=percentage)

        return redirect('/all_states')

    return render (request, 'admin_panel/state.html',{'states_db':states_db})

@staff_member_required(login_url='/')
def delete_states(request,id):

    states.objects.filter(id=id).delete()
    return redirect('/all_states')

import csv
@login_required(login_url='/')
@staff_member_required(login_url='/')
def getfile(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="Ketan More.csv"'
    orders_db = orders.objects.all()
    writer = csv.writer(response)
    for order in orders_db:
        writer.writerow([order.user_name,order.user_id,order.full_name,order.email,order.company_name,order.company_type,order.p_number,order.created_date,order.state,order.total_states,order.total_amount,order.payment])
    return response

def Maharashtra(requests):

    return render(requests, 'states/Maharashtra.html')

def Delhi(requests):

    return render(requests, 'states/Delhi.html')

def Karnataka(requests):

    return render(requests, 'states/Karnataka.html')

def Telangana(requests):

    return render(requests, 'states/Telangana.html')

def Gujarat(requests):

    return render(requests, 'states/Gujarat.html')

def Punjab(requests):

    return render(requests, 'states/Punjab.html')