import sgtk
import fnmatch
from .plugin import PublishPlugin, CollectorPlugin
from .item import Item
from .task import Task

logger = sgtk.platform.get_logger(__name__)

class PluginManager(object):
    """
    Manager which handles hook initialization and execution.
    """

    def __init__(self, publish_logger):
        """
        :param publish_logger: A logger object that the
            various hooks can send logging information to.
        """
        logger.debug("Plugin manager waking up")

        self._bundle = sgtk.platform.current_bundle()

        self._logger = publish_logger

        # here we maintain a lookup items generated for a given path. this dict
        # is rebuild whenever a full refresh of the tree is created.
        self._path_to_item_lookup = {}

        logger.debug("Loading plugin configuration")
        self._publish_plugins = []

        plugin_defs = self._bundle.get_setting("publish_plugins")

        # create publish plugin objects
        for plugin_def in plugin_defs:
            logger.debug("Find config chunk %s" % plugin_def)

            publish_plugin_instance_name = plugin_def["name"]
            publish_plugin_hook_path = plugin_def["hook"]
            publish_plugin_settings = plugin_def["settings"]

            plugin = PublishPlugin(
                publish_plugin_instance_name,
                publish_plugin_hook_path,
                publish_plugin_settings,
                self._logger
            )
            self._publish_plugins.append(plugin)
            logger.debug("Created %s" % plugin)

        # create collector plugin object
        collector_hook_path = self._bundle.get_setting("collector")
        collector_settings = self._bundle.get_setting("collector_settings")

        self._collector = CollectorPlugin(
            collector_hook_path,
            collector_settings,
            self._logger
        )

        # create an item root
        self._root_item = Item.create_invisible_root_item()

        # initalize tasks
        self._tasks = []

    @property
    def top_level_items(self):
        """
        Returns a list of the items which reside on the top level
        of the tree, e.g. all the children of the invisible root item.

        :returns: List if :class:`Item` instances
        """
        return self._root_item.children

    def remove_top_level_item(self, item_to_remove):
        """
        Removed the given item from the list of top level items
        """

        # since we keep a look of items created from browsed/dropped external
        # files, we can check to see if the item to be deleted is one of those.
        # if it is, we need to remove that item from the lookup and remove the
        # path from the list of external files we're tracking. first, see if the
        # item matches any items created for a path. there should only be one,
        # but we'll check all items in the lookup to be sure.
        paths_to_delete = []
        for (path, item_for_path) in self._path_to_item_lookup.iteritems():
            if item_for_path == item_to_remove:
                paths_to_delete.append(path)

        # now we iterate over the matched paths and remove them from the list of
        # external files and the item lookup.
        for path in paths_to_delete:
            del self._path_to_item_lookup[path]

        self._root_item.remove_item(item_to_remove)

    @property
    def plugins(self):
        """
        Returns a list of the plugin instances loaded from the configuration

        :returns: List of :class:`Plugin` instances.
        """
        return self._publish_plugins

    def run_collectors(self):
        """
        Reestablishes the state. Recomputes everything.

        :returns: List of items created
        """
        logger.debug("Refresh: Removing existing objects")
        self._root_item = Item.create_invisible_root_item()

        # this is a full refresh, so we clear out this lookup. it will be
        # regenerated as items are created for all the external files. before
        # clearning the lookup, get a list of all external files collected.
        external_files = self._path_to_item_lookup.keys()
        self._path_to_item_lookup = {}

        logger.debug("Refresh: Running collection on current scene")
        # process the current scene
        current_scene_items_created = self._collect(
            collect_current_scene=True
        )

        logger.debug("Refresh: Running collection on all external files")
        # process all external paths
        external_files_items_created = self._collect(
            collect_current_scene=False,
            paths=external_files
        )

        return len(current_scene_items_created) + len(external_files_items_created)

    def add_external_files(self, paths):
        """
        Runs the collector for the given set of paths

        :param str paths: List of full file path
        :returns: Number of items created by collectors
        """

        # now run the collection for all the new items
        logger.debug("Running scene collection for new paths")
        items_created = self._collect(
            collect_current_scene=False,
            paths=paths
        )
        return len(items_created)

    def clear_external_files(self):
        """
        Removes all external files
        """
        self._path_to_item_lookup = {}

        self.run_collectors()

    def _collect(self, collect_current_scene, paths=None):
        """
        Runs the collector and generates fresh items.

        :param bool collect_current_scene: Boolean to indicate if collection should
            be performed for the current scene, e.g. if the collector hook's
            process_current_session() method should be executed.
        :param paths: List of paths for which the collector hook's method
            process_file() should be executed for
        :returns: Items created by collectors
        """
        # get existing items
        all_items_before = self._get_item_tree_as_list()

        # pass 1 - collect stuff from the scene and other places
        logger.debug("Executing collector")

        if collect_current_scene:
            self._collector.run_process_current_session(self._root_item)

        if paths:
            for path in paths:
                # we only want to collect file paths for which items have not
                # already been created. we can check the path to item lookup
                # to see if the path already exists as a key. this means that
                # this file path has already been collected and an item
                # created. if the path doesn't exist, we run the collection
                # method and populate the lookup with the item created
                if path in self._path_to_item_lookup:
                    logger.debug("Skipping duplicate path '%s'" % path)
                else:
                    logger.debug("Adding external file '%s'" % path)
                    item = self._collector.run_process_file(self._root_item, path)
                    self._path_to_item_lookup[path] = item

        # get all items after scan
        all_items_after = self._get_item_tree_as_list()

        # get list of new things
        all_new_items = list(set(all_items_after) - set(all_items_before))

        # now we have a series of items from the scene, visit our plugins
        # to see if there is interest
        logger.debug("Visiting all plugins and offering items")
        for plugin in self._publish_plugins:

            for item in self._get_matching_items(plugin.item_filters, all_new_items):
                logger.debug("seeing if %s is interested in %s" % (plugin, item))
                accept_data = plugin.run_accept(item)
                if accept_data.get("accepted"):
                    # this item was accepted by the plugin!
                    # look for bools accepted/visible/enabled/checked

                    # TODO: Implement support for this!
                    # all things are visible by default unless stated otherwise
                    is_visible = accept_data.get("visible", True)

                    # all things are checked by default unless stated otherwise
                    is_checked = accept_data.get("checked", True)

                    # all things are enabled by default unless stated otherwise
                    is_enabled = accept_data.get("enabled", True)

                    task = Task.create_task(plugin, item, is_visible, is_enabled, is_checked)
                    self._tasks.append(task)

        return all_new_items

    def _get_matching_items(self, item_filters, all_items):
        """
        Given a list of item filters from a plugin,
        yield a series of matching items. Items are
        randomly ordered.

        :param item_filters: List of item filters to glob against.
        :param all_items: Items to match against.
        """
        for item_filter in item_filters:
            logger.debug("Checking matches for item filter %s" % item_filter)
            # "maya.*"
            for item in all_items:
                if fnmatch.fnmatch(item.type, item_filter):
                    yield item

    def _get_item_tree_as_list(self):
        """
        Returns the item tree as a flat list.

        :returns: List if item objects
        """
        def _get_subtree_as_list_r(parent):
            items = []
            for child in parent.children:
                items.append(child)
                items.extend(_get_subtree_as_list_r(child))
            return items

        parent = self._root_item
        return _get_subtree_as_list_r(parent)


"""Translates uploader's user email to Ftrack username.

Should run before 'CollectAnatomyContextData' so the user on context is
changed before it's stored to context anatomy data or instance anatomy data.

Requires:
    instance.data.get("user_email") or os.environ.get("USER_EMAIL")

Provides:
   os.environ["FTRACK_API_USER"]
   context.data["user"]
"""

import os

import ftrack_api
import pyblish.api
import ayon_api

from openpype.pipeline import KnownPublishError

class CollectWebpublisherCredentials(pyblish.api.ContextPlugin):
    """
        Translates uploader's user email to Ftrack username.

    It expects that user email in Ftrack is same as user email in Ayon server,
    Ftrack username is needed to load data to Ftrack.

    Resets `context.data["user"] to correctly populate `version.author` and
    `representation.context.username`

    """

    settings_category = "ftrack"

    order = pyblish.api.CollectorOrder + 0.0015
    label = "Collect webpublisher credentials"
    hosts = ["webpublisher", "photoshop"]
    targets = ["webpublish"]

    def process(self, context):
        service_api_key, service_username = self._get_username_key(context)
        os.environ["FTRACK_API_USER"] = service_username
        os.environ["FTRACK_API_KEY"] = service_api_key

        user_email = self._get_user_email(context)

        if not user_email:
            self.log.warning("No email found")
            return

        username = self._get_ftrack_username(user_email)
        os.environ["FTRACK_API_USER"] = username

    def _get_ftrack_username(self, user_email):
        """Queries Ftrack api for user with 'user_email'.

        Raises:
            ValueError: if user not found
        """
        session = ftrack_api.Session(auto_connect_event_hub=False)
        user = session.query(
            "User where email like '{}'".format(user_email)
        ).first()
        if not user:
            raise ValueError(
                "Couldn't find user with '{}' email".format(user_email))
        username = user.get("username")
        self.log.debug("Resolved ftrack username:: '{}'".format(username))
        return username

    def _get_user_email(self, context):
        """Collect uploader's email address to lookup user in Ftrack"""
        # for publishes with studio processing
        user_email = os.environ.get("USER_EMAIL")
        self.log.debug("Email from env:: {}".format(user_email))
        if not user_email:
            # for basic webpublishes
            for instance in context:
                user_email = instance.data.get("user_email")
                self.log.debug("Email from instance:: {}".format(user_email))
                break
        return user_email

    def _get_username_key(self, context):
        """Query settings for ftrack credentials."""
        project_settings = context.data["project_settings"]
        service_settings = project_settings["ftrack"]["service_settings"]

        api_key_secret = service_settings["api_key"]
        username_secret = service_settings["username"]

        secrets_by_name = {
            secret["name"]: secret["value"]
            for secret in ayon_api.get_secrets()
        }
        api_key = secrets_by_name.get(api_key_secret)
        username = secrets_by_name.get(username_secret)
        if not api_key or not username:
            raise KnownPublishError("Missing ftrack credentials in settings. "
                                    "Please let admin fill in 'ayon+settings://ftrack/service_settings'")  # noqa
        return api_key, username


# Braden Allmond, October 31st 2022
# push-button submission of one run from one dataset to Condor using Tau DQM workflow

import subprocess # use instead of os, needed for dasgoclient, mkdir, and wc

###### GET USER INPUT
print("Common datasets are /Muon/Run2022E-v1/RAW and /Muon/Run2022F-v1/RAW ...")
print("Remember to copy your voms-proxy into this directory before proceeding ...")
DATASET = input("Please enter the dataset you'd like to use: ")
RUN     = input("Please enter the run number you'd like to use: ")

###### CHECK NUMBER OF FILES AVAILABLE AT VIABLE SITES
# this is technically vulnerable to arbitrary code-injection from the user (DATASET can be anything)
print("The dataset is at least partially available at the following sites ...")
command_dataset_sites = "dasgoclient --query \"site dataset=" + DATASET + "\""
# run the command via commandline, and capture the stdout, assigning it to a variable below and then processing the string to an array
command_dataset_sites = subprocess.run(command_dataset_sites, shell=True, check=True, stdout=subprocess.PIPE, universal_newlines=True)
output_dataset_sites  = command_dataset_sites.stdout.split('\n')
output_dataset_sites.pop(-1) # remove empty string
output_dataset_sites = [site for site in output_dataset_sites if (("T2" in site) or (site == "T0_CH_CERN_Disk"))]
print(f"Viable sites are {output_dataset_sites} ...")

# find site with most files
site_with_most_files = ""
most_number_of_files = -1
output_filelist_name = "filelist_"+RUN+".txt"
first_file = ""
for SITE in output_dataset_sites:
  command_files_at_site = "dasgoclient --query \"file dataset=" + DATASET + " run in [" + RUN +"] site=" + SITE + "\""
  command_files_at_site = subprocess.run(command_files_at_site, shell=True, check=True, stdout=subprocess.PIPE, universal_newlines=True)
  output_files_at_site  = command_files_at_site.stdout.split('\n')
  output_files_at_site.pop(-1)
  number_of_files = len(output_files_at_site)
  print(f"At {SITE} there are {len(output_files_at_site)} files")
  if number_of_files != 0 and number_of_files > most_number_of_files:
    most_number_of_files = number_of_files
    site_with_most_files = SITE
    first_file = output_files_at_site[0]
    print(f"Writing files to {output_filelist_name} ...")
    print(f"Best site so far is {site_with_most_files} ...")
    # make output file, if there is another site with more files later this output file will be overwritten
    with open(output_filelist_name, "w") as output_filelist:
      for line in output_files_at_site:
        output_filelist.write("".join(line) + "\n")

# generate python config file
print(f"Generating python config file ...")
command_generate_python_config_file = """\
cmsDriver.py step2 -s RAW2DIGI,L1Reco,RECO,DQM --eventcontent DQM \
--datatier DQMIO --conditions 124X_dataRun3_v9 --era Run3 \
--geometry DB:Extended --process reRECO -n -1 --data \
--filein """ + first_file + """ --no_exec"""
print(command_generate_python_config_file)
command_generate_python_config_file = subprocess.run(command_generate_python_config_file, shell=True)

# set up steps before executing condor command
output_EOS = "/eos/user/b/ballmond/DQM/run_" + RUN
command_mkdir_output_EOS = "mkdir " + output_EOS
print(f"Making new DQM directory on EOS here {output_EOS} ...")
command_mkdir_output_EOS = subprocess.run(command_mkdir_output_EOS, shell=True)

command_generate_condor_jobs="""\
python3 condor_handling_standard_DQM.py \
-i step2_RAW2DIGI_L1Reco_RECO_DQM.py \
-o """ + output_EOS + """ \
-f """ + output_filelist_name + """ \
-p /afs/cern.ch/user/b/ballmond/public/DQM_CMSSW_12_4_0/src/x509up_u134427 \
-n 1 -j tomorrow \
-b DQM_run_""" + RUN
command_generate_condor_jobs = subprocess.run(command_generate_condor_jobs, shell=True)

print("Run ./sub_total.jobb to submit")


from time import time
import random
from error_codes import error_codes
import sys
from thread import start_new_thread

from player import Player
from base_socket import BaseSocket
import game_logic

class GameSession(BaseSocket):
    interaction_timeout = 6000
    session_timeout = 30000

    def __init__(self, deck_cards, attributes):
        self.id = None
        self.generate_id()
        self.begin = time()
        self.last_interaction = time()
        self.active = True
        self.players = [Player(), Player()]
        self.game_state = game_logic.GameLogic(deck_cards=deck_cards, attributes=attributes)

    def generate_id(self):
        beginning_of_day_in_millis = int(time() / 86400) * 86400000
        epoch_in_millis = int(time() * 1000)
        day_millis = epoch_in_millis - beginning_of_day_in_millis
        self.id = str(format(day_millis, '08')) + str(format(random.getrandbits(16), '05'))

    def serialize(self):
        data = {
            'begin': self.begin,
            'id': self.id,
            'last_interaction': self.last_interaction,
        }

        for idx, player in enumerate(self.players):
            data['player_%d' % idx] = str(player.id),

        return data

    def inactivate_sessions(self, error):
        if not error or error not in error_codes:
            data = {'code': 0, 'message': 'Unknown error'}
        else:
            data = error_codes[error]

        for player in self.players:
            self._send_dict(data, player.connection)
        self.active = False

    def check_timeout(self):
        if time() - self.last_interaction > self.interaction_timeout:
            self.inactivate_sessions('inactive_timeout')

        if time() - self.begin > self.session_timeout:
            self.inactivate_sessions('timeout')

    def can_player_join(self, player_id):
        for player in self.players:
            if player.id is None:
                return True
        return False

    def add_player(self, player_id, connection):
        new_player = Player(connection=connection, id=player_id)
        message = [None] * len(self.players)

        for idx, player in enumerate(self.players):
            if player.id is None:
                self.players[idx] = new_player
                message[idx] = {"action": "player_joined", "player_id": player_id}
                self.communicate_game_logic(message)
                break

    def communicate_game_logic(self, player_data):
        for player_idx, player in enumerate(self.players):
            if player_data[player_idx]:
                response = self.game_state.process_input(player_data[player_idx], player_idx)
                self.broadcast_game_logic_response(response)

            elif player_data[player_idx] is False:
                self.inactivate_sessions('invalid_message')

    def broadcast_game_logic_response(self, response):
        for response_idx, response_for_player in enumerate(response):
            if response_for_player:
                self._send_dict(self.players[response_idx].connection, response_for_player)

    def close(self):
        for player in self.players:
            if player.connection:
                player.connection.close()

    def session_loop(self):
        while self.active:
            player_data = [None] * len(self.players)

            for player_idx, player in enumerate(self.players):
                player_data[player_idx] = self._receive_dict_async(player.connection)

            self.communicate_game_logic(player_data)

        self.game_state.end_match()
        sys.exit(0)


from __future__ import print_function
import roslib
import rospy

from rosbridge_library.internal import ros_loader

import math
import re
import string
from base64 import standard_b64encode, standard_b64decode

from rosbridge_library.util import string_types, bson

import sys
if sys.version_info >= (3, 0):
    type_map = {
    "bool":    ["bool"],
    "int":     ["int8", "byte", "uint8", "char",
                "int16", "uint16", "int32", "uint32",
                "int64", "uint64", "float32", "float64"],
    "float":   ["float32", "float64"],
    "str":     ["string"]
    }
    primitive_types = [bool, int, float]
    python2 = False
else:
    type_map = {
    "bool":    ["bool"],
    "int":     ["int8", "byte", "uint8", "char",
                "int16", "uint16", "int32", "uint32",
                "int64", "uint64", "float32", "float64"],
    "float":   ["float32", "float64"],
    "str":     ["string"],
    "unicode": ["string"],
    "long":    ["int64", "uint64"]
    }
    primitive_types = [bool, int, long, float]  # noqa: F821
    python2 = True

list_types = [list, tuple]
ros_time_types = ["time", "duration"]
ros_primitive_types = ["bool", "byte", "char", "int8", "uint8", "int16",
                       "uint16", "int32", "uint32", "int64", "uint64",
                       "float32", "float64", "string"]
ros_header_types = ["Header", "std_msgs/Header", "roslib/Header"]
ros_binary_types = ["uint8[]", "char[]"]
list_braces = re.compile(r'\[[^\]]*\]')
ros_binary_types_list_braces = [("uint8[]", re.compile(r'uint8\[[^\]]*\]')),
                                ("char[]", re.compile(r'char\[[^\]]*\]'))]

binary_encoder = None
bson_only_mode = None

def get_encoder():
    global binary_encoder,bson_only_mode
    if binary_encoder is None:
        binary_encoder_type = rospy.get_param('~binary_encoder', 'default')
        bson_only_mode = rospy.get_param('~bson_only_mode', False)

        if binary_encoder_type == 'bson' or bson_only_mode:
            binary_encoder = bson.Binary
        elif binary_encoder_type == 'default' or binary_encoder_type == 'b64':
             binary_encoder = standard_b64encode
        else:
            print("Unknown encoder type '%s'"%binary_encoder_type)
            exit(0)
    return binary_encoder

class InvalidMessageException(Exception):
    def __init__(self, inst):
        Exception.__init__(self, "Unable to extract message values from %s instance" % type(inst).__name__)

class NonexistentFieldException(Exception):
    def __init__(self, basetype, fields):
        Exception.__init__(self, "Message type %s does not have a field %s" % (basetype, '.'.join(fields)))

class FieldTypeMismatchException(Exception):
    def __init__(self, roottype, fields, expected_type, found_type):
        if roottype == expected_type:
            Exception.__init__(self, "Expected a JSON object for type %s but received a %s" % (roottype, found_type))
        else:
            Exception.__init__(self, "%s message requires a %s for field %s, but got a %s" % (roottype, expected_type, '.'.join(fields), found_type))

def extract_values(inst):
    rostype = getattr(inst, "_type", None)
    if rostype is None:
        raise InvalidMessageException(inst=inst)
    return _from_inst(inst, rostype)

def populate_instance(msg, inst):
    """ Returns an instance of the provided class, with its fields populated
    according to the values in msg """
    return _to_inst(msg, inst._type, inst._type, inst)

def _from_inst(inst, rostype):
    global bson_only_mode
    # Special case for uint8[], we encode the string
    for binary_type, expression in ros_binary_types_list_braces:
        if expression.sub(binary_type, rostype) in ros_binary_types:
            encoded = get_encoder()(inst)
            return encoded if python2 else encoded.decode('ascii')

    # Check for time or duration
    if rostype in ros_time_types:
        return {"secs": inst.secs, "nsecs": inst.nsecs}

    if(bson_only_mode is None):bson_only_mode = rospy.get_param('~bson_only_mode', False)
    # Check for primitive types
    if rostype in ros_primitive_types:
        #JSON does not support Inf and NaN. They are mapped to None and encoded as null
        if (not bson_only_mode) and (rostype in ["float32", "float64"]):
            if math.isnan(inst) or math.isinf(inst):
                return None
        return inst

    # Check if it's a list or tuple
    if type(inst) in list_types:
        return _from_list_inst(inst, rostype)

    # Assume it's otherwise a full ros msg object
    return _from_object_inst(inst, rostype)

def _from_list_inst(inst, rostype):
    # Can duck out early if the list is empty
    if len(inst) == 0:
        return []

    # Remove the list indicators from the rostype
    rostype = list_braces.sub("", rostype)

    # Shortcut for primitives
    if rostype in ros_primitive_types and not rostype in ["float32", "float64"]:
        return list(inst)

    # Call to _to_inst for every element of the list
    return [_from_inst(x, rostype) for x in inst]

def _from_object_inst(inst, rostype):
    # Create an empty dict then populate with values from the inst
    msg = {}
    for field_name, field_rostype in zip(inst.__slots__, inst._slot_types):
        field_inst = getattr(inst, field_name)
        msg[field_name] = _from_inst(field_inst, field_rostype)
    return msg

def _to_inst(msg, rostype, roottype, inst=None, stack=[]):
    # Check if it's uint8[], and if it's a string, try to b64decode
    for binary_type, expression in ros_binary_types_list_braces:
        if expression.sub(binary_type, rostype) in ros_binary_types:
            return _to_binary_inst(msg)

    # Check the type for time or rostime
    if rostype in ros_time_types:
        return _to_time_inst(msg, rostype, inst)

    # Check to see whether this is a primitive type
    if rostype in ros_primitive_types:
        return _to_primitive_inst(msg, rostype, roottype, stack)

    # Check whether we're dealing with a list type
    if inst is not None and type(inst) in list_types:
        return _to_list_inst(msg, rostype, roottype, inst, stack)

    # Otherwise, the type has to be a full ros msg type, so msg must be a dict
    if inst is None:
        inst = ros_loader.get_message_instance(rostype)

    return _to_object_inst(msg, rostype, roottype, inst, stack)

def _to_binary_inst(msg):
    if type(msg) in string_types:
        try:
            return standard_b64decode(msg)
        except :
            return msg
    else:
        try:
            return bytes(bytearray(msg))
        except:
            return msg

def _to_time_inst(msg, rostype, inst=None):
    # Create an instance if we haven't been provided with one
    if rostype == "time" and msg == "now":
        return rospy.get_rostime()

    if inst is None:
        if rostype == "time":
            inst = rospy.rostime.Time()
        elif rostype == "duration":
            inst = rospy.rostime.Duration()
        else:
            return None

    # Copy across the fields
    for field in ["secs", "nsecs"]:
        try:
            if field in msg:
                setattr(inst, field, msg[field])
        except TypeError:
            continue

    return inst

def _to_primitive_inst(msg, rostype, roottype, stack):
    # Typecheck the msg
    msgtype = type(msg)
    if msgtype in primitive_types and rostype in type_map[msgtype.__name__]:
        return msg
    elif msgtype in string_types and rostype in type_map[msgtype.__name__]:
        return msg.encode("utf-8", "ignore") if python2 else msg
    raise FieldTypeMismatchException(roottype, stack, rostype, msgtype)

def _to_list_inst(msg, rostype, roottype, inst, stack):
    # Typecheck the msg
    if type(msg) not in list_types:
        raise FieldTypeMismatchException(roottype, stack, rostype, type(msg))

    # Can duck out early if the list is empty
    if len(msg) == 0:
        return []

    # Remove the list indicators from the rostype
    rostype = list_braces.sub("", rostype)

    # Call to _to_inst for every element of the list
    return [_to_inst(x, rostype, roottype, None, stack) for x in msg]

def _to_object_inst(msg, rostype, roottype, inst, stack):
    # Typecheck the msg
    if type(msg) is not dict:
        raise FieldTypeMismatchException(roottype, stack, rostype, type(msg))

    # Substitute the correct time if we're an std_msgs/Header
    try:
        if rostype in ros_header_types:
            cur_time = rospy.get_rostime()
            # copy attributes of global Time obj to inst.stamp
            inst.stamp.secs = cur_time.secs
            inst.stamp.nsecs = cur_time.nsecs
    except rospy.exceptions.ROSInitException as e:
        rospy.logdebug("Not substituting the correct header time: %s" % e)

    inst_fields = dict(zip(inst.__slots__, inst._slot_types))

    for field_name in msg:
        # Add this field to the field stack
        field_stack = stack + [field_name]

        # Raise an exception if the msg contains a bad field
        if not field_name in inst_fields:
            raise NonexistentFieldException(roottype, field_stack)

        field_rostype = inst_fields[field_name]
        field_inst = getattr(inst, field_name)

        field_value = _to_inst(msg[field_name], field_rostype,
                    roottype, field_inst, field_stack)

        setattr(inst, field_name, field_value)

    return inst


"""
Write a function that, given a list of words, returns the longest word.
"""

def longer(list):
    """Given a list of words, returns the longest word.

    Args:
        list (String): A list of words (String).

    Returns:
        String: Returns the longest word in the list.
    """
    res = ""
    for i in list:
        if len(i) >= len(res):
            res = i

    return res

print(longer(["Hiiiiii", "World"]))


# 구구단 쓰기
# with - as 구문에서는 f.close()를 사용하지 않음

try:
    with open("c:/coding/pyfile/output/gugu.txt", 'w') as f:
        for i in range(2, 10):
            for j in range(1, 10):
                gugu = f'{i} x {j} = {i*j}\n'
                f.write(gugu)
            f.write('\n')

except FileExistsError:
    print("파일을 찾을 수 없습니다.")


from enum import Enum
import random as rnd
import cromosoma as cr

class Ruleta():

    def aplicarSeleccion(self,poblacionAnterior,cantCromosomasPobla):
        ruleta=[0]
        valor=0
        paresPadres=[]
        for cromosoma in poblacionAnterior.arrCromosomas:
            valor+=cromosoma.funcFitness
            ruleta.append(valor)
        for _ in range ((len(poblacionAnterior.arrCromosomas)- cantCromosomasPobla) // 2): #Se debe armar 5 pares de longitud 2, tengo que castearlo a entero por que tira error
                pares=[]
                while ( len(pares) < 2 ): #Se debe armar el par, esto garantiza que siempre se forme
                    numAleatorio = rnd.random()
                    for j in range(len(poblacionAnterior.arrCromosomas)-1):  #Esto es ya que se debe recorrer toda la ruleta hasta encontrar el intervalo
                        if (numAleatorio >= ruleta[j] and numAleatorio < ruleta[j+1]):
                            pares.append(poblacionAnterior.arrCromosomas[j])
                paresPadres.append(pares)
        return paresPadres

class Torneo():

    def aplicarSeleccion(self, poblacionAnterior,cantCromosomasPobla):
        t = 2
        paresPadres = []
        for _ in range ((len(poblacionAnterior.arrCromosomas)- cantCromosomasPobla)//2):
            pares = []
            for _ in range(0, t):
                # Elijo aleatoriamente 2 cromosomas
                crom1 = poblacionAnterior.arrCromosomas[rnd.randint(0,9)]
                crom2 = poblacionAnterior.arrCromosomas[rnd.randint(0,9)]
                if crom1 >= crom2: # Elijo el mejor de ambos
                    pares.append(crom1)
                else:
                    pares.append(crom2)
            paresPadres.append(pares)
        return paresPadres

class CrossOverUnPunto():

    def __init__(self,probCross):
        self.probCrossover=probCross

    def aplicoCrossover(self,padres):
        nuevosCromosomas=[]
        for par in padres:  #Padres viene de a pares
            padre=par[0]
            madre=par[1]
            if(rnd.random() <= self.probCrossover):
                hijo1=cr.Cromosoma()
                hijo2=cr.Cromosoma()
                posicionCorte=rnd.randint(0,cr.Cromosoma.tCromo-1)
                #Se instancia los primeros N genes de cada padre hasta la posicion de corte en cada hijo
                for j in range(0,posicionCorte):
                    hijo1.insertoGen(padre.arrGenes[j])
                    hijo2.insertoGen(madre.arrGenes[j])
                    #Posteriormente se intercambian los genes de cada padre en los hijos, luego de la posicion del corte
                for k in range(posicionCorte,cr.Cromosoma.tCromo):
                    hijo1.insertoGen(madre.arrGenes[k])
                    hijo2.insertoGen(padre.arrGenes[k])
                #Se guarda cada cromosoma en la nueva poblacion
                nuevosCromosomas.append(hijo1)
                nuevosCromosomas.append(hijo2)
            else:
                #Se guarda cada cromosoma en la nueva poblacion
                nuevosCromosomas.append(padre)
                nuevosCromosomas.append(madre)
        return nuevosCromosomas

class MutacionInvertida():

    def __init__(self,probMuta):
        self.probMutacion=probMuta

    def aplicoMutacion(self,cromosomas):
        for cromosoma in cromosomas:
            if(rnd.random() <= self.probMutacion):
                cromosoma.mutoGen()


import os, sys
import time
import json
import argparse
from typing import Optional

import numpy as np
import torch
import imageio

import utils
import hydra

TEST_EPISODES = 10 # tests per model per perturb exp
TEST_NUM = 1 # perturb exp

class VideoRecorder(object):
    def __init__(self, save_dir, height=480, width=480, camera_id=0, fps=30):
        self.save_dir = save_dir
        if save_dir is not None:
            os.makedirs(self.save_dir, exist_ok=True)
        self.height = height
        self.width = width
        self.camera_id = camera_id
        self.fps = fps
        self.frames = []

    def reset(self, enabled=True):
        self.frames = []
        self.enabled = self.save_dir is not None and enabled

    def record(self, env):
        if self.enabled:
            frame = env.render(mode='rgb_array') # ,
                               # height=self.height,
                               # width=self.width,
                               # camera_id=self.camera_id)
            self.frames.append(frame)

    def save(self, file_name):
        if self.enabled:
            path = os.path.join(self.save_dir, file_name)
            imageio.mimsave(path, self.frames, fps=self.fps)

class Tester(object):
    def __init__(
        self, args,
        results_dir: str,
        agent_dir: Optional[str],
        num_steps: Optional[int] = None,
    ):
        # setup path
        self.args = args
        self.results_path = results_dir
        self.test_path = os.path.join(self.results_path, "test")
        os.makedirs(self.test_path, exist_ok=True)

        # setup rollout steps
        self.num_steps = num_steps

        # load cfg
        self.cfg = utils.load_hydra_cfg(self.results_path)

        # load env
        self._set_env()
        self.cfg.agent.params.obs_dim = self.env.observation_space.shape[0]
        self.cfg.agent.params.action_dim = self.env.action_space.shape[0]
        self.cfg.agent.params.action_range = [
            float(self.env.action_space.low.min()),
            float(self.env.action_space.high.max())
        ]

        # load agent
        self.agent = hydra.utils.instantiate(self.cfg.agent)
        self.agent.load(agent_dir)

        # video recorder
        self.video_path = os.path.join(self.results_path, "videos")
        self.video_recorder = VideoRecorder(self.video_path)

    def _set_env(self, perturb_spec=None):
        if perturb_spec is not None and self.cfg.env.params.task_kwargs is not None:
            self.cfg.env.params.task_kwargs.perturb_spec = perturb_spec
        elif perturb_spec is not None and 'dr' in perturb_spec['param']:
            self.cfg.env.params.task_name = f'{self.cfg.env.params.task_name}-dr'

        # recreate env
        self.env = utils.make_env(self.cfg)

    def _test(self, perturb_spec):
        # setup perturb to both env and wolrd model
        self._set_env(perturb_spec)
        self.video_recorder.reset(args.save_video)

        start_time = time.time()
        episode_rewards, episode_lengths = [], []
        for ep_id in range(TEST_EPISODES):
            obs = self.env.reset()
            done, i, total_reward = False, 0, 0
            while not done:
                action = self.agent.act(obs, sample=False)
                next_obs, reward, done, _ = self.env.step(action)
                self.video_recorder.record(self.env)
                total_reward += reward
                obs = next_obs
                i += 1
                if self.num_steps and i == self.num_steps:
                    break
            episode_rewards.append(total_reward)
            episode_lengths.append(i)

        mean_reward, std_reward, min_reward = np.mean(episode_rewards), np.std(episode_rewards), np.min(episode_rewards)
        mean_ep_length, std_ep_length = np.mean(episode_lengths), np.std(episode_lengths)

        print("\n=== Test {} on {} Episodes ===".format(perturb_spec, len(episode_lengths)))
        print("min_reward: {:.2f}".format(min_reward))
        print("episode_reward: {:.2f} +/- {:.2f}".format(mean_reward,std_reward))
        print("episode_length: {:.2f} +/- {:.2f}".format(mean_ep_length, std_ep_length))
        print("Cost {:.2f} seconds.".format(time.time()-start_time))

        # save test results
        save_data = {"episode_rewards": episode_rewards,
                     "episode_lengths": episode_lengths,
                     "mean_reward": mean_reward,
                     "std_reward": std_reward,
                     "min_reward": min_reward,
                     "perturb_spec": perturb_spec}

        # save videos
        test_name = "{}-{:.3e}.json".format(perturb_spec["param"], perturb_spec["start"])
        save_path = os.path.join(self.video_path, test_name)
        with open(save_path, "w") as wf:
            json.dump(save_data, wf)
        video_name = "{}-{:.3e}.mp4".format(perturb_spec["param"], perturb_spec["start"])
        self.video_recorder.save(video_name)
        print(f"Video saved to {self.video_path}/{video_name}")

    def robust_test(self):

        perturb_min, perturb_max = self.args.perturb_min, self.args.perturb_max

        for perturb_val in np.linspace(perturb_min, perturb_max, TEST_NUM):
            test_perturb_spec = {"enable": True, "period": 1, "param": self.args.perturb_param, "scheduler": "constant", "start": float(perturb_val)}
            self._test(test_perturb_spec)

        print("\n=== All Robust Test Finished! ===")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--experiments_dir",
        type=str,
        default=None,
        help="The directory where the original experiment was run.",
    )
    parser.add_argument(
        "--agent_dir",
        type=str,
        default=None,
        help="The directory where the agent configuration and data is stored. "
        "If not provided, a random agent will be used.",
    )
    parser.add_argument(
        "--perturb_param",
        type=str,
        default="pole_length",
        help="Number of samples from the model, to visualize uncertainty.",
    )
    parser.add_argument("--perturb_min", type=float, default=0.1)
    parser.add_argument("--perturb_max", type=float, default=0.1)
    parser.add_argument("--num_steps", type=int, default=1000)
    parser.add_argument("--save_video", action="store_true")
    args = parser.parse_args()

    tester = Tester(
        args,
        results_dir=args.experiments_dir,
        agent_dir=args.agent_dir,
        num_steps=args.num_steps
    )
    tester.robust_test()


import boto3
import json

credentials = {}
with open('secrets.json') as json_file:
    credentials = json.load(json_file)

#initialize comprehend module
comprehend = boto3.client(
    'comprehend',
    aws_access_key_id=credentials['aws_access_key_id'],
    aws_secret_access_key=credentials['aws_secret_access_key'],region_name=credentials['aws_region'])

sentimentData = comprehend.detect_sentiment(Text='teste', LanguageCode='pt')
print(sentimentData)


from sys import stdin
input = stdin.readline
import math

n, a, b, k = map(int, input().split())
h = list(map(int, input().split()))

ans = 0; s = []
for e in h:
    if e % (a + b) == 0:
        rem = a + b
    else:
        rem = e % (a + b)

    if a >= rem:
        ans += 1
        continue
    else:
        need = int(math.ceil(rem / a))
        s.append(need - 1)

s.sort()
for e in s:
    if k >= e:
        k -= e
        ans += 1

print(ans)

CodeForces/PyPy 3/1296D | Fight with Monsters/182062260.py


# Дан список повторяющихся элементов.
# Вернуть список с повторяющимися элементами.
# В результирющем списке не должно быть дубликатов.

some_list = ['a', 'a', 'a', 'f', 'aa', 'a', 'g', 'h', 'j', 2, 3, 2, 'f']
my_list = []
for i in some_list:
    if some_list.count(i) > 1:
        if i not in my_list:
            my_list.append(i)
print(my_list)


import jax
import jax.numpy as jnp

import haiku as hk
from .common import get_matrix_from_vector_and_parameter_indeces

class ParametrizedConstantSkewSymmetricMatrixModule(hk.Module):
    def __init__(self,
                matrix_shape : tuple,
                w_init : hk.initializers.Initializer = hk.initializers.Constant(0.0),
                name=None):
        super().__init__(name=name)
        self.matrix_shape = matrix_shape
        self.w_init = w_init

    def __call__(self, x):
        w = hk.get_parameter(
                'w',
                shape=self.matrix_shape,
                init=self.w_init
            )

        out = w - jnp.transpose(w)

        return out

class ParametrizedConstantSkewSymmetricMatrix(object):
    """
    Model a parametrized skew symmetric matrix with real entries.
    """

    def __init__(self,
                rng_key : jax.random.PRNGKey,
                model_setup : dict,
                model_name : str = 'constant_skew_symmetric_matrix',
                ):
        self.rng_key = rng_key
        self.init_rng_key = rng_key
        self.model_name = model_name
        self.model_setup = model_setup

        self._build_model()

    def _build_model(self):
        """
        Build the model.
        """
        matrix_shape = tuple(self.model_setup['matrix_shape'])

        def R_net_forward(x):
            return ParametrizedConstantSkewSymmetricMatrixModule(matrix_shape)(x)

        R_net_forward_pure = hk.without_apply_rng(hk.transform(R_net_forward))

        self.rng_key, subkey = jax.random.split(self.rng_key)
        init_params = R_net_forward_pure.init(rng=subkey, x=jnp.zeros((matrix_shape[1],)))

        def forward(params, x):
            """
            Forward pass of the model.
            """
            return R_net_forward_pure.apply(params, x)

        self.forward = jax.jit(forward)
        self.init_params = init_params


import time
import tkinter as tk
from tkinter import *
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import Mark10Controller
import GUI_tools as gui
import numpy as np

MIN_CTRL_SPD = 0.5  # mm/min
MAX_CTRL_SPD = 1100 # mm/min

MAX_CTRL_ACC = 20 # (mm/min)/s

THROUGH_STAND = "Through stand"

CTRL_SPD_DEFAULT_VAL = 500

import os
base_folder = os.path.dirname(__file__)

class ControlFrame(ttk.Frame):
    def __init__(self, win, controller : Mark10Controller.Controller):
        super().__init__(win)
        self.win = win
        self.controller = controller

        self.controller.subscribe(self.controller_callback)

        self.needs_ui_update=False
        self.initUI()
        self.updateUI()

        self.updateForceLabel() # TODO move to connect_gauge
        self.updatePositionLabel() # TODO move to connect_stand

        self.var_ctrl_vel.set(0)
        self.checkNeedsUpdate()

    def controller_callback(self):
        self.needs_ui_update=True

    def stand_port_changed(self,port):
        if(port==gui.PortSelector.ENTRY_DISCONNECT):
            self.controller.disconnect()
        else:
            print("Open stand at "+port)
            self.controller.connect_stand(port)

    def gauge_port_changed(self,port):
        if(port==THROUGH_STAND):
            self.controller.connect_gauge(Mark10Controller.CONN_THROUGH_STAND)
        else:
            print("Open gauge at "+ port)
            self.controller.connect_gauge(port)

    def zero_force_clicked(self):
        self.controller.zero_force()
        self.updateUI()

    def ctrl_up_clicked(self):
        self.controller.go_up(speed=self.ent_speed.getValue())
        self.updateUI()

    def ctrl_down_clicked(self):
        self.controller.go_down(speed=self.ent_speed.getValue())
        self.updateUI()

    def ctrl_stop_clicked(self):
        self.controller.go_stop()
        self.updateUI()

    def ctrl_goto_clicked(self):
        if(self.ent_goto.isValid()):
            self.controller.goto(self.ent_goto.getValue(),relative=False)
            self.updateUI()

    def reset_pos_clicked(self):
        self.controller.zero_position()

    def ctrl_uprel_clicked(self):
        if(self.ent_gorel.isValid()):
            self.controller.goto(self.ent_gorel.getValue(),relative=True)
            self.updateUI()

    def ctrl_downrel_clicked(self):
        if(self.ent_gorel.isValid()):
            self.controller.goto(-self.ent_gorel.getValue(),relative=True)
            self.updateUI()

    def ctrl_vel_changed(self,var):
        self.controller.command_velocity(var.get())
        # self.controller.velocity_control_set_target(var.get())
        self.lbl_ctrl_vel['text'] = f"{var.get():.1f}"

    def ctrl_vel_released(self, event):
        self.decrease_ctrl_vel()

    def decrease_ctrl_vel(self):
        DELTA_T = 10 # ms
        MAX_CTRL_SPD_CHANGE = MAX_CTRL_ACC * DELTA_T

        if(self.var_ctrl_vel.get() > 0):
            self.var_ctrl_vel.set( max(0, self.var_ctrl_vel.get()-MAX_CTRL_SPD_CHANGE ) )
        elif(self.var_ctrl_vel.get() < 0):
            self.var_ctrl_vel.set( min(0, self.var_ctrl_vel.get()+MAX_CTRL_SPD_CHANGE ) )

        if(self.var_ctrl_vel.get() != 0):
            self.after(DELTA_T, self.decrease_ctrl_vel)

    def use_hilimit_changed(self,*_):
        if(not self.ent_hilimit.isValid()):
            self.var_use_hilimit.set(0)

        if(self.var_use_hilimit.get()==1):
            self.controller.set_upper_limit(self.ent_hilimit.getValue())
        else:
            self.controller.set_upper_limit(500)

    def use_lolimit_changed(self,*_):
        if(not self.ent_lolimit.isValid()):
            self.var_use_lolimit.set(0)

        if(self.var_use_lolimit.get()==1):
            self.controller.set_lower_limit(self.ent_lolimit.getValue())
        else:
            self.controller.set_lower_limit(-500)

    def hilimit_changed(self,_):
        if(self.var_use_hilimit.get()==1): self.var_use_hilimit.set(0)

    def lolimit_changed(self,_):
        if(self.var_use_lolimit.get()==1): self.var_use_lolimit.set(0)

    def apply_force_clicked(self):
        steps=self.ent_force_steps.getArray()
        times=self.ent_force_time.getArray()

        if(steps.size==0 or times.size==0):
            return

        if(times.size==1 and steps.size>1):
            times = np.repeat(times,steps.size)

        if(steps.size!=times.size):
            print("Steps and times must have same number of values")
            return

        print(self.var_hold_force_pos)
        self.controller.apply_forces(steps, times, active_hold=(self.var_hold_force_pos.get()=="force"))
        self.updateUI()

    def cycles_show_clicked(self):
        self.tabControl.select(0)

    def apply_cycles_control_clicked(self):
        if(self.ent_cycles_force.isValid() and self.ent_cycles_incr.isValid() and self.ent_cycles_period.isValid()
            and self.ent_cycles_period2.isValid() and self.ent_cycles.isValid()):

            self.controller.apply_force_cycles(self.ent_cycles_force.getValue(), self.ent_cycles_period.getValue(),
                                                self.ent_cycles_period2.getValue(), self.ent_cycles.getValue(), self.ent_cycles_incr.getValue(),
                                                active_hold=(self.var_hold_force_pos.get()=="force"))

        self.updateUI()

    def apply_comp_control_clicked(self):
        if(self.controller.current_force() > 0.001):
            print("Warning: compliance control - force not 0!")

        if(self.ent_comp_maxforce.isValid() and self.ent_comp_maxsqueeze.isValid() and self.ent_comp_speed.isValid()):
            self.controller.test_compliance(self.ent_comp_maxforce.getValue(), self.ent_comp_maxsqueeze.getValue(), self.ent_comp_speed.getValue())
            self.updateUI()

    def apply_dyn_control_clicked(self):
        self.controller.start_dynamic_control(self.ent_dyn_force.getValue(), self.ent_dyn_speed.getValue(), self.ent_dyn_period.getValue(), self.ent_dyn_cycles.getValue())
        self.updateUI()

    def approach_control_clicked(self):
        self.controller.approach()

    def release_control_clicked(self):
        self.controller.release()

    def force_steps_changed(self,_):
        steps = self.ent_force_steps.getArray()
        times = self.ent_force_time.getArray()
        if(steps.size>0 and (steps.size==times.size or times.size==1)):
            if(times.size==1): times=np.repeat(times, steps.size)
            times = np.insert(times,0,0)
            times = np.cumsum(times)
            x = np.repeat(times,2)
            steps = np.repeat(steps,2)
            steps = np.insert(steps,0,0)
            y = np.append(steps,0)
            self.force_graph.set_data(x,y)
            x_size = max(1,times.max())
            self.ax.set_xlim((-x_size*0.15, x_size*1.15))
            self.ax.set_ylim((0,steps.max()*1.15))
        else:
            self.force_graph.set_data([],[])

        self.canvas.draw()

    def updateForceLabel(self):
        self.lbl_force["text"] = "{:.3f}".format(self.controller.current_force())
        self.after(100, self.updateForceLabel)

    def updatePositionLabel(self):
        self.lbl_pos["text"] = "{:.2f}".format(self.controller.current_position())
        self.after(100, self.updatePositionLabel)

    def updateMaxForceLabel(self,_):
        maxforce = self.ent_cycles_force.getValue()+(self.ent_cycles.getValue()-1)*self.ent_cycles_incr.getValue()
        self.lbl_cycles_maxforce["text"] = str(maxforce)
        if(maxforce > Mark10Controller.FORCE_MAX_LIMIT):
            self.lbl_cycles_maxforce["fg"]='red'
        else:
            self.lbl_cycles_maxforce["fg"]='black'

    def checkNeedsUpdate(self):
        if(self.needs_ui_update): self.updateUI()
        self.after(100,self.checkNeedsUpdate)

    def updateUI(self):
        self.lbl_con_status["text"] = self.controller.error_string()

        ctrl_ready = self.controller.control_is_ready()

        gui.set_enabled(self.btn_zero_force, ctrl_ready)

        gui.set_enabled(self.btn_ctrl_up,   ctrl_ready)
        gui.set_enabled(self.btn_ctrl_stop, self.controller.is_ok())
        gui.set_enabled(self.btn_ctrl_down, ctrl_ready)

        if(ctrl_ready):
            self.sld_ctrl_vel.state(["!disabled"])
        else:
            self.sld_ctrl_vel.state(["disabled"])

        gui.set_enabled(self.ent_hilimit, ctrl_ready)
        gui.set_enabled(self.ent_lolimit, ctrl_ready)

        gui.set_enabled(self.btn_reset_pos,     ctrl_ready)
        gui.set_enabled(self.btn_goto,          ctrl_ready)
        gui.set_enabled(self.btn_gorel_down,    ctrl_ready)
        gui.set_enabled(self.btn_gorel_up,      ctrl_ready)

        gui.set_enabled(self.btn_apply_control, ctrl_ready)
        gui.set_enabled(self.btn_apply_cycles_control, ctrl_ready)
        gui.set_enabled(self.btn_apply_comp_control, ctrl_ready)
        gui.set_enabled(self.btn_approach, ctrl_ready)
        gui.set_enabled(self.btn_release, ctrl_ready)
        self.needs_ui_update=False

    def initUI(self):

        self.pack(fill=BOTH, expand=1)

        conn_frame = ttk.Frame(self)
        conn_frame.grid(row=0,column=0,sticky=NSEW)
        conn_frame.rowconfigure(0,weight=0)
        conn_frame.columnconfigure(0,weight=1)
        self.rowconfigure(0,weight=0,minsize=30)

        gauge_frame = ttk.Frame(self)
        gauge_frame.grid(row=1,column=0,sticky=NSEW)
        self.rowconfigure(0,weight=1)
        self.rowconfigure(1,weight=2,minsize=60)

        ctrl_frame = ttk.Frame(self)
        ctrl_frame.grid(row=2,column=0,sticky=NSEW, padx=5)
        self.rowconfigure(2,weight=1)

        ## CONNECTION FRAME ##
        self.cmb_port = gui.PortSelector(conn_frame, "Stand: ", command=self.stand_port_changed, entries=[gui.PortSelector.ENTRY_DISCONNECT])
        self.cmb_port.grid(row=0,column=0,pady=(6,0),sticky=EW,padx=5)
        # gui.PortSelector(conn_frame, "Gauge: ", command=self.gauge_port_changed, entries=[THROUGH_STAND]).grid(row=1,column=0,sticky=EW,pady=(2,5),padx=5)

        self.lbl_stand_ok = Label(conn_frame, text="   ")
        self.lbl_stand_ok.grid(row=0,column=1, padx=5)
        self.lbl_gauge_ok = Label(conn_frame, text="   ")
        self.lbl_gauge_ok.grid(row=1,column=1, padx=5)

        self.lbl_con_status = Label(conn_frame, text="no response")
        self.lbl_con_status.grid(row=1, column=0, pady=(1,4))

        ## GAUGE FRAME ##
        gauge_frame.columnconfigure(0,weight=3)
        gauge_frame.columnconfigure(1,weight=0)
        gauge_frame.columnconfigure(2,weight=2)
        self.lbl_force = Label(gauge_frame, text="0.000", font=("Arial", 25), justify="center")
        self.lbl_force.grid(row=0,column=1, sticky=EW, pady=5)
        self.btn_zero_force = Button(gauge_frame, text="zero", width=1, command=self.zero_force_clicked)
        self.btn_zero_force.grid(row=0,column=2, sticky=W, pady=5)

        ## CONTROL FRAME ##
        ctrl_frame.rowconfigure(0, weight=0)
        Label(ctrl_frame, text="Speed",    font=("Arial", 14), anchor="center").grid(row=0,column=0, sticky=N, pady=(0,7))
        Label(ctrl_frame, text="Position", font=("Arial", 14), anchor="center").grid(row=0,column=1, sticky=N, pady=(0,7))
        ctrl_speed_frame = ttk.Frame(ctrl_frame)
        ctrl_speed_frame.grid(row=1,column=0,sticky=NSEW, padx=5)
        ctrl_pos_frame = ttk.Frame(ctrl_frame)
        ctrl_pos_frame.grid(row=1,column=1,sticky=NSEW)
        ctrl_frame.rowconfigure(1, weight=0)

        ctrl_frame.rowconfigure(2, weight=0)

        Label(ctrl_frame, text="Force Control", font=("Arial", 14)).grid(row=2,column=0, columnspan=2, sticky=S, pady=(20,5))
        ctrl_force_frame = ttk.Frame(ctrl_frame)
        ctrl_force_frame.grid(row=3,column=0,columnspan=2, sticky=NSEW, pady=(5,10))

        ctrl_frame.rowconfigure(3, weight=1)

        ### SPEED FRAME ###
        self.img_btn_up   = PhotoImage(file = os.path.join(base_folder, 'res/btn_up.png'))
        self.img_btn_down = PhotoImage(file = os.path.join(base_folder, 'res/btn_down.png'))
        self.img_btn_stop = PhotoImage(file = os.path.join(base_folder, 'res/btn_stop.png'))

        BUTTON_PAD = 5
        self.btn_ctrl_up = Button(ctrl_speed_frame, image=self.img_btn_up, command=self.ctrl_up_clicked)
        self.btn_ctrl_up.grid(row=0, column=0, pady=BUTTON_PAD)

        self.btn_ctrl_stop = Button(ctrl_speed_frame, image=self.img_btn_stop, command=self.ctrl_stop_clicked)
        self.btn_ctrl_stop.grid(row=1, column=0, pady=BUTTON_PAD)

        self.btn_ctrl_down = Button(ctrl_speed_frame, image=self.img_btn_down, command=self.ctrl_down_clicked)
        self.btn_ctrl_down.grid(row=2, column=0, pady=BUTTON_PAD)

        self.var_ctrl_vel = tk.DoubleVar()
        self.sld_ctrl_vel = ttk.Scale(ctrl_speed_frame, from_=MAX_CTRL_SPD, to_=-MAX_CTRL_SPD, orient="vertical", length=100, variable=self.var_ctrl_vel)
        self.sld_ctrl_vel.grid(row=0, column=1, rowspan=3, sticky=NS, padx=20, pady=BUTTON_PAD)
        self.sld_ctrl_vel.bind("<ButtonRelease-1>", self.ctrl_vel_released)
        self.var_ctrl_vel.trace("w", lambda name, index, mode, var=self.var_ctrl_vel: self.ctrl_vel_changed(var))

        self.ent_speed = gui.NumberEntry(ctrl_speed_frame, width=8, min_val=MIN_CTRL_SPD, max_val=MAX_CTRL_SPD, decimals=1, allow_negative=False, step_size=0.5, justify='center')
        self.ent_speed.insert(0,str(CTRL_SPD_DEFAULT_VAL))
        self.ent_speed.grid(row=3, column=0)

        self.lbl_ctrl_vel = Label(ctrl_speed_frame)
        self.lbl_ctrl_vel.grid(row=3,column=1)

        Label(ctrl_speed_frame, text="mm/min").grid(row=4,column=0)

        ### POS FRAME ###
        self.lbl_pos = Label(ctrl_pos_frame, font=("Arial", 14))
        self.lbl_pos.grid(row=1,column=0)
        self.btn_reset_pos = Button(ctrl_pos_frame, text="zero", width=2, command=self.reset_pos_clicked)
        self.btn_reset_pos.grid(row=1,column=1)

        self.ent_goto = gui.NumberEntry(ctrl_pos_frame, decimals=2, step_size=0.02, width=6, justify="center")
        self.ent_goto.grid(row=2, column=0)
        self.btn_goto = Button(ctrl_pos_frame, text="go", width=2, command=self.ctrl_goto_clicked)
        self.btn_goto.grid(row=2,column=1)

        GOREL_WIDTH = 6
        self.btn_gorel_up = Button(ctrl_pos_frame, text="up", width=GOREL_WIDTH-3, command=self.ctrl_uprel_clicked)
        self.btn_gorel_up.grid(row=7,column=0, columnspan=2, pady=(15,0))
        self.ent_gorel = gui.NumberEntry(ctrl_pos_frame, decimals=2, step_size=0.02, allow_negative=False, width=GOREL_WIDTH, justify="center")
        self.ent_gorel.grid(row=8, column=0, columnspan=2)
        self.btn_gorel_down = Button(ctrl_pos_frame, text="down", width=GOREL_WIDTH-3, command=self.ctrl_downrel_clicked)
        self.btn_gorel_down.grid(row=9,column=0, columnspan=2, pady=(0,20))

        Label(ctrl_pos_frame, text="Upper limit:").grid(row=11, column=0)
        self.ent_hilimit = gui.NumberEntry(ctrl_pos_frame, decimals=2, step_size=0.02, width=6, on_edit=self.hilimit_changed)
        self.ent_hilimit.grid(row=11, column=1)

        self.var_use_hilimit = IntVar()
        self.var_use_hilimit.trace_add("write", self.use_hilimit_changed)
        chk_use_hilimit = Checkbutton(ctrl_pos_frame, variable=self.var_use_hilimit)
        chk_use_hilimit.grid(row=11, column=2)

        Label(ctrl_pos_frame, text="Lower limit:").grid(row=12, column=0)
        self.ent_lolimit = gui.NumberEntry(ctrl_pos_frame, decimals=2, step_size=0.02, width=6, on_edit=self.lolimit_changed)
        self.ent_lolimit.grid(row=12, column=1, columnspan=1)

        self.var_use_lolimit = IntVar()
        self.var_use_lolimit.trace_add("write", self.use_lolimit_changed)
        chk_use_lolimit = Checkbutton(ctrl_pos_frame, variable=self.var_use_lolimit)
        chk_use_lolimit.grid(row=12, column=2)

        ### FORCE CONTORL FRAME ###
        self.tabControl = ttk.Notebook(ctrl_force_frame)
        self.tabControl.pack(expand=1, fill="both")

        step_ctrl_frame = ttk.Frame(self.tabControl)
        self.tabControl.add(step_ctrl_frame, text='Stepwise', pad=5)
        # Label(ctrl_force_frame, text="Type:").grid(row=0, column=0,sticky=EW)
        # self.cmb_control_type = ttk.Combobox(ctrl_force_frame,values=["Stepwise","Ramp"])
        # self.cmb_control_type.grid(row=0, column=1,sticky=EW)

        #### STEP CONTORL TAP ####
        step_ctrl_frame.rowconfigure(0, weight=0)
        step_ctrl_frame.rowconfigure(1, weight=0)
        step_ctrl_frame.rowconfigure(2, weight=1)
        step_ctrl_frame.columnconfigure(0, weight=1)
        step_ctrl_frame.columnconfigure(1, weight=1)

        Label(step_ctrl_frame, text="Steps [N]:").grid(row=0,column=0,padx=2,pady=(5,1))
        self.ent_force_steps = gui.ListEntry(step_ctrl_frame, val_type=float, allow_negative=False, on_edit=self.force_steps_changed)
        self.ent_force_steps.grid(row=0,column=1, sticky=EW, padx=2, pady=(5,1))

        Label(step_ctrl_frame, text="Time [s]:").grid(row=1,column=0,padx=2,pady=(1,5))
        self.ent_force_time = gui.ListEntry(step_ctrl_frame, val_type=float, allow_negative=False, on_edit=self.force_steps_changed)
        self.ent_force_time.grid(row=1,column=1, sticky=EW, padx=2, pady=(1,5))

        self.fig = Figure(figsize=(3,2))
        self.ax = self.fig.add_subplot(111)
        self.force_graph = self.ax.plot([], [], '-', ms=5, lw=1)[0]
        self.ax.set_xlim((0,1))
        self.ax.set_ylim((0,1))
        self.ax.grid()

        self.canvas = FigureCanvasTkAgg(self.fig, master=step_ctrl_frame)
        # self.canvas.get_tk_widget().pack(padx=30, pady=30)
        self.canvas.get_tk_widget().grid(row=2, column=0, columnspan=2, sticky=tk.NSEW, padx=10, pady=5)
        self.canvas.draw()

        # self.btn_apply_control = Button(step_ctrl_frame, text="Apply", command=self.apply_force_clicked)
        # self.btn_apply_control.grid(row=3,column=1, sticky=SE)

        step_ctrl_foot_frame = tk.Frame(step_ctrl_frame)
        step_ctrl_foot_frame.grid(row=3, column=0, columnspan=3, sticky=S+E+W, pady=(5,0))

        step_ctrl_foot_frame.columnconfigure(0,weight=1)
        step_ctrl_foot_frame.columnconfigure(1,weight=0)
        step_ctrl_foot_frame.columnconfigure(2,weight=0)

        self.var_hold_force_pos = tk.StringVar()
        Label(step_ctrl_foot_frame, text="Hold: ").grid(row=0,column=0,sticky=E)
        self.btn_step_hold_force = tk.Radiobutton(step_ctrl_foot_frame, text="Force", variable=self.var_hold_force_pos,indicatoron=False, value="force", width=8)
        self.btn_step_hold_force.grid(row=0, column=1, sticky=EW)
        self.btn_step_hold_pos   = tk.Radiobutton(step_ctrl_foot_frame, text="Position", variable=self.var_hold_force_pos,indicatoron=False, value="pos", width=8)
        self.btn_step_hold_pos.grid(row=0, column=2, sticky=W)
        self.var_hold_force_pos.set("force")

        step_ctrl_foot_frame.columnconfigure(3,weight=2)
        step_ctrl_foot_frame.columnconfigure(4,weight=1)

        self.btn_apply_control = Button(step_ctrl_foot_frame, text="Apply", command=self.apply_force_clicked)
        self.btn_apply_control.grid(row=0,column=4, sticky=SE, padx=(10,0))

        #### CYCLES TAB ####
        cycle_ctrl_frame = ttk.Frame(self.tabControl)
        self.tabControl.add(cycle_ctrl_frame, text='Cycles', pad=5)

        cycle_ctrl_frame.columnconfigure(0,weight=1)
        cycle_ctrl_frame.columnconfigure(1,weight=0)
        cycle_ctrl_frame.columnconfigure(2,weight=1)
        r=0
        Label(cycle_ctrl_frame, text="Force: ").grid(row=r,column=0,sticky=E)
        self.ent_cycles_force = gui.NumberEntry(cycle_ctrl_frame, allow_negative=False, min_val=Mark10Controller.FORCE_MIN_LIMIT, max_val=Mark10Controller.FORCE_MAX_LIMIT, width=6, on_edit=self.updateMaxForceLabel)
        self.ent_cycles_force.grid(row=r,column=1)
        Label(cycle_ctrl_frame, text="N").grid(row=r,column=2, sticky=W)
        r+=1
        Label(cycle_ctrl_frame, text="Increment: ").grid(row=r,column=0,sticky=E)
        self.ent_cycles_incr = gui.NumberEntry(cycle_ctrl_frame, allow_negative=False, width=6, on_edit=self.updateMaxForceLabel)
        self.ent_cycles_incr.grid(row=r,column=1)
        Label(cycle_ctrl_frame, text="N").grid(row=r,column=2, sticky=W)
        r+=1
        self.cmb_cycles_holdtime = ttk.Combobox(cycle_ctrl_frame, values=["Hold time","Step time"], width=8, state="readonly")
        self.cmb_cycles_holdtime.grid(row=r,column=0,sticky=E)
        self.cmb_cycles_holdtime.current(0)
        # Label(cycle_ctrl_frame, text="Hold period: ").grid(row=r,column=0,sticky=E)
        self.ent_cycles_period = gui.NumberEntry(cycle_ctrl_frame, allow_negative=False, width=6)
        self.ent_cycles_period.grid(row=r,column=1)
        Label(cycle_ctrl_frame, text="s").grid(row=r,column=2, sticky=W)
        r+=1
        self.cmb_cycles_holdtime = ttk.Combobox(cycle_ctrl_frame, values=["Off period","Cycle period"], width=8, state="readonly")
        self.cmb_cycles_holdtime.grid(row=r,column=0,sticky=E)
        self.cmb_cycles_holdtime.current(0)
        # Label(cycle_ctrl_frame, text="Off period: ").grid(row=r,column=0,sticky=E)
        self.ent_cycles_period2 = gui.NumberEntry(cycle_ctrl_frame, allow_negative=False, width=6)
        self.ent_cycles_period2.grid(row=r,column=1)
        Label(cycle_ctrl_frame, text="s").grid(row=r,column=2, sticky=W)
        r+=1
        Label(cycle_ctrl_frame, text="Cycles: ").grid(row=r,column=0,sticky=E)
        self.ent_cycles = gui.NumberEntry(cycle_ctrl_frame, value_type=int, min_val=1, allow_negative=False, width=6, on_edit=self.updateMaxForceLabel)
        self.ent_cycles.grid(row=r,column=1)
        r+=1
        Label(cycle_ctrl_frame, text="Max force: ").grid(row=r,column=0,sticky=E)
        self.lbl_cycles_maxforce = Label(cycle_ctrl_frame, text="0.0")
        self.lbl_cycles_maxforce.grid(row=r,column=1)
        Label(cycle_ctrl_frame, text="N").grid(row=r,column=2, sticky=W)

        # r+=1
        # self.btn_cycles_show = Button(cycle_ctrl_frame, text="Show", command=self.cycles_show_clicked)
        # self.btn_cycles_show.grid(row=r,column=0, columnspan=3, pady=(12,0))
        r+=1
        cycle_ctrl_frame.rowconfigure(r,weight=1)
        r+=1

        cycle_ctrl_foot_frame = tk.Frame(cycle_ctrl_frame)
        cycle_ctrl_foot_frame.grid(row=r, column=0, columnspan=3, sticky=S+E+W, pady=(5,0))

        cycle_ctrl_foot_frame.columnconfigure(0,weight=1)
        cycle_ctrl_foot_frame.columnconfigure(1,weight=0)
        cycle_ctrl_foot_frame.columnconfigure(2,weight=0)

        # Repeated in Step Control
        Label(cycle_ctrl_foot_frame, text="Hold: ").grid(row=0,column=0,sticky=E)
        self.btn_cycle_hold_force = tk.Radiobutton(cycle_ctrl_foot_frame, text="Force", variable=self.var_hold_force_pos,indicatoron=False, value="force", width=8)
        self.btn_cycle_hold_force.grid(row=0, column=1, sticky=EW)
        self.btn_cycle_hold_pos   = tk.Radiobutton(cycle_ctrl_foot_frame, text="Position", variable=self.var_hold_force_pos,indicatoron=False, value="pos", width=8)
        self.btn_cycle_hold_pos.grid(row=0, column=2, sticky=W)

        cycle_ctrl_foot_frame.columnconfigure(3,weight=2)
        cycle_ctrl_foot_frame.columnconfigure(4,weight=1)

        self.btn_apply_cycles_control = Button(cycle_ctrl_foot_frame, text="Apply", command=self.apply_cycles_control_clicked)
        self.btn_apply_cycles_control.grid(row=0,column=4, sticky=SE, padx=(10,0))

        #### COMPLIANCE TAB ####
        comp_ctrl_frame = ttk.Frame(self.tabControl)
        self.tabControl.add(comp_ctrl_frame, text='Compliance', pad=5)

        comp_ctrl_frame.columnconfigure(0,weight=1)
        comp_ctrl_frame.columnconfigure(1,weight=0)
        comp_ctrl_frame.columnconfigure(2,weight=1)
        Label(comp_ctrl_frame, text="Max force: ").grid(row=0,column=0,sticky=E)
        self.ent_comp_maxforce = gui.NumberEntry(comp_ctrl_frame, allow_negative=False, max_val=Mark10Controller.FORCE_MAX_LIMIT, width=6)
        self.ent_comp_maxforce.grid(row=0,column=1)
        Label(comp_ctrl_frame, text="N").grid(row=0,column=2, sticky=W)
        Label(comp_ctrl_frame, text="Max compression: ").grid(row=1,column=0,sticky=E)
        self.ent_comp_maxsqueeze = gui.NumberEntry(comp_ctrl_frame, allow_negative=False, width=6)
        self.ent_comp_maxsqueeze.grid(row=1,column=1)
        Label(comp_ctrl_frame, text="mm").grid(row=1,column=2, sticky=W)
        Label(comp_ctrl_frame, text="Speed: ").grid(row=2,column=0,sticky=E)
        self.ent_comp_speed = gui.NumberEntry(comp_ctrl_frame, min_val=MIN_CTRL_SPD, max_val=MAX_CTRL_SPD, decimals=1, allow_negative=False, step_size=0.5, width=6)
        self.ent_comp_speed.grid(row=2,column=1)
        Label(comp_ctrl_frame, text="mm/min").grid(row=2,column=2, sticky=W)

        # self.ent_comp_maxforce.setValue(1)
        # self.ent_comp_maxsqueeze.setValue(10)
        # self.ent_comp_speed.setValue(100)

        comp_ctrl_frame.rowconfigure(3,weight=1)

        self.btn_apply_comp_control = Button(comp_ctrl_frame, text="Apply", command=self.apply_comp_control_clicked)
        self.btn_apply_comp_control.grid(row=4,column=2, sticky=SE)

        #### GOTO TAB ####
        approach_ctrl_frame = ttk.Frame(self.tabControl)
        self.tabControl.add(approach_ctrl_frame, text='Goto', pad=5)

        approach_ctrl_frame.columnconfigure(0,weight=1)
        approach_ctrl_frame.columnconfigure(1,weight=0)
        approach_ctrl_frame.columnconfigure(2,weight=1)

        self.btn_approach = Button(approach_ctrl_frame, text="Approach", command=self.approach_control_clicked)
        self.btn_approach.grid(row=0,column=1)

        self.btn_release = Button(approach_ctrl_frame, text="Release", command=self.release_control_clicked)
        self.btn_release.grid(row=1,column=1)

        # #### DYNAMIC TAB ####
        # dyn_ctrl_frame = ttk.Frame(self.tabControl)
        # tabControl.add(dyn_ctrl_frame, text='Dynamic', pad=5)

        # dyn_ctrl_frame.columnconfigure(0,weight=1)
        # dyn_ctrl_frame.columnconfigure(1,weight=0)
        # dyn_ctrl_frame.columnconfigure(2,weight=1)
        # r = 0
        # Label(dyn_ctrl_frame, text="Force: ").grid(row=r,column=0,sticky=E)
        # self.ent_dyn_force = gui.NumberEntry(dyn_ctrl_frame, allow_negative=False, max_val=Mark10Controller.FORCE_MAX_LIMIT, width=6)
        # self.ent_dyn_force.grid(row=0,column=1)
        # Label(dyn_ctrl_frame, text="N").grid(row=r,column=2, sticky=W)
        # r+=1
        # Label(dyn_ctrl_frame, text="Period: ").grid(row=r,column=0,sticky=E)
        # self.ent_dyn_period = gui.NumberEntry(dyn_ctrl_frame, allow_negative=False, width=6)
        # self.ent_dyn_period.grid(row=r,column=1)
        # Label(dyn_ctrl_frame, text="s").grid(row=r,column=2, sticky=W)
        # r+=1
        # Label(dyn_ctrl_frame, text="Speed: ").grid(row=r,column=0,sticky=E)
        # self.ent_dyn_speed = gui.NumberEntry(dyn_ctrl_frame, min_val=MIN_CTRL_SPD, max_val=MAX_CTRL_SPD, decimals=1, allow_negative=False, step_size=0.5, width=6)
        # self.ent_dyn_speed.grid(row=r,column=1)
        # Label(dyn_ctrl_frame, text="mm/min").grid(row=r,column=2, sticky=W)
        # r+=1
        # Label(dyn_ctrl_frame, text="Cycles: ").grid(row=r,column=0,sticky=E)
        # self.ent_dyn_cycles = gui.NumberEntry(dyn_ctrl_frame, min_val=1, value_type=int, allow_negative=False, width=6)
        # self.ent_dyn_cycles.grid(row=r,column=1)

        # # self.ent_comp_maxforce.setValue(1)
        # # self.ent_comp_maxsqueeze.setValue(10)
        # # self.ent_comp_speed.setValue(100)
        # r+=1
        # dyn_ctrl_frame.rowconfigure(r,weight=1)

        # self.btn_apply_dyn_control = Button(dyn_ctrl_frame, text="Apply", command=self.apply_dyn_control_clicked)
        # self.btn_apply_dyn_control.grid(row=4,column=2, sticky=SE)

    def save_UI_state(self, db):
        db["stand_gui"] = {
            "cmb_port":self.cmb_port.get(),

            "ent_speed":self.ent_speed.get(),
            "ent_goto":self.ent_goto.get(),
            "ent_gorel":self.ent_gorel.get(),

            "tab_select":self.tabControl.select(),

            "ent_force_steps":self.ent_force_steps.get(),
            "ent_force_time":self.ent_force_time.get(),

            "ent_cycles_force":self.ent_cycles_force.get(),
            "ent_cycles_incr":self.ent_cycles_incr.get(),
            "ent_cycles_period":self.ent_cycles_period.get(),
            "ent_cycles_period2":self.ent_cycles_period2.get(),
            "ent_cycles":self.ent_cycles.get(),

            "ent_comp_maxforce":self.ent_comp_maxforce.get(),
            "ent_comp_maxsqueeze":self.ent_comp_maxsqueeze.get(),
            "ent_comp_speed":self.ent_comp_speed.get()
        }
        return db

    def load_UI_state(self, db):
        d = db["stand_gui"]
        if(d==None): return
        self.cmb_port.set_if_available(d["cmb_port"])

        gui.setText(self.ent_speed, d["ent_speed"])
        gui.setText(self.ent_goto, d["ent_goto"])
        gui.setText(self.ent_gorel, d["ent_gorel"])

        self.tabControl.select(d["tab_select"]),

        gui.setText(self.ent_force_steps, d["ent_force_steps"])
        gui.setText(self.ent_force_time, d["ent_force_time"])

        gui.setText(self.ent_cycles_force, d["ent_cycles_force"])
        gui.setText(self.ent_cycles_incr, d["ent_cycles_incr"])
        gui.setText(self.ent_cycles_period, d["ent_cycles_period"])
        gui.setText(self.ent_cycles_period2, d["ent_cycles_period2"])
        gui.setText(self.ent_cycles, d["ent_cycles"])

        gui.setText(self.ent_comp_maxforce, d["ent_comp_maxforce"])
        gui.setText(self.ent_comp_maxsqueeze, d["ent_comp_maxsqueeze"])
        gui.setText(self.ent_comp_speed, d["ent_comp_speed"])

if __name__ == "__main__":
    window = tk.Tk()
    window.title("Test Setup")
    window.style = ttk.Style()
    window.style.theme_use("alt")
    mark = Mark10Controller.Controller()
    ControlFrame(window, mark)
    window.mainloop()

    mark.shutdown()


# This script allows you to take screen captures with high resolution

# Set the image size in pixels
# Larger sizes may depend on your graphic card
IMAGE_WIDTH = 8000
IMAGE_HEIGHT = 6000

from robodk.robolink import *
from robodk.robomath import *  # Robot toolbox
import datetime

from tkinter import *
from tkinter import filedialog

RDK = Robolink()

date_str = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
path_rdk = RDK.getParam('PATH_OPENSTATION')
file_name = "RoboDK-Screenshot-HQ-" + date_str + ".png"

root = Tk()
root.withdraw()
types = (("PNG files", "*.png"), ("JPEG files", "*.jpg"), ("All files", "*.*"))
file_path = filedialog.asksaveasfilename(title="Select image file to save", defaultextension=types, filetypes=types, initialdir=path_rdk, initialfile=file_name)
if not file_path:
    print("Operation cancelled")
    quit()

#RDK.Render(False) # prevent flickering

# Add a new reference frame at the camera view location
ref_cam = RDK.AddFrame("Camera Position")
ref_cam.setVisible(False)
ref_cam.setPose(RDK.ViewPose().inv() * rotx(pi))

# Turn auto render back on (otherwise we get a black view)
#RDK.Render(True)

# Create a new 2D camera view with high snapshot resolution, take a snapshot and close
# More information here: https://robodk.com/doc/en/PythonAPI/robodk.html#robodk.robolink.Robolink.Cam2D_Snapshot
cam_id = RDK.Cam2D_Add(ref_cam, "SNAPSHOT=%ix%i NO_TASKBAR" % (IMAGE_WIDTH, IMAGE_HEIGHT))
#cam_id = RDK.Cam2D_Add(ref_cam, "NEAR_LENGTH=5 FAR_LENGTH=100000 FOV=30 SNAPSHOT=%ix%i NO_TASKBAR BG_COLOR=black" % (IMAGE_WIDTH, IMAGE_HEIGHT))
RDK.Cam2D_Snapshot(file_path, cam_id)
RDK.Cam2D_Close(cam_id)

# Delete the temporary reference added
ref_cam.Delete()

print("Done")
RDK.ShowMessage("High resolution snapshot saved: " + file_path, False)


##
#  File:           CARDTargetFeatureProvider.py
#  Date:           12-Jun-2021 jdw
#
#  Updates:
#   14-Mar-2023 dwp  CARD feature data will stop being used temporarily (in favor of CARD annotations)
##
"""
Accessors and generators for CARD target feature data.
"""

import datetime
import logging
import os.path
import time

from rcsb.utils.io.FileUtil import FileUtil
from rcsb.utils.io.MarshalUtil import MarshalUtil
from rcsb.utils.io.StashableBase import StashableBase
from rcsb.utils.targets.CARDTargetProvider import CARDTargetProvider

logger = logging.getLogger(__name__)

class CARDTargetFeatureProvider(StashableBase):
    """Accessors and generators for CARD target feature data."""

    def __init__(self, **kwargs):
        #
        self.__cachePath = kwargs.get("cachePath", ".")
        self.__dirName = "CARD-features"
        super(CARDTargetFeatureProvider, self).__init__(self.__cachePath, [self.__dirName])
        self.__dirPath = os.path.join(self.__cachePath, self.__dirName)
        #
        self.__mU = MarshalUtil(workPath=self.__dirPath)
        self.__fD = self.__reload(self.__dirPath, **kwargs)
        #

    def testCache(self, minCount=590):
        logger.info("CARD feature count %d", len(self.__fD["features"]) if "features" in self.__fD else 0)
        if self.__fD and "features" in self.__fD and len(self.__fD["features"]) > minCount:
            return True
        else:
            return False

    def hasFeatures(self, rcsbEntityId):
        return rcsbEntityId.upper() in self.__fD["features"]

    def getFeatures(self, rcsbEntityId):
        try:
            return self.__fD["features"][rcsbEntityId.upper()]
        except Exception:
            pass
        return []

    def reload(self):
        self.__fD = self.__reload(self.__dirPath, useCache=True)
        return True

    def __getFeatureDataPath(self):
        return os.path.join(self.__dirPath, "CARD-feature-data.json")

    def __reload(self, dirPath, **kwargs):
        startTime = time.time()
        fD = {}
        useCache = kwargs.get("useCache", True)
        ok = False
        featurePath = self.__getFeatureDataPath()
        #
        logger.info("useCache %r featurePath %r", useCache, featurePath)
        if useCache and self.__mU.exists(featurePath):
            fD = self.__mU.doImport(featurePath, fmt="json")
        else:
            fU = FileUtil()
            fU.mkdir(dirPath)
        # ---
        logger.info("Completed reload (%r) at %s (%.4f seconds)", ok, time.strftime("%Y %m %d %H:%M:%S", time.localtime()), time.time() - startTime)
        return fD

    def buildFeatureList(self, sequenceMatchFilePath, useTaxonomy=False):
        """Build polymer entity feature list for the matching entities in the input sequence match file.

        Args:
            sequenceMatchFilePath (str): sequence match output file path
            useTaxonomy (bool): apply taxonomy criteria to filter matches. Defaults to False

        Returns:
            bool: True for success or False otherwise

        """
        rDL = []
        cardP = CARDTargetProvider(cachePath=self.__cachePath, useCache=False)
        mD = self.__mU.doImport(sequenceMatchFilePath, fmt="json")
        #
        provenanceSource = "CARD"
        refScheme = "PDB entity"
        assignVersion = cardP.getAssignmentVersion()
        for queryId, matchDL in mD.items():
            qCmtD = self.__decodeComment(queryId)
            modelId = qCmtD["modelId"]
            if not cardP.hasModel(modelId):
                logger.info("Skipping CARD model %r", modelId)
                continue

            for matchD in matchDL:
                #
                fpL = []
                if "alignedRegions" in matchD:
                    fpL = [{"beg_seq_id": arD["targetBegin"], "end_seq_id": arD["targetEnd"]} for arD in matchD["alignedRegions"]]
                else:
                    fpL = [{"beg_seq_id": matchD["targetBegin"], "end_seq_id": matchD["targetEnd"]}]
                tCmtD = self.__decodeComment(matchD["target"])
                entryId = tCmtD["entityId"].split("_")[0]
                entityId = tCmtD["entityId"].split("_")[1]
                nm = cardP.getModelValue(modelId, "modelName")
                descr = cardP.getModelValue(modelId, "descr")
                featureId = cardP.getModelValue(modelId, "cvTermId")
                rD = {
                    "entry_id": entryId,
                    "entity_id": entityId,
                    "type": "CARD_MODEL",
                    "feature_id": featureId,
                    "name": nm,
                    "description": descr,
                    "provenance_source": provenanceSource,
                    "reference_scheme": refScheme,
                    "assignment_version": assignVersion,
                    "feature_positions": fpL,
                    "query_tax_name": matchD["queryTaxName"] if "queryTaxName" in matchD else None,
                    "target_tax_name": matchD["targetTaxName"] if "targetTaxName" in matchD else None,
                    "match_status": matchD["taxonomyMatchStatus"] if "taxonomyMatchStatus" in matchD else None,
                    "lca_tax_name": matchD["lcaTaxName"] if "lcaTaxName" in matchD else None,
                }
                rDL.append(rD)
        #
        qD = {}
        dD = {}
        for rD in rDL:
            eId = rD["entry_id"] + "_" + rD["entity_id"]
            fId = rD["feature_id"]
            if (eId, fId) in dD:
                continue
            dD[(eId, fId)] = True
            qD.setdefault(eId, []).append(rD)
        # --
        if useTaxonomy:
            fqD = {}
            for eId, rDL in qD.items():
                mL = []
                oL = []
                for rD in rDL:
                    if "match_status" not in rD:
                        continue
                    if rD["match_status"] in ["matched", "alternate strain", "alternate strain/serotype/isolate/genotype", "query is ancestor", "query is descendant"]:
                        mL.append(rD)
                    elif rD["match_status"] == "orthologous match (by lca)":
                        oL.append(rD)
                    else:
                        continue
                if not mL and not oL:
                    continue
                logger.debug("eId %r mL (%d) oL (%d)", eId, len(mL), len(oL))
                fqD.setdefault(eId, []).extend(mL if mL else oL)
        else:
            fqD = qD
        # --

        fp = self.__getFeatureDataPath()
        tS = datetime.datetime.now().isoformat()
        vS = datetime.datetime.now().strftime("%Y-%m-%d")
        ok = self.__mU.doExport(fp, {"version": vS, "created": tS, "features": fqD}, fmt="json", indent=3)
        return ok

    def __decodeComment(self, comment, separator="|"):
        dD = {}
        try:
            ti = iter(comment.split(separator))
            dD = {tup[1]: tup[0] for tup in zip(ti, ti)}
        except Exception:
            pass
        return dD


import requests
from lxml import etree
import json
from time import sleep

def get_response(url):
    response=requests.get(url,headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"})
    return  response

def parse_html(response):
    html=etree.HTML(response.text)
    page_img_urls=[]
    page_img_content=html.xpath('//div[@class="postlist"]//ul//li')
    for img_content in page_img_content:
        img_info={}
        img_info['name']=img_content.xpath('./a[1]/img/@alt')
        img_info['url']=img_content.xpath('./a[1]/@href')
        page_img_urls.append(img_info)
    print(page_img_urls)
    return page_img_urls

def get_next_page_url(response):
    html=etree.HTML(response.text)
    next_page_url=html.xpath('//a[text()="下一页»"]/@href')
    if len(next_page_url)>0:
        return next_page_url[0]
    else:
        return None

def save_to_file(filename,data):
    data=json.dumps(data)
    filename=filename+'.json'
    with open(filename,'w') as f:
        print('正在保存{}'.format(filename))
        f.write(data)
    print('保存完成')

def run():
    '''
    1.获取首页response 内容
    2. 解析内容套图名称及url地址
    3. 循环获取下一页的response内容
    '''
    index_urls=['https://www.mzitu.com/xinggan/','https://www.mzitu.com/japan/','https://www.mzitu.com/taiwan/','https://www.mzitu.com/mm/']
    index_url='https://www.mzitu.com/xinggan/'
    all_img_info=[]
    response=get_response(index_url)
    parse_html(response)
    next_page_url=get_next_page_url(response)
    while next_page_url:
        sleep(2)
        print(next_page_url)
        response = get_response(next_page_url)
        all_img_info.extend(parse_html(response))
        next_page_url = get_next_page_url(response)
    print('爬取完成，共%d条数据'%len(all_img_info))
    save_to_file(filename='meizi',data=all_img_info)

if __name__ == '__main__':
    run()


import subprocess
import time
import re
import os
from datetime import datetime

from flask import g
from common_libs.common.dbconnect import DBConnectWs
from common_libs.common.util import get_timestamp, get_maintenance_mode_setting
from common_libs.ci.util import log_err

from common_libs.terraform_driver.cli.Const import Const as TFCLIConst
from libs import functions as func

def backyard_main(organization_id, workspace_id):
    g.applogger.debug(g.appmsg.get_log_message("BKY-00001"))

    retBool = main_logic(organization_id, workspace_id)
    if retBool is True:
        # 正常終了
        g.applogger.debug(g.appmsg.get_log_message("BKY-00002"))
    else:
        # エラーが一つでもある
        g.applogger.debug(g.appmsg.get_log_message("BKY-00003"))

def main_logic(organization_id, workspace_id):
    # db instance
    wsDb = DBConnectWs(workspace_id)  # noqa: F405

    # メンテナンスモードのチェック
    try:
        maintenance_mode = get_maintenance_mode_setting()
        # data_update_stopの値が"1"の場合、メンテナンス中のためreturnする。
        if str(maintenance_mode['data_update_stop']) == "1":
            g.applogger.debug(g.appmsg.get_log_message("BKY-00005", []))
            return True
    except Exception:
        # エラーログ出力
        g.applogger.error(g.appmsg.get_log_message("BKY-00008", []))
        return False

    # 準備中/実行中の作業インスタンスに関して、実行プロセスが存在しているかの存在確認
    if child_process_exist_check(wsDb, organization_id, workspace_id) is False:
        g.applogger.debug(g.appmsg.get_log_message("MSG-10059"))
        return False

    # 実行中インスタンスのワークスペースIDをキャッシュ
    records = get_running_process(wsDb)
    executed_worksapce_id_list = []
    for record in records:
        worksapce_id = record['I_WORKSPACE_ID']

        if worksapce_id in executed_worksapce_id_list:
            continue
        executed_worksapce_id_list.append(worksapce_id)

    # backyard_execute_stopの値が"1"の場合、メンテナンス中のためreturnする。
    if str(maintenance_mode['backyard_execute_stop']) == "1":
        g.applogger.debug(g.appmsg.get_log_message("BKY-00006", []))
        return True

    # 未実行（実行待ち）の作業を実行
    retBool = run_unexecuted(wsDb, organization_id, workspace_id, executed_worksapce_id_list)
    if retBool is False:
        return False

    return True

def child_process_exist_check(wsDb: DBConnectWs, organization_id, workspace_id):
    """
    実行中の子プロの起動確認

    Returns:
        bool
    """
    # psコマンドでbackyard_child_init.pyの起動プロセスリストを作成
    # psコマンドがマレに起動プロセスリストを取りこぼすことがあるので3回分を作成
    # command = ["python3", "backyard/backyard_child_init.py", organization_id, workspace_id, execution_no]

    child_process_1 = child_process_exist_check_ps()
    time.sleep(0.05)
    child_process_2 = child_process_exist_check_ps()
    time.sleep(0.05)
    child_process_3 = child_process_exist_check_ps()

    records = get_running_process(wsDb)
    for rec in records:
        tf_workspace_id = rec["I_WORKSPACE_ID"]
        execution_no = rec["EXECUTION_NO"]

        # 子プロ起動確認
        is_running = False

        # command = ["python3", "backyard/backyard_child_init.py", organization_id, workspace_id, tf_workspace_id, execution_no]
        command_args = "{} {} {} {} {}".format('backyard/backyard_child_init.py', g.ORGANIZATION_ID, g.WORKSPACE_ID, tf_workspace_id, execution_no)  # noqa: E501

        child_process_arr = child_process_1.split('\n')
        for r_child_process in child_process_arr:
            if re.search(command_args, r_child_process) is not None:
                is_running = True

        if is_running is False:
            child_process_arr = child_process_2.split('\n')
            for r_child_process in child_process_arr:
                if re.search(command_args, r_child_process) is not None:
                    is_running = True

        if is_running is False:
            child_process_arr = child_process_3.split('\n')
            for r_child_process in child_process_arr:
                if re.search(command_args, r_child_process) is not None:
                    is_running = True

        # DBのステータスが実行中なのに、子プロセスが存在しない
        if is_running is False:
            g.applogger.info(g.appmsg.get_log_message("MSG-10056", [execution_no, tf_workspace_id]))

            # 情報を再取得して、想定外エラーにする
            result = func.get_execution_process_info(wsDb, TFCLIConst, execution_no)
            if result[0] is False:
                log_err(result[1])
                return False
            execute_data = result[1]

            # 実行中か再確認
            status_id_list = [TFCLIConst.STATUS_PREPARE, TFCLIConst.STATUS_PROCESSING, TFCLIConst.STATUS_PROCESS_DELAYED]
            if execute_data["STATUS_ID"] in status_id_list:
                # 想定外エラーに更新
                wsDb.db_transaction_start()
                time_stamp = get_timestamp()
                update_data = {
                    "EXECUTION_NO": execution_no,
                    "STATUS_ID": TFCLIConst.STATUS_EXCEPTION,
                    "TIME_START": time_stamp,
                    "TIME_END": time_stamp,
                }
                result, execute_data = func.update_execution_record(wsDb, TFCLIConst, update_data)
                if result is True:
                    wsDb.db_commit()
                    g.applogger.debug(g.appmsg.get_log_message("MSG-10060", [execution_no, tf_workspace_id]))

    return True

def child_process_exist_check_ps():
    """
    実行中の子プロのpsコマンド取得結果

    Returns:
        stdout row
    """
    # ps -efw | grep backyard/backyard_child_init.py | grep -v grep
    cp1 = subprocess.run(
        ["ps", "-efw"],
        capture_output=True,
        text=True
    )
    if cp1.returncode != 0 and cp1.stderr:
        cp1.check_returncode()

    cp2 = subprocess.run(
        ["grep", "backyard/backyard_child_init.py"],
        capture_output=True,
        text=True,
        input=cp1.stdout
    )
    if cp2.returncode != 0 and cp2.stderr:
        cp2.check_returncode()

    cp3 = subprocess.run(
        ["grep", "-v", "grep"],
        capture_output=True,
        text=True,
        input=cp2.stdout
    )

    if cp3.returncode == 0:
        # 検索結果あり
        return cp3.stdout
    elif cp3.returncode == 1 and not cp3.stderr:
        # 検索結果なし（0行）
        return ""
    else:
        # 例外を起こす
        cp3.check_returncode()

def get_running_process(wsDb):
    """
    実行中の作業データを取得

    Returns:
        records
    """
    status_id_list = [TFCLIConst.STATUS_PREPARE, TFCLIConst.STATUS_PROCESSING, TFCLIConst.STATUS_PROCESS_DELAYED]
    prepared_list = list(map(lambda a: "%s", status_id_list))

    condition = 'WHERE `DISUSE_FLAG`=0 AND `STATUS_ID` in ({})'.format(','.join(prepared_list))
    records = wsDb.table_select(TFCLIConst.T_EXEC_STS_INST, condition, status_id_list)

    return records

def get_now_datetime(format='%Y/%m/%d %H:%M:%S', type='str'):
    dt = datetime.now().strftime(format)
    if type == 'str':
        return '{}'.format(dt)
    else:
        return dt

def run_unexecuted(wsDb: DBConnectWs, organization_id, workspace_id, executed_worksapce_id_list):
    """
    未実行（実行待ち）の作業を実行

    Returns:
        bool
        err_msg
    """
    condition = """WHERE `DISUSE_FLAG`=0 AND (
        ( `TIME_BOOK` IS NULL AND `STATUS_ID` = %s ) OR
        ( `TIME_BOOK` <= %s AND `STATUS_ID` = %s )
    ) ORDER BY TIME_REGISTER ASC"""
    records = wsDb.table_select(TFCLIConst.T_EXEC_STS_INST, condition, [TFCLIConst.STATUS_NOT_YET, get_now_datetime(), TFCLIConst.STATUS_RESERVE])

    # 処理対象レコードが0件の場合は処理終了
    if len(records) == 0:
        g.applogger.debug(g.appmsg.get_log_message("MSG-10749"))
        return True

    # 実行順リストを作成する
    # ・並び替え（workspace混在・同一ワークスペースを含む）
    execution_info_datalist = {}
    execution_order_list = []

    for rec in records:
        execution_no = rec["EXECUTION_NO"]

        # 予約時間or最終更新日+ソート用カラム+作業番号（判別用）でリスト生成
        id = str(rec["LAST_UPDATE_TIMESTAMP"]) + "_" + str(rec["TIME_REGISTER"]) + "_" + execution_no
        if rec["TIME_BOOK"]:
            if rec["LAST_UPDATE_TIMESTAMP"] < rec["TIME_BOOK"]:
                id = str(rec["TIME_BOOK"]) + "_" + str(rec["TIME_REGISTER"]) + "_" + execution_no
        execution_order_list.append(id)
        execution_info_datalist[id] = rec
    # ソート
    execution_order_list.sort()

    # 現在の実行数
    num_of_run_instance = 0
    # 並列実行数の上限
    num_of_parallel_exec = 100

    # 処理実行順に対象作業インスタンスを実行
    is_fail = False  # 一つでも失敗した
    for execution_order in execution_order_list:
        execute_data = execution_info_datalist[execution_order]
        execution_no = execute_data['EXECUTION_NO']
        tf_workspace_id = execute_data['I_WORKSPACE_ID']

        # 既に実行に回されたワークスペースは実行しない（次回のサービス実行まで待つ）
        if tf_workspace_id in executed_worksapce_id_list:
            continue

        # 並列実行数判定
        if num_of_run_instance >= num_of_parallel_exec:
            g.applogger.info(g.appmsg.get_log_message("BKY-10001"))
            return False
        num_of_run_instance = num_of_run_instance + 1

        # ワークスペースを配列にキャッシュ
        executed_worksapce_id_list.append(tf_workspace_id)

        # データを取り直して、作業実行
        result = run_child_process(wsDb, execute_data, organization_id, workspace_id)
        if result[0] is False:
            # 準備中にエラーが発生
            g.applogger.error(result[1])
            is_fail = True
            # 処理対象の作業インスタンスのステータスを想定外エラーに設定
            wsDb.db_transaction_start()
            time_stamp = get_timestamp()
            update_data = {
                "EXECUTION_NO": execution_no,
                "STATUS_ID": TFCLIConst.STATUS_EXCEPTION,
                "TIME_START": time_stamp,
                "TIME_END": time_stamp,
            }
            result, execute_data = func.update_execution_record(wsDb, TFCLIConst, update_data)
            if result is True:
                wsDb.db_commit()
                g.applogger.debug(g.appmsg.get_log_message("MSG-10060", [execution_no, tf_workspace_id]))

    if is_fail is True:
        return False
    else:
        return True

def run_child_process(wsDb, execute_data, organization_id, workspace_id):
    """
    作業実行するための子プロセスの準備・実行

    Returns:
        bool
        err_msg
    """
    execution_no = execute_data['EXECUTION_NO']
    tf_workspace_id = execute_data['I_WORKSPACE_ID']
    # tf_workspace_name = execute_data['I_WORKSPACE_NAME']

    # 処理対象の作業インスタンス情報取得(再取得)
    retBool, result = func.get_execution_process_info(wsDb, TFCLIConst, execution_no)
    if retBool is False:
        return False, result
    execute_data = result

    # 未実行状態かを判定
    status_id = execute_data["STATUS_ID"]
    if status_id != TFCLIConst.STATUS_NOT_YET and status_id != TFCLIConst.STATUS_RESERVE:
        return False, g.appmsg.get_log_message("BKY-10002", [execution_no])

    # 処理対象の作業インスタンスのステータスを準備中に設定
    wsDb.db_transaction_start()
    update_data = {
        "EXECUTION_NO": execution_no,
        "STATUS_ID": TFCLIConst.STATUS_PREPARE,
        "TIME_START": get_timestamp()
    }
    result, execute_data = func.update_execution_record(wsDb, TFCLIConst, update_data)
    if result is True:
        wsDb.db_commit()
        g.applogger.debug(g.appmsg.get_log_message("MSG-10730", [execution_no]))

    # ワークスペース毎のディレクトリを準備
    base_dir = os.environ.get('STORAGEPATH') + "{}/{}".format(g.get('ORGANIZATION_ID'), g.get('WORKSPACE_ID'))
    workspace_work_dir = base_dir + TFCLIConst.DIR_WORK + "/{}/work".format(tf_workspace_id)
    os.makedirs(workspace_work_dir, exist_ok=True)
    os.chmod(workspace_work_dir, 0o777)

    # （前回実行した）ファイルを削除しておく
    # ・緊急停止ファイル
    emergency_stop_file_path = "{}/{}".format(workspace_work_dir, TFCLIConst.FILE_EMERGENCY_STOP)
    rm_file_list = [emergency_stop_file_path]
    for rm_file in rm_file_list:
        if os.path.isfile(rm_file):
            os.remove(rm_file)

    # 子プロセスにして、実行
    g.applogger.debug(g.appmsg.get_log_message("MSG-10745", [execution_no, tf_workspace_id]))

    command = ["python3", "backyard/backyard_child_init.py", organization_id, workspace_id, tf_workspace_id, execution_no]
    # print(" ".join(command))
    cp = subprocess.Popen(command)  # noqa: F841

    return True,


# This script creates a Keywords table and for each image adds rows of (image_id, token) into the table.
# This data is used to create the postings list for each term in query time.

import re
import sqlite3

DB_FILE_NAME = "db.sqlite3"
KEYWORDS_TABLE_NAME = "KEYWORDS"
IMAGES_TABLE_NAME = "IMAGE"

def create_connection():
    conn = sqlite3.connect(DB_FILE_NAME)
    return conn

def create_table(cursor, table_name):

    command = "CREATE TABLE IF NOT EXISTS {} (" \
              "IMAGE INT NOT NULL," \
              "TOKEN TEXT NOT NULL)".format(table_name)

    cursor.execute(command)

def populate_keywords_db(cursor):
    regex = re.compile("(\w+)")
    command = "SELECT ID, TOKENS from {}".format(IMAGES_TABLE_NAME)
    cursor.execute(command)
    results = cursor.fetchall()
    for row in results:
        tokens = regex.findall(row[1])
        id = row[0]
        for token in tokens:
            command = "INSERT INTO {} VALUES({}, \"{}\")".format(KEYWORDS_TABLE_NAME, id, token)
            cursor.execute(command)

def main():
    conn = create_connection()
    cursor = conn.cursor()
    create_table(cursor, KEYWORDS_TABLE_NAME)
    populate_keywords_db(cursor)
    conn.commit()
    conn.close()

if __name__ == "__main__":
    main()


#!/bin/python3

import math
import os
import random
import re
import sys

#
# Complete the 'insertionSort1' function below.
#
# The function accepts following parameters:
#  1. INTEGER n
#  2. INTEGER_ARRAY arr
#

def insertionSort1(n, arr):
    # Write your code here
    temp = arr[len(arr) - 1]
    for i in range(len(arr)-1, -1, -1):
        if i >= 1 and arr[i-1] > temp:
            arr[i] = arr[i-1]
            print(' '.join(list(map(str, arr))))
        else:
            arr[i] = temp
            print(' '.join(list(map(str, arr))))
            break

if __name__ == '__main__':
    n = int(input().strip())

    arr = list(map(int, input().rstrip().split()))

    insertionSort1(n, arr)

Insertion Sort - Part 1/insertion sort.py