import logging
import math
import sys
import time
from collections import OrderedDict, deque
from datetime import datetime
from typing import Dict, List, Optional

import botocore

from samcli.commands._utils.table_print import MIN_OFFSET, newline_per_item, pprint_column_names, pprint_columns
from samcli.commands.deploy import exceptions as deploy_exceptions
from samcli.commands.deploy.exceptions import (
    ChangeSetError,
    DeployBucketInDifferentRegionError,
    DeployFailedError,
    DeployStackOutPutFailedError,
    DeployStackStatusMissingError,
)
from samcli.lib.deploy.utils import DeployColor, FailureMode
from samcli.lib.package.local_files_utils import get_uploaded_s3_object_name, mktempfile
from samcli.lib.package.s3_uploader import S3Uploader
from samcli.lib.utils.colors import Colored, Colors
from samcli.lib.utils.s3 import parse_s3_url
from samcli.lib.utils.time import to_datetime, utc_to_timestamp

LOG = logging.getLogger(__name__)

DESCRIBE_STACK_EVENTS_FORMAT_STRING = (
    "{ResourceStatus:<{0}} {ResourceType:<{1}} {LogicalResourceId:<{2}} {ResourceStatusReason:<{3}}"
)
DESCRIBE_STACK_EVENTS_DEFAULT_ARGS = OrderedDict(
    {
        "ResourceStatus": "ResourceStatus",
        "ResourceType": "ResourceType",
        "LogicalResourceId": "LogicalResourceId",
        "ResourceStatusReason": "ResourceStatusReason",
    }
)

DESCRIBE_STACK_EVENTS_TABLE_HEADER_NAME = "CloudFormation events from stack operations (refresh every {} seconds)"

DESCRIBE_CHANGESET_FORMAT_STRING = "{Operation:<{0}} {LogicalResourceId:<{1}} {ResourceType:<{2}} {Replacement:<{3}}"
DESCRIBE_CHANGESET_DEFAULT_ARGS = OrderedDict(
    {
        "Operation": "Operation",
        "LogicalResourceId": "LogicalResourceId",
        "ResourceType": "ResourceType",
        "Replacement": "Replacement",
    }
)

DESCRIBE_CHANGESET_TABLE_HEADER_NAME = "CloudFormation stack changeset"

OUTPUTS_FORMAT_STRING = "{Outputs:<{0}}"
OUTPUTS_DEFAULTS_ARGS = OrderedDict({"Outputs": "Outputs"})

OUTPUTS_TABLE_HEADER_NAME = "CloudFormation outputs from deployed stack"

# 500ms of sleep time between stack checks and describe stack events.
DEFAULT_CLIENT_SLEEP = 0.5

class Deployer:
    def __init__(self, cloudformation_client, changeset_prefix="samcli-deploy", client_sleep=DEFAULT_CLIENT_SLEEP):
        self._client = cloudformation_client
        self.changeset_prefix = changeset_prefix
        try:
            self.client_sleep = float(client_sleep)
        except ValueError:
            self.client_sleep = DEFAULT_CLIENT_SLEEP
        if self.client_sleep <= 0:
            self.client_sleep = DEFAULT_CLIENT_SLEEP
        # 2000ms of backoff time which is exponentially used, when there are exceptions during describe stack events
        self.backoff = 2
        # Maximum number of attempts before raising exception back up the chain.
        self.max_attempts = 3
        self.deploy_color = DeployColor()
        self._colored = Colored()

    # pylint: disable=inconsistent-return-statements
    def has_stack(self, stack_name):
        """
        Checks if a CloudFormation stack with given name exists

        :param stack_name: Name or ID of the stack
        :return: True if stack exists. False otherwise
        """
        try:
            resp = self._client.describe_stacks(StackName=stack_name)
            if not resp["Stacks"]:
                return False

            # When you run CreateChangeSet on a a stack that does not exist,
            # CloudFormation will create a stack and set it's status
            # REVIEW_IN_PROGRESS. However this stack is cannot be manipulated
            # by "update" commands. Under this circumstances, we treat like
            # this stack does not exist and call CreateChangeSet will
            # ChangeSetType set to CREATE and not UPDATE.
            stack = resp["Stacks"][0]
            return stack["StackStatus"] != "REVIEW_IN_PROGRESS"

        except botocore.exceptions.ClientError as e:
            # If a stack does not exist, describe_stacks will throw an
            # exception. Unfortunately we don't have a better way than parsing
            # the exception msg to understand the nature of this exception.

            if "Stack with id {0} does not exist".format(stack_name) in str(e):
                LOG.debug("Stack with id %s does not exist", stack_name)
                return False

            LOG.debug("Unknown ClientError recieved: %s. Cannot determine if stack exists.", str(e))
            raise DeployFailedError(stack_name=stack_name, msg=str(e)) from e
        except botocore.exceptions.BotoCoreError as e:
            # If there are credentials, environment errors,
            # catch that and throw a deploy failed error.

            LOG.debug("Botocore Exception : %s", str(e))
            raise DeployFailedError(stack_name=stack_name, msg=str(e)) from e

        except Exception as e:
            # We don't know anything about this exception. Don't handle
            LOG.debug("Unable to get stack details.", exc_info=e)
            raise e

    def create_changeset(
        self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
    ):
        """
        Call Cloudformation to create a changeset and wait for it to complete

        :param stack_name: Name or ID of stack
        :param cfn_template: CloudFormation template string
        :param parameter_values: Template parameters object
        :param capabilities: Array of capabilities passed to CloudFormation
        :param role_arn: the Arn of the role to create changeset
        :param notification_arns: Arns for sending notifications
        :param s3_uploader: S3Uploader object to upload files to S3 buckets
        :param tags: Array of tags passed to CloudFormation
        :return:
        """
        if not self.has_stack(stack_name):
            changeset_type = "CREATE"
            # When creating a new stack, UsePreviousValue=True is invalid.
            # For such parameters, users should either override with new value,
            # or set a Default value in template to successfully create a stack.
            parameter_values = [x for x in parameter_values if not x.get("UsePreviousValue", False)]
        else:
            changeset_type = "UPDATE"
            # UsePreviousValue not valid if parameter is new
            summary = self._client.get_template_summary(StackName=stack_name)
            existing_parameters = [parameter["ParameterKey"] for parameter in summary["Parameters"]]
            parameter_values = [
                x
                for x in parameter_values
                if not (x.get("UsePreviousValue", False) and x["ParameterKey"] not in existing_parameters)
            ]

        # Each changeset will get a unique name based on time.
        # Description is also setup based on current date and that SAM CLI is used.
        kwargs = {
            "ChangeSetName": self.changeset_prefix + str(int(time.time())),
            "StackName": stack_name,
            "TemplateBody": cfn_template,
            "ChangeSetType": changeset_type,
            "Parameters": parameter_values,
            "Description": "Created by SAM CLI at {0} UTC".format(datetime.utcnow().isoformat()),
            "Tags": tags,
        }

        kwargs = self._process_kwargs(kwargs, s3_uploader, capabilities, role_arn, notification_arns)
        return self._create_change_set(stack_name=stack_name, changeset_type=changeset_type, **kwargs)

    @staticmethod
    def _process_kwargs(
        kwargs: dict,
        s3_uploader: Optional[S3Uploader],
        capabilities: Optional[List[str]],
        role_arn: Optional[str],
        notification_arns: Optional[List[str]],
    ) -> dict:
        # If an S3 uploader is available, use TemplateURL to deploy rather than
        # TemplateBody. This is required for large templates.
        if s3_uploader:
            with mktempfile() as temporary_file:
                temporary_file.write(kwargs.pop("TemplateBody"))
                temporary_file.flush()
                remote_path = get_uploaded_s3_object_name(file_path=temporary_file.name, extension="template")
                # TemplateUrl property requires S3 URL to be in path-style format
                parts = parse_s3_url(s3_uploader.upload(temporary_file.name, remote_path), version_property="Version")
                kwargs["TemplateURL"] = s3_uploader.to_path_style_s3_url(parts["Key"], parts.get("Version", None))

        # don't set these arguments if not specified to use existing values
        if capabilities is not None:
            kwargs["Capabilities"] = capabilities
        if role_arn is not None:
            kwargs["RoleARN"] = role_arn
        if notification_arns is not None:
            kwargs["NotificationARNs"] = notification_arns
        return kwargs

    def _create_change_set(self, stack_name, changeset_type, **kwargs):
        try:
            resp = self._client.create_change_set(**kwargs)
            return resp, changeset_type
        except botocore.exceptions.ClientError as ex:
            if "The bucket you are attempting to access must be addressed using the specified endpoint" in str(ex):
                raise DeployBucketInDifferentRegionError(f"Failed to create/update stack {stack_name}") from ex
            raise ChangeSetError(stack_name=stack_name, msg=str(ex)) from ex

        except Exception as ex:
            LOG.debug("Unable to create changeset", exc_info=ex)
            raise ChangeSetError(stack_name=stack_name, msg=str(ex)) from ex

    @pprint_column_names(
        format_string=DESCRIBE_CHANGESET_FORMAT_STRING,
        format_kwargs=DESCRIBE_CHANGESET_DEFAULT_ARGS,
        table_header=DESCRIBE_CHANGESET_TABLE_HEADER_NAME,
    )
    def describe_changeset(self, change_set_id, stack_name, **kwargs):
        """
        Call Cloudformation to describe a changeset

        :param change_set_id: ID of the changeset
        :param stack_name: Name of the CloudFormation stack
        :param kwargs: Other arguments to pass to pprint_columns()
        :return: dictionary of changes described in the changeset.
        """
        paginator = self._client.get_paginator("describe_change_set")
        response_iterator = paginator.paginate(ChangeSetName=change_set_id, StackName=stack_name)
        changes = {"Add": [], "Modify": [], "Remove": []}
        changes_showcase = {"Add": "+ Add", "Modify": "* Modify", "Remove": "- Delete"}
        changeset = False
        for item in response_iterator:
            cf_changes = item.get("Changes")
            for change in cf_changes:
                changeset = True
                resource_props = change.get("ResourceChange")
                action = resource_props.get("Action")
                changes[action].append(
                    {
                        "LogicalResourceId": resource_props.get("LogicalResourceId"),
                        "ResourceType": resource_props.get("ResourceType"),
                        "Replacement": "N/A"
                        if resource_props.get("Replacement") is None
                        else resource_props.get("Replacement"),
                    }
                )

        for k, v in changes.items():
            for value in v:
                row_color = self.deploy_color.get_changeset_action_color(action=k)
                pprint_columns(
                    columns=[
                        changes_showcase.get(k, k),
                        value["LogicalResourceId"],
                        value["ResourceType"],
                        value["Replacement"],
                    ],
                    width=kwargs["width"],
                    margin=kwargs["margin"],
                    format_string=DESCRIBE_CHANGESET_FORMAT_STRING,
                    format_args=kwargs["format_args"],
                    columns_dict=DESCRIBE_CHANGESET_DEFAULT_ARGS.copy(),
                    color=row_color,
                )

        if not changeset:
            # There can be cases where there are no changes,
            # but could be an an addition of a SNS notification topic.
            pprint_columns(
                columns=["-", "-", "-", "-"],
                width=kwargs["width"],
                margin=kwargs["margin"],
                format_string=DESCRIBE_CHANGESET_FORMAT_STRING,
                format_args=kwargs["format_args"],
                columns_dict=DESCRIBE_CHANGESET_DEFAULT_ARGS.copy(),
            )

        return changes

    def wait_for_changeset(self, changeset_id, stack_name):
        """
        Waits until the changeset creation completes

        :param changeset_id: ID or name of the changeset
        :param stack_name:   Stack name
        """
        sys.stdout.write("\n\nWaiting for changeset to be created..\n\n")
        sys.stdout.flush()

        # Wait for changeset to be created
        waiter = self._client.get_waiter("change_set_create_complete")
        # Use default client_sleep to set the delay between polling
        # To override use SAM_CLI_POLL_DELAY environment variable
        waiter_config = {"Delay": self.client_sleep}
        try:
            waiter.wait(ChangeSetName=changeset_id, StackName=stack_name, WaiterConfig=waiter_config)
        except botocore.exceptions.WaiterError as ex:
            resp = ex.last_response
            status = resp.get("Status")
            reason = resp.get("StatusReason")

            if not status or not reason:
                # not a CFN DescribeChangeSet response, re-raising
                LOG.debug("Failed while waiting for changeset: %s", ex)
                raise ex

            if (
                status == "FAILED"
                and "The submitted information didn't contain changes." in reason
                or "No updates are to be performed" in reason
            ):
                raise deploy_exceptions.ChangeEmptyError(stack_name=stack_name)

            raise ChangeSetError(stack_name=stack_name, msg=f"ex: {ex} Status: {status}. Reason: {reason}") from ex

    def execute_changeset(self, changeset_id, stack_name, disable_rollback):
        """
        Calls CloudFormation to execute changeset

        :param changeset_id: ID of the changeset
        :param stack_name: Name or ID of the stack
        :param disable_rollback: Preserve the state of previously provisioned resources when an operation fails.
        """
        try:
            self._client.execute_change_set(
                ChangeSetName=changeset_id, StackName=stack_name, DisableRollback=disable_rollback
            )
        except botocore.exceptions.ClientError as ex:
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

    def get_last_event_time(self, stack_name, default_time=time.time()):
        """
        Finds the last event time stamp that presents for the stack, if not return the default_time
        :param stack_name: Name or ID of the stack
        :param default_time: the default unix epoch time to be returned in case if the stack provided does not exist
        :return: unix epoch
        """
        try:
            return utc_to_timestamp(
                self._client.describe_stack_events(StackName=stack_name)["StackEvents"][0]["Timestamp"]
            )
        except KeyError:
            return default_time

    @pprint_column_names(
        format_string=DESCRIBE_STACK_EVENTS_FORMAT_STRING,
        format_kwargs=DESCRIBE_STACK_EVENTS_DEFAULT_ARGS,
        table_header=DESCRIBE_STACK_EVENTS_TABLE_HEADER_NAME,
        display_sleep=True,
    )
    def describe_stack_events(
        self, stack_name: str, time_stamp_marker: float, on_failure: FailureMode = FailureMode.ROLLBACK, **kwargs
    ):
        """
        Calls CloudFormation to get current stack events
        :param stack_name: Name or ID of the stack
        :param time_stamp_marker: last event time on the stack to start streaming events from.
        :param on_failure: The action to take if the stack fails to deploy
        :param kwargs: Other arguments to pass to pprint_columns()
        """

        stack_change_in_progress = True
        events = set()
        retry_attempts = 0

        while stack_change_in_progress and retry_attempts <= self.max_attempts:
            try:
                # Only sleep if there have been no retry_attempts
                LOG.debug("Trial # %d to get the stack %s create events", retry_attempts, stack_name)
                time.sleep(0 if retry_attempts else self.client_sleep)
                paginator = self._client.get_paginator("describe_stack_events")
                response_iterator = paginator.paginate(StackName=stack_name)

                # Event buffer
                new_events = deque()  # type: deque

                for event_items in response_iterator:
                    for event in event_items["StackEvents"]:
                        LOG.debug("Stack Event: %s", event)
                        # Skip already shown old event entries or former deployments
                        if utc_to_timestamp(event["Timestamp"]) <= time_stamp_marker:
                            LOG.debug(
                                "Skip previous event as time_stamp_marker: %s is after the event time stamp: %s",
                                to_datetime(time_stamp_marker),
                                event["Timestamp"],
                            )
                            break
                        if event["EventId"] not in events:
                            events.add(event["EventId"])
                            # Events are in reverse chronological order
                            # Pushing in front reverse the order to display older events first
                            new_events.appendleft(event)
                    else:  # go to next loop (page of events) if not break from inside loop
                        LOG.debug("Still in describe_stack_events loop, got to next page")
                        continue
                    break  # reached here only if break from inner loop!
                LOG.debug("Exit from the describe event loop")
                # Override timestamp marker with latest event (last in deque)
                if len(new_events) > 0:
                    time_stamp_marker = utc_to_timestamp(new_events[-1]["Timestamp"])

                for new_event in new_events:
                    row_color = self.deploy_color.get_stack_events_status_color(status=new_event["ResourceStatus"])
                    pprint_columns(
                        columns=[
                            new_event["ResourceStatus"],
                            new_event["ResourceType"],
                            new_event["LogicalResourceId"],
                            new_event.get("ResourceStatusReason", "-"),
                        ],
                        width=kwargs["width"],
                        margin=kwargs["margin"],
                        format_string=DESCRIBE_STACK_EVENTS_FORMAT_STRING,
                        format_args=kwargs["format_args"],
                        columns_dict=DESCRIBE_STACK_EVENTS_DEFAULT_ARGS.copy(),
                        color=row_color,
                    )
                    # Skip events from another consecutive deployment triggered during sleep by another process
                    if self._is_root_stack_event(new_event) and self._check_stack_not_in_progress(
                        new_event["ResourceStatus"]
                    ):
                        LOG.debug(
                            "Stack %s is not in progress. Its status is %s, and event is %s",
                            stack_name,
                            new_event["ResourceStatus"],
                            new_event,
                        )
                        stack_change_in_progress = False
                        break

                # Reset retry attempts if iteration is a success to use client_sleep again
                retry_attempts = 0
            except botocore.exceptions.ClientError as ex:
                if (
                    "Stack with id {0} does not exist".format(stack_name) in str(ex)
                    and on_failure == FailureMode.DELETE
                ):
                    LOG.debug("Stack %s does not exist", stack_name)
                    return

                LOG.debug("Trial # %d failed due to exception %s", retry_attempts, str(ex))

                retry_attempts = retry_attempts + 1
                if retry_attempts > self.max_attempts:
                    LOG.error("Describing stack events for %s failed: %s", stack_name, str(ex))
                    return
                # Sleep in exponential backoff mode
                time.sleep(math.pow(self.backoff, retry_attempts))

    @staticmethod
    def _is_root_stack_event(event: Dict) -> bool:
        return bool(
            event["ResourceType"] == "AWS::CloudFormation::Stack"
            and event["StackName"] == event["LogicalResourceId"]
            and event["PhysicalResourceId"] == event["StackId"]
        )

    @staticmethod
    def _check_stack_not_in_progress(status: str) -> bool:
        return "IN_PROGRESS" not in status

    def wait_for_execute(
        self,
        stack_name: str,
        stack_operation: str,
        disable_rollback: bool,
        on_failure: FailureMode = FailureMode.ROLLBACK,
        time_stamp_marker: float = 0,
    ) -> None:
        """
        Wait for stack operation to execute and return when execution completes.
        If the stack has "Outputs," they will be printed.

        Parameters
        ----------
        stack_name : str
            The name of the stack
        stack_operation : str
            The type of the stack operation, 'CREATE' or 'UPDATE'
        disable_rollback : bool
            Preserves the state of previously provisioned resources when an operation fails
        on_failure : FailureMode
            The action to take when the operation fails
        time_stamp_marker:
            last event time on the stack to start streaming events from.
        """
        sys.stdout.write(
            "\n{} - Waiting for stack create/update "
            "to complete\n".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        )
        sys.stdout.flush()

        self.describe_stack_events(stack_name, time_stamp_marker, on_failure)

        # Pick the right waiter
        if stack_operation == "CREATE":
            waiter = self._client.get_waiter("stack_create_complete")
        elif stack_operation == "UPDATE":
            waiter = self._client.get_waiter("stack_update_complete")
        else:
            raise RuntimeError("Invalid stack operation type {0}".format(stack_operation))

        # Poll every 30 seconds. Polling too frequently risks hitting rate limits
        # on CloudFormation's DescribeStacks API
        waiter_config = {"Delay": 30, "MaxAttempts": 120}

        try:
            waiter.wait(StackName=stack_name, WaiterConfig=waiter_config)
        except botocore.exceptions.WaiterError as ex:
            LOG.debug("Execute stack waiter exception", exc_info=ex)
            if disable_rollback and on_failure is not FailureMode.DELETE:
                # This will only display the message if disable rollback is set or if DO_NOTHING is specified
                msg = self._gen_deploy_failed_with_rollback_disabled_msg(stack_name)
                LOG.info(self._colored.color_log(msg=msg, color=Colors.FAILURE), extra=dict(markup=True))

            raise deploy_exceptions.DeployFailedError(stack_name=stack_name, msg=str(ex))

        try:
            outputs = self.get_stack_outputs(stack_name=stack_name, echo=False)
            if outputs:
                self._display_stack_outputs(outputs)
        except DeployStackOutPutFailedError as ex:
            # Show exception if we aren't deleting stacks
            if on_failure != FailureMode.DELETE:
                raise ex

    def create_and_wait_for_changeset(
        self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
    ):
        try:
            result, changeset_type = self.create_changeset(
                stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
            )
            self.wait_for_changeset(result["Id"], stack_name)
            self.describe_changeset(result["Id"], stack_name)
            return result, changeset_type
        except botocore.exceptions.ClientError as ex:
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

    def create_stack(self, **kwargs):
        stack_name = kwargs.get("StackName")
        try:
            resp = self._client.create_stack(**kwargs)
            return resp
        except botocore.exceptions.ClientError as ex:
            if "The bucket you are attempting to access must be addressed using the specified endpoint" in str(ex):
                raise DeployBucketInDifferentRegionError(f"Failed to create/update stack {stack_name}") from ex
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

        except Exception as ex:
            LOG.debug("Unable to create stack", exc_info=ex)
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

    def update_stack(self, **kwargs):
        stack_name = kwargs.get("StackName")
        try:
            resp = self._client.update_stack(**kwargs)
            return resp
        except botocore.exceptions.ClientError as ex:
            if "The bucket you are attempting to access must be addressed using the specified endpoint" in str(ex):
                raise DeployBucketInDifferentRegionError(f"Failed to create/update stack {stack_name}") from ex
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

        except Exception as ex:
            LOG.debug("Unable to update stack", exc_info=ex)
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

    def sync(
        self,
        stack_name: str,
        cfn_template: str,
        parameter_values: List[Dict],
        capabilities: Optional[List[str]],
        role_arn: Optional[str],
        notification_arns: Optional[List[str]],
        s3_uploader: Optional[S3Uploader],
        tags: Optional[Dict],
        on_failure: FailureMode,
    ):
        """
        Call the sync command to directly update stack or create stack

        Parameters
        ----------
        :param stack_name: The name of the stack
        :param cfn_template: CloudFormation template string
        :param parameter_values: Template parameters object
        :param capabilities: Array of capabilities passed to CloudFormation
        :param role_arn: the Arn of the role to create changeset
        :param notification_arns: Arns for sending notifications
        :param s3_uploader: S3Uploader object to upload files to S3 buckets
        :param tags: Array of tags passed to CloudFormation
        :param on_failure: FailureMode enum indicating the action to take on stack creation failure
        :return:
        """
        exists = self.has_stack(stack_name)

        if not exists:
            # When creating a new stack, UsePreviousValue=True is invalid.
            # For such parameters, users should either override with new value,
            # or set a Default value in template to successfully create a stack.
            parameter_values = [x for x in parameter_values if not x.get("UsePreviousValue", False)]
        else:
            summary = self._client.get_template_summary(StackName=stack_name)
            existing_parameters = [parameter["ParameterKey"] for parameter in summary["Parameters"]]
            parameter_values = [
                x
                for x in parameter_values
                if not (x.get("UsePreviousValue", False) and x["ParameterKey"] not in existing_parameters)
            ]

        kwargs = {
            "StackName": stack_name,
            "TemplateBody": cfn_template,
            "Parameters": parameter_values,
            "Tags": tags,
        }

        kwargs = self._process_kwargs(kwargs, s3_uploader, capabilities, role_arn, notification_arns)

        try:
            disable_rollback = False
            if on_failure == FailureMode.DO_NOTHING:
                disable_rollback = True
            msg = ""

            if exists:
                kwargs["DisableRollback"] = disable_rollback  # type: ignore
                # get the latest stack event, and use 0 in case if the stack does not exist
                marker_time = self.get_last_event_time(stack_name, 0)
                result = self.update_stack(**kwargs)
                self.wait_for_execute(
                    stack_name, "UPDATE", disable_rollback, on_failure=on_failure, time_stamp_marker=marker_time
                )
                msg = "\nStack update succeeded. Sync infra completed.\n"
            else:
                # Pass string representation of enum
                kwargs["OnFailure"] = str(on_failure)

                result = self.create_stack(**kwargs)
                self.wait_for_execute(stack_name, "CREATE", disable_rollback, on_failure=on_failure)
                msg = "\nStack creation succeeded. Sync infra completed.\n"

            LOG.info(self._colored.color_log(msg=msg, color=Colors.SUCCESS), extra=dict(markup=True))

            return result
        except botocore.exceptions.ClientError as ex:
            raise DeployFailedError(stack_name=stack_name, msg=str(ex)) from ex

    @staticmethod
    @pprint_column_names(
        format_string=OUTPUTS_FORMAT_STRING, format_kwargs=OUTPUTS_DEFAULTS_ARGS, table_header=OUTPUTS_TABLE_HEADER_NAME
    )
    def _display_stack_outputs(stack_outputs: List[Dict], **kwargs) -> None:
        for counter, output in enumerate(stack_outputs):
            for k, v in [
                ("Key", output.get("OutputKey")),
                ("Description", output.get("Description", "-")),
                ("Value", output.get("OutputValue")),
            ]:
                pprint_columns(
                    columns=["{k:<{0}}{v:<{0}}".format(MIN_OFFSET, k=k, v=v)],
                    width=kwargs["width"],
                    margin=kwargs["margin"],
                    format_string=OUTPUTS_FORMAT_STRING,
                    format_args=kwargs["format_args"],
                    columns_dict=OUTPUTS_DEFAULTS_ARGS.copy(),
                    color=Colors.SUCCESS,
                    replace_whitespace=False,
                    break_long_words=False,
                    drop_whitespace=False,
                )
            newline_per_item(stack_outputs, counter)

    def get_stack_outputs(self, stack_name, echo=True):
        try:
            stacks_description = self._client.describe_stacks(StackName=stack_name)
            try:
                outputs = stacks_description["Stacks"][0]["Outputs"]
                if echo:
                    sys.stdout.write("\nStack {stack_name} outputs:\n".format(stack_name=stack_name))
                    sys.stdout.flush()
                    self._display_stack_outputs(stack_outputs=outputs)
                return outputs
            except KeyError:
                return None

        except botocore.exceptions.ClientError as ex:
            raise DeployStackOutPutFailedError(stack_name=stack_name, msg=str(ex)) from ex

    def rollback_delete_stack(self, stack_name: str):
        """
        Try to rollback the stack to a sucessful state, if there is no good state then delete the stack

        Parameters
        ----------
        :param stack_name: str
            The name of the stack
        """
        kwargs = {
            "StackName": stack_name,
        }
        current_state = self._get_stack_status(stack_name)

        try:
            if current_state == "UPDATE_FAILED":
                LOG.info("Stack %s failed to update, rolling back stack to previous state...", stack_name)

                # get the latest stack event
                marker_time = self.get_last_event_time(stack_name, 0)
                self._client.rollback_stack(**kwargs)
                self.describe_stack_events(stack_name, marker_time, FailureMode.DELETE)
                self._rollback_wait(stack_name)

                current_state = self._get_stack_status(stack_name)

            failed_states = ["CREATE_FAILED", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED"]

            if current_state in failed_states:
                LOG.info("Stack %s failed to create/update correctly, deleting stack", stack_name)

                # get the latest stack event
                marker_time = self.get_last_event_time(stack_name, 0)
                self._client.delete_stack(**kwargs)

                # only a stack that failed to create will have stack events, deleting
                # from a ROLLBACK_COMPLETE state will not return anything
                # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.delete_stack
                if current_state == "CREATE_FAILED":
                    self.describe_stack_events(stack_name, marker_time, FailureMode.DELETE)

                waiter = self._client.get_waiter("stack_delete_complete")
                waiter.wait(StackName=stack_name, WaiterConfig={"Delay": 30, "MaxAttempts": 120})

                LOG.info("\nStack %s has been deleted", stack_name)
            else:
                LOG.info("Stack %s has rolled back successfully", stack_name)
        except botocore.exceptions.ClientError as ex:
            raise DeployStackStatusMissingError(stack_name) from ex
        except botocore.exceptions.WaiterError:
            LOG.error(
                "\nStack %s failed to delete properly! Please manually clean up any persistent resources.",
                stack_name,
            )
        except KeyError:
            LOG.info("Stack %s is not found, skipping", stack_name)

    def _get_stack_status(self, stack_name: str) -> str:
        """
        Returns the status of the stack

        Parameters
        ----------
        :param stack_name: str
            The name of the stack

        Parameters
        ----------
        :return: str
            A string representing the status of the stack
        """
        stack = self._client.describe_stacks(StackName=stack_name)
        stack_status = str(stack["Stacks"][0]["StackStatus"])

        return stack_status

    def _rollback_wait(self, stack_name: str, wait_time: int = 30, max_retries: int = 120):
        """
        Manual waiter for rollback status, waits until we get *_ROLLBACK_COMPLETE or ROLLBACK_FAILED

        Parameters
        ----------
        :param stack_name: str
            The name of the stack
        :param wait_time: int
            The time to wait between polls, default 30 seconds
        :param max_retries: int
            The number of polls before timing out
        """
        status = ""
        retries = 0

        while retries < max_retries:
            status = self._get_stack_status(stack_name)

            if "ROLLBACK_COMPLETE" in status or status == "ROLLBACK_FAILED":
                return

            retries = retries + 1
            time.sleep(wait_time)

        LOG.error(
            "Stack %s never reached a *_ROLLBACK_COMPLETE or ROLLBACK_FAILED state, we got %s instead.",
            stack_name,
            status,
        )

    @staticmethod
    def _gen_deploy_failed_with_rollback_disabled_msg(stack_name):
        return """\nFailed to deploy. Automatic rollback disabled for this deployment.\n
Actions you can take next
=========================
[*] Fix issues and try deploying again
[*] Roll back stack to the last known stable state: aws cloudformation rollback-stack --stack-name {stack_name}
""".format(
            stack_name=stack_name
        )


# python standard library
import logging

# apetools modules
from apetools.log_setter import DATA_FRIENDLY_FORMAT, LOG_TIMESTAMP
from apetools import log_setter
from apetools.baseclass import BaseClass

class SubLogger(BaseClass):
    """
    A creator and holder of sub-logs (meant for the Hortator)
    """
    def __init__(self, log_format=DATA_FRIENDLY_FORMAT,
                 timestamp=LOG_TIMESTAMP,
                 level=logging.INFO):
        """
        :param:

         - `log_format`: a format string for the logging output
         - `timestamp`: a format string for the logging timestamp
         - `level`: the level of messages to record
        """
        super(SubLogger, self).__init__()
        self.log_format = log_format
        self.timestamp = timestamp
        self.level = level
        self._handlers = None
        return

    @property
    def handlers(self):
        """
        :return: dictionary of name:file-handlers
        """
        if self._handlers is None:
            self._handlers = {}
        return self._handlers

    def add(self, logname, logger=None):
        """
        :param:

         - `logname`: path (name) for the log
         - `logger`: logging instarce to add handler to

        :postcondition: file-handler has been added to logger
        """
        if logger is None:
            logger = log_setter.logger
        handler = self.handlers[logname] = logging.FileHandler(logname)
        handler_format = logging.Formatter(self.log_format,
                                           datefmt=self.timestamp)
        handler.setFormatter(handler_format)
        handler.setLevel(self.level)
        logger.addHandler(handler)
        return

    def remove(self, logger=None, logname=None):
        """
        :param:

         - `logname`: name of the log to remove
         - `logger`: logging instance with one of self.handlers

        :postcondition: log removed from logging
        """
        if logger is None:
            logger = log_setter.logger
        try:
            logger.removeHandler(self.handlers[logname])
        except KeyError as error:
            self.logger.debug(error)
            if logname is None and len(self.handlers) == 1:
                logger.removeHandler(self.handlers[self.handlers.keys()[0]])
        return
# end class SubLogger


import datetime

from doctor import Doctor
from patient import Patient

patients: list[Patient] = []
doctors: list[Doctor] = []

def add_doctor():
    print('Ingrese a un médico')
    firstname = input('Ingrese el nombre: ')
    lastname = input('Ingrese los apellidos: ')
    dpi = int(input('Ingrese el DPI'))
    day = int(input('Ingrese el día de nacimiento: '))
    month = int(input('Ingrese el mes de nacimiento: '))
    year = int(input('Ingrese el año de nacimiento: '))
    birthdate = datetime.date(year, month, day)
    phone = input('Ingrese el teléfono: ')
    email = input('Ingrese el correo electrónico: ')
    specialty = input('Ingrese la especialidad: ')
    college_id = int(input('Ingrese el colegiado: '))
    sallary = float(input('Ingrese el salario: '))

    doctor = Doctor(
        firstname,
        lastname,
        dpi,
        birthdate,
        phone,
        email,
        specialty,
        college_id,
        sallary
    )

    doctors.append(doctor)

def add_patient():
    print('Ingrese a un paciente')
    firstname = input('Ingrese el nombre: ')
    lastname = input('Ingrese los apellidos: ')
    dpi = int(input('Ingrese el DPI'))
    day = int(input('Ingrese el día de nacimiento: '))
    month = int(input('Ingrese el mes de nacimiento: '))
    year = int(input('Ingrese el año de nacimiento: '))
    birthdate = datetime.date(year, month, day)
    phone = input('Ingrese el teléfono: ')
    email = input('Ingrese el correo electrónico: ')
    weight = float(input('Ingrese el peso: '))
    height = float(input('Ingrese la altura: '))
    gender = input('Ingrese el género: ')
    trigliceridos = int(input('Ingrese los trigliceridos: '))
    presion_sistolica = int(input('Ingrese presión sistolica: '))
    presion_diastolica = int(input('Ingrese presión diastolica: '))

    patient = Patient(
        firstname,
        lastname,
        dpi,
        birthdate,
        phone,
        email,
        weight,
        height,
        gender,
        trigliceridos,
        presion_sistolica,
        presion_diastolica
    )

    patients.append(patient)

def show_health(patient: Patient):
    print('Datos del paciente:')
    print(patient.firstname, patient.lastname)
    print(patient.get_imc())
    print(patient.get_presion())
    print(patient.get_trigliceridos())

def main():
    while True:
        print('Menú')
        print('1- Ingreso de médicos')
        print('2- Ingreso de pacientes')
        print('3- Ver estado de paciente')
        print('4- Ver pacientes de un médico')
        print('5- Salir')
        option = int(input('Ingrese una opción: '))

        if option == 1:
            add_doctor()
        elif option == 2:
            add_patient()
        elif option == 3:
            dpi = int(input('Ingrese el DPI: '))
            for patient in patients:
                if patient.dpi == dpi:
                    show_health(patient)
                    break
        elif option == 4:
            dpi = int(input('Ingrese el DPI: '))
            for doctor in doctors:
                if doctor.dpi == dpi:
                    for patient in doctor.patients:
                        print(patient)
        elif option == 5:
            break
        else:
            continue

main()


from __future__ import absolute_import

import xml.etree.ElementTree as ET
from xml.dom import minidom
from StringIO import StringIO

from ..entities import Player, Table, Card, Deck
from .. import transport
from . import interface

__all__ = ['dump', 'open', 'parse', 'echo', 'to_public']

def dump(state, public=True, pretty=True):
    root = ET.Element('game')
    appendNotEmpty(root, dump_table(state.table))
    appendNotEmpty(root, dump_posts(state.posts))
    appendNotEmpty(root, dump_betting(state.betting))
    appendNotEmpty(root, dump_community(state.community))
    if state.showdown:
        appendNotEmpty(root, dump_showdown(state.showdown))
    if not public:
        appendNotEmpty(root, dump_deck(state.deck))
    if public:
        root = to_public(root)
    return echo(root, pretty)

def echo(root, pretty=True):
    s = StringIO()
    ET.ElementTree(root).write(s)
    if pretty:
        xml = minidom.parseString(s.getvalue())
        return xml.toprettyxml()
    return s.getvalue()

def to_public(root, player=None):
    players = root.findall('table/player')
    for p in players:
        p.remove(p.find('pocket'))
        p.remove(p.find('transport'))
    deck = root.find('deck')
    if deck is not None:
        root.remove(deck)
    acts = root.findall('betting/round/action')
    for a in acts:
        w = a.find('error')
        if w is None:
            continue
        if not player or a.get('player') != player:
            a.remove(w)
    return root

def dump_posts(posts):
    if not posts:
        return
    root = ET.Element('posts')
    for post in posts:
        attrs = {
            'player': post['player'],
            'amount': str(post['amount']),
            'type': post['type']
        }
        ET.SubElement(root, 'post', attrs)
    return root

def dump_betting(betting):
    root = ET.Element('betting')
    for round in ('preflop', 'flop', 'turn', 'river'):
        actions = betting[round]
        sub = ET.SubElement(root, 'round', {'name': round})
        for act in actions:
            attrs = {
                'player': act['player'],
                'type': act['type'],
                'amount': str(act['amount'])
            }
            action = ET.SubElement(sub, 'action', attrs)
            if act['error']:
                err = ET.SubElement(action, 'error', {'rel': act['error'][1]})
                err.text = act['error'][0]
    return root

def dump_community(community):
    root = ET.Element('community')
    for card in community:
        root.append(dump_card(card))
    return root

def dump_table(table):
    root = ET.Element('table', {'button': str(table.button)})
    for k in table.occupied_sits():
        player = table.sits[k]
        attrs = {
            'name': player.name,
            'in_stack': str(player.initial_stack),
            'stack': str(player.stack),
            'sit': str(k)
        }
        player_el = ET.SubElement(root, 'player', attrs)
        pocket = ET.SubElement(player_el, 'pocket')
        for card in player.pocket.cards:
            pocket.append(dump_card(card))
        tr = player.transport
        ET.SubElement(player_el, 'transport',
            {'type': tr.type(), 'service': tr.service})
    return root

def dump_deck(deck):
    if not deck.cards:
        return
    root = ET.Element('deck')
    for card in deck.cards:
        root.append(dump_card(card))
    return root

def dump_showdown(showdown):
    if not showdown:
        return
    root = ET.Element('showdown')
    for show in showdown:
        sub = ET.SubElement(root, 'player',
            {
                'name': show['player'],
                'win': str(show['win'])
            })
        if show['hand']:
            hand = ET.SubElement(sub, 'hand')
            for card in show['hand'].cards:
                hand.append(dump_card(card))
    return root

def dump_card(card):
    return ET.Element('card', {'rank': card.rank, 'suit': card.suit})

def open(source):
    return ET.parse(source).getroot()

def parse(source):
    root = open(source)
    table = parse_table(root.find('table'))
    deck = parse_deck(root.find('deck'))
    res = interface.HandState(table, deck)
    return res

def parse_table(el):
    table = Table()
    table.button = int(el.get('button'))
    for p_el in el:
        tr = p_el.find('transport').attrib
        p = Player(
            p_el.get('name'),
            transport.create(tr['type'], tr['service']), int(p_el.get('stack'))
        )
        table.sit_in(p, int(p_el.get('sit')))
    return table

def parse_deck(el):
    cards = []
    for card_el in el:
        cards.append(Card(card_el.get('rank'), card_el.get('suit')))
    return Deck(cards)

def appendNotEmpty(parent, child):
    if child is not None:
        parent.append(child)


import os
import subprocess
from zachaire_files.fileManager import getExtension, rm
from zachaire_files.themeHtmlInjector import injectTheme
from zachaire_files.utils import printBuilder

def __makeHtmlFromMarkdown(mdPath):
    # Perform the Markdown -> Html conversion
    htmlPath = mdPath.replace(".md", ".html")
    cmd = f"pandoc -o {htmlPath} {mdPath}"
    returnCode = subprocess.call(cmd, shell=True)
    if returnCode > 0:
        raise Exception( "Pandoc returned non-zero return code:\n"
                       +f"\treturnCode = {returnCode}")
    return htmlPath

def __generateAsMarkdown(mdFile, themeName):
    htmlFile = __makeHtmlFromMarkdown(mdFile)
    injectTheme(htmlFile, themeName)
    rm(mdFile)

def __generateAsHtml(rawHtmlFile, themeName):
    injectTheme(rawHtmlFile, themeName)

def __generateAsPhp(rawPhpFile, themeName):
    injectTheme(rawPhpFile, themeName)

actionForEachExt = {".md" :__generateAsMarkdown, ".html":__generateAsHtml,
                    ".php":__generateAsPhp     }

def build(subdirToBuild, dirBuildCfg):
    themeName = dirBuildCfg["themeToUse"]
    for srcFile in os.scandir(subdirToBuild):
        if srcFile.is_file(): #Filter out subdirs
            fileExt = getExtension(srcFile)
            if fileExt in actionForEachExt: #Filter out images, etc.
                printBuilder(f"Generating from \"{srcFile.path}\"…")
                generator = actionForEachExt[fileExt]
                generator(srcFile.path, themeName)


## 1. 라이브러리 읽어 들이기 ##

# 텐서플로 라이브러리
import tensorflow as tf
# tflearn 라이브러리
import tflearn
# mnist 데이터 세트를 다루기 위한 라이브러리
import tflearn.datasets.mnist as mnist

import numpy as np

## 2. 데이터 읽어 들이고 전처리하기 ##
# MNIST 데이터를 ./data/mnist에 내려받고, 압축을 해제한 다음 각 변수에 할당하기
trainX, trainY, testX, testY = mnist.load_data('./data/mnist/', one_hot=True)

# 이미지 픽셀 데이터를 1차원에서 시계열 데이터로 변환하기
trainX = np.reshape(trainX, (-1, 28, 28))

## 3. 신경망 만들기 ##

# 초기화
# tf.reset_default_graph()

# 입력 레이어 만들기
net = tflearn.input_data(shape=[None, 28, 28])

# 중간 레이어 만들기
# LSTM 블록
net = tflearn.lstm(net, 128)

# 출력 레이어 만들기
net = tflearn.fully_connected(net, 10, activation='softmax')
net = tflearn.regression(net, optimizer='sgd',
                         learning_rate=0.5, loss='categorical_crossentropy')

## 4. 모델 만들기(학습) ##
# 학습 실행하기
model = tflearn.DNN(net)
model.fit(trainX, trainY, n_epoch=20, batch_size=100,
          validation_set=0.1, show_metric=True)


import falcon
import json
import jsonschema
import logging

def _get_request_schema(req, resource):
    if resource is None or req.method not in ['POST', 'PUT', 'PATCH']:
        return None

    # First try to get schema from method itself
    schema = getattr(
        getattr(resource, {'POST': 'on_post', 'PUT': 'on_put', 'PATCH': 'on_patch'}[req.method], None),
        '__request_schema__',
        None
    # Otherwise, fall back to schema defined directly in class
    ) or getattr(resource, '__request_schemas__', {}).get(
        {'POST': 'on_post', 'PUT': 'on_put', 'PATCH': 'on_patch'}[req.method]
    )
    return schema

def _get_response_schema(resource, req):
    try:
        method_name = {'POST': 'on_post', 'PUT': 'on_put', 'PATCH': 'on_patch', 'GET': 'on_get', 'DELETE': 'on_delete'}[req.method]
    except KeyError:
        return

    # First try to get schema from method itself
    return getattr(
        getattr(resource, method_name, None),
        '__response_schema__',
        None
    # Otherwise, fall back to schema defined directly in class
    ) or getattr(resource, '__response_schemas__', {}).get(method_name)

class _null_handler(logging.Handler):
    def emit(self, record):
        pass

class Middleware(object):
    def __init__(self, logger=None):
        if logger is None:
            # Default to no logging if no logger provided
            logger = logging.getLogger(__name__)
            logger.addHandler(_null_handler())
        self.logger = logger

    def process_resource(self, req, resp, resource, params):
        if _get_response_schema(resource, req) and not req.client_accepts_json:
            raise falcon.HTTPNotAcceptable('This API supports only JSON-encoded responses')

        if resource is None or req.method not in ['POST', 'PUT', 'PATCH']:
            return

        if _get_request_schema(req, resource) is not None:
            if req.content_type is None or 'application/json' not in req.content_type:
                raise falcon.HTTPUnsupportedMediaType('This API supports only JSON-encoded requests')

        if 'application/json' in req.content_type:
            body = req.stream.read()
            if not body:
                raise falcon.HTTPBadRequest(
                    'Empty request body',
                    'A valid JSON document is required'
                )

            schema = _get_request_schema(req, resource)
            try:
                req.context['doc'] = json.loads(body.decode('utf-8'))
            except (ValueError, UnicodeDecodeError) as error:
                if schema is not None:
                    raise falcon.HTTPBadRequest(
                        'Malformed JSON',
                        'Could not decode the request body.  The JSON was incorrect or not encoded as UTF-8'
                    )
                req.context['doc'] = body

            if schema is not None:
                try:
                    jsonschema.validate(req.context['doc'], schema)
                except jsonschema.exceptions.ValidationError as error:
                    raise falcon.HTTPBadRequest(
                        'Invalid request body',
                        json.dumps({'error': str(error)})
                    )

    def process_response(self, req, resp, resource):
        if 'result' not in req.context:
            return

        resp.body = json.dumps(req.context['result'])

        schema = _get_response_schema(resource, req)
        if schema is None:
            return

        try:
            jsonschema.validate(req.context['result'], schema)
        except jsonschema.exceptions.ValidationError as error:
            method_name = {'POST': 'on_post', 'PUT': 'on_put', 'PATCH': 'on_patch', 'GET': 'on_get', 'DELETE': 'on_delete'}[req.method]
            self.logger.error('Blocking proposed response from being sent from {0}.{1}.{2} to client as it does not match the defined schema: {3}'.format(resource.__module__, resource.__class__.__name__, method_name, str(error)))
            raise falcon.HTTPInternalServerError('Internal Server Error', 'Undisclosed')


import pygame
pygame.init()

class Character(pygame.sprite.Sprite):
	## A pygame Sprite object which is used to create
	## the player and the computer
	def __init__(self, gridX, gridY, width, colour):
		super().__init__()

		self.gridX = gridX
		self.gridY = gridY
		self.width = width
		self.colour = colour

		## Create the image for the Character
		## and fill it a single colour
		self.image = pygame.Surface([self.width-1, self.width-1])
		self.image.fill(self.colour)
		self.rect = self.image.get_rect()

		## Set the co-ordinates of the character based off its grid position
		self.rect.x = self.gridX * self.width
		self.rect.y = self.gridY * self.width


from Path import *

#vectorize configuration
VEC_config={
    "dataset_name":"imdb",
    "method":"TF_IDF",
    #"saving_format": "numpy", #numpy, mtx, mat, binary, txt, mtx2
    "saving_format":['numpy'],
    "load_saved": False, #resume if possible in any stage (data loading, model loading etc.)
    "visualize":False
}

def vectorize():
    if(VEC_config['method']=="doc2vec"):
        from Vectorize import VectorizeDataset
        Doc2Vec_config = {
            "max_epochs": 500,
            "vec_size": 300
        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, Doc2Vec_config)

    elif (VEC_config['method'] == "word2vec"):
        from Vectorize import VectorizeDataset
        Word2Vec_config = {
            "vec_size": 300,
            "pretrained_model": pretrained_model,
            "pretrained_model_name": "GOOGLE",
        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, Word2Vec_config)
        return

    elif(VEC_config['method']=="word2vec_avg"):
        from Vectorize import VectorizeDataset
        Word2Vec_config = {
            "vec_size": 300,
            "pretrained_model": pretrained_model,
            "pretrained_model_name": "GOOGLE",
        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, Word2Vec_config)

    elif (VEC_config['method'] == "TF_IDF"):
        from Vectorize import VectorizeDataset
        TF_IDF_config = {
            'max_features':5000,
            'ngram':(1,1) #min max range
        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, TF_IDF_config)

    elif (VEC_config['method'] == "LSI"):
        from Vectorize import VectorizeDataset
        LSI_config = {
            'tfidf_features':5000,
            'max_features': 300,
            'ngram': (1, 1)  # min max range
        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, LSI_config)

    elif (VEC_config['method'] == "LDA"):
        from Vectorize import VectorizeDataset
        LDA_config = {
            'ngram': (1, 1),  # min max range
            'features':3000, #use -1 to adapt this automatically
            'max_iter':10,
            'max_features': 100 #final number of feature

        }
        VectorizeDataset.main(dataset_info[VEC_config['dataset_name']], VEC_config, LDA_config)

    elif (VEC_config['method'] == "FAST_TEXT"):
        print("Not implemented yet")

    else:
        print("improper vectorize name")
        sys.exit(0)

    return

GRAPH_data_config={
    "algorithm":'knn',
    "dataset_name":'imdb',
    "method":'TF_IDF',
    "multi_label":False,
    #"saving_format":["numpy","mat","gephi","mtx","txt"]  #avilable formats are: "saving_format": "numpy", #numpy, mtx, mat
    "saving_format":["numpy","gephi"]  #avilable formats are: "saving_format": "numpy", #numpy, mtx, mat
}

def construct_graph():

    if(GRAPH_data_config['algorithm']=='knn'):
        from GraphConstruction.KNN.knn import KNN_construction
        KNN_config = {
            # "k": range(5,101,5),
            "k": [5],
            "mode": 'distance',  # connectivity will give 1,0
            "metric": "cosine",
            "include_self": False
        }
        KNN_construction(dataset_info[GRAPH_data_config['dataset_name']],GRAPH_data_config,KNN_config)

    elif (GRAPH_data_config['algorithm'] == 'complete'):
        from GraphConstruction.Complete.ScipyComplete import csr2graph
        complete_config = {
            "metric": "cosine",
            "include_self": False
        }
        csr2graph(dataset_info[GRAPH_data_config['dataset_name']], GRAPH_data_config, complete_config)

    elif(GRAPH_data_config['algorithm']=='bmatch'):
        from GraphConstruction.Bmatch.bmatch import bmatch_construction
        bMatching_config = {
            #"b":range(5,101,5),
            "b": [5,100],
            "mode": 'distance',  # connectivity will give 1,0
            "metric": "euclidean",  # used internally
            "max_iterations":[-1,-1], #this is given, -1 means default max_interations
            #"max_iterations": np.ones(len(range(5,101,5)))*-1,  # this is given, -1 means default max_interations
            "include_self": False
        }
        bmatch_construction(dataset_info[GRAPH_data_config['dataset_name']], GRAPH_data_config, bMatching_config)

    else:
        print(GRAPH_data_config['algorithm']," -> The graph construction algorithm is not implemented yet")
        sys.exit(0)

    return

LEARNING_data_config={
    "algorithm":'GCN_DGL',
    "dataset_name":'karate',
    "directed":False,
    "labled_only": True, #load only data that has known labels
    "vectorize":"TF_IDF",
    "graph_algorithm":"knn",
    "train": 0.5,
    "val": 0.2}

def learn():
    if (LEARNING_data_config['algorithm'] == 'GCN_DGL'):
        from GNN.GCN_DGL.GCN_DGL_main import learn
        from GNN_configuration import getSettings,load_data_DGL
        if LEARNING_data_config["dataset_name"]=="karate":LEARNING_data_config["vectorize"]=""
        LEARNING_data_config['input_path']=dataset_info[LEARNING_data_config['dataset_name']]['path']+LEARNING_data_config["vectorize"]+"/"
        data=load_data_DGL(LEARNING_data_config)

        gnn_settings = getSettings(LEARNING_data_config['dataset_name'],data)
        gnn_settings['output_path']=dataset_info[LEARNING_data_config['dataset_name']]['output_path']

        learn(gnn_settings,data)

    elif (LEARNING_data_config['algorithm'] == 'GSAGE_DGL'):
        from GNN.GSAGE_DGL.GSAGE_DGL_main import learn
        from GNN_configuration import getSettings,load_data_DGL

        if LEARNING_data_config["dataset_name"] == "karate": LEARNING_data_config["vectorize"] = ""
        LEARNING_data_config['input_path'] = dataset_info[LEARNING_data_config['dataset_name']]['path'] + \
                                             LEARNING_data_config["vectorize"] + "/"
        data=load_data_DGL(LEARNING_data_config)

        gnn_settings = getSettings(LEARNING_data_config['dataset_name'])
        gnn_settings['output_path']=dataset_info[LEARNING_data_config['dataset_name']]['output_path']

        learn(gnn_settings,data)

    elif (LEARNING_data_config['algorithm'] == 'GAT_DGL'):
        from GNN.GAT_DGL.GAT_DGL_main import learn
        from GNN_configuration import getSettings,load_data_DGL

        if LEARNING_data_config["dataset_name"] == "karate": LEARNING_data_config["vectorize"] = ""
        LEARNING_data_config['input_path'] = dataset_info[LEARNING_data_config['dataset_name']]['path'] + \
                                             LEARNING_data_config["vectorize"] + "/"
        data=load_data_DGL(LEARNING_data_config)

        gnn_settings = getSettings(LEARNING_data_config['dataset_name'])
        gnn_settings['output_path']=dataset_info[LEARNING_data_config['dataset_name']]['output_path']

        learn(gnn_settings,data)

    elif (LEARNING_data_config['algorithm'] == 'FC'):
        from GNN.FNN_PT_TF_Keras.FC import learn
        from GNN_configuration import getSettings,load_data_DGL

        if LEARNING_data_config["dataset_name"] == "karate": LEARNING_data_config["vectorize"] = ""
        LEARNING_data_config['input_path'] = dataset_info[LEARNING_data_config['dataset_name']]['path'] + \
                                             LEARNING_data_config["vectorize"] + "/"
        data=load_data_DGL(LEARNING_data_config)

        gnn_settings = getSettings(LEARNING_data_config['dataset_name'])
        gnn_settings['output_path']=dataset_info[LEARNING_data_config['dataset_name']]['output_path']

        learn(gnn_settings,data)

    else:
        print("this GNN is not incorporated yet")
        sys.exit(0)

    return

#running the program
RUN_vectorize=False
RUN_graph_construction=False
RUN_learn=True

if __name__ == '__main__':
    if(RUN_vectorize):
        vectorize()

    if(RUN_graph_construction):
        construct_graph()

    if(RUN_learn):
        learn()


usernameInput = input("Username :")
passwordInput = input("Password :")
if usernameInput  == "admin" and passwordInput == "1234":
    print("Done !")
    if True :
        print("====Welcome to Vegetables Mall====")
        print("1. Carrot           ","Price/Unit:","10")
        print("2. Cucumber         ","Price/Unit:","5")
        print("3. Basil            ","Price/Unit:","5")
        priceCarrot = 10
        priceCucum = 5
        priceBasil = 5
        totalPriceCarrot = 0
        totalPriceCucum = 0
        totalPriceBasil = 0
        userSelectInput = int(input("Select Product:"))

        while userSelectInput != 0:
            if userSelectInput == 1 :
                userAmountInput = int(input("Carrot Amount:"))
                totalPriceCarrot = userAmountInput * priceCarrot
                print("Product in Your Cart")
                print("1. Carrot","Amount: ",userAmountInput,"Total Price: ",totalPriceCarrot)
                userSelectInput = int(input("เลือกสินค้าอื่นๆเพิ่มเติมหรือไม่ ไม่รับกด 0:"))
            elif userSelectInput == 2 :
                userAmountInput = int(input("Cucumber Amount:"))
                totalPriceCucum = userAmountInput * priceCucum
                print("Product in Your Cart")
                print("2. Cucumber","Amount: ",userAmountInput,"Total Price: ",totalPriceCucum)
                userSelectInput = int(input("เลือกสินค้าอื่นๆเพิ่มเติมหรือไม่ ไม่รับกด 0:"))
            elif userSelectInput == 3 :
                userAmountInput = int(input("Basil Amount:"))
                totalPriceBasil = userAmountInput * priceBasil
                print("Product in Your Cart")
                print("3. Basil","Amount: ",userAmountInput,"Total Price: ",totalPriceBasil)
                userSelectInput = int(input("เลือกสินค้าอื่นๆเพิ่มเติมหรือไม่ ไม่รับกด 0:"))
        else:
            totalOrderPrice = totalPriceCarrot + totalPriceCucum + totalPriceBasil
            print("Total Price :", totalOrderPrice, "THB")
else:
    print("รหัสผ่านผิดพลาด")


# https://towardsdatascience.com/q-learning-and-sasar-with-python-3775f86bd178
import numpy as np
import copy

# Environemnts
rewards = np.array([[-float('inf'), -float('inf'), 0, 0, -float('inf'), -float('inf'), -float('inf')],
                    [-float('inf'), -float('inf'), -float('inf'), -float('inf'), 0, -float('inf'), 0],
                    [0, -float('inf'), -float('inf'), 0, -float('inf'), 100, -float('inf')],
                    [0, -float('inf'), 0, -float('inf'), 0, -float('inf'), -float('inf')],
                    [-float('inf'), 0, -float('inf'), 0, -float('inf'), 100, 0],
                    [-float('inf'), -float('inf'), 0, -float('inf'), 0, 100, 0],
                    [-float('inf'), 0, -float('inf'), -float('inf'), 0, 100, -float('inf')]])

# print("Rewards:", rewards)
# print("Rewards shape:", rewards.shape)

# Parameters
gamma = 0.8
alpha = 0.01
num_episode = 50000
min_difference = 1e-3
goal_state = 5

def QLearning(rewards, goal_state=None, gamma=0.99, alpha=0.01, num_episode=1000, min_difference=1e-5):
    """
    Run Q-learning loop for num_episode iterations or till difference between Q is below min_difference.
    """
    Q = np.zeros(rewards.shape)
    # #print("Q:", Q)
    # #print("Q shape:", Q.shape)
    all_states = np.arange(len(rewards))
    # #print("all_states", all_states)
    # #print("all_states shape:", all_states.shape)
    for i in range(num_episode):
        # #print("episode", i)
        Q_old = copy.deepcopy(Q)
        # initialize state
        initial_state = np.random.choice(all_states)
        # #print("state:", initial_state)
        action = np.random.choice(np.where(rewards[initial_state] != -float('inf'))[0])
        # #print("action:", action)
        Q[initial_state][action] = Q[initial_state][action] + alpha * (
                rewards[initial_state][action] + gamma * np.max(Q[action]) - Q[initial_state][action])
        # print("Q:", Q)
        # print("rewards[initial_state][action]", rewards[initial_state][action])
        # print("Q[action]", Q[action])
        # print("np.max(Q[action])", np.max(Q[action]))
        # input("Press Enter to continue...")
        cur_state = action

        # loop for each step of episode, until reaching goal state
        while cur_state != goal_state:
            # choose action form states using policy derived from Q
            # print("cur_state", cur_state)
            action = np.random.choice(np.where(rewards[cur_state] != -float('inf'))[0])
            # print("action:", action)
            Q[cur_state][action] = Q[cur_state][action] + alpha * (
                    rewards[cur_state][action] + gamma * np.max(Q[action]) - Q[cur_state][action])
            # print("Q:", Q)
            cur_state = action

        # break the loop if converge
        diff = np.sum(Q - Q_old)
        # print("diff", diff)
        if diff < min_difference:
            # print("diff < min_difference:", diff, "<", min_difference)
            break

        # print("The return value is", np.around(Q / np.max(Q) * 100))
    return np.around(Q / np.max(Q) * 100)

Q = QLearning(rewards, goal_state=goal_state, gamma=gamma, alpha=alpha, num_episode=num_episode,
              min_difference=min_difference)
print("Q: ", Q)


graph = {'A':set([('B',29),('F',10)]), #각각의 튜플로 구성된 리스트가 집합화된다.
         'B':set([('A',29),('C',16),('G',15)]),
         'C':set([('B',16),('D',12)]),
         'D':set([('C',12),('E',22),('G',18)]),
         'E':set([('D',22),('F',27),('G',25)]),
         'F':set([('A',10),('E',27)]),
         'G':set([('B',15),('D',18),('E',25)])
}
#graph의 'A'를 key값으로 해서 접근하면, 'B','F'간선에 접근할 수 있다.
#앞서 했던 가중치 합이랑 간선구하는 함수를 똑같이 구현해보자.
def weightSum(graph): #각 간선에 접근하는 방법을 익혀보자.
    sum =0
    for v in graph: #각 key값으로 순회하면서,
        for e in graph[v]: #각 key값으로 얻을 수 있는 graph내의 정보들 순회. 여기에서 e는 각 튜플들이다.
            sum += e[1] #각 튜플들 내에서 index 1번째에 있는 가중치를 더한다.
    return sum//2 #중복돼서 더해지는 것들 제하기 위해 2로 나눈다.

def printAllEdges(graph):
    for v in graph:
        for e in graph[v]:
            print("(%s,%s,%d)"%(v,e[0],e[1]),end=' ')
print("AM: weight sum = ",weightSum(graph))
printAllEdges(graph) #총 18개의 edge가 나온다.


import numpy as np
import cv2
import video_channels
import camera
import calibrate_camera
import select_camera_to_calibrate

myConfig = video_channels.channel(select_camera_to_calibrate.Camera)
# make sure step1-4 are completed0
# this script will be looking for a elongated white line (tape). This will be the y axis,
# There should also be a dot over the line (like an i); this indicates positive y direction
# and y = 0 will be at the center of the dot

# keystrokes for zoom '+', unzoom '-', pan rigth 'r', pan left 'l', 'u', 'd'
# to select white line: press 's'
# to accept selection: press 'a'
try:
    myCamera = camera.camera(myConfig.imSize[0:2], myConfig.calScale, myConfig.calib_path+"preXYref.txt", myConfig.useGeometricCenterAsRotAxis)
except:
    raise Exception('step 1-4 needs to be completed!')

cv2.namedWindow('cameraview',cv2.WINDOW_NORMAL)
zoomState = 1.0
zoomStep = 0.35
leftrightstate = 0
updownstate = 0
panStep = 50

xPix, yPix = np.meshgrid(np.linspace(0, myCamera.n1-1, myCamera.n1), np.linspace(0, myCamera.n2-1, myCamera.n2))
imShape = xPix.shape
xMap, yMap = myCamera.getGroundPosition(xPix.flatten(),yPix.flatten())
xMap = np.reshape(xMap,imShape)
yMap = np.reshape(yMap,imShape)

cap = cv2.VideoCapture(myConfig.CapturePath)
drawCaption = True
keepLooping = True
while(keepLooping):
    ret, frame = cap.read()
    # draw things in the frame now...

    centerX = int(myCamera.n1/2 + leftrightstate)
    centerV = int(myCamera.n2/2 + updownstate)
    Left = max(0,int(centerX - myCamera.n1/2/zoomState))
    Right = min(myCamera.n1,int(centerX + myCamera.n1/2/zoomState))
    Top = max(0,int(centerV - myCamera.n2/2/zoomState))
    Bottom = min(myCamera.n2,int(centerV + myCamera.n2/2/zoomState))
    #oh, and write instructions too
    if drawCaption:
        cv2.putText(frame,'Point to the reference marker (white line). Select reference by pressing s.  Zoom and pan +,-,r, l, u, d.',(Left+50,Top+50), cv2.FONT_ITALIC,  1,(0,0,255),3)

    cv2.imshow('cameraview',frame[Top:Bottom,Left:Right])
    key = cv2.waitKey(1)
    if key & 0xFF == ord('q'):
        break
    if key & 0xFF == ord('c'):
        numExistingIms += 1
        cv2.imwrite(myStream.Folder+'cb_im'+str(numExistingIms)+'.jpg',frame)
    elif key & 0xFF == ord('+'):
        zoomState += zoomStep
    elif key & 0xFF == ord('-'):
        if zoomState > 1.0:
            zoomState -= zoomStep
    elif key & 0xFF == ord('u'):
        updownstate -= panStep
    elif key & 0xFF == ord('d'):
        updownstate += panStep
    elif key & 0xFF == ord('l'):
        leftrightstate -= panStep
    elif key & 0xFF == ord('r'):
        leftrightstate += panStep
    elif key & 0xFF == ord('s'):
        # will search for the marker, only inside the zoomed view
        im = frame[Top:Bottom,Left:Right]
        imSize = im.shape

        hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
        # Define range of white color in HSV
        if 0:
            Mask = cv2.inRange(hsv, (0, 0, 0), (190, 30,255))
            Mask = cv2.bitwise_and(Mask,cv2.inRange(hsv, (0, 0, 220), (190, 255,255)))
        else:
            if myConfig.camName == "axis-pole":
                Mask = cv2.inRange(hsv, (0, 0, 150), (190, 100,255))
                Mask = cv2.inRange(hsv, (0, 0, 140), (190, 110,255))
                Mask = cv2.inRange(hsv, (0, 0, 170), (190, 60,255))
            elif myConfig. camName == "axis-wall":
                Mask = cv2.inRange(hsv, (0, 0, 170), (190, 60,255))

        whiteness = cv2.cvtColor(Mask,cv2.COLOR_GRAY2BGR) #Mask.astype(np.float)
        windowName = 'test'
        cv2.namedWindow('test',cv2.WINDOW_NORMAL)
        cv2.putText(whiteness,'Press y if youre happy with the thresholding of white from backgroudn and n if not.',(50,50), cv2.FONT_ITALIC,  1,(0,0,255),3)
        cv2.imshow('test',whiteness)
        keepLooping = True
        while keepLooping:
            key = cv2.waitKey(1)
            if key & 0xFF == ord('y'):
                break
            elif key & 0xFF == ord('n'):
                keepLooping = False

        if not keepLooping:
            break
        #kernel = np.ones((3,3), np.uint8)
        ret, labels = cv2.connectedComponents(Mask)
        (y2,x2) = np.where(Mask.astype(bool))
        bb,ibb,nbb = np.unique(labels[[y2],[x2]],return_inverse=True,return_counts=True)
        numObj = bb.shape[0]

        if numObj >= 2:

            # good we got the dotted i, presumably
            Means = np.zeros((2,numObj),np.float)
            eigVals = np.zeros((2,numObj),np.float)
            eigVecs = np.zeros((2,2,numObj),np.float)
            for i in range(numObj):
                inds = np.where(ibb == i)[0]

                if inds.size < 4:
                    continue
                x = x2[inds] + Left
                y = y2[inds] + Top

                Pos2D = np.vstack((xMap[[y],[x]] , yMap[[y],[x]]))
                Cov = np.cov(Pos2D)

                eigVals[:,i], eigVecs[:,:,i] = np.linalg.eig(Cov);
                Means[:,i] = np.mean(Pos2D,axis=1)

            lineInd = np.argmax(np.sum(eigVals,axis=0))
            Axis = eigVecs[:,np.argmax(eigVals[:,lineInd]),lineInd]
            # Now we want the rotation matrix to be such that it turns Axis to the [0,1]
            rotMat = np.array([[Axis[1],-Axis[0]],[Axis[0],Axis[1]]])

            transCenters = np.matmul(rotMat,Means)
            # find the dot now
            dotInd = -1

            for i in range(numObj):
                if i != lineInd and np.abs(transCenters[0,i] - transCenters[0,lineInd]) < 0.030:
                    dotInd = i

            if dotInd == -1:
                raise Exception('Could not locate the dot near the line...')
            # now make sure that the dot has a higher y value than the line...

            #dotInd = int(1 - lineInd)
            #dotPos = np.matmul(rotMat,Means[:,dotInd])
            #linePos = np.matmul(rotMat,Means[:,lineInd])
            dotPos = transCenters[:,dotInd]
            linePos = transCenters[:,lineInd]

            whiteness[:] = 0
            inds = np.where(ibb == lineInd)[0]
            whiteness[y2[inds],x2[inds],0] = 255
            inds = np.where(ibb == dotInd)[0]
            whiteness[y2[inds],x2[inds],1] = 255

            cv2.putText(whiteness,'Press y if youre happy with the selection of the i and n if not.',(50,50), cv2.FONT_ITALIC,  1,(0,0,255),3)
            cv2.imshow('test',whiteness)
            keepLooping = True
            while keepLooping:
                key = cv2.waitKey(1)
                if key & 0xFF == ord('y'):
                    break
                elif key & 0xFF == ord('n'):
                    keepLooping = False

            if not keepLooping:
                break

            if dotPos[1] < linePos[1]:
                # turn another 180 degrees
                rotMat = -rotMat
            # now we need to add this rotation to the quaternion, then determine the Translation in x and y from the center of linePos (Means[:,lineInd]
            theta = -np.arctan2(rotMat[0,1],rotMat[0,0])
            quatZ = np.array([np.cos(theta/2), 0.0, 0.0, np.sin(theta/2)])
            quatComb = camera.quaternion_mult(np.expand_dims(quatZ,axis=1), np.expand_dims(myCamera.quat,axis=1)).flatten()
            TransXY = -np.matmul(rotMat,Means[:,dotInd])
            Trans = np.append(TransXY,myCamera.trans[2])
            camPars = np.array([myCamera.fx,myCamera.fy,myCamera.cx,myCamera.cy,myCamera.k1,myCamera.k2,myCamera.p1,myCamera.p2,myCamera.k3])
            camParsClassic = np.array([myCamera.k1class,myCamera.k2class,myCamera.p1class,myCamera.p2class,myCamera.k3class])

            calibrate_camera.saveCamCalFile(myConfig.calib_path, camPars, camParsClassic, Trans, quatComb)
            break
        d = 1
        key = cv2.waitKey(5000)

cap.release()
cv2.destroyAllWindows()


import datetime
import functools
import os
import pathlib
from typing import Tuple

SOURCE_DIRECTORY: os.PathLike = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIRECTORY: os.PathLike = os.path.dirname(SOURCE_DIRECTORY)

def get_now_str() -> str:
    return datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")

@functools.lru_cache
def get_project_subdirectory(name: str) -> os.PathLike:
    absolute_path: os.PathLike = os.path.join(PROJECT_DIRECTORY, name)
    if os.path.exists(absolute_path) is False:
        os.mkdir(absolute_path)
    return absolute_path

LOG_DIRECTORY: os.PathLike = get_project_subdirectory("logs")

def human_bytes_str(num_bytes: int) -> str:
    units = ("B", "KB", "MB", "GB")
    power = 2**10

    for unit in units:
        if num_bytes < power:
            return f"{num_bytes:.1f} {unit}"

        num_bytes /= power

    return f"{num_bytes:.1f} TB"

def human_seconds_str(seconds: int) -> str:
    units: Tuple[str] = ("seconds", "milliseconds", "microseconds")
    power: int = 1

    for unit in units:
        if seconds > power:
            return f"{seconds:.1f} {unit}"

        seconds *= 1000

    return f"{int(seconds)} nanoseconds"


from __future__ import division, print_function
import sys
import os
import numpy as np
from scipy.signal import find_peaks

# Arguments
GSASII_path = '/Users/mandy/g2full/GSASII'
project = 'pkfit.gpx'
instrument = 'NC_Empyrean.prm'
refined_output = 'pkfit.txt'
tthetas = [29, 37, 57, 60]
datadir = "../data"
PathWrap = lambda fil: os.path.join(datadir, fil)

# auto search by Scipy
def auto_finding(dic, output):
    dp = dic["xrdMeasurements"]["xrdMeasurement"]["scan"]["dataPoints"]
    y = dp["intensities"]["#text"]
    y = list(map(int, y.split()))
    sp = float(dp["positions"][0]["startPosition"])
    ep = float(dp["positions"][0]["endPosition"])
    x = np.arange(sp, ep, (ep - sp) / len(y))
    peaks, _ = find_peaks(y)
    peak_position = []
    peak_intensity = []
    f = open(output, 'w')
    f.write('pos' + '\t' + 'int' + '\t' + '\n')

    for peak in peaks:
        position = format(x[peak], '.4f')
        intensity = format(y[peak], '.2f')
        peak_position.append(position)
        peak_intensity.append(intensity)
        f.write(str(position) + '\t' + str(intensity) + '\t' + '\n')
    f.close()

    return peak_position, peak_intensity

# Validate peaklist with GSAS-II
def val_with_gsas2(gsas2_peak_file, our_pos, our_int):
    f = open(gsas2_peak_file)
    line = f.readline()
    count = 1
    peak_position = []
    peak_intensity = []

    while line:
        if count <= 3:
            count += 1
            line = f.readline()
            continue
        a = line.split()
        position = format(float(a[0]), '.4f')
        intensity = format(float(a[3]), '.2f')
        peak_position.append(position)
        peak_intensity.append(intensity)
        line = f.readline()
    f.close()

    count = 0
    err = 0.1
    for i in range(len(peak_position)):
        for j in range(len(our_pos)):
            diff = abs(float(peak_position[i]) - float(our_pos[j]))
            if diff < err and peak_intensity[i] == our_int[j]:
                count += 1
                break

    print("GSAS-II got %d peaks, we detected %d peaks, %d of them are overlap. "
          "(Allowed error in position: %.2f)"
          %(len(peak_position), len(our_pos), count, err))

def GSAS2(xrdml_file, instrument, output):
    # Load GSAS-II
    sys.path.insert(0, GSASII_path)
    import GSASIIscriptable as G2sc

    # Create a GSAS-II project
    gpx = G2sc.G2Project(newgpx=PathWrap(project))

    # Add powder diffraction datasets (histograms) to the project
    hist = gpx.add_powder_histogram(xrdml_file, instrument)

    # Refine peaks
    for i in range(len(tthetas)):
        hist.add_peak(1, ttheta=tthetas[i])
    hist.set_peakFlags(area=True)
    hist.refine_peaks()
    hist.set_peakFlags(area=True, pos=True)
    hist.refine_peaks()
    hist.set_peakFlags(area=True, pos=True, sig=True, gam=True)
    hist.refine_peaks()

    # Export peak list
    hist.Export_peaks(output)

    # Save GSAS-II project
    gpx.save()


import csv

with open ('UNY.csv', "wb") as f:
    writer = csv.writer(f)
    with open('UNY Address.csv') as csvDataFile:
        csvReader = csv.reader(csvDataFile)
        for row in csvReader:
            row2 = row[1]
            row3 = row2.split(' ')
            bnum = row3[0]
            state = row3[-1]
            city = row3[-2]
            street = row2.split(' ', 1)[1].rsplit(' ', 2)[0]
            ##print bnum
            ##print state
            ##print city
            ##print street
            writer.writerow([bnum, street, city, state])


# This script reads all the items in a csv with the following format:
# bib title, item call number, item total checkouts, item total number of renewals, item location
# and outputs the total number of checkouts and renewals for each matching title.

# The purpose of the script is to condense circultion information on bibliographic
# titles which have serialized items (e.g. magazines).

file_name = 'MagazineHoldings.txt'

location_code = 'location-code'

file = open(file_name, 'r', encoding="UTF-8")
Lines = file.readlines()

TITLES = []
STATS = []

for line in Lines:
    if line == Lines[0]:
        pass
    else:
        stats = {}
        l = line.split(",")

        BibTitle = l[0]
        CallNo = l[-4]
        TotalChk = l[-3].strip("\"").strip("'")
        Renew = l[-2]
        location = l[-1].replace('"', "").replace(" ", "")

        if location[0:len(location_code)] == location_code and TotalChk != '':
            if BibTitle not in TITLES:
                TITLES.append(BibTitle)
                stats[BibTitle] = int(TotalChk)
                STATS.append(stats)
            else:
                for i in STATS:
                    for key in i:
                        if key == BibTitle:
                            i[key] = int(i[key]) + int(TotalChk)

with open("CleanData.txt", "a") as newfile:
    for i in STATS:
        for key in i:
            newfile.write(key + " : " + str(i[key]))
            newfile.write("\n")


f = open('School/Maturita/texty/6.txt','r')
eskit = f.read()
import re
teploty = re.findall(r'[+-][0-9]+[,][0-9]+',eskit)
miesta = re.findall(r'[M][A-Za-z0-9]+',eskit)
print (teploty,miesta,'\nmerania =',len(teploty))
maxx = -9000
maxd = -1
skit = []
for pi in teploty:
    x = re.sub(r',','.',pi)
    x = float(x)
    if x > maxx:
        maxx = x
        maxd += 1
    skit.append(x)
print(maxx,' v ',miesta[maxd])
def average(arr):
    return sum(arr)/len(arr)
print(average(skit))


from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .serializers import PredictResultSerializer

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

def validate_data(height, weight, days):
    if height <= 0 or weight <= 0 or days <- 0:
        raise ValueError("키와 몸무게, 일수는 양수이어야 합니다.")

class PredictResultAPIView(APIView):
    def post(self,request):
        # CSV 파일 경로
        csv_file_path = 'fstpage/baby_growth_df.csv'
        # CSV 파일을 데이터 프레임으로 로드
        baby_growth_df = pd.read_csv(csv_file_path)

        #babycare 앱에서 데이터 받아옴
        height = float(request.data.get('height'))
        weight = float(request.data.get('weight'))
        gender = str(request.data.get('gender'))
        days = float(request.data.get('days'))

        try:
            validate_data(height, weight, days)
        except ValueError as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        if gender == "female":
            gender = 0.0
        elif gender == "male":
            gender = 1.0
        else:
            raise ValidationError('Invalid gender')#모델 사용 위해 float형으로 변경

        # 입력값을 DataFrame으로 변환
        input_data = {'days': [days], 'height': [height], 'weight': [weight], 'gender': [gender]}
        df = pd.DataFrame(input_data)

        # DataFrame을 기존 데이터에 추가
        baby_growth_df = pd.concat([baby_growth_df, df], ignore_index=True)

        baby_growth_df.to_csv(csv_file_path, index=False)
        #height 예측 모델링
        # 독립 변수와 종속 변수 분리

        filtered_df = baby_growth_df[baby_growth_df['gender'] == gender]

        # 키에 대한 예측 모델 학습
        height_X = filtered_df['days'].values.reshape(-1, 1)
        height_y = filtered_df['height'].values

        # 데이터 분할
        # height_X_train, height_X_test, height_y_train, height_y_test = train_test_split(height_X, height_y, test_size=0.2, random_state=42)

        height_model = LinearRegression()
        height_model.fit(height_X, height_y)

        # 몸무게에 대한 예측 모델 학습
        weight_X = filtered_df['days'].values.reshape(-1, 1)
        weight_y = filtered_df['weight'].values

        # 데이터 분할
        # weight_X_train, weight_X_test, weight_y_train, weight_y_test = train_test_split(weight_X, weight_y, test_size=0.2, random_state=42)

        weight_model = LinearRegression()
        weight_model.fit(weight_X, weight_y)

        prediction_range = range(1, 181)

        predicted_heights = []

        # 입력한 일 수에 대한 예측값 계산
        predicted_height = height_model.predict([[days]])

        if height > predicted_height:
            slope_height = height_model.coef_[0]
            for i in prediction_range:
                prediction_days = days + i
                predicted_height_sync = slope_height * prediction_days + (height_model.intercept_ + (height - predicted_height))
                predicted_heights.append(predicted_height_sync)
        else:
            slope_height = height_model.coef_[0]
            for i in prediction_range:
                prediction_days = days + i
                predicted_height_sync = slope_height * prediction_days + (height_model.intercept_ - (predicted_height - height))
                predicted_heights.append(predicted_height_sync)

        predicted_heights_val = []
        for i in range(len(predicted_heights)):
            predicted_heights_val.append(round(predicted_heights[i][0],2))

        prediction_range = range(1, 181)

        # 예측 결과를 저장할 리스트 초기화
        predicted_weights = []

        # 입력한 일 수에 대한 예측값 계산
        predicted_weight = weight_model.predict([[days]])

        if weight> predicted_weight:
            slope_weight = weight_model.coef_[0]
            for i in prediction_range:
                prediction_days = days + i
                predicted_weight_sync = slope_weight * prediction_days + (weight_model.intercept_ + (weight- predicted_weight))
                predicted_weights.append(predicted_weight_sync)
        else:
            slope_weight = weight_model.coef_[0]
            for i in prediction_range:
                prediction_days = days + i
                predicted_weight_sync = slope_weight * prediction_days + (weight_model.intercept_ - (predicted_weight - weight))
                predicted_weights.append(predicted_weight_sync)

        predicted_weights_val = []
        for i in range(len(predicted_weights)):
            predicted_weights_val.append(round(predicted_weights[i][0],2))

        result = {
            'gender': gender,
            'days': days,
            'one_month_pred_height': predicted_heights_val[29], #cm
            'three_month_pred_height': predicted_heights_val[89], #cm
            'six_month_pred_height': predicted_heights_val[179], #cm
            'one_month_pred_weight': predicted_weights_val[29], #kg
            'three_month_pred_weight': predicted_weights_val[89], #kg
            'six_month_pred_weight': predicted_weights_val[179], #kg
            'graph_height': predicted_heights_val,
            'graph_weight': predicted_weights_val,
            'height': height,
            'weight': weight,
        }

        serializer = PredictResultSerializer(data=result)

        if serializer.is_valid():
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def get(self, request):
        return Response({'detail': 'Method Not Allowed'}, status=status.HTTP_405_METHOD_NOT_ALLOWED)

    def put(self, request):
        return Response({'detail': 'Method Not Allowed'}, status=status.HTTP_405_METHOD_NOT_ALLOWED)

    def delete(self, request):
        return Response({'detail': 'Method Not Allowed'}, status=status.HTTP_405_METHOD_NOT_ALLOWED)


# variable parameters
def report(*grades):
  total_grade = 0;
  for grade in grades:
    total_grade += grade
  return total_grade

# keywords parameters
def portrait(**kw):
  for k,v in kw.items():
    print(k,v)

if __name__ == '__main__':
  print('total grade is', report(10,30,20))
  portrait(age=24, country='China', education='bachelor')

#universal_func(*args, **kw)


from abc import ABC, abstractmethod
from typing import List, Dict, Union, Type, Tuple

from aiogram.utils.callback_data import CallbackData
from aiogram.types import (InlineKeyboardButton,
                           KeyboardButton,
                           ReplyKeyboardMarkup,
                           KeyboardButtonPollType,
                           LoginUrl,
                           InlineKeyboardMarkup)

class InheritanceCreateMarkupError(Exception):
    pass

class CreateMarkup(ABC):
    __Buttons = List[Union[InlineKeyboardButton, KeyboardButton]]
    __Buttons_list = List[List[Union[InlineKeyboardButton, KeyboardButton]]]
    __Buttons_type = Union[Type[InlineKeyboardButton], Type[KeyboardButton]]

    def __init__(self, data_buttons: List, schema: List[int]):
        self.data_buttons = data_buttons
        self.schema = schema
        self._max_row = max(schema)

    @abstractmethod
    def create(self):
        pass

    def _create_buttons(self):
        buttons = []
        for row_button in self.data_buttons:
            buttons.append(self.__auto_select_type_button()(**row_button))
        return buttons

    def _create_schema(self, buttons: __Buttons) -> __Buttons_list:
        if sum(self.schema) != len(buttons):
            raise ValueError("The quantity of buttons doesn't match the schema")
        markup = []
        for btn in self.schema:
            markup.append([])
            for _ in range(btn):
                markup[-1].append(buttons.pop(0))
        return markup

    @classmethod
    def __auto_select_type_button(cls) -> __Buttons_type:
        if 'inline' in cls.__name__.lower():
            return InlineKeyboardButton
        if 'default' in cls.__name__.lower():
            return KeyboardButton
        else:
            raise InheritanceCreateMarkupError(
                'Inherited class naming error. Class name must include '
                '"default" or "inline".'
            )

class InlineMarkup(CreateMarkup):
    __Actions = Union[str, bool, LoginUrl, Tuple[Dict[str, str], CallbackData]]
    __Buttons = List[Dict[str, __Actions]]

    BUTTONS_AVAILABLE_KEYS = [
        'text',
        'callback_data',
        'url',
        'login_url',
        'switch_inline_query',
        'switch_inline_query_current_chat',
        'callback_game',
        'pay'
    ]

    def __init__(self, data_buttons: __Buttons, schema):
        super().__init__(data_buttons, schema)

    @property
    def data_buttons(self):
        return self.__data_buttons

    @data_buttons.setter
    def data_buttons(self, data_buttons):
        self._check_input_data(data_buttons)
        self.__data_buttons = data_buttons

    def create(self) -> InlineKeyboardMarkup:
        keyboard = InlineKeyboardMarkup()
        keyboard.row_width = self._max_row
        buttons = self._create_buttons()
        keyboard.inline_keyboard = self._create_schema(buttons)
        return keyboard

    @classmethod
    def _check_input_data(cls, data_buttons) -> None:
        for button in data_buttons:
            provided_keys = []
            for key, _ in button.items():
                provided_keys.append(key)
            for key in provided_keys:
                if key not in cls.BUTTONS_AVAILABLE_KEYS:
                    raise KeyError(f"The key <{key}=> doesn't allowed. "
                                   f"Allowed keys is: {cls.BUTTONS_AVAILABLE_KEYS}")
            if 'text' not in provided_keys:
                raise KeyError(f"The button settings must contain the key <text=>")
            available_keys = cls.BUTTONS_AVAILABLE_KEYS.copy()
            available_keys.remove('text')
            if len(provided_keys) < 2:
                raise KeyError('Not enough data to create a button. '
                               f'Need to add one of the keys: {available_keys}')
            if len(provided_keys) > 2:
                raise KeyError('Only one of the optional keys must be used: '
                               f'{available_keys}')

class DefaultMarkup(CreateMarkup):
    __Buttons = List[Union[str, Dict[str, Union[str, bool, KeyboardButtonPollType]]]]

    BUTTONS_AVAILABLE_KEYS = [
        'text',
        'request_contact',
        'request_location',
        'request_poll',
        'request_user',
        'request_chat',
        'web_app'
    ]

    def __init__(self,
                 data_buttons: __Buttons,
                 schema,
                 *,
                 resize_keyboard: bool = True,
                 selective: bool = False,
                 one_time_keyboard: bool = False,
                 is_persistent: bool = True
                 ):
        super().__init__(data_buttons, schema)
        self.settings = dict(resize_keyboard=resize_keyboard,
                             selective=selective,
                             one_time_keyboard=one_time_keyboard,
                             is_persistent=is_persistent)

    @property
    def data_buttons(self):
        return self.__data_buttons

    @data_buttons.setter
    def data_buttons(self, data_buttons):
        self.__data_buttons = self._check_input_data(data_buttons)

    def create(self) -> ReplyKeyboardMarkup:
        keyboard = ReplyKeyboardMarkup(**self.settings)
        keyboard.row_width = self._max_row
        buttons = self._create_buttons()
        keyboard.keyboard = self._create_schema(buttons)
        return keyboard

    @classmethod
    def _check_input_data(cls, data_buttons: __Buttons) -> __Buttons:
        for button in data_buttons:
            if isinstance(button, str):
                index = data_buttons.index(button)
                data_buttons[index] = {'text': button}
                continue
            if len(button) > 1:
                raise KeyError('Only one key must be used.')
            key = list(button.keys())[0]
            if key not in cls.BUTTONS_AVAILABLE_KEYS:
                raise KeyError(f"The key <{key}=> doesn't allowed. "
                               f"Allowed keys is: {cls.BUTTONS_AVAILABLE_KEYS}")
        return data_buttons


from turtle import Turtle
import Bullet

class Ship(Turtle):
    def __init__(self, start_position):
        # --- INITIALIZE THE SHIP --- #
        self.x_pointer_pos = 0
        super().__init__()
        self.shape('square')
        self.color('white')
        self.shapesize(stretch_wid=1, stretch_len=7)
        self.penup()
        self.goto(start_position)

        # Variable for bullet
        self.bullet = None
        # variable for moving speed of the bullet
        self.ymove = 10
        # variable for initial position of the bullet
        self.bullet_start = None

    def check_bullet(self):
        # If the ship fired the bullet, move it
        if self.bullet:
            self.move_bullet(self.bullet.ycor() + self.ymove)

    def shoot(self):
        # If there is no current fired bullet, initiate new one with the x-coordinates of the ship
        if not self.bullet:
            self.bullet_start = self.xcor()
            self.bullet = Bullet.Bullet(self.bullet_start)

    def move_bullet(self, new_y):
        self.bullet.sety(new_y)

        # If the bullet moves out of the screen, remove it
        if new_y > 250:
            self.bullet.reset()
            self.bullet = None

    # Functions to move ship left and right
    def go_left(self):
        if self.xcor() > - 300:
            self.setx(self.xcor() - 20)

    def go_right(self):
        if self.xcor() < 300:
            self.setx(self.xcor() + 20)


# olp.py: Useful functions for streamlining one-line python programming
#  primitives["while"](scopevar: dict, cond: str, do: str) -> dict:
#    params:
#      scopevar: dict: a dict to update the scope of the while loop,
#        This is usually globals(), locals(), or globals() | locals()
#      cond: str: the condition to check at each iteration (passed into eval)
#      do: str: the code to execute at each iteration (passed into exec)
#    returns:
#      dict: the globals() at the end of the while loop execution, to allow
#       access to variables modified within the loop
#  primitives["dowhile"](scopevar: dict, cond: str, do: str) -> dict:
#    A do-while equivalent to the while loop, with the same params and return value
primitives = {"while": lambda scopevar, cond, do: globals().update(scopevar) or globals().update(wl = [1]) or any([wl.append(1) or exec(do) for i in wl if eval(cond) and len(wl) < 1000000]) or globals(), "dowhile": lambda scopevar, cond, do: globals().update(scopevar) or globals().update(wl = [1]) or (exec(do) and False) or any([wl.append(1) or exec(do) for i in wl if eval(cond) and len(wl) < 1000000]) or globals(), "inherit": lambda class_: class_.update(class_["superclass"]) if "superclass" in class_ else False}


from kolabria.settings.base import *

DEBUG = False
TEMPLATE_DEBUG = False

INSTALLED_APPS += ('sentry,')

DATABASES['default']['NAME'] = 'kolabria_production.db'

SENTRY_LOG_DIR = os.path.join(PROJECT_DIR, 'logs')
SENTRY_RUN_DIR= os.path.join(PROJECT_DIR, 'run')
SENTRY_WEB_HOST = '127.0.0.1'
SENTRY_WEB_PORT = 9000
SENTRY_KEY = '86%96ay-ct*)3)s2(=0rozcb$0*9z(6#zxfi$-)#kih_ah_fu^'

SENTRY_FILTERS = (
    'sentry.filters.StatusFilter',
    'sentry.filters.LoggerFilter',
    'sentry.filters.LevelFilter',
    'sentry.filters.ServerNameFilter',
    'sentry.filters.SiteFilter',
)

SENTRY_VIEWS = (
    'sentry.views.Exception',
    'sentry.views.Message',
    'sentry.views.Query',
)