#!/usr/bin/python3
""" Class to control webcam arm """
import asyncio
from enum import Enum
import math
import logging
import stepper_control
from octorest import OctoRest

async def arm_control(board,pins,loop_delay,motor_delay, command_queue: asyncio.Queue, octoprint, steps, logger):
    """ Main function to control the camera arm """
    class ArmState(Enum):
        """Sate to determine state of the arm"""
        HOME = 1 #Arm in home position, Ready to start taking timelapse
        PAUSE = 2 #Paused at current position
        RUN = 3 #Running a timelapse
        MOVE = 4 #Moving to next step position (e.g. stepping)
        RETURN = 5 #Returning to home position
    current_arm_state = ArmState.RETURN
    #Set inputs and output pins
    await board.set_pin_mode_digital_input_pullup(pins["limit"])
    await board.set_pin_mode_digital_output(pins["motor1"])
    await board.set_pin_mode_digital_output(pins["motor2"])
    await board.set_pin_mode_digital_output(pins["motor3"])
    await board.set_pin_mode_digital_output(pins["motor4"])
    current_step = 0
    target_step = 0
    total_number_of_layers = 0
    steps_per_layer = 0
    while True:
        await asyncio.sleep(loop_delay)
        if not command_queue.empty():
            logger.debug("Arm item in queue:")
            request = command_queue.get_nowait()
            if request["type"] != "request" or not request["message"].startswith("arm."):
                logger.debug("Arm, message Not of request type or not of arm type")
                await command_queue.put(request) #put back the message, it wasn't for us
            else:
                logger.debug(request)
                msg = request["message"]
                answer = {
                    "type": "answer",
                    "message": "error"
                }
                if str(msg) == "arm.start": #Start rotating the arm
                    logger.debug("Arm Start Requested")
                    if current_arm_state == ArmState.HOME:#Arm is home and can be started
                        try:
                            client = OctoRest(url=octoprint["url"], apikey=octoprint["apikey"])
                            await asyncio.sleep(0.5) #Wait a bit to not overloat the octoprint API
                            layer_values=client._get('plugin/DisplayLayerProgress/values')
                            logger.debug("number of layers of current job:")
                            total_number_of_layers = layer_values["layer"]["total"]
                            logger.debug(total_number_of_layers)
                            if not total_number_of_layers.isnumeric():
                                logger.error("Nothing loaded!")
                                answer["message"] = "arm.error.could not get number of layers"
                            elif int(total_number_of_layers) > 0:
                                logger.debug("number of steps per layer:")
                                steps_per_layer = int(steps)/int(total_number_of_layers)
                                logger.debug(steps_per_layer)
                                current_step = 0
                                current_arm_state = ArmState.RUN
                                answer["message"] = "arm.running"
                            else:
                                logger.error("Layers cannot be zero!")
                                answer["message"] = "arm.error.zero layer won't work"
                        except Exception as e:
                            logger.error(e)
                            answer["message"] = "arm.error.could not connect to octoprint"
                    await command_queue.put(answer)
                elif str(msg) == "arm.move": #Perform one (between layer) movement of the arm
                    logger.debug("Arm move Requested")
                    if current_arm_state == ArmState.RUN:#Arm is actually running
                        logger.debug("Increasing target step position")
                        target_step = current_step + steps_per_layer
                        if current_step < math.floor(target_step): #if we can start moving
                            logger.debug("Time to move!")
                            current_arm_state = ArmState.MOVE
                            answer["message"] = "arm.moving"
                        else:
                            answer["message"] = "arm.moved"
                    else:
                        answer["message"] = "arm.notrunning"
                    await command_queue.put(answer)
                elif str(msg) == "arm.stop": #Time to go back home
                    logger.debug("Arm stop Requested")
                    current_arm_state = ArmState.RETURN
                    answer["message"] = "arm.returning"
                    await command_queue.put(answer)
                elif str(msg) == "arm.test": #Do a test run of the arm
                    logger.debug("Arm test run Requested")
                    if current_arm_state == ArmState.HOME:#Arm is home and can be started
                        current_step = 0
                        target_step = steps #set target step to maximum step value
                        current_arm_state = ArmState.MOVE
                        answer["message"] = "arm.moving"
                    else:
                        answer["message"] = "arm.notrunning"
                    await command_queue.put(answer)
                elif str(msg) == "arm.status": #What are we doing?
                    if current_arm_state == ArmState.HOME:#Arm is actually running
                        answer["message"] = "arm.ready"
                    elif current_arm_state == ArmState.PAUSE:
                        answer["message"] = "arm.paused"
                    elif current_arm_state == ArmState.RUN:
                        answer["message"] = "arm.running"
                    elif current_arm_state == ArmState.MOVE:
                        answer["message"] = "arm.moving"
                    elif current_arm_state == ArmState.RETURN:
                        answer["message"] = "arm.returning"
                    else:
                        answer["message"] = "arm.error.unknown state"
                    await command_queue.put(answer)
                else:
                    logger.error("No recognized ARM request")
                    answer["message"] = "arm.error.unkonwn command"
                    await command_queue.put(answer)
        if current_arm_state == ArmState.RETURN:
            limit_switch_value = await board.digital_read(pins["limit"])
            logger.debug(limit_switch_value[0])
            if limit_switch_value[0] != 1:
                await stepper_control.counterclockwise(board, pins, motor_delay)
            else:
                await stepper_control.stop(board,pins)
                current_arm_state = ArmState.HOME
                logger.debug("Arm is Ready!")
        elif current_arm_state == ArmState.MOVE: #Time to move some steps
            if current_step >= math.floor(target_step): #We have reached our designated position
                await stepper_control.stop(board,pins)
                current_arm_state = ArmState.RUN
                logger.debug("Arm is In New position")
            else:
                logger.debug("Moving arm to next target, one step at a time!")
                await stepper_control.clockwise(board, pins, motor_delay)
                current_step += 1


from .util import read_data
from .data import make_comp_data
from .span_scorer import SpanScorer
from .full_scorer import FullScorer
from .form_scorer import (
        FormScorer,
        FormCMScorer)
from .unit_scorer import (
        UnitScorer,
        UnitCMScorer)

def compare_main(
        reference,
        hypothesis,
        verbose = False):

    ref_data, hyp_data = read_data(reference, hypothesis)
    data_edits = make_comp_data(ref_data, hyp_data)

    span_scorer = SpanScorer().update(data_edits)
    span_scorer.show()

    #full_scorer = FullScorer().update(data_edits)
    #full_scorer.show()

    #form_scorer = FormScorer().update(data_edits)
    #form_scorer.show()

    unit_scorer = UnitScorer().update(data_edits)
    unit_scorer.show()

    #form_cm_scorer = FormCMScorer().update(data_edits)
    #form_cm_scorer.show()

    unit_cm_scorer = UnitCMScorer().update(data_edits)
    unit_cm_scorer.show()


import argparse
import asyncio
import copy
import logging
import os

import pandas as pd
import yaml

from measuring_ci.artifacts import get_artifact_costs
from measuring_ci.costs import fetch_all_worker_costs, taskgraph_cost
from measuring_ci.shipit import fetch_shipit_taskgraph_ids
from measuring_ci.utils import semaphore_wrapper
from taskhuddler.aio.graph import TaskGraph

LOG_LEVEL = logging.INFO

# AWS artisinal log handling, they've already set up a handler by the time we get here
log = logging.getLogger()
log.setLevel(LOG_LEVEL)
# some modules are very chatty
logging.getLogger("taskcluster").setLevel(logging.INFO)
logging.getLogger("aiohttp").setLevel(logging.INFO)

def parse_args():
    """Extract arguments."""
    parser = argparse.ArgumentParser(description="CI Costs")
    parser.add_argument('--config', type=str, default='releases.yml')
    return parser.parse_args()

def categorize_version(product, version):
    """Return a descriptive string of the release type.

    Perhaps this should be in mozilla_version
    """
    if product == 'devedition':
        return product
    elif 'b' in version:
        return 'beta'
    elif 'a' in version:
        return 'nightly'
    elif 'esr' in version:
        return 'esr'
    return 'release'

async def scan_releases(config):
    """Scan recent history for complete task graphs."""
    config = copy.deepcopy(config)
    cost_dataframe_columns = [
        'product', 'groupid',
        'graph_date', 'category',
        'phase', 'version', 'build_number',
        'totalcost', 'idealcost', 'taskcount',
        'compute_time', 'artifact_size', 'artifact_projected_cost',
    ]

    try:
        existing_costs = pd.read_parquet(config['total_cost_output'])
        log.info("Loaded existing release costs")
    except Exception:
        log.info("Couldn't load existing release costs, using empty data set: %s",
                 config['total_cost_output'])
        existing_costs = pd.DataFrame(columns=cost_dataframe_columns)

    log.info("Looking up taskgraph IDs")
    taskgraph_ids = fetch_shipit_taskgraph_ids()
    log.info("Found %d taskgraph IDs", len(taskgraph_ids))

    tasks = list()
    semaphore = asyncio.Semaphore(10)

    for graph_id in taskgraph_ids:
        if str(graph_id) in existing_costs['groupid'].values:
            log.debug("Already examined taskgroup %s, skipping.", graph_id)
            continue
        tasks.append(asyncio.ensure_future(semaphore_wrapper(semaphore, TaskGraph(graph_id))))

    log.info('Gathering %d task graphs', len(tasks))
    taskgraphs = await asyncio.gather(*tasks)

    costs = list()

    log.info('Calculating costs')
    worker_costs = fetch_all_worker_costs(
        tc_csv_filename=config['costs_csv_file'],
        other_csv_filename=config.get('other_costs_csv_file'),
    )
    for graph in taskgraphs:
        full_cost, final_runs_cost = taskgraph_cost(graph, worker_costs)
        artifact_size, artifact_cost = await get_artifact_costs(graph)
        product = taskgraph_ids[graph.groupid]['product']
        version = taskgraph_ids[graph.groupid]['version'].replace('rc', '')
        try:
            costs.append(
                [
                    product,
                    graph.groupid,
                    graph.earliest_start_time.strftime("%Y-%m-%d"),  # date bucket
                    categorize_version(product, version),
                    taskgraph_ids[graph.groupid]['phase'],
                    version,
                    taskgraph_ids[graph.groupid]['build_number'],
                    full_cost,
                    final_runs_cost,
                    len([t for t in graph.tasks()]),  # task count
                    graph.total_compute_time().total_seconds(),
                    artifact_size,
                    artifact_cost,
                ])
        except Exception as e:
            log.warning('Something screwy with %s, skipping that graph: %s', graph.groupid, e)

    costs_df = pd.DataFrame(costs, columns=cost_dataframe_columns)

    new_costs = existing_costs.merge(costs_df, how='outer')
    log.info("Writing parquet file %s", config['total_cost_output'])
    new_costs.to_parquet(config['total_cost_output'], compression='gzip')

async def main(args):
    """Main program."""
    with open(args['config'], 'r') as cfg:
        config = yaml.load(cfg)
    os.environ['TC_CACHE_DIR'] = config['TC_CACHE_DIR']
    config['backfill_count'] = args.get('backfill_count', None)

    await scan_releases(config)

def lambda_handler(args, context):
    """AWS Lambda entry point."""
    assert context  # not currently used
    if 'config' not in args:
        args['config'] = 'releases.yml'
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(args))

if __name__ == '__main__':
    logging.basicConfig(level=LOG_LEVEL)
    # Use command-line arguments instead of json blob if not running in AWS Lambda
    lambda_handler(vars(parse_args()), {'dummy': 1})


import logging
from .state import StateManager
from .army import *

class CommandManager:
    @staticmethod
    def add(info, timestamp):
        role: str = info.pop()
        troop_obj = eval((f"{role.capitalize()}"))

        if troop_obj.price > StateManager.get_money_status():
            return "not enough money"
        elif StateManager.check_army_capacity(troop_obj):
            return "too many army"
        elif role == "miner":
            miner = eval(f"{role.capitalize()}(timestamp)")
            StateManager.add_miner(miner)
            return miner.idx
        else:
            troop = eval(f"{role.capitalize()}(timestamp)")
            StateManager.add_troop(troop)
            return troop.idx

    @staticmethod
    def damage(info, timestamp):
        damage = int(info.pop())
        idx = int(info.pop())
        result = StateManager.damage(idx, damage, timestamp)
        return result

    @staticmethod
    def money_status(*args):
        return StateManager.get_money_status()

    @staticmethod
    def enemy_status(*args):
        return StateManager.get_enemy_status()

    @staticmethod
    def army_status(*args):
        army = StateManager.get_troops()
        stats = []
        troops = ["Miner", "Swordwrath", "Archidon", "Spearton", "Magikill", "Giant"]
        for i in troops:
            stats.append(
                len(list(troop for _, troop in army.items() if type(troop) is eval(i)))
            )
        return stats

    @staticmethod
    def mine(*args):
        mines = [mine.capacity for mine in StateManager.mines]
        return mines

    @staticmethod
    def run(move, info, timestamp):
        controller = getattr(CommandManager, move)
        result = controller(info, timestamp)
        if result:
            return result

class Game:
    def __init__(self) -> None:
        pass

    @staticmethod
    def handle_input(userin):
        logging.info(f"user input : {userin}")
        userin = userin.split()
        timestamp = userin.pop()
        func_name: str = userin.pop(0).replace("-", "_")

        time_seconds = (lambda l: l[0] * 60 + l[1] + l[2] / 1000)(
            list(map(int, timestamp.split(":")))
        )

        return func_name, userin, time_seconds

    def run(self, num, dragon_health):
        logging.info("Inside Game.run")
        StateManager.set_state(num, dragon_health)

        for _ in range(num):
            move, info, timestamp = self.handle_input(input())
            end = StateManager.add_event(move, info, timestamp)
            if StateManager.game_over:
                print("game over")

            res = CommandManager.run(move, info, timestamp)
            print(*res) if isinstance(res, list) else print(res)

"""

write the main class as a context manager that has a time attr

"""


from ctypes import py_object

class Vector(object):
    """
    A python implementation of a dynamically resizing array
    """

    def __init__(self, capacity=10):
        self.capacity = capacity
        self.n = 0
        self.arr = self.__make_arr(self.capacity)

    def __make_arr(self, capacity):
        """
        Make an array of size = capacity and return it

        :param capacity:
        :return:
        """
        return (capacity * py_object)()

    def __resize(self, new_capacity):
        """
        Resize an array to a new capacity

        :param new_capacity:
        :return:
        """

        # Create the new array
        new_arr = self.__make_arr(new_capacity)

        # Copy the current array's elements over
        for i in range(self.n):
            new_arr[i] = self.arr[i]

        # Set the reference to the new array and update the capacity
        self.arr = new_arr
        self.capacity = new_capacity

    def append(self, item):
        """
        A public method used to append a new item to the array

        :param item:
        :return:
        """

        # Check if we need to resize the array
        if self.n == self.capacity:
            self.__resize(self.capacity * 2)

        # Now perform the append
        self.arr[self.n] = item
        self.n += 1

    def pop(self, idx=None):
        """
        A public method to pop an element off the array

        :param idx:
        :return:
        """

        if idx is None:
            self.n -= 1
            item = self.arr[self.n]

        elif 0 <= idx < self.n:
            self.n -= 1
            item = self.arr[idx]

            # Now we need to shift all elements left by 1
            for i in range(idx, self.n):
                self.arr[i] = self.arr[i + 1]

        else:
            raise IndexError("Index out of bounds")

        # Now check if we should shrink the array
        if self.n < self.capacity // 4:
            self.__resize(self.capacity // 2)

        return item

    def is_empty(self):
        """
        Check if the array is empty or not
        :return:
        """
        return True if self.n == 0 else False

    def __setitem__(self, idx, item):
        """
        Sets an item at a specific index
        :param key:
        :param value:
        :return:
        """

        if 0 <= idx < self.n:
            self.arr[idx] = item
        else:
            raise IndexError("Index out of bounds!")

    def insert(self, idx, item):
        """
        Inserts an item at the given index

        :param idx:
        :param item:
        :return:
        """

        # Check if we need to resize the array
        if self.n == self.capacity:
            self.__resize(self.capacity * 2)

        # Shift all the elements rightward by 1
        for i in reversed(range(idx, self.n)):
            self.arr[idx + 1] = self.arr[i]

        # Insert the element
        self.arr[idx] = item
        self.n += 1

    def prepend(self, item):
        """
        Inserts an item at the head of the list

        :param item:
        :return:
        """
        self.insert(0, item)

    def delete(self, idx):
        """
        Deletes an item from the index

        :param idx:
        :return:
        """

        if 0 <= idx < self.n:
            self.n -= 1

            # We need to shift all elements left by 1
            for i in range(idx, self.n):
                self.arr[i] = self.arr[i + 1]

        else:
            raise IndexError("Index out of bounds")

        # Now check if we should shrink the array
        if self.n < self.capacity // 4:
            self.__resize(self.capacity // 2)

    def remove(self, item):
        """
        Remove all items from the array that match with item

        :param item:
        :return:
        """

        # Find all indexes that match the item
        idxes = []
        for i in range(self.n):
            if self.arr[i] == item:
                idxes.append(i)

        # Delete all found idxes, adjusting for the idx values if there are multiple
        for i, idx in enumerate(idxes):
            self.delete(idx - i)

    def find(self, item):
        """
        Returns the index of the first item that matches the item, else return None if no matches

        :param item:
        :return:
        """

        for i in range(self.n):
            if self.arr[i] == item:
                return i
        return None

    def __len__(self):
        return self.n

    def __getitem__(self, idx):
        if not 0 <= idx < self.capacity:
            raise IndexError("Index out of bounds")
        return self.arr[idx]

    def __str__(self):
        out = "["
        for k in range(self.n):
            out += f"{self.arr[k]}, "

        out = out.rstrip(", ") + "]"
        return out

if __name__ == '__main__':
    # Test our vector class
    # Make a new vector of size 5
    vec = Vector(5)
    print(f"Empty Vector: {vec}")

    # Add a few numbers
    vec.append(0)
    vec.append(1)
    vec.append(2)
    print(f"Vector of 3 elements: {vec}")

    # Make it auto resize
    print(f"Old Capacity: {vec.capacity}")
    vec.append(3)
    vec.append(4)
    vec.append(5)
    vec.append(6)
    print(f"New Vector: {vec}")
    print(f"New Capacity: {vec.capacity}")

    # Now pop 1 elements
    e = vec.pop()
    print(f"Popped last element: {e}")
    print(f"New Vector: {vec}")

    # Now pop all but 1 element
    print("Popping 5 elements more...")
    print(vec.pop())
    print(vec.pop())
    print(vec.pop())
    print(vec.pop())
    print(vec.pop())
    print(f"New Capacity: {vec.capacity}")
    print(f"Num Items: {vec.n}")
    print(f"Vector: {vec}")


"""portfolio URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.conf import settings as conf_settings
from django.urls import path
from . import views, dataforms

urlpatterns = [
    # home page / Hero page views
    path('', views.hero_page_view),

    # redirections representing site structure
    path('home', views.hero_page_view),
    path('about', views.about_page_view),
    path('projects', views.consolidated_education_page_view),
    #path('projects', views.projects_page_view),
    path('web', views.web_page_view),
    path('blog', views.blog_page_view),
    path('contacts', views.contacts_page_view),

    # data management views
    path('competence', dataforms.competence_form_view),
    path('course', dataforms.course_form_view),
    path('discipline', dataforms.discipline_form_view),
    path('institution', dataforms.institution_form_view),
    path('interest', dataforms.interest_form_view),
    path('person', dataforms.person_form_view),
    path('project', dataforms.project_form_view),
    path('technology', dataforms.technology_form_view),

    # consolidated education view
    path('education', views.consolidated_education_page_view,
        name=conf_settings.GLOBAL_SETTINGS['EDUCATION_PAGE']),
]


# [Make folder path]

import os

# 建立多層資料夾因為太深失敗 因為從上一層都沒有
temp_path = 'C:/Users/Academy/Documents'
folder_list = ['/temp', '/first', '/second', '/third']

for folder in folder_list:
    # 檢查第一層資料夾路徑是否存在
    if os.path.isdir(temp_path) == True:
        # 添加下一層資料夾名稱字串
        temp_path += folder
        #　創建該資料夾
        os.mkdir(temp_path)
        print(temp_path)

# 將路徑分割成清單後再接續串起來建立資料夾路徑
folder_list = ['D:', 'temp', 'twr', 'sq0010', 'sh0010', 'lighting']
new_path = ''

for folder in folder_list:
    new_path = new_path + '/' + folder
    print(new_path[1:])

# 將以上寫成函式
def make_dir(input_path):

    folder_path = ''
    folder_list = input_path.split('/')

    for folder in folder_list:
        folder_path = folder_path + '/' + folder
        if os.path.isdir(folder_path[1:]) != True:
            os.mkdir(folder_path[1:])

    print(folder_path[1:])

# 請使用func塞入任何很長的資料夾路徑 未存在ㄉ
make_dir('D:/temp/twr/sq0010/sh0010/lighting')

# 東西全部印出來再丟進func
# find exr file
def get_image_name_list(input_path):

    result_list = []
    path_data = os.listdir(input_path)

    for image_file in  path_data:
        if 'exr' in image_file:
            result_list.append(image_file)

    return result_list

# 印出特定類型檔案路徑
image_list = get_image_name_list('Z:/images')
for exr in image_list:
    print(exr)

# find missing exr file
def find_missing_file(input_list):

    num_list = []

    for item in input_list:
        num_list.append(int(item.split('.')[-2][-3]))

        for num in range(num_list[0], num_list[1]):
            if num not in num_list:
                print(num, 'missing')

#使用func把image_list丟進去

# find missing exr file new
def find_missing_file(input_list):
    num_list = []
    missing_list = []

    for item in input_list:
        num_list.append(int(item.split('.')[1]))

    num_list.sort()
    for num in range(num_list[0], num_list[-1]+1):
        if num not in num_list:
            missing_list.append(num)
            #print(num, 'missing!')

    return missing_list

class/note/0927 - make folder, print file list, find missing file.py


import string

plain = 'Not going to be that easy :)'

shift = 0

def caesar(plain, shift=1):
    key = {l:string.ascii_lowercase[(i+shift) % 26] for i,l in enumerate(string.ascii_lowercase)}
    output = ''
    for l in plain:
        if l in key:
            output += key[l]
        else:
            output += l
    return output

result = caesar(plain.lower(), shift)

with open('caesar.txt', 'w') as f:
    f.write(result)


from django.contrib import admin
from parler.admin import TranslatableAdmin
from parler.forms import TranslatableModelForm
from ckeditor_uploader.widgets import CKEditorUploadingWidget

from catalog.models import Category, Product, ProductGallery, ProductTag

__all__ = ("ImageAdminInline", "ProductAdmin")

class ImageAdminInline(admin.TabularInline):
    model = ProductGallery
    fields = ("file", "order", "size_mb")
    readonly_fields = ("size_mb",)

    def get_queryset(self, request):
        return ProductGallery.objects.select_related("product").prefetch_related("product__translations")

class ProductAdminForm(TranslatableModelForm):
    class Meta:
        model = Product
        exclude = ()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        tags_qs = ProductTag.objects.filter(is_active=True).prefetch_related("translations")
        self.fields["category"].queryset = Category.objects.prefetch_related("translations")
        self.fields["tags"].queryset = tags_qs
        self.fields["seo_tags"].queryset = tags_qs
        self.fields["composition_and_care"].widget = CKEditorUploadingWidget()

@admin.register(Product)
class ProductAdmin(TranslatableAdmin):
    list_display = ["id", "order", "name", "price", "price_with_sale", "is_active"]
    search_fields = ["id", "translations__name", "slug"]
    list_filter = ["is_active", "category",  "color"]
    autocomplete_fields = ["category", "major_category", "tags", "seo_tags", "materials"]
    inlines = [ImageAdminInline]
    form = ProductAdminForm
    actions = ["turn_on", "turn_off"]

    fieldsets = (
        (
            "Главное",
            {
                "classes": ("collapse",),
                "fields": (
                    "name",
                    "sku",
                    "slug",
                    "is_active",
                    "category",
                    "major_category",
                    "color",
                    "description",
                    "model_measurements",
                    "composition_and_care",
                    "weight",
                ),
            },
        ),
        (
            "Общее",
            {
                "classes": ("collapse",),
                "fields": (
                    "is_new",
                    "materials",
                    "tags",
                    "seo_tags",
                    "price",
                    "price_with_sale",
                    "order",
                ),
            },
        ),
    )

    def get_queryset(self, request):
        return Product.objects.all()

    def turn_on(self, request, queryset):
        queryset.update(is_active=True)
        self.message_user(request, "Продукты включены")

    turn_on.short_description = "Включить выбранные продукты"

    def turn_off(self, request, queryset):
        queryset.update(is_active=False)
        self.message_user(request, "Продукты выключены")

    turn_off.short_description = "Выключить выбранные продукты"


import asyncio
import socket

import pytest

import asyncio_either

barrier = asyncio.Barrier(2)
log = []

@asyncio_either.either
async def mul(a: int, b: int) -> int:
    """Multiply two numbers in one way."""
    await barrier.wait()
    await asyncio.sleep(0.1)
    return a * b

@asyncio_either.or_(mul)
async def mul_alt(a: int, b: int) -> int:
    """Multiply two numbers after blocking on stuff."""
    await barrier.wait()
    try:
        log.append('mul_alt waits')
        rsock, wsock = socket.socketpair()
        await asyncio.sleep(100500)
        log.append('mul_alt wakes up')
        return b * a
    except asyncio.exceptions.CancelledError:
        log.append('mul_alt cancelled')
    finally:
        await asyncio.sleep(0)  # still can do asyncio stuff after cancellation
        rsock.close()
        wsock.close()
        log.append('mul_alt exits')
    return 0

@pytest.mark.asyncio()
async def test_smoke() -> None:
    """Smoke-test the basic functionality."""
    assert await mul(2, 3) == 6  # noqa: PLR2004
    assert log == ['mul_alt waits', 'mul_alt cancelled', 'mul_alt exits']
    assert 'mul_alt wakes up' not in log


# https://atcoder.jp/contests/arc011/tasks/arc011_2

N = input()
words = input().split()
rule = 'zrbcdwtjfqlvsxpmhkng'
d = {}
for i in range(10):
    x,y = rule[2*i:2*i+2]
    d[x] = str(i)
    d[y] = str(i)

def convert(word):
    res = ''
    for char in word.lower():
        if char not in d:
            continue
        res += d[char]
    return res

words = [convert(word) for word in words]
words = [word for word in words if word]
print(*words)


import numpy as np
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.optim as optim
import torch.nn.functional as func
import model
import utils

def infoNCELoss(q, k, params):
    tau = 0.1
    cos = nn.CosineSimilarity(dim=0)
    pos = torch.exp(cos(q, k[0,:])/tau)
    neg = 0
    for i in range(k.size(0)):
        neg += torch.exp(cos(q, k[i,:])/tau)
    loss = -1 * torch.log(pos/neg)
    return loss

def negCosSim(p, z):
    z = z.detach()
    z = func.normalize(z, dim = 1)
    p = func.normalize(p, dim = 1)
    loss = -(p*z).sum(dim=1).mean()
    return loss

def varLoss(q, k):
    stdQ = torch.sqrt(q.var(dim=0) + 1e-04)
    stdK = torch.sqrt(k.var(dim=0) + 1e-04)
    stdLoss = torch.mean(func.relu(1 - stdQ)) + torch.mean(func.relu(1 - stdK))
    return stdLoss

def covarLoss(q, k):
    nB, nD = q.size()
    q = q - q.mean(dim=0)
    k = k - k.mean(dim=0)
    qT = torch.transpose(q, 0, 1)
    kT = torch.transpose(k, 0, 1)
    covQ = torch.matmul(qT, q) / (nB-1)
    covK = torch.matmul(kT, k) / (nB-1)
    offCovQ = covQ.fill_diagonal_(0)
    offCovK = covK.fill_diagonal_(0)
    sumCovQ = torch.square(offCovQ).sum() / nD
    sumCovK = torch.square(offCovK).sum() / nD
    covLoss = sumCovQ + sumCovK
    return covLoss

def siam(net, proj, pred, optimizer, scheduler, dataLoader, params):
    print("Starting Training Loop...")
    device = torch.device("cuda:0" if (torch.cuda.is_available() and params.nGPU > 0) else "cpu")
    losses = []

    for epoch in range(params.startEpoch, params.nEpochs):
        for data1, data2 in dataLoader:
            net.zero_grad()
            proj.zero_grad()
            pred.zero_grad()

            q = data1.to(device)
            k = data2.to(device)

            qOut = net(q)
            qZ   = proj(qOut)
            qP   = pred(qZ)
            kOut = net(k)
            kZ   = proj(kOut)
            kP   = pred(kZ)

            err = 0.5 * negCosSim(qP, kZ) + 0.5 * negCosSim(kP, qZ)
            err.backward()
            optimizer.step()
            scheduler.step()
            losses.append(err.item())

        if ((epoch+1) % params.nCheckpoint == 0):
            print('[%d/%d]\tTrain Loss: %.4f' % (epoch+1, params.nEpochs, err.item()))
            if params.saveCheckpoint:
                utils.saveCkpt(net, optimizer, epoch+1, params)
    if params.plotLoss:
        utils.showLoss(losses)
    return net, optimizer

def vic(net, proj, optimizer, scheduler, dataLoader, params):
    print("Starting Training Loop...")
    device = torch.device("cuda:0" if (torch.cuda.is_available() and params.nGPU > 0) else "cpu")
    losses = []

    for epoch in range(params.startEpoch, params.nEpochs):
        for data1, data2 in dataLoader:
            net.zero_grad()
            proj.zero_grad()

            q = data1.to(device)
            k = data2.to(device)

            qOut = net(q)
            qZ   = proj(qOut)
            kOut = net(k)
            kZ   = proj(kOut)

            simLoss = func.mse_loss(qZ, kZ)
            stdLoss = varLoss(qZ, kZ)
            covLoss = covarLoss(qZ, kZ)

            err = simLoss + stdLoss + 4e-2 * covLoss
            err.backward()
            optimizer.step()
            scheduler.step()
            losses.append(err.item())

        if ((epoch+1) % params.nCheckpoint == 0):
            print('[%d/%d]\tTrain Loss: %.4f' % (epoch+1, params.nEpochs, err.item()))
            if params.saveCheckpoint:
                utils.saveCkpt(net, optimizer, epoch+1, params)

    if params.plotLoss:
        utils.showLoss(losses)
    return net, optimizer


from itertools import chain
from typing import Dict, Iterator, List, Optional, Set

from networkx import DiGraph, from_dict_of_lists, topological_generations

from dbt_dry_run.models.manifest import Manifest, Node

class ManifestScheduler:
    MODEL = "model"
    SEED = "seed"
    SNAPSHOT = "snapshot"
    SOURCE = "source"
    TEST = "test"
    RUNNABLE_RESOURCE_TYPE = (MODEL, SEED, SNAPSHOT, SOURCE, TEST)
    RUNNABLE_MATERIAL = ("view", "table", "incremental", "seed", "snapshot", "test")

    def __init__(self, manifest: Manifest, model: Optional[str] = None):
        self._manifest = manifest
        self._model_filter = model
        self._status: Dict[str, bool] = {
            node_key: False for node_key in self._manifest.all_nodes.keys()
        }

    def _filter_manifest(self) -> Set[str]:
        if self._model_filter is None:
            return set(self._manifest.all_nodes.keys())
        try:
            leaf_node = self._manifest.all_nodes[self._model_filter]
        except KeyError:
            raise KeyError(f"Model {self._model_filter} does not exist in manifest")
        upstream_node_keys = leaf_node.depends_on.nodes
        filtered_nodes = [self._model_filter, *upstream_node_keys]
        while upstream_node_keys:
            upstream_nodes = list(
                filter(
                    lambda val: val is not None,
                    [self._manifest.all_nodes.get(k) for k in upstream_node_keys],
                )
            )
            upstream_node_keys = list(
                filter(
                    lambda n: n in self._manifest.all_nodes.keys(),
                    chain.from_iterable(
                        [n.depends_on.nodes for n in upstream_nodes if n]
                    ),
                )
            )
            filtered_nodes.extend(upstream_node_keys)
        return set(filtered_nodes)

    def _get_runnable_keys(self) -> Set[str]:
        remaining_nodes = set(
            filter(self._node_key_is_runnable, self._manifest.all_nodes.keys())
        )

        if self._model_filter:
            remaining_nodes = remaining_nodes.intersection(self._filter_manifest())
            if self._model_filter not in remaining_nodes:
                model_filter_config = self._manifest.all_nodes[
                    self._model_filter
                ].config
                model_message = (
                    f"Model {self._model_filter} is not runnable: {model_filter_config}"
                )
                raise KeyError(model_message)
        return remaining_nodes

    def __iter__(self) -> Iterator[List[Node]]:
        generation: List[str]
        for gen_id, generation in enumerate(self._calculate_depths()):
            nodes = [
                self._manifest.all_nodes[k]
                for k in generation
                if k in self._manifest.all_nodes
            ]
            yield nodes  # list(filter(self._node_is_runnable, nodes))

    def __len__(self) -> int:
        return len(self._get_runnable_keys())

    def _node_is_runnable(self, node: Node) -> bool:
        node_is_runnable_type = (
            node.resource_type in self.RUNNABLE_RESOURCE_TYPE
            and node.config.materialized in self.RUNNABLE_MATERIAL
        ) or (node.is_external_source())
        return node.config.enabled and node_is_runnable_type

    def _node_key_is_runnable(self, node_key: str, default: bool = False) -> bool:
        try:
            return self._node_is_runnable(self._manifest.all_nodes[node_key])
        except KeyError:
            return default

    def _get_runnable_dependencies(self, node: Node) -> List[str]:
        # Deeply traverse 'depends_on' so we catch nodes that depend on ephemerals that depends on nodes
        upstream_deps: List[str] = []
        for upstream_node_key in node.depends_on.nodes:
            up_node = self._manifest.all_nodes.get(upstream_node_key)
            if up_node is None:
                continue
            if self._node_is_runnable(up_node):
                upstream_deps.append(up_node.unique_id)
            else:
                upstream_deps.extend(self._get_runnable_dependencies(up_node))
        # This is a bit grim but we need the 'deep' dependencies in the Node for inserting the correct SQL literals
        # TODO: Create a NodeWrapper object that has the original Node and better dependency information
        node.depends_on.deep_nodes = upstream_deps
        return upstream_deps

    def _calculate_depths(self) -> List[List[str]]:
        remaining_nodes = self._get_runnable_keys()
        graph_data = {
            node_id: self._get_runnable_dependencies(node)
            for node_id, node in self._manifest.all_nodes.items()
            if node_id in remaining_nodes
        }
        graph = from_dict_of_lists(graph_data, create_using=DiGraph).reverse()
        return list(topological_generations(graph))