import calendar
from functools import reduce

from dateutil.relativedelta import relativedelta

from odoo import _, api, exceptions, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_is_zero, float_round

class AccountPaymentTermHoliday(models.Model):
    _name = "account.payment.term.holiday"
    _description = "Payment Term Holidays"

    payment_id = fields.Many2one(comodel_name="account.payment.term")
    holiday = fields.Date(required=True)
    date_postponed = fields.Date(string="Postponed date", required=True)

    @api.constrains("holiday", "date_postponed")
    def check_holiday(self):
        for record in self:
            if fields.Date.from_string(
                record.date_postponed
            ) <= fields.Date.from_string(record.holiday):
                raise ValidationError(
                    _("Holiday %s can only be postponed into the future")
                    % record.holiday
                )
            if (
                record.search_count(
                    [
                        ("payment_id", "=", record.payment_id.id),
                        ("holiday", "=", record.holiday),
                    ]
                )
                > 1
            ):
                raise ValidationError(
                    _("Holiday %s is duplicated in current payment term")
                    % record.holiday
                )
            if (
                record.search_count(
                    [
                        ("payment_id", "=", record.payment_id.id),
                        "|",
                        ("date_postponed", "=", record.holiday),
                        ("holiday", "=", record.date_postponed),
                    ]
                )
                >= 1
            ):
                raise ValidationError(
                    _("Date %s cannot is both a holiday and a Postponed date")
                    % record.holiday
                )

class AccountPaymentTermLine(models.Model):
    _inherit = "account.payment.term.line"

    amount_round = fields.Float(
        string="Amount Rounding",
        digits="Account",
        help="Sets the amount so that it is a multiple of this value.",
    )
    months = fields.Integer(string="Number of Months")
    weeks = fields.Integer(string="Number of Weeks")
    value = fields.Selection(
        selection_add=[
            ("percent_amount_untaxed", "Percent (Untaxed amount)"),
            ("fixed",),
        ],
        ondelete={"percent_amount_untaxed": lambda r: r.write({"value": "percent"})},
    )

    @api.constrains("value", "value_amount")
    def _check_value_amount_untaxed(self):
        for term_line in self:
            if (
                term_line.value == "percent_amount_untaxed"
                and not 0 <= term_line.value_amount <= 100
            ):
                raise ValidationError(
                    _(
                        "Percentages on the Payment Terms lines "
                        "must be between 0 and 100."
                    )
                )

    def compute_line_amount(self, total_amount, remaining_amount, precision_digits):
        """Compute the amount for a payment term line.
        In case of procent computation, use the payment
        term line rounding if defined

            :param total_amount: total balance to pay
            :param remaining_amount: total amount minus sum of previous lines
                computed amount
            :returns: computed amount for this line
        """
        self.ensure_one()
        if self.value == "fixed":
            return float_round(self.value_amount, precision_digits=precision_digits)
        elif self.value in ("percent", "percent_amount_untaxed"):
            amt = total_amount * self.value_amount / 100.0
            if self.amount_round:
                amt = float_round(amt, precision_rounding=self.amount_round)
            return float_round(amt, precision_digits=precision_digits)
        elif self.value == "balance":
            return float_round(remaining_amount, precision_digits=precision_digits)
        return None

    def _decode_payment_days(self, days_char):
        # Admit space, dash and comma as separators
        days_char = days_char.replace(" ", "-").replace(",", "-")
        days_char = [x.strip() for x in days_char.split("-") if x]
        days = [int(x) for x in days_char]
        days.sort()
        return days

    @api.constrains("payment_days")
    def _check_payment_days(self):
        for record in self:
            if not record.payment_days:
                continue
            try:
                payment_days = record._decode_payment_days(record.payment_days)
                error = any(day <= 0 or day > 31 for day in payment_days)
            except Exception:
                error = True
            if error:
                raise exceptions.Warning(_("Payment days field format is not valid."))

    payment_days = fields.Char(
        string="Payment day(s)",
        help="Put here the day or days when the partner makes the payment. "
        "Separate each possible payment day with dashes (-), commas (,) "
        "or spaces ( ).",
    )

class AccountPaymentTerm(models.Model):
    _inherit = "account.payment.term"

    sequential_lines = fields.Boolean(
        string="Sequential lines",
        default=False,
        help="Allows to apply a chronological order on lines.",
    )
    holiday_ids = fields.One2many(
        string="Holidays",
        comodel_name="account.payment.term.holiday",
        inverse_name="payment_id",
    )

    def apply_holidays(self, date):
        holiday = self.holiday_ids.search(
            [("payment_id", "=", self.id), ("holiday", "=", date)]
        )
        if holiday:
            return fields.Date.from_string(holiday.date_postponed)
        return date

    def apply_payment_days(self, line, date):
        """Calculate the new date with days of payments"""
        if line.payment_days:
            payment_days = line._decode_payment_days(line.payment_days)
            if payment_days:
                new_date = None
                payment_days.sort()
                days_in_month = calendar.monthrange(date.year, date.month)[1]
                for day in payment_days:
                    if date.day <= day:
                        if day > days_in_month:
                            day = days_in_month
                        new_date = date + relativedelta(day=day)
                        break
                if not new_date:
                    day = payment_days[0]
                    if day > days_in_month:
                        day = days_in_month
                    new_date = date + relativedelta(day=day, months=1)
                return new_date
        return date

    def compute(self, value, date_ref=False, currency=None):
        """Complete overwrite of compute method for adding extra options."""
        # FIXME: Find an inheritable way of doing this
        self.ensure_one()
        last_account_move = self.env.context.get("last_account_move", False)
        date_ref = date_ref or fields.Date.today()
        amount = value
        result = []
        if not currency:
            if self.env.context.get("currency_id"):
                currency = self.env["res.currency"].browse(
                    self.env.context["currency_id"]
                )
            else:
                currency = self.env.company.currency_id
        precision_digits = currency.decimal_places
        next_date = fields.Date.from_string(date_ref)
        for line in self.line_ids:
            if line.value == "percent_amount_untaxed" and last_account_move:
                if last_account_move.company_id.currency_id == currency:
                    amount_untaxed = -last_account_move.amount_untaxed_signed
                else:
                    raise UserError(
                        _(
                            "Percentage of amount untaxed can't be used with foreign "
                            "currencies"
                        )
                    )
                amt = line.compute_line_amount(amount_untaxed, amount, precision_digits)
            else:
                amt = line.compute_line_amount(value, amount, precision_digits)
            if not self.sequential_lines:
                # For all lines, the beginning date is `date_ref`
                next_date = fields.Date.from_string(date_ref)
                if float_is_zero(amt, precision_digits=precision_digits):
                    continue
            if line.option == "day_after_invoice_date":
                next_date += relativedelta(
                    days=line.days, weeks=line.weeks, months=line.months
                )
            elif line.option == "day_following_month":
                # Getting last day of next month
                next_date += relativedelta(day=line.days, months=1)
            elif line.option == "day_current_month":
                # Getting last day of next month
                next_date += relativedelta(day=line.days, months=0)
            # From Odoo
            elif line.option == "after_invoice_month":
                # Getting 1st of next month
                next_first_date = next_date + relativedelta(day=1, months=1)
                # Then add days
                next_date = next_first_date + relativedelta(
                    days=line.days - 1, weeks=line.weeks, months=line.months
                )
            next_date = self.apply_payment_days(line, next_date)
            next_date = self.apply_holidays(next_date)
            if not float_is_zero(amt, precision_digits=precision_digits):
                result.append((fields.Date.to_string(next_date), amt))
                amount -= amt
        amount = reduce(lambda x, y: x + y[1], result, 0.0)
        dist = round(value - amount, precision_digits)
        if dist:
            last_date = result and result[-1][0] or fields.Date.today()
            result.append((last_date, dist))
        return result


import pandas as pd
from PortCom import *
import numpy as np
import urllib
import pygame,os
import time

def SeuilX(Val, Seuil):
    if Val>=0:
        return max(Val-Seuil,0)
    else:
        return min(Val+Seuil,0)

class monitoring(object):
    def __init__(self, ListVar):
        self.NbVal = len(ListVar)
        self.ListVar=ListVar
        self.iVar=0
        self.indexdataframe=0
        self.datas = pd.DataFrame(index=[0])
        for VarName in ListVar:
            self.datas[VarName+"tps"]=0
            self.datas[VarName+"val"]=0

    def getValues(self, ser,nbTest=5):
        self.datas[self.ListVar[self.iVar]+"tps"][self.indexdataframe]= time.clock()
        self.datas[self.ListVar[self.iVar]+"val"][self.indexdataframe]= readFloat(ser,self.ListVar[self.iVar]+"?",nbTest=nbTest)
        self.iVar += 1
        if self.iVar >= self.NbVal:
            self.iVar = 0
            self.indexdataframe += 1

    def plot(self,x,y,linewidth=2.5, linestyle="-", marker='', RangeX=None, RangeY=None):
        plt.plot(self.datas[x], self.datas[y], linewidth=linewidth, linestyle=linestyle, marker=marker)
        if (RangeX != None):
            Xmax=max(self.datas[x])
            plt.axis([Xmax-RangeX,Xmax, None, None])
        if (RangeY != None):
            Ymax=max(self.datas[y])
            plt.axis([None, None, Ymax-RangeY, Ymax])

    def scatter(self,x,y, s=20, c=u'b', marker=u'o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None,
        linewidths=None, verts=None, hold=None, RangeX=None, RangeY=None):
        plt.scatter(self.datas[x], self.datas[y], s=s, c=c, marker=marker, cmap=cmap, norm=norm,
            vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, hold=hold)
        if (RangeX != None):
            Xmax=max(self.datas[x])
            plt.axis([Xmax-RangeX,Xmax, None, None])
        if (RangeY != None):
            Ymax=max(self.datas[y])
            plt.axis([None, None, Ymax-RangeY, Ymax])

class joystick_XBox_class(object):
    def __init__(self, VtsMax=500.0, VtsDeadzone=0.07, VtsOffSet=0.05, VtsAcc=30.0, RotMax=500.0, RotDeadzone =0.07, RotOffSet=0.05, RotAcc=30.0):
        pygame.init()
        nb_joysticks = pygame.joystick.get_count()
        disable=0
        #Bouton pour limiter la vitesse du pointeur
        LIMITER=7
        self.mon_joystick = pygame.joystick.Joystick(0)
        self.mon_joystick.init()
        self.jname= self.mon_joystick.get_name()

        #On compte les boutons
        self.nb_boutons = self.mon_joystick.get_numbuttons()

        # Init de la memoire pour les etats des bouton
        self.membstate=map(self.mon_joystick.get_button,range(self.nb_boutons))

        # Init de la memoire pour l' etats de la croix
        self.memhat=[False,False]
        self.hat=[False,False]

        #etat initial des combinaison de touche
        self.bstate_int=0

        self.VtsDeadzone=VtsDeadzone
        self.VtsOffSet=VtsOffSet
        self.VtsMax = VtsMax
        self.RotDeadzone =RotDeadzone
        self.RotOffSet=RotOffSet
        self.RotMax = RotMax
        self.RotAcc = RotAcc
        self.VtsAcc = VtsAcc

        self.VtsD = 0
        self.VtsA = 0

        self.axe = [0.0,0.0]

    def getState(self):
        ev=pygame.event.get()
        #recuperation de l'etat des boutons
        bstate=map(self.mon_joystick.get_button,range(self.nb_boutons))
        # Permet de verifier si un changement d'etat a eu lieu entre 2 boucles
        bstate_ev=map(lambda c,m:c-m,bstate,self.membstate)
        # Conversion de bstate en decimal pour detecter les conbinaion de touches
        self.bstate_int=sum(map(lambda b:int(self.mon_joystick.get_button(b))*(2**b),range(self.nb_boutons)))
        # Gestion du pointeur avec les analogiques
        self.JoyRotRob= - SeuilX(-self.mon_joystick.get_axis(0) + self.RotOffSet, self.RotDeadzone )*self.RotMax*(1-self.RotDeadzone )
        self.JoyVtsRob= - SeuilX(self.mon_joystick.get_axis(2) + self.VtsOffSet, self.VtsDeadzone)*self.VtsMax*(1-self.VtsDeadzone)

        # Gestion des touche HAUT,BAS,GAUCHE,DROITE du clavier avec
        # la croix de la manette
        #nb_hats=self.mon_joystick.get_numhats()
        #if nb_hats>0:
        #    self.hat=self.mon_joystick.get_hat(0)
        #    hat_ev=map(lambda hc,hm:hc!=hm,self.hat,self.memhat)

        self.membstate=bstate
        self.memhat=self.hat

        return [self.JoyVtsRob, self.JoyRotRob]
    def getVtsMString(self):
        self.getState()

        if (self.VtsD + self.VtsAcc) < self.JoyVtsRob:
            self.VtsD += self.VtsAcc
        elif (self.VtsD - self.VtsAcc) > self.JoyVtsRob:
            self.VtsD -= self.VtsAcc
        else :
            self.VtsD = self.JoyVtsRob

        if (self.VtsA + self.RotAcc) < self.JoyRotRob:
            self.VtsA += self.RotAcc
        elif (self.VtsD - self.VtsAcc) > self.JoyVtsRob:
            self.VtsA -= self.RotAcc
        else :
            self.VtsA = self.JoyRotRob

        VtsM1 = int(self.VtsD - self.VtsA/2)
        VtsM2 = int(self.VtsD + self.VtsA/2)
        return "VtsM\t%s\t%s \n" % (VtsM1, VtsM2)


from http import HTTPStatus

test_data_for_create_role_successful = [
    (
        {
            "label": "test",
        },
        {'msg': 'Role created!'},
        {'status': HTTPStatus.OK},
    ),
]

test_data_for_create_role_fail = [
    (
        {
            "label": "test2",
        },
        {
            "validation_error": {
                "body_params": [
                    {
                        "ctx": {
                            "enum_values": [
                                "standard",
                                "privileged",
                                "admin",
                                "test"
                            ]
                        },
                        "loc": [
                            "label"
                        ],
                        "msg": "value is not a valid enumeration member; "
                               "permitted: 'standard', 'privileged', 'admin', 'test'",
                        "type": "type_error.enum"
                    }
                ]
            }
        },
        {'status': HTTPStatus.BAD_REQUEST},
    ),
    (
        {
            "label": "admin",
        },
        {
            "msg": "Role exists!"
        },
        {'status': HTTPStatus.CONFLICT},
    ),
    (
        {
            "lael": "test",
        },
        {
            "validation_error": {
                "body_params": [
                    {
                        "loc": [
                            "label"
                        ],
                        "msg": "field required",
                        "type": "value_error.missing"
                    }
                ]
            }
        },
        {'status': HTTPStatus.BAD_REQUEST},
    ),
]

test_data_for_get_scope_roles = [
    (
        ['standard', 'privileged', 'admin', 'test'],
        {'status': HTTPStatus.OK, 'len': 4},
    ),
]

test_data_for_get_single_role = [
    (
        '815aa676-902d-4505-bbb5-42ca9727617f',
        {
            'id': '815aa676-902d-4505-bbb5-42ca9727617f',
            'label': 'standard'
        },
        {'status': HTTPStatus.OK},
    ),
    (
        'c07d6c8c-1d4f-4f54-b88f-2c2b962ad7b6',
        {
            'id': 'c07d6c8c-1d4f-4f54-b88f-2c2b962ad7b6',
            'label': 'admin'
        },
        {'status': HTTPStatus.OK},
    ),
]

test_data_for_get_single_role_fail = [
    (
        'qwe',
        {
            "msg": "Bad format uuid"
        },
        {'status': HTTPStatus.UNPROCESSABLE_ENTITY},
    ),
    (
        '16bdbb16-d3bf-4184-b08e-e2b2eabeee7f',
        {
            'msg': 'Role not found'
        },
        {'status': HTTPStatus.NOT_FOUND},
    ),
]

test_data_for_delete_role_fail = [
    (
        '16bdbb16-d3bf-4184-b08e-e2b2eabeee7f',
        {
            "msg": "Role not found"
        },
        {'status': HTTPStatus.NOT_FOUND},
    ),
    (
        'qwef',
        {
            "msg": "Bad format uuid"
        },
        {'status': HTTPStatus.UNPROCESSABLE_ENTITY},
    ),
]

test_data_for_delete_role = [
    (
        'c07d6c8c-1d4f-4f54-b88f-2c2b962ad7b6',
        {
            "msg": "Role deleted!"
        },
        {'status': HTTPStatus.OK},
    ),
]

test_data_for_role_appoint_successful = [
    (
        '15e794ed-2286-4690-b092-793f5e51f0ec',
        'privileged',
        {
            "msg": "Role changed!"
        },
        {'status': HTTPStatus.OK},
    ),
]

test_data_for_role_appoint_fail = [
    (
        '13f82a7f-49ca-4e29-8731-8fed5e37695f',
        'privileged',
        {
            "msg": "User not found"
        },
        {'status': HTTPStatus.NOT_FOUND},
    ),
    (
        'qwe',
        'privileged',
        {
            "msg": "Bad format uuid"
        },
        {'status': HTTPStatus.UNPROCESSABLE_ENTITY},
    ),
    (
        '15e794ed-2286-4690-b092-793f5e51f0ec',
        'privileg',
        {
            "validation_error": {
                "query_params": [
                    {
                        "ctx": {
                            "enum_values": [
                                "standard",
                                "privileged",
                                "admin",
                                "test"
                            ]
                        },
                        "loc": [
                            "label"
                        ],
                        "msg": "value is not a valid enumeration member; "
                               "permitted: 'standard', 'privileged', 'admin', 'test'",
                        "type": "type_error.enum"
                    }
                ]
            }
        },
        {'status': HTTPStatus.BAD_REQUEST},
    ),
]

test_data_for_role_take_away_successful = [
    (
        '15e794ed-2286-4690-b092-793f5e51f0ec',
        {
            "msg": "Role changed!"
        },
        {'status': HTTPStatus.OK},
    )
]

test_data_for_role_take_away_fail = [
    (
        'qwe',
        {
            "msg": "Bad format uuid"
        },
        {'status': HTTPStatus.UNPROCESSABLE_ENTITY},
    ),
    (
        '13f82a7f-49ca-4e29-8731-8fed5e37695f',
        {
            "msg": "User not found"
        },
        {'status': HTTPStatus.NOT_FOUND},
    )
]


import discord
from discord.ext import commands
import datetime
import humanize
from classes import checks
import typing
from classes import CustomBotClass

class AltFinder(commands.Cog):

    def __init__(self, bot: CustomBotClass.CustomBot):
        self.bot = bot

    @commands.command()
    @commands.has_permissions(kick_members=True)
    @commands.guild_only()
    async def alts(self, ctx, dur: typing.Optional[str]=None, size: int = 10):
        if dur:
            if not dur.lower().endswith("d"):
                return await ctx.send("The duration must be days like `7d`")
        alts = []

        paginator = commands.Paginator(prefix="```yaml", suffix="```", max_size=1000)
        counter = 0
        if dur:
            for member in ctx.guild.members:
                delta = datetime.timedelta(days=int(dur[:-1]))
                if (datetime.datetime.now() - member.created_at) < delta:
                    if counter > size:
                        break
                    alts.append(member)
                    counter += 1
            alts.sort(reverse=True, key=checks.created_at)
            for alt in alts:
                paginator.add_line(f"{alts.index(alt)+1}) {alt} Created at {humanize.naturaldate(alt.created_at.date())} ({humanize.precisedelta(datetime.datetime.now() - alt.created_at)})\n")
        if not dur:
            all_membrs = ctx.guild.members
            all_membrs.sort(reverse=True, key=checks.created_at)
            new_all_membrs = all_membrs[:size]
            for alt in new_all_membrs:
                paginator.add_line(
                    f"{alts.index(alt)+1}) {alt} Created at {humanize.naturaldate(alt.created_at.date())} ({humanize.precisedelta(datetime.datetime.now() - alt.created_at)})\n")
        for page in paginator.pages:
            title = f"Accounts less than {humanize.precisedelta(delta)} old" if dur else "Newest accounts"
            embed=discord.Embed(title=title, description=page, color=self.bot.color)
            await ctx.send(embed=embed)

def setup(bot):
    bot.add_cog(AltFinder(bot))


from collections import deque

n = int(input())

# 화면 이모티콘, 클립보드 이모티콘 배열
dist = [ [-1] * (n+1) for _ in range(n+1) ]

q = deque()
q.append((1,0))
dist[1][0] = 0
while q:
    s, c = q.popleft()
    # 1. 화면 클립보드에 복사
    if dist[s][s] == -1:
        dist[s][s] = dist[s][c] + 1
        q.append((s,s))
    # 2. 클립보드를 화면에 복사
    if s + c <= n and dist[s+c][c] == -1:
        dist[s+c][c] = dist[s][c] + 1
        q.append((s+c,c))
    # 3. 화면 내용 하나 빼기
    if s - 1 >= 0 and dist[s-1][c] == -1:
        dist[s-1][c] = dist[s][c] + 1
        q.append((s-1,c))

answer = -1

for i in range(n+1):
    if dist[n][i] != -1:
        if answer == -1 or answer > dist[n][i]:
            answer = dist[n][i]
print(answer)


from typing import List

import torch
import torchvision

from ffcv.fields import IntField, RGBImageField
from ffcv.fields.decoders import IntDecoder, SimpleRGBImageDecoder
from ffcv.fields.rgb_image import CenterCropRGBImageDecoder, \
    RandomResizedCropRGBImageDecoder
from ffcv.loader import Loader, OrderOption
from ffcv.pipeline.operation import Operation
from ffcv.transforms import RandomResizedCrop, RandomHorizontalFlip, Cutout, \
    RandomTranslate, Convert, ToDevice, ToTensor, ToTorchImage, NormalizeImage
from ffcv.transforms.common import Squeeze
from ffcv.writer import DatasetWriter

def save_data_for_beton(dataset, root='../data'):
    if dataset == 'cifar10':
        trainset = torchvision.datasets.CIFAR10(root=root, train=True, download=True)
        testset = torchvision.datasets.CIFAR10(root=root, train=False, download=True)

    if dataset == 'cifar100':
        trainset = torchvision.datasets.CIFAR100(root=root, train=True, download=True)
        testset = torchvision.datasets.CIFAR100(root=root, train=False, download=True)

    if dataset == 'svhn':
        trainset = torchvision.datasets.SVHN(root=root, split='train', download=True)
        testset = torchvision.datasets.SVHN(root=root, split='test', download=True)

    if dataset == 'tiny':
        trainset = torchvision.datasets.ImageFolder(root + '/tiny-imagenet-200/train')
        testset = torchvision.datasets.ImageFolder(root + '/tiny-imagenet-200/val')

    if dataset == 'imagenet':
        trainset = torchvision.datasets.ImageFolder('imagenet_train_path')
        testset = torchvision.datasets.ImageFolder('imagenet_test_path')

        # for large dataset
        datasets = {
            'train': trainset,
            'test': testset
        }
        for (name, ds) in datasets.items():

            writer = DatasetWriter(f'imagenet_path/{dataset}_{name}.beton', {
                'image': RGBImageField(write_mode='jpg',
                                       max_resolution=256,
                                       compress_probability=0.5,
                                       jpeg_quality=90),
                'label': IntField(),
            }, num_workers=16)
            writer.from_indexed_dataset(ds, chunksize=100)

    else:
        # for small dataset
        datasets = {
            'train': trainset,
            'test': testset
        }
        for (name, ds) in datasets.items():
            writer = DatasetWriter(f'{root}/../ffcv_data/{dataset}/{dataset}_{name}.beton', {
                'image': RGBImageField(),
                'label': IntField(),},
                num_workers=16)
            writer.from_indexed_dataset(ds)

def get_fast_dataloader(dataset, train_batch_size, test_batch_size, num_workers=20, dist=True, shuffle=False):

    gpu = f'cuda:{torch.cuda.current_device()}'
    decoder = None

    if dataset == 'cifar10':
        mean = torch.tensor([0.4914, 0.4822, 0.4465])*255
        img_size = 32
    if dataset == 'cifar100':
        mean = torch.tensor([0.5071, 0.4867, 0.4408])*255
        img_size = 32
    if dataset == 'svhn':
        mean = torch.tensor([0.43090966, 0.4302428, 0.44634357])*255
        img_size = 32
    if dataset == 'tiny':
        mean = torch.tensor([0.48024578664982126, 0.44807218089384643, 0.3975477478649648])*255
        img_size = 64

    if dataset == 'imagenet':
        # fix size
        init_size = 160
        orgin_size = 256
        test_size = 224

        decoder = RandomResizedCropRGBImageDecoder((init_size, init_size))

        paths = {
            'train': 'imagenet_train_path',
            'test': 'imagenet_test_path'
        }
        # for large dataset
        loaders = {}
        for name in ['train', 'test']:
            if name == 'train':
                image_pipeline: List[Operation] = [decoder,
                                                   RandomHorizontalFlip()]
            else:
                image_pipeline: List[Operation] = [CenterCropRGBImageDecoder((test_size, test_size), test_size/orgin_size)]

            label_pipeline: List[Operation] = [IntDecoder(), ToTensor(), Squeeze(), ToDevice_modified(torch.device(gpu), non_blocking=True)]

            image_pipeline.extend([
                ToTensor(),
                ToDevice_modified(torch.device(gpu), non_blocking=True),
                ToTorchImage(),
                Normalize_and_Convert(torch.float16, True)
            ])
            if shuffle:
                order = OrderOption.RANDOM
            else:
                order = OrderOption.RANDOM if name == 'train' else OrderOption.SEQUENTIAL

            loaders[name] = Loader(paths[name], batch_size=train_batch_size if name == 'train' else test_batch_size,
                                   num_workers=num_workers, order=order, drop_last=(name == 'train'), os_cache=True,
                                   distributed=dist, pipelines={'image': image_pipeline, 'label': label_pipeline},
                                   seed = 0)
    else:
        # for small dataset
        paths = {
            'train': f'../ffcv_data/{dataset}/{dataset}_train.beton',
            'test': f'../ffcv_data/{dataset}/{dataset}_test.beton'
        }

        loaders = {}
        for name in ['train', 'test']:
            image_pipeline: List[Operation] = [SimpleRGBImageDecoder()]
            label_pipeline: List[Operation] = [IntDecoder(), ToTensor(), ToDevice_modified(torch.device(gpu)), Squeeze()]
            if name == 'train':
                image_pipeline.extend([
                    RandomHorizontalFlip(),
                    RandomTranslate(padding=int(img_size / 8.), fill=tuple(map(int, mean))),
                ])
            image_pipeline.extend([
                ToTensor(),
                ToDevice_modified(torch.device(gpu), non_blocking=True),
                ToTorchImage(),
                Normalize_and_Convert(torch.float16, True)
            ])

            order = OrderOption.RANDOM if name == 'train' else OrderOption.SEQUENTIAL

            loaders[name] = Loader(paths[name], batch_size=train_batch_size if name == 'train' else test_batch_size,
                                num_workers=num_workers, order=order, drop_last=(name == 'train'), os_cache=True,
                                   pipelines={'image': image_pipeline, 'label': label_pipeline})

    return loaders['train'], loaders['test'], decoder

from typing import Callable, Optional, Tuple
from dataclasses import replace
from ffcv.pipeline.state import State
from ffcv.pipeline.allocation_query import AllocationQuery

class Normalize_and_Convert(Operation):
    def __init__(self, target_dtype, target_norm_bool):
        super().__init__()
        self.target_dtype = target_dtype
        self.target_norm_bool = target_norm_bool

    def generate_code(self) -> Callable:
        def convert(inp, dst):
            if self.target_norm_bool:
                inp = inp / 255.0
            return inp.type(self.target_dtype)

        convert.is_parallel = True

        return convert

    def declare_state_and_memory(self, previous_state: State) -> Tuple[State, Optional[AllocationQuery]]:
        return replace(previous_state, dtype=self.target_dtype), None

from ffcv.transforms import ToDevice
class ToDevice_modified(ToDevice):
    def __init__(self, device, non_blocking=True):
        super(ToDevice_modified, self).__init__(device, non_blocking)

    def generate_code(self):
        def to_device(inp, dst):
            if len(inp.shape) == 4:
                if inp.is_contiguous(memory_format=torch.channels_last):
                    dst = dst.reshape(inp.shape[0], inp.shape[2], inp.shape[3], inp.shape[1])
                    dst = dst.permute(0, 3, 1, 2)

            if len(inp.shape) == 0:
                inp = inp.unsqueeze(0)

            dst = dst[:inp.shape[0]]
            dst.copy_(inp, non_blocking=self.non_blocking)
            return dst

        return to_device


'''
Created on 2021年5月6日

@author: 40215
'''
from flask import Flask
from .extensions import db, api, cors, csrf
from pyadmin.admin import bp_admin, bp_general
from dotenv import load_dotenv

def create_app():
    load_dotenv(verbose=True)
    app = Flask(__name__.split('.')[0])
    # 加载配置
    app.config.from_object('pyadmin.config.DevelopmentConfig')

    register_extensions(app)
    register_blueprints(app)
    return app

def register_extensions(app):
    """ Register Flask extensions. """
    db.init_app(app)
    api.init_app(app)
    cors.init_app(app)      # 跨域配置
    csrf.init_app(app)      # 跨域攻击

def register_blueprints(app):
    """ Register Flask BluePrints """
    app.register_blueprint(bp_admin)
    app.register_blueprint(bp_general)


import pickle
import numpy as np, pandas as pd
from flask import Flask, render_template, request
import regex as re
from sklearn.feature_extraction.text import TfidfVectorizer
import en_core_web_md
nlp = en_core_web_md.load()

app = Flask(__name__)
# pickle.dump(recommend, open('model.pkl','wb'))
model = pickle.load(open('../Deployment/model/model_rfw2v.pkl','rb'))

train = pd.read_csv('../datasets/final combined file/troll_data.csv')
# list_sequences_train = train["content"]
# max_features = 22000
# tokenizer = Tokenizer(num_words=max_features)
# train = tokenizer.fit_on_texts(list(list_sequences_train))

def preprocess_text(sen):
    # Remove punctuations and numbers
    sentence = re.sub('[^a-zA-Z]', ' ', sen)
    # Single character removal
    sentence = re.sub(r"\s+[a-zA-Z]\s+", ' ', sentence)
    # Removing multiple spaces
    sentence = re.sub(r'\s+', ' ', sentence)
    return sentence

def get_vec(x):
  doc = nlp(x)
  return doc.vector

@app.route('/')
def Home():
    return render_template('index.html')

@app.route('/predict', methods=['POST'])
def predict():
        comment = [x for x in request.form.values()]
        comment = [preprocess_text(comment[0])]
        comment = pd.DataFrame(comment)
        comment = comment[0].apply(lambda x: get_vec(x))
        XX = comment.to_numpy()
        XX = XX.reshape(-1,1)
        XX = np.concatenate(np.concatenate(XX,axis = 0),axis = 0).reshape(-1,300)
        data=XX
        prediction = model.predict(data)
        return render_template('index.html', predict = prediction)

if __name__ == '__main__':
    app.run(debug=True)


# Warning... INSTALL NUMPY FIRST

###########################
'''
# direct run
from function.framework.reloadWrapper import reloadWrapper as reload
from function.rigging.skin.nsSkinClusterIO import nsSkinClusterIO_reFunc as skinIO
reload(skinIO)
skinIO.saveSkin()
skinIO.loadSkin()
'''

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

#Title: skinClusterIO.py
#Author: Noah Schnapp
#LinkedIn: https://www.linkedin.com/in/wnschnapp/
#22.11.Nov.17.Thu.15_nsSkinClusterIO\version

from function.framework.reloadWrapper import reloadWrapper as reload

###########################
####### IMPORTS ###########
###########################

import maya.cmds as cmds
import maya.mel as mel
import pymel.core as pm
import maya.api.OpenMaya as OpenMayaAPI
import maya.api.OpenMayaAnim as OpenMayaAnimAPI
import maya.OpenMayaAnim as OpenMayaAnim
import maya.OpenMaya as OpenMaya
import os
import imp
import numpy as np
import json
import time

####################################################################
#...CODE START

from function.pipeline import logger
reload(logger)

class SkinIOLogger(logger.MayaLogger):
	LOGGER_NAME = "SkinIO"

SkinIOLogger.info('Run function at:{0}'.format(__name__))

class SkinClusterIO(object):

	def __init__(self):

		#...class init
		self.cDataIO 			    = DataIO()

		#...vars
		self.name                   = ''
		self.type                  	= 'skinCluster'
		self.weightsNonZero_Array   = []
		self.weights_Array          = []
		self.infMap_Array 			= []
		self.vertSplit_Array        = []
		self.inf_Array              = []
		self.skinningMethod         = 1
		self.normalizeWeights       = 1
		self.geometry               = None
		self.blendWeights           = []
		self.vtxCount               = 0
		self.envelope        		= 1
		self.skinningMethod         = 1
		self.useComponents         	= 0
		self.normalizeWeights       = 1
		self.deformUserNormals      = 1

		pass

	def get_mesh_components_from_tag_expression(self, skinPy, tag='*'):
		geo_types = ["mesh", "nurbsSurface", "nurbsCurve"]
		for t in geo_types:
			obj = skinPy.listConnections(et=True, t=t)
			if obj:
				geo = obj[0].getShape().name()
		# Get attr out of shape
		attr_out = cmds.deformableShape(geo, localShapeOutAttr=True)[0]
		# Get the output geometry data as MObject
		sel = OpenMaya.MSelectionList()
		sel.add(geo)
		dep = OpenMaya.MObject()
		sel.getDependNode(0, dep)
		fn_dep = OpenMaya.MFnDependencyNode(dep)
		plug = fn_dep.findPlug(attr_out, True)
		obj = plug.asMObject()
		# Use the MFnGeometryData class to query the components for a tag expression
		fn_geodata = OpenMaya.MFnGeometryData(obj)
		# Components MObject
		components = fn_geodata.resolveComponentTagExpression(tag)
		dagPath = OpenMaya.MDagPath.getAPathTo(dep)
		return dagPath, components

	def get_data(self, skinCluster):

		#...get PyNode skinCluster
		skinPy = pm.PyNode(skinCluster)

		#...Pre Maya 2022 or new compoent tag expression
		try:
			fnSet = OpenMaya.MFnSet(skinPy.__apimfn__().deformerSet())
			members = OpenMaya.MSelectionList()
			fnSet.getMembers(members, False)
			dagPath = OpenMaya.MDagPath()
			components = OpenMaya.MObject()
			members.getDagPath(0, dagPath, components)
		except:
			dagPath, components = self.get_mesh_components_from_tag_expression(skinPy)

		#...get mesh
		geometry = cmds.skinCluster(skinCluster, query = True, geometry = True)[0]

		#...get vtxID_Array
		vtxID_Array = list(range(0,len(cmds.ls('%s.vtx[*]'%geometry,fl=1))))

		#...get skin
		selList = OpenMayaAPI.MSelectionList()
		selList.add(mel.eval('findRelatedSkinCluster %s' % geometry))
		skinPath = selList.getDependNode(0)

		#...get mesh
		selList = OpenMayaAPI.MSelectionList()
		selList.add(geometry)
		meshPath = selList.getDagPath(0)

		#...get vtxs
		fnSkinCluster = OpenMayaAnimAPI.MFnSkinCluster(skinPath);
		fnVtxComp = OpenMayaAPI.MFnSingleIndexedComponent()
		vtxComponents = fnVtxComp.create( OpenMayaAPI.MFn.kMeshVertComponent )
		fnVtxComp.addElements(vtxID_Array);

		#...get weights/infs
		dWeights, infCount = fnSkinCluster.getWeights(meshPath, vtxComponents)
		# weights_Array = np.array(list(dWeights), dtype='float64')
		#... change this due to warning
		weights_Array = np.array(list(dWeights), dtype='object')
		inf_Array = [dp.partialPathName() for dp in fnSkinCluster.influenceObjects()]

		#...convert to weightsNonZero_Array
		weightsNonZero_Array, infMap_Array, vertSplit_Array = self.compress_weightData(weights_Array, infCount)

		#...gatherBlendWeights
		blendWeights_mArray = OpenMaya.MDoubleArray()
		skinPy.__apimfn__().getBlendWeights(dagPath, components, blendWeights_mArray)

		#...set data to self vars
		self.name 					= skinCluster
		self.weightsNonZero_Array   = np.array(weightsNonZero_Array)
		self.infMap_Array       	= np.array(infMap_Array)
		self.vertSplit_Array       	= np.array(vertSplit_Array)
		self.inf_Array              = np.array(inf_Array)
		self.geometry               = geometry
		self.blendWeights           = np.array(blendWeights_mArray)
		self.vtxCount               = len(vertSplit_Array)-1

		#...get attrs
		self.envelope         		= cmds.getAttr(skinCluster + ".envelope")
		self.skinningMethod         = cmds.getAttr(skinCluster + ".skinningMethod")
		self.useComponents       	= cmds.getAttr(skinCluster + ".useComponents")
		self.normalizeWeights       = cmds.getAttr(skinCluster + ".normalizeWeights")
		self.deformUserNormals      = cmds.getAttr(skinCluster + ".deformUserNormals")

		return True

	def set_data(self, skinCluster):

		#...get PyNode skinCluster
		skinPy = pm.PyNode(skinCluster)

		#...Pre Maya 2022 or new compoent tag expression
		try:
			fnSet = OpenMaya.MFnSet(skinPy.__apimfn__().deformerSet())
			members = OpenMaya.MSelectionList()
			fnSet.getMembers(members, False)
			dagPath = OpenMaya.MDagPath()
			components = OpenMaya.MObject()
			members.getDagPath(0, dagPath, components)
		except:
			dagPath, components = self.get_mesh_components_from_tag_expression(skinPy)

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

		#...set infs
		influencePaths = OpenMaya.MDagPathArray()
		infCount = skinPy.__apimfn__().influenceObjects(influencePaths)
		influences_Array = [influencePaths[i].partialPathName() for i in range(influencePaths.length())]

		#...change the order in set(i,i)
		influenceIndices = OpenMaya.MIntArray(infCount)
		[influenceIndices.set(i,i) for i in range(infCount)]

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

		#...construct mArrays from normal/numpy arrays
		infCount = len(influences_Array)
		weightCounter = 0
		weights_Array = []
		weights_mArray = OpenMaya.MDoubleArray()
		length = len(self.vertSplit_Array)
		for vtxId, splitStart in enumerate(self.vertSplit_Array):
			if vtxId < length-1:
				vertChunk_Array = [0]*infCount
				splitEnd = self.vertSplit_Array[vtxId+1]

				#...unpack data and replace zeros with nonzero weight vals
				for i in range(splitStart, splitEnd):
					infMap = self.infMap_Array[i]
					val = self.weightsNonZero_Array[i]
					vertChunk_Array[infMap] = val

				#...append to raw data array
				for vert in vertChunk_Array:
					weights_mArray.append(vert)

		blendWeights_mArray = OpenMaya.MDoubleArray()
		for i in self.blendWeights:
			blendWeights_mArray.append(i)

		###################################################
		#...set data
		# skinPy.setWeights(dagPath, components, influenceIndices, weights_mArray, False)
		SkinIOLogger.info('ERROR HERE WHY')
		skinPy.__apimfn__().setWeights(dagPath, components, influenceIndices, weights_mArray, False)
		SkinIOLogger.info('ERROR HERE WHY')
		#skinPy.setBlendWeights(dagPath, components, blendWeights_mArray)
		skinPy.__apimfn__().setBlendWeights(dagPath, components, blendWeights_mArray)
		###################################################
		#...set attrs of skinCluster
		cmds.setAttr('%s.envelope'%skinCluster, self.envelope)
		cmds.setAttr('%s.skinningMethod'%skinCluster, self.skinningMethod)
		cmds.setAttr('%s.useComponents'%skinCluster, self.useComponents)
		cmds.setAttr('%s.normalizeWeights'%skinCluster, self.normalizeWeights)
		cmds.setAttr('%s.deformUserNormals'%skinCluster, self.deformUserNormals)

		#...name
		cmds.rename(skinCluster, self.name)

		return True
	'''
	def save(self, node=None, dirpath=None):

		#...get selection
		if node == None:
			node = cmds.ls(sl=1)
			if node == None:
				print('ERROR: Select Something!')
				return False
			else:
				node = node[0]

		#...get skinCluster
		skinCluster = mel.eval('findRelatedSkinCluster ' + node)
		if not cmds.objExists(skinCluster):
			print('ERROR: Node has no skinCluster!')
			return False

		#...get dirpath
		if dirpath == None:
			startDir = cmds.workspace(q=True, rootDirectory=True)
			dirpath = cmds.fileDialog2(caption='Save Skinweights', dialogStyle=2, fileMode=3, startingDirectory=startDir, fileFilter='*.npy', okCaption = "Select")

		#...get filepath
		skinCluster = 'skinCluster_%s'%node
		filepath = '%s/%s.npy'%(dirpath, skinCluster)

		#...timeStart
		timeStart = time.time()

		#...get data
		self.get_data(skinCluster)

		#...timeEnd
		timeEnd = time.time()
		timeElapsed = timeEnd-timeStart

		#...print time
		print('GetData Elapsed: %s'%timeElapsed)

		#...construct data_array
		legend =            (   'legend',
								'weightsNonZero_Array',
								'vertSplit_Array',
								'infMap_Array',

								'inf_Array',
								'geometry',
								'blendWeights',
								'vtxCount',

								'name',
								'envelope',
								'skinningMethod',
								'useComponents',

								'normalizeWeights',
								'deformUserNormals',

								'type',
								)

		data =              [   legend,
								self.weightsNonZero_Array,
								self.vertSplit_Array,
								self.infMap_Array,

								self.inf_Array,
								self.geometry,
								self.blendWeights,
								self.vtxCount,

								self.name,
								self.envelope,
								self.skinningMethod,
								self.useComponents,

								self.normalizeWeights,
								self.deformUserNormals,

								self.type,
								]

		#...timeStart
		timeStart = time.time()

		#...write data
		np.save(filepath, data)

		#...timeEnd
		timeEnd = time.time()
		timeElapsed = timeEnd-timeStart

		#...print time
		print('SaveData Elapsed: %s'%timeElapsed)

		return True

	def load(self, node=None, dirpath=None):

		#...get selection
		if node == None:
			node = cmds.ls(sl=1)
			if node == None:
				print('ERROR: Select Something!')
				return False
			else:
				node = node[0]

		#...get dirpath
		if dirpath == None:
			startDir = cmds.workspace(q=True, rootDirectory=True)
			dirpath = cmds.fileDialog2(caption='Load Skinweights', dialogStyle=2, fileMode=1, startingDirectory=startDir, fileFilter='*.npy', okCaption = "Select")

		#...get filepath
		skinCluster = 'skinCluster_%s'%node
		filepath = '%s/%s.npy'%(dirpath, skinCluster)

		#...check if skinCluster exists
		if not os.path.exists(filepath):
			print('ERROR: SkinCluster for node "%s" not found on disk!'%node)
			return False

		#...unbind current skinCluster
		skinCluster = mel.eval('findRelatedSkinCluster ' + node)
		if cmds.objExists(skinCluster):
			mel.eval('skinCluster -e  -ub ' + skinCluster)

		#...timeStart
		timeStart = time.time()

		#...read data
		data = np.load(filepath, allow_pickle=True)

		#...timeEnd
		timeEnd = time.time()
		timeElapsed = timeEnd-timeStart

		#...print time
		print('ReadData Elapsed: %s'%timeElapsed)

		#...get item data from numpy array
		self.legend_Array = self.cDataIO.get_legendArrayFromData(data)
		self.weightsNonZero_Array = self.cDataIO.get_dataItem(data, 'weightsNonZero_Array', self.legend_Array)
		self.infMap_Array = self.cDataIO.get_dataItem(data, 'infMap_Array', self.legend_Array)
		self.vertSplit_Array = self.cDataIO.get_dataItem(data, 'vertSplit_Array', self.legend_Array)
		self.inf_Array = self.cDataIO.get_dataItem(data, 'inf_Array', self.legend_Array)
		self.blendWeights = self.cDataIO.get_dataItem(data, 'blendWeights', self.legend_Array)
		self.vtxCount = self.cDataIO.get_dataItem(data, 'vtxCount', self.legend_Array)
		self.geometry = self.cDataIO.get_dataItem(data, 'geometry', self.legend_Array)
		self.name = self.cDataIO.get_dataItem(data, 'name', self.legend_Array)
		self.envelope = self.cDataIO.get_dataItem(data, 'envelope', self.legend_Array)
		self.skinningMethod = self.cDataIO.get_dataItem(data, 'skinningMethod', self.legend_Array)
		self.useComponents = self.cDataIO.get_dataItem(data, 'useComponents', self.legend_Array)
		self.normalizeWeights = self.cDataIO.get_dataItem(data, 'normalizeWeights', self.legend_Array)
		self.deformUserNormals = self.cDataIO.get_dataItem(data, 'deformUserNormals', self.legend_Array)

		#...bind skin
		for inf in self.inf_Array:
			if not cmds.objExists(inf):
				cmds.select(cl=True)
				cmds.joint(n=inf)
		skinCluster = 'skinCluster_%s'%node
		skinCluster = cmds.skinCluster(self.inf_Array, node, n=skinCluster, tsb = True)[0]

		#...timeStart
		timeStart = time.time()

		#...set data
		self.set_data(skinCluster)

		#...timeEnd
		timeEnd = time.time()
		timeElapsed = timeEnd-timeStart

		#...print time
		print('SetData Elapsed: %s'%timeElapsed)

		return True
	'''
	###################################
	def compress_weightData(self, weights_Array, infCount):

		#...convert to weightsNonZero_Array
		weightsNonZero_Array = []
		infCounter = 0
		infMap_Chunk = []
		infMap_ChunkCount = 0
		vertSplit_Array = [infMap_ChunkCount]
		infMap_Array = []

		for w in weights_Array:
			if w != 0.0:
				weightsNonZero_Array.append(w)
				infMap_Chunk.append(infCounter)

			#...update inf counter
			infCounter += 1
			if infCounter ==  infCount:
				infCounter = 0

				#...update vertSplit_Array
				infMap_Array.extend(infMap_Chunk)
				infMap_ChunkCount = len(infMap_Chunk)+infMap_ChunkCount
				vertSplit_Array.append(infMap_ChunkCount)
				infMap_Chunk = []

		return weightsNonZero_Array, infMap_Array, vertSplit_Array

class DataIO(object):

	def __init__(self):

		pass

	@staticmethod
	def get_legendArrayFromData(data):

		return data[0]

	@staticmethod
	def get_dataItem(data, item, legend_Array=None):
		if item not in data[0]:
			print('ERROR: "%s" Not Found in data!'%item)
			return False
		#...no legend_Array
		if legend_Array is None:
			legend_Array = [key for key in data[0]]
		#...with legend_Array
		return data[legend_Array.index(item)]

	@staticmethod
	def set_dataItems(data, itemData_Array):

		return data

from function.pipeline import fileTools as fileTools
reload (fileTools)

def _processPath():
	dataRaw = fileTools.getAssetData()
	mayaFileName = dataRaw[0][-1]

	if 'hero' in mayaFileName:
		print ('This is hero file')
		filePath  = fileTools.desinatePath('\\RIG\\data\\skinData')

	else:
		print ('This is version file')
		filePath  = fileTools.desinatePath('\\data\\skinData')

	fileTools.currentFilePath(filePath)
	return filePath

def saveSkin():
	node = cmds.ls(sl=True)[0]

	dirpath = _processPath()
	if dirpath == None:
		print('ERROR: There are no path.')
		return	False
	######################################
	## run line by line
	## SAVE
	####################################

	# run line by line

	#... get selection
	if node == None:
		print('ERROR: Please select something!.')
		return	False

	#... get skinCluster
	skinCluster_name = mel.eval('findRelatedSkinCluster ' + node)
	if not cmds.objExists(skinCluster_name):
		print('ERROR: Node has no skinCluster!!')
		#return False

	#... get file path
	skinCluster = '%s_skinCluster' %node
	filepath = '%s/%s.npy'%(dirpath, skinCluster)

	#... time start
	timeStart = time.time()

	SkinClusterExport = SkinClusterIO()
	SkinClusterExport.get_data(skinCluster_name)

	#... time end
	timeEnd = time.time()
	timeElapsed = timeEnd-timeStart
	print('Get data Elapsed: %s'%timeElapsed)

	#...construct data_array
	legend =            (   'legend',
							'weightsNonZero_Array',
							'vertSplit_Array',
							'infMap_Array',

							'inf_Array',
							'geometry',
							'blendWeights',
							'vtxCount',

							'name',
							'envelope',
							'skinningMethod',
							'useComponents',

							'normalizeWeights',
							'deformUserNormals',

							'type',
							)

	data =              [   legend,
							SkinClusterExport.weightsNonZero_Array,
							SkinClusterExport.vertSplit_Array,
							SkinClusterExport.infMap_Array,

							SkinClusterExport.inf_Array,
							SkinClusterExport.geometry,
							SkinClusterExport.blendWeights,
							SkinClusterExport.vtxCount,

							SkinClusterExport.name,
							SkinClusterExport.envelope,
							SkinClusterExport.skinningMethod,
							SkinClusterExport.useComponents,

							SkinClusterExport.normalizeWeights,
							SkinClusterExport.deformUserNormals,

							SkinClusterExport.type,
							]

	timeStart = time.time()

	np.save(filepath, data)
	cmds.select(deselect=True)
	timeEnd = time.time()
	timeElapsed = timeEnd-timeStart
	print('Saved data Elapsed: %s'%timeElapsed)

	print('File save at: ---> %s' %filepath)

	return True

def loadSkin():

	######################################
	## run line by line
	## LOAD
	####################################

	dirpath = _processPath()
	if dirpath == None:
		print('ERROR: There are no path.')
		return	False

	node = cmds.ls(sl=True)[0]

	#... get selection
	if node == None:
		print('ERROR: Please select something!.')
		return	False
	print('selected name is ---> %s' %node)

	#...get filepath
	skinCluster_name = '%s_skinCluster'%node
	filepath = '%s/%s.npy'%(dirpath, skinCluster_name)

	filepath = os.path.normpath(filepath)

	#...check if skinCluster exists
	if not os.path.exists(filepath):
		print('ERROR: SkinCluster for node "%s" not found on disk!'%node)
		#return False

	#...unbind current skinCluster
	skinCluster = mel.eval('findRelatedSkinCluster ' + node)
	if cmds.objExists(skinCluster):
		mel.eval('skinCluster -e  -ub ' + skinCluster)

	#...timeStart
	timeStart = time.time()

	SkinIOLogger.info('This is filepath: {0}'.format(filepath))

	#...read data
	data = np.load(filepath, allow_pickle=True)

	SkinClusterImport = SkinClusterIO()

	#...get item data from numpy array
	legend_Array = SkinClusterImport.cDataIO.get_legendArrayFromData(data)
	weightsNonZero_Array = SkinClusterImport.cDataIO.get_dataItem(data, 'weightsNonZero_Array',legend_Array)
	infMap_Array = SkinClusterImport.cDataIO.get_dataItem(data, 'infMap_Array',legend_Array)
	vertSplit_Array = SkinClusterImport.cDataIO.get_dataItem(data, 'vertSplit_Array',legend_Array)
	inf_Array = SkinClusterImport.cDataIO.get_dataItem(data, 'inf_Array',legend_Array)
	blendWeights = SkinClusterImport.cDataIO.get_dataItem(data, 'blendWeights',legend_Array)
	vtxCount = SkinClusterImport.cDataIO.get_dataItem(data, 'vtxCount',legend_Array)
	geometry = SkinClusterImport.cDataIO.get_dataItem(data, 'geometry',legend_Array)
	name = SkinClusterImport.cDataIO.get_dataItem(data, 'name',legend_Array)
	envelope = SkinClusterImport.cDataIO.get_dataItem(data, 'envelope',legend_Array)
	skinningMethod = SkinClusterImport.cDataIO.get_dataItem(data, 'skinningMethod',legend_Array)
	useComponents = SkinClusterImport.cDataIO.get_dataItem(data, 'useComponents',legend_Array)
	normalizeWeights = SkinClusterImport.cDataIO.get_dataItem(data, 'normalizeWeights',legend_Array)
	deformUserNormals = SkinClusterImport.cDataIO.get_dataItem(data, 'deformUserNormals',legend_Array)

	#...bind skin
	for inf in inf_Array:
		if not cmds.objExists(inf):
			cmds.select(cl=True)
			cmds.joint(n=inf)
	skinCluster = '%s_skinCluster'%node
	skinCluster_name = cmds.skinCluster(inf_Array, node, n=skinCluster, tsb = True)[0]

	#...timeStart
	timeStart = time.time()

	#...set data
	# not work breakdown the code
	#SkinClusterImport.set_data(skinCluster)

	#...timeEnd

	#...get PyNode skinCluster

	#skinCluster_name = skinCluster
	skinPy = pm.PyNode(skinCluster_name)

	#...Pre Maya 2022 or new compoent tag expression
	try:
		fnSet = OpenMaya.MFnSet(skinPy.__apimfn__().deformerSet())
		members = OpenMaya.MSelectionList()
		fnSet.getMembers(members, False)
		dagPath = OpenMaya.MDagPath()
		components = OpenMaya.MObject()
		members.getDagPath(0, dagPath, components)
	except:
		dagPath, components = SkinClusterImport.get_mesh_components_from_tag_expression(skinPy)

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

	#...set infs
	influencePaths = OpenMaya.MDagPathArray()
	infCount = skinPy.__apimfn__().influenceObjects(influencePaths)
	influences_Array = [influencePaths[i].partialPathName() for i in range(influencePaths.length())]

	#...change the order in set(i,i)
	influenceIndices = OpenMaya.MIntArray(infCount)
	[influenceIndices.set(i,i) for i in range(infCount)]

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

	#...construct mArrays from normal/numpy arrays
	infCount = len(influences_Array)
	weightCounter = 0
	weights_Array = []
	weights_mArray = OpenMaya.MDoubleArray()
	length = len(vertSplit_Array)
	for vtxId, splitStart in enumerate(vertSplit_Array):
		if vtxId < length-1:
			vertChunk_Array = [0]*infCount
			splitEnd = vertSplit_Array[vtxId+1]

			#...unpack data and replace zeros with nonzero weight vals
			for i in range(splitStart, splitEnd):
				infMap = infMap_Array[i]
				val = weightsNonZero_Array[i]
				vertChunk_Array[infMap] = val

			#...append to raw data array
			for vert in vertChunk_Array:
				weights_mArray.append(vert)

	blendWeights_mArray = OpenMaya.MDoubleArray()
	for i in blendWeights:
		blendWeights_mArray.append(i)

	###################################################
	#...set data
	# skinPy.setWeights(dagPath, components, influenceIndices, weights_mArray, False)

	skinPy.__apimfn__().setWeights(dagPath, components, influenceIndices, weights_mArray, False)
	#skinPy.setBlendWeights(dagPath, components, blendWeights_mArray)
	skinPy.__apimfn__().setBlendWeights(dagPath, components, blendWeights_mArray)
	###################################################
	#...set attrs of skinCluster
	cmds.setAttr('%s.envelope'%skinCluster_name, envelope)
	cmds.setAttr('%s.skinningMethod'%skinCluster_name, skinningMethod)
	cmds.setAttr('%s.useComponents'%skinCluster_name, useComponents)
	cmds.setAttr('%s.normalizeWeights'%skinCluster_name, normalizeWeights)
	cmds.setAttr('%s.deformUserNormals'%skinCluster_name, deformUserNormals)

	#...timeEnd
	timeEnd = time.time()
	timeElapsed = timeEnd-timeStart
	#...print time
	print('ReadData Elapsed: %s'%timeElapsed)

	#...name
	cmds.rename(skinCluster_name, name)
	print('File load at: ---> %s' %filepath)

	cmds.select(deselect=True)
	return True

'''
saveSkin()
loadSkin()
'''


from __future__ import absolute_import, print_function

from invenio_indexer.signals import before_record_index

from . import config
from .cli import openaire
from .indexer import indexer_receiver

class InvenioOpenAIRE(object):
    """Invenio-OpenAIRE extension."""

    def __init__(self, app=None):
        """Extension initialization."""
        if app:
            self.init_app(app)

    def init_app(self, app):
        """Flask application initialization."""
        self.init_config(app)
        app.cli.add_command(openaire)
        before_record_index.connect(indexer_receiver, sender=app)
        app.extensions['invenio-openaire'] = self

    def init_config(self, app):
        """Initialize configuration."""
        for k in dir(config):
            if k.startswith("OPENAIRE_"):
                app.config.setdefault(k, getattr(config, k))


# -*- coding: utf-8 -*-
# Imports: {{{
import codecs
import glob
import logging
import os
import sys
from platform import system

logging.basicConfig()
logger = logging.getLogger(name=__name__)

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

    find_packages = None
else:
    from setuptools import find_packages

try:
    import sphinx
except ImportError:
    cmd_class = {}
else:
    from sphinx.setup_command import BuildDoc

    cmd_class = {"build_sphinx": BuildDoc}

if find_packages is not None:
    packages = find_packages()
else:
    packages=["src/winreadline"]

# for distutils setuptools compatabilit.
# TODO: add zip_safe=False
# }}}

# Metadata: {{{
# Name of the package for release purposes.  This is the name which labels
# the tarballs and RPMs made by distutils, so it's best to lowercase it.
name = "pyreadline"

version = "2.1"

description = "A python implmementation of GNU readline."

ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
README = os.path.join(ROOT_PATH, "", "README.rst")

with codecs.open(README, encoding="utf-8") as f:
    long_description = "\n" + f.read()

keywords = ["readline", "windows", "pyreadline"]
platforms = ["Windows XP/2000/NT", "Windows 95/98/ME", "Windows 10"]
# url = "https://github.com/pyreadline/pyreadline"
# download_url = "https://pypi.python.org/pypi/pyreadline"

license = "BSD"

authors = {
    # "Jorgen": ("Jorgen Stenarson", "jorgen.stenarson@kroywen.se"),
    # "Gary": ("Gary Bishop", ""),
    # "Jack": ("Jack Trainor", ""),
}

setup(
    name=name,
    version=version,
    description=description,
    long_description=long_description,
        long_description_content_type="text/restructuredtext",
        # python_requires=REQUIRES_PYTHON,
    # author=authors["Jorgen"][0],
    # author_email=authors["Jorgen"][1],
    # maintainer=authors["Jorgen"][0],
    # maintainer_email=authors["Jorgen"][1],
    src_root="src/winreadline",
    license=license,
    # where did we define classifiers?
    classifiers=[
        # Trove classifiers
        # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
        "Development Status :: 5 - Production/Stable",
        "Environment :: Console",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: BSD License",
        "Natural Language :: English",
        "Operating System :: Microsoft :: Windows",
        "Operating System :: Microsoft :: Windows :: Windows 10",
        "Programming Language :: Python",
        # "Programming Language :: Python :: 2",
        # "Programming Language :: Python :: 2.6",
        # "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.2",
        "Programming Language :: Python :: 3.3",
        "Programming Language :: Python :: 3.4",
        "Programming Language :: Python :: 3.5",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: Implementation :: CPython",
    ],
    # url=url,
    # check that this exists in setuptools
    # download_url=download_url,
    platforms=platforms,
    keywords=keywords,
    packages=packages,
    py_modules=["readline"],
    data_files=[("docs", glob.glob("docs/*")),],
    cmdclass=cmd_class,
    python_requires=">=2.6.0",
    requires=["setuptools", "pywin32", "wheel"],
    # whats the difference between requires, install_requires, setup_requires again?
    # i dont even understand what error this raised but let's leave this
    # commented out
    # setup_requires=["pkg_resources", "pipenv"],
    # They actually really don't but hey
    # install_requires=REQUIRED,
    tests_require=["pytest"],
        test_suite="test",
        include_package_data=True,
    extras_require={"docs": ["sphinx"], "test": ["pytest"]},
        package_data={
            # If any package contains *.txt or *.rst files, include them:
            "": ["*.txt", "*.rst"],
        },
        # # $ setup.py publish support.
        # cmdclass={"upload": UploadCommand},
        # # project home page, if any
        # project_urls={
        #     "Bug Tracker": "https://www.github.com/farisachugthai/dynamic_ipython/issues",
        #     "Documentation": "https://farisachugthai.github.io/dynamic_ipython",
        #     "Source Code": "https://www.github.com/farisachugthai/dynamic_ipython",
        # }
)

# Vim: set fdm=marker:


import os
import torch as th
import numpy as np
from .. import configs
import torch
import torch.nn as nn
from torch.autograd import Variable
from .miner import miner
import functools as ft
import itertools as it
import pytest
import torch.nn.functional as F
import rich
c = rich.get_console()

def fn_ptriplet_kernel(repA: th.Tensor, repP: th.Tensor, repN: th.Tensor,
                       *, metric: str, margin: float):
    '''
    <functional> the core computation for spc-2 triplet loss.
    '''
    if metric == 'C':
        dap = 1 - F.cosine_similarity(repA, repP, dim=-1)
        dan = 1 - F.cosine_similarity(repA, repN, dim=-1)
        loss = (dap - dan + margin).relu().mean()
    elif metric in ('E', 'N'):
        #dap = F.pairwise_distance(repA, repP, p=2)
        #dan = F.pairwise_distance(repA, repN, p=2)
        #loss = (dap - dan + margin).relu().mean()
        loss = F.triplet_margin_loss(repA, repP, repN, margin=margin)
    else:
        raise ValueError(f'Illegal metric type {metric}!')
    return loss

def fn_ptriplet(repres: th.Tensor, labels: th.Tensor,
                *, metric: str, minermethod: str, p_switch: float = -1.0, xa: bool = False):
    '''
    Variant of triplet loss that accetps [cls=1,cls=1,cls=2,cls=2] batch.
    This corresponds to the SPC-2 setting in the ICML20 paper.

    metrics: C = cosine, E = euclidean, N = normalization + euclidean
    '''
    # Determine the margin for the specific metric
    if metric in ('C', 'N'):
        margin = configs.triplet.margin_cosine
        repres = F.normalize(repres, p=2, dim=-1)
    elif metric in ('E',):
        margin = configs.triplet.margin_euclidean
    # Sample the triplets
    anc, pos, neg = miner(repres, labels, method=minermethod,
                          metric=metric, margin=margin, p_switch=p_switch)
    if xa:
        return fn_ptriplet_kernel(repres[anc, :].detach(), repres[pos, :],
                                  repres[neg, :], metric=metric, margin=margin)
    loss = fn_ptriplet_kernel(repres[anc, :], repres[pos, :], repres[neg, :],
                              metric=metric, margin=margin)
    return loss

class ptriplet(th.nn.Module):
    _datasetspec = 'SPC-2'
    _minermethod = 'spc2-random'
    _xa = False

    def __call__(self, *args, **kwargs):
        if hasattr(self, '_minermethod'):
            return ft.partial(fn_ptriplet,
                              metric=self._metric,
                              minermethod=self._minermethod,
                              xa=self._xa)(*args, **kwargs)
        else:
            return ft.partial(fn_ptriplet,
                              metric=self._metric,
                              xa=self._xa)(*args, **kwargs)

    def determine_metric(self):
        return self._metric

    def datasetspec(self):
        return self._datasetspec

    def raw(self, repA, repP, repN, *, override_margin: float = None):
        if self._metric in ('C', 'N'):
            margin = configs.triplet.margin_cosine
        elif self._metric in ('E',):
            margin = configs.triplet.margin_euclidean
        if override_margin is not None:
            margin = override_margin
        loss = fn_ptriplet_kernel(repA, repP, repN,
                                  metric=self._metric, margin=margin)
        return loss

class ptripletC(ptriplet):
    _metric = 'C'

class ptripletE(ptriplet):
    _metric = 'E'

class ptripletN(ptriplet):
    _metric = 'N'

class ptripxaN(ptriplet):
    _metric = 'N'
    _xa = True

class pmtriplet(ptriplet):
    _minermethod = 'spc2-semihard'

class pmtripletC(pmtriplet):
    _metric = 'C'

class pmtripletE(pmtriplet):
    _metric = 'E'

class pmtripletN(pmtriplet):
    _metric = 'N'

class phtriplet(ptriplet):
    _minermethod = 'spc2-hard'

class phtripletC(phtriplet):
    _metric = 'C'

class phtripletE(phtriplet):
    _metric = 'E'

class phtripletN(phtriplet):
    _metric = 'N'

class pstriplet(ptriplet):
    _minermethod = 'spc2-softhard'

class pstripletC(pstriplet):
    _metric = 'C'

class pstripletE(pstriplet):
    _metric = 'E'

class pstripletN(pstriplet):
    _metric = 'N'

class pdtriplet(ptriplet):
    _minermethod = 'spc2-distance'

class pdtripletC(pdtriplet):
    _metric = 'C'

class pdtripletE(pdtriplet):
    _metric = 'E'

class pdtripletN(pdtriplet):
    _metric = 'N'

class pDtripletN(pdtripletN):
    def __call__(self, *args, **kwargs):
        return ft.partial(fn_ptriplet, metric=self._metric,
                          minermethod=self._minermethod,
                          p_switch=0.15)(*args, **kwargs)

@pytest.mark.parametrize('metric, minermethod', it.product(('C', 'E', 'N'),
                                                           ('spc2-random', 'spc2-distance', 'spc2-hard', 'spc2-softhard', 'spc2-semihard')))
def test_fn_ptriplet(metric: str, minermethod: str):
    output, labels = th.rand(10, 32, requires_grad=True), th.randint(3, (10,))
    loss = fn_ptriplet(output, labels, metric=metric, minermethod=minermethod)
    loss.backward()

@pytest.mark.parametrize('func', (ptripletN, ptripletE, ptripletC,
                                  pmtripletN, pmtripletE, pmtripletC,
                                  pstripletN, pstripletE, pstripletC,
                                  pdtripletN, pdtripletE, pdtripletC,
                                  phtripletN, phtripletE, phtripletC))
def test_ptriplet(func: object):
    output, labels = th.rand(10, 32, requires_grad=True), th.randint(3, (10,))
    loss = func()(output, labels)
    loss.backward()


from typing import Any, List, Optional, Tuple, TypedDict, Union

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser
from django.contrib.contenttypes.models import ContentType
from django.db.models import Case, IntegerField, Q, QuerySet, Value, When

from baserow.core.handler import CoreHandler
from baserow.core.mixins import TrashableModelMixin
from baserow.core.models import Group, GroupUser
from baserow.core.object_scopes import CoreObjectScopeType
from baserow.core.registries import object_scope_type_registry, subject_type_registry
from baserow.core.types import ScopeObject, Subject
from baserow_enterprise.exceptions import ScopeNotExist, SubjectNotExist
from baserow_enterprise.models import RoleAssignment
from baserow_enterprise.role.models import Role
from baserow_enterprise.signals import (
    role_assignment_created,
    role_assignment_deleted,
    role_assignment_updated,
)
from baserow_enterprise.teams.models import Team, TeamSubject

from .constants import (
    NO_ACCESS_ROLE_UID,
    NO_ROLE_LOW_PRIORITY_ROLE_UID,
    ROLE_ASSIGNABLE_OBJECT_MAP,
    SUBJECT_PRIORITY,
)

User = get_user_model()

class RoleAssignmentDict(TypedDict):
    scope: Any
    scope_id: int
    scope_type: ContentType
    subject: Any
    subject_id: int
    subject_type: ContentType
    role: Role

class RoleAssignmentHandler:
    def _get_role_assignments_for_valid_subjects_qs(self) -> QuerySet:
        """
        Constructs base queryset for role_assignments in order to filter out any
        role assignment related to a trashed subject.
        """

        id_filters = Q()
        for subject_type in subject_type_registry.get_all():
            if issubclass(subject_type.model_class, TrashableModelMixin):
                id_filters.add(
                    ~Q(
                        subject_id__in=subject_type.model_class.trash.all().values_list(
                            "id", flat=True
                        ),
                        subject_type=subject_type.get_content_type(),
                    ),
                    Q.AND,
                )

        return RoleAssignment.objects.filter(id_filters)

    @classmethod
    def _get_role_caches(cls):
        if not getattr(cls, "_init", False):
            cls._init = True
            cls._role_cache_by_uid = {}
            cls._role_cache_by_id = {}
            for role in Role.objects.prefetch_related("operations").all():
                cls._role_cache_by_uid[role.uid] = role
                cls._role_cache_by_id[role.id] = role

        return cls._role_cache_by_uid, cls._role_cache_by_id

    def get_role_by_uid(self, role_uid: str, use_fallback=False) -> Role:
        """
        Returns the role for the given uid.
        If `role_uid` isn't found, we fall back to `role_fallback`.

        :param role_uid: The uid we want the role for.
        :param use_fallback: If true and the role_uid is not found, we fallback to
           the NO_ACCESS role.
        :return: A role.
        """

        # Translate MEMBER to builder for compatibility with basic "role" name.
        if role_uid == "MEMBER":
            role_uid = "BUILDER"

        try:
            if role_uid not in self._get_role_caches()[0]:
                raise Role.DoesNotExist()

            return self._get_role_caches()[0][role_uid]
        except Role.DoesNotExist:
            if use_fallback:
                return self.get_role_by_uid(NO_ACCESS_ROLE_UID)
            else:
                raise

    def get_role_by_id(self, role_id: int) -> Role:
        """
        Returns the role for the given id.

        :param role_id: The id we want the role for.
        :return: A role.
        """

        if role_id not in self._get_role_caches()[1]:
            raise Role.DoesNotExist()

        return self._get_role_caches()[1][role_id]

    def get_current_role_assignment(
        self,
        subject: Union[AbstractUser, Team],
        group: Group,
        scope: Optional[Any] = None,
    ) -> Union[RoleAssignment, None]:
        """
        Returns the current assigned role for the given Group/Subject/Scope.

        :param subject: The subject we want the role for.
        :param group: The group in which you want the user or team role.
        :param scope: An optional scope on which the role applies.
        :return: The current `RoleAssignment` or `None` if no role is defined for the
            given parameters.
        """

        if scope is None:
            scope = group

        content_types = ContentType.objects.get_for_models(scope, subject)

        # If we are assigning a role to a group, we store the role uid in
        # the `.permissions` property of the `GroupUser` object instead to stay
        # compatible with other permission manager. This make it easy to switch from
        # one permission manager to another without losing nor duplicating information
        if (
            isinstance(scope, Group)
            and scope.id == group.id
            and isinstance(subject, User)
        ):
            try:
                group_user = group.get_group_user(subject)
                role = self.get_role_by_uid(group_user.permissions, use_fallback=True)
                # We need to fake a RoleAssignment instance here to keep the same
                # return interface
                return RoleAssignment(
                    subject=subject,
                    subject_id=subject.id,
                    subject_type=content_types[subject],
                    role=role,
                    group=group,
                    scope=scope,
                    scope_type=content_types[scope],
                )
            except GroupUser.DoesNotExist:
                return None

        try:
            role_assignment = (
                self._get_role_assignments_for_valid_subjects_qs()
                .select_related("role")
                .get(
                    scope_id=scope.id,
                    scope_type=content_types[scope],
                    group=group,
                    subject_id=subject.id,
                    subject_type=content_types[subject],
                )
            )

            return role_assignment
        except RoleAssignment.DoesNotExist:
            return None

    def get_roles_per_scope(
        self, group: Group, actor: AbstractUser, include_trash=False
    ) -> List[Tuple[Any, List[Role]]]:
        """
        Returns the RoleAssignments for the given actor in the given group. The roles
        are ordered by position of the role scope in the object hierarchy: the highest
        parent is before the lowest.

        :param group: The group the RoleAssignments belong to.
        :param actor: The actor for whom we want the RoleAssignments for.
        :param include_trash: If true then also checks even if given group has been
            trashed instead of raising a DoesNotExist exception.
        :return: A list of tuple containing the scope and the role ordered by scope.
            The higher a scope is high in the object hierarchy, the higher the tuple in
            the list.
        """

        actor_subject_type = subject_type_registry.get_by_model(actor)

        content_types = ContentType.objects.get_for_models(
            *[s.model_class for s in subject_type_registry.get_all()], Group
        )

        subjects_q = Q(
            subject_type=content_types[actor_subject_type.model_class],
            subject_id=actor.id,
        )

        # Add team roles
        user_teams_qs = TeamSubject.objects.filter(
            subject_type=content_types[actor_subject_type.model_class],
            subject_id=actor.id,
        )

        user_teams_ids = user_teams_qs.values_list("team_id", flat=True)

        subjects_q |= Q(
            subject_type=content_types[Team],
            subject_id__in=user_teams_ids,
        )

        scope_cases = [
            When(
                scope_type=ContentType.objects.get_for_model(scope_type.model_class),
                then=Value(scope_type.level),
            )
            for scope_type in [
                object_scope_type_registry.get(name)
                for name in ROLE_ASSIGNABLE_OBJECT_MAP
            ]
            if scope_type.type != CoreObjectScopeType.type
        ]

        role_priority_cases = [
            When(
                subject_type=content_types[subject_type.model_class],
                then=Value(order),
            )
            for order, subject_type in enumerate(
                [
                    subject_type_registry.get(subject_name)
                    for subject_name in SUBJECT_PRIORITY
                ]
            )
        ]

        role_assignments = (
            RoleAssignment.objects.filter(
                group=group,
            )
            .filter(subjects_q, ~Q(role__uid=NO_ROLE_LOW_PRIORITY_ROLE_UID))
            .annotate(
                scope_type_order=Case(
                    *scope_cases,
                    default=Value(0),
                    output_field=IntegerField(),
                ),
                role_priority=Case(
                    *role_priority_cases,
                    default=Value(0),
                    output_field=IntegerField(),
                ),
            )
            .order_by(
                "scope_type_order", "scope_id", "role_priority", "subject_id", "id"
            )
            .select_related("subject_type")
        )

        group_scope_param = (group.id, content_types[Group].id)
        # we are using a tuple of (scope.id, content_type.id) to prevent the query
        # automatically done when accessing the property from the role assignments
        # to query them all at once later
        roles_by_scope = {group_scope_param: []}
        priorities_by_scope = {}

        for role_assignment in role_assignments:
            scope_param = (role_assignment.scope_id, role_assignment.scope_type_id)
            role = self.get_role_by_id(role_assignment.role_id)
            priority = role_assignment.role_priority

            # We don't use defaultdict here to be sure we have the right key order
            if scope_param not in roles_by_scope:
                roles_by_scope[scope_param] = []

            existing_priority = priorities_by_scope.setdefault(scope_param, priority)
            if priority < existing_priority:
                roles_by_scope[scope_param] = [role]
            elif existing_priority == priority:
                roles_by_scope[scope_param].append(role)

        # Get the group level role by reading the GroupUser permissions property for
        # User actors.
        if isinstance(actor, User):

            # We want to get the group user even if the group is trashed as we still
            # want to support checking permissions on a trashed scope (the group).
            group_user = group.get_group_user(actor, include_trash=include_trash)

            group_level_role = self.get_role_by_uid(
                group_user.permissions,
                use_fallback=True,
            )
            if group_level_role.uid == NO_ROLE_LOW_PRIORITY_ROLE_UID:
                # Low priority role -> Use team role or NO_ACCESS if no team role
                if not roles_by_scope.get(group_scope_param):
                    roles_by_scope[group_scope_param] = [
                        self.get_role_by_uid(NO_ACCESS_ROLE_UID)
                    ]
            else:
                # Otherwise user role wins
                roles_by_scope[group_scope_param] = [group_level_role]

        roles_by_scope = [
            (self.get_scope(*key), value) for (key, value) in roles_by_scope.items()
        ]

        return roles_by_scope

    def get_computed_roles(
        self, group: Group, actor: AbstractUser, context: Any, include_trash=False
    ) -> List[Role]:
        """
        Returns the computed roles for the given actor on the given context.

        :param group: The group in which we want the roles.
        :param actor: The actor for whom we want the roles.
        :param context: The context on which we want to now the role.
        :param include_trash: If true then also checks even if given group has been
            trashed instead of raising a DoesNotExist exception.
        :return: A list of roles that applies on this context.
        """

        roles_by_scopes = self.get_roles_per_scope(
            group, actor, include_trash=include_trash
        )
        most_precise_roles = [
            RoleAssignmentHandler().get_role_by_uid(NO_ACCESS_ROLE_UID)
        ]

        for (scope, roles) in roles_by_scopes:
            if object_scope_type_registry.scope_includes_context(scope, context):
                # Check if this scope includes the context. As the role assignments
                # are sorted, the new scope is more precise than the previous one.
                # So we keep this new role.
                most_precise_roles = roles
            elif object_scope_type_registry.scope_includes_context(
                context, scope
            ) and any([r.uid != NO_ACCESS_ROLE_UID for r in roles]):
                # Here the user has a permission on a scope that is a child of the
                # context, then we grant the user permission on all read operations
                # for all parents of that scope and hence this context should be
                # readable.
                # For example, if you have a "BUILDER" role on only a table scope,
                # and the context here is the parent database of this table the
                # user should be able to read this database object so they can
                # actually have access to lower down.
                most_precise_roles.append(self.get_role_by_uid("VIEWER"))

        return most_precise_roles

    def assign_role(
        self,
        subject,
        group,
        role=None,
        scope=None,
    ) -> Optional[RoleAssignment]:
        """
        Assign a role to a subject in the context of the given group over a specified
        scope.

        :param subject: The subject targeted by the role.
        :param group: The group in which we want to assign the role.
        :param role: The role we want to assign. If the role is `None` then we remove
            the current role of the subject in the group for the given scope.
        :param scope: An optional scope on which the role applies. If no scope is given
            the group is used as scope.
        :return: The created RoleAssignment if role is not `None` else `None`.
        """

        if scope is None:
            scope = group

        if role is None:
            # Early return, we are removing the current role.
            self.remove_role(subject, group, scope=scope)
            return

        subject_type = subject_type_registry.get_by_model(subject)
        if not subject_type.is_in_group(subject.id, group):
            raise SubjectNotExist()

        if not object_scope_type_registry.scope_includes_context(group, scope):
            raise ScopeNotExist()

        content_types = ContentType.objects.get_for_models(scope, subject)

        # Group level permissions are not stored as RoleAssignment records
        if RoleAssignmentHandler.is_group_level_assignment(group, scope, subject):
            group_user = group.get_group_user(subject)
            new_permissions = "MEMBER" if role.uid == "BUILDER" else role.uid
            CoreHandler().force_update_group_user(
                None, group_user, permissions=new_permissions
            )

            # We need to fake a RoleAssignment instance here to keep the same
            # return interface
            return RoleAssignment(
                subject=subject,
                subject_id=subject.id,
                subject_type=content_types[subject],
                role=role,
                group=group,
                scope=scope,
                scope_type=content_types[scope],
            )

        role_assignment, created = RoleAssignment.objects.update_or_create(
            subject_id=subject.id,
            subject_type=content_types[subject],
            group=group,
            scope_id=scope.id,
            scope_type=content_types[scope],
            defaults={"role": role},
        )

        if created:
            role_assignment_created.send(
                self, subject=subject, group=group, scope=scope, role=role
            )
        else:
            role_assignment_updated.send(
                self, subject=subject, group=group, scope=scope, role=role
            )

        return role_assignment

    def remove_role(self, subject: Union[AbstractUser, Team], group: Group, scope=None):
        """
        Remove the role of a subject in the context of the given group over a specified
        scope.

        :param subject: The subject for whom we want to remove the role for.
        :param group: The group in which we want to remove the role.
        :param scope: An optional scope on which the existing role applied.
            If no scope is given the group is used as scope.
        """

        if scope is None:
            scope = group

        if RoleAssignmentHandler.is_group_level_assignment(group, scope, subject):
            group_user = group.get_group_user(subject)
            new_permissions = NO_ACCESS_ROLE_UID
            CoreHandler().force_update_group_user(
                None, group_user, permissions=new_permissions
            )
            return

        content_types = ContentType.objects.get_for_models(scope, subject)

        RoleAssignment.objects.filter(
            subject_id=subject.id,
            subject_type=content_types[subject],
            group=group,
            scope_id=scope.id,
            scope_type=content_types[scope],
        ).delete()
        role_assignment_deleted.send(self, subject=subject, group=group, scope=scope)

    def get_subject(self, subject_id: int, subject_type: str):
        """
        Helper method that returns the actual subject object given the subject_id and
        the subject type.

        :param subject_id: The subject id.
        :param subject_type: The subject type.
        """

        content_type = subject_type_registry.get(subject_type).get_content_type()
        return content_type.get_object_for_this_type(id=subject_id)

    def get_scope(self, scope_id: int, content_type_id: int) -> Any:
        """
        Helper method that returns the actual scope object given the scope ID and
        the scope type.

        :param scope_id: The scope id.
        :param content_type_id: The content_type id
        """

        content_type = ContentType.objects.get_for_id(content_type_id)
        return content_type.get_object_for_this_type(id=scope_id)

    def assign_role_batch(
        self,
        user: AbstractUser,
        group: Group,
        new_role_assignments: List[RoleAssignmentDict],
    ):
        """
        TODO: this function is orphaned until we have implemented it properly

        Creates role assignments in batch, this should be used if many role assignments
        are created at once, to be more efficient.

        :return:
        """

        return [
            self.assign_role(
                role_assignment["subject"],
                group,
                role=role_assignment["role"],
                scope=role_assignment["scope"],
            )
            for role_assignment in new_role_assignments
        ]

    def get_role_assignments(self, group: Group, scope=None):
        """
        Helper method that returns all role assignments given a scope and group.

        :param group: The group that the scope belongs to
        :param scope: The scope used for filtering role assignments
        """

        if isinstance(scope, Group) and scope.id == group.id:
            group_users = GroupUser.objects.filter(group=group)

            role_assignments = []

            for group_user in group_users:
                role_uid = group_user.permissions
                role = self.get_role_by_uid(role_uid, use_fallback=True)
                subject = group_user.user

                # TODO we probably shouldn't do this in a loop (performance)
                content_types = ContentType.objects.get_for_models(scope, subject)
                # We need to fake a RoleAssignment instance here to keep the same
                # return interface
                role_assignments.append(
                    RoleAssignment(
                        subject=subject,
                        subject_id=subject.id,
                        subject_type=content_types[subject],
                        role=role,
                        group=group,
                        scope=scope,
                        scope_type=content_types[scope],
                    )
                )
            return role_assignments
        else:
            qs = self._get_role_assignments_for_valid_subjects_qs()
            role_assignments = qs.filter(
                group=group,
                scope_id=scope.id,
                scope_type=ContentType.objects.get_for_model(scope),
            )
            return list(role_assignments)

    @classmethod
    def is_group_level_assignment(
        cls, group: Group, scope: ScopeObject, subject: Subject
    ):
        return scope == group and scope.id == group.id and isinstance(subject, User)