# https://docs.aws.amazon.com/sagemaker/latest/dg/lineage-tracking-entities.html
# https://github.com/aws/amazon-sagemaker-examples/blob/master/sagemaker-lineage/sagemaker-lineage.ipynb

import base64
from sagemaker.lineage.artifact import Artifact
from sagemaker.lineage.context import Context
from sagemaker.lineage.action import Action
from sagemaker.lineage.association import Association
from typing import Optional, Iterator
from sagemaker.lineage._api_types import AssociationSummary
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import boto3
from .utils import SageMakerSession

class MLLineageHelper:
    def __init__(
        self, sagemaker_session=None, sagemaker_model_name_or_model_s3_uri=None
    ):
        """Default constructor

        Args:
            sagemaker_model_name_or_model_s3_uri (str): SageMaker model name or S3 URI of model package
            sagemaker_session (SageMakerSession): SageMakerSession object
        """
        if sagemaker_session:
            self.sagemaker_session = sagemaker_session
        else:
            self.sagemaker_session = SageMakerSession()
        self.sagemaker_model_name_or_model_s3_uri = sagemaker_model_name_or_model_s3_uri
        self.df = None
        if self.sagemaker_model_name_or_model_s3_uri:
            self.df = self.get_ml_lineage()

    def get_associations(
        self, src_arn: Optional[str] = None, dest_arn: Optional[str] = None
    ) -> Iterator[AssociationSummary]:
        """Given an arn retrieve all associated lineage entities.
        The arn must be one of: experiment, trial, trial component, artifact, action, or context.
        Args:
            src_arn (str, optional): The arn of the source. Defaults to None.
            dest_arn (str, optional): The arn of the destination. Defaults to None.
        Returns:
            array: An array of associations that are either incoming or outgoing from the lineage
            entity of interest.
        """
        if src_arn:
            associations: Iterator[AssociationSummary] = Association.list(
                source_arn=src_arn, sagemaker_session=self.sagemaker_session.session
            )
        else:
            associations: Iterator[AssociationSummary] = Association.list(
                destination_arn=dest_arn,
                sagemaker_session=self.sagemaker_session.session,
            )
        return associations

    def get_ml_lineage(self):
        """Crawl up the lineage graph"""
        # If input is model name, find the model package uri in S3
        if not self.sagemaker_model_name_or_model_s3_uri.endswith(".tar.gz"):
            sagemaker_client = boto3.client(
                "sagemaker", region_name=self.sagemaker_session.region
            )
            s3_uri_of_model = sagemaker_client.describe_model(
                ModelName=self.sagemaker_model_name_or_model_s3_uri
            )["PrimaryContainer"]["ModelDataUrl"]
        # Otherwise the input was already the S3 uri of the model package
        else:
            s3_uri_of_model = self.sagemaker_model_name_or_model_s3_uri

        # Start with the model artifact (the end of the DAG)
        model_summary = list(
            Artifact.list(
                source_uri=s3_uri_of_model,
                max_results=1,
                sagemaker_session=self.sagemaker_session.session,
            )
        )[0]
        model_artifact_arn = model_summary.artifact_arn

        # Start working upstream in the DAG
        upstream_associations = list(
            self.get_associations(dest_arn=model_artifact_arn)
        )  # Training job
        second_order_upstream_associations = list(
            self.get_associations(dest_arn=upstream_associations[0].source_arn)
        )
        upstream_associations.extend(second_order_upstream_associations)

        # Find the TrainingData artifact so we can fetch the SageMaker Processing artifact if it exists
        for association in second_order_upstream_associations:
            source_name = association.source_name
            if not source_name:
                source_name = Artifact.load(association.source_arn).artifact_name
            if source_name == "TrainingData" or source_name == "TestingData":
                training_data_artifact_summary = association
                third_order_upstream_associations = list(
                    self.get_associations(
                        dest_arn=training_data_artifact_summary.source_arn
                    )
                )
                upstream_associations.extend(third_order_upstream_associations)
                fourth_order_upstream_associations = []
                for each in third_order_upstream_associations:
                    fourth_order_upstream_associations.extend(
                        list(
                            self.get_associations(
                                dest_arn=third_order_upstream_associations[0].source_arn
                            )
                        )
                    )
                upstream_associations.extend(fourth_order_upstream_associations)

                try:
                    # Get artifacts that fed into the SM processing job
                    fifth_order_upstream_associations = list(
                        self.get_associations(
                            dest_arn=fourth_order_upstream_associations[0].source_arn
                        )
                    )
                    # Get feature groups produced by SM processing job
                    fifth_order_upstream_associations.extend(
                        list(
                            self.get_associations(
                                src_arn=fourth_order_upstream_associations[0].source_arn
                            )
                        )
                    )
                    upstream_associations.extend(fifth_order_upstream_associations)
                except:
                    pass
        data = []
        for association in upstream_associations:
            name_source = association.source_name
            association_type = association.association_type
            name_destination = association.destination_name
            artifact_source_arn = association.source_arn
            if not name_source:
                name_source = Artifact.load(artifact_source_arn).artifact_name
            artifact_destination_arn = association.destination_arn
            if not name_destination:
                name_destination = Artifact.load(artifact_destination_arn).artifact_name
            try:
                source_uri = Artifact.load(association.source_arn).source.source_uri
            except:
                source_uri = None
            if name_source == "TrainingData":
                # Set properties
                artifact = Artifact.load(training_data_artifact_summary.source_arn)
                try:
                    base64_feature_store_query_string = artifact.properties[
                        "Base64FeatureStoreQueryString"
                    ]
                except:
                    pass
            else:
                base64_feature_store_query_string = None

            git_url = None
            if name_source == "TrainingCode" or name_source == "ProcessingCode":
                # Set properties
                artifact = Artifact.load(artifact_source_arn)
                try:
                    git_url = artifact.properties["GitURL"]
                except:
                    pass

            if name_source:
                data.append(
                    [
                        name_source,
                        association_type,
                        name_destination,
                        artifact_source_arn,
                        artifact_destination_arn,
                        source_uri,
                        base64_feature_store_query_string,
                        git_url,
                    ]
                )
        df = pd.DataFrame(
            data=data,
            columns=[
                "Name/Source",
                "Association",
                "Name/Destination",
                "Artifact Source ARN",
                "Artifact Destination ARN",
                "Source URI",
                "Base64 Feature Store Query String",
                "Git URL",
            ],
        )
        df.drop_duplicates(ignore_index=True, inplace=True)
        self.df = df
        return df

    def graph(self):
        """Visually represent the lineage DAG"""
        plt.figure(3, figsize=(16, 14))
        graph = nx.DiGraph()
        graph.add_edges_from([(each[0], each[2]) for each in self.df.values])
        nx.draw_networkx(
            graph,
            node_size=800,
            node_color="orange",
            alpha=0.9,
            font_size=14,
            pos=nx.spring_layout(graph),
        )
        plt.show()

    def get_artifact_if_exists(self, artifact_source_uri, artifact_name, properties):
        """Check if artifact already exists

        Args:
            artifact_source_uri (str): Source URI of the artifact
            artifact_name (str): Name of the artifact

        Returns:
            sagemaker.lineage.Artifact or None

        """
        # Check if artifact_source_uri already exists
        try:
            artifact_list = list(
                Artifact.list(
                    source_uri=artifact_source_uri,
                    max_results=1,
                    sagemaker_session=self.sagemaker_session.session,
                )
            )
        except Exception as e:
            artifact_list = []
        if artifact_list:
            artifact_summary = artifact_list[0]
            print(
                f"Using existing artifact, {artifact_name}: {artifact_summary.artifact_arn}\n"
            )
            try:
                artifact_properties = artifact_summary.properties
            except:
                artifact_properties = properties
            if properties or artifact_properties:
                artifact = Artifact(
                    artifact_arn=artifact_summary.artifact_arn,
                    artifact_name=artifact_name,
                    source=artifact_summary.source,
                    artifact_type=artifact_summary.artifact_type,
                    properties=properties,
                    sagemaker_session=self.sagemaker_session.session,
                )
                artifact.save()
            else:
                artifact = Artifact(
                    artifact_arn=artifact_summary.artifact_arn,
                    artifact_name=artifact_name,
                    source=artifact_summary.source,
                    artifact_type=artifact_summary.artifact_type,
                    sagemaker_session=self.sagemaker_session.session,
                )
                artifact.save()
            return artifact
        else:
            return None

    def create_artifact(
        self,
        artifact_name,
        artifact_source_uri,
        artifact_type,
        properties=None,
    ):
        """Check if artifact already exists

        Args:
            artifact_name (str): Name of the artifact
            artifact_source_uri (str): Source URI of the artifact
            artifact_type (str): Type of the artiface ('Code', 'DataSet', etc...)
            properties (dict): Optional custom properties for the artifact

        Returns:
            sagemaker.lineage.Artifact

        """
        artifact = self.get_artifact_if_exists(
            artifact_source_uri, artifact_name, properties
        )
        if not artifact:
            artifact = Artifact.create(
                artifact_name=artifact_name,
                source_uri=artifact_source_uri,
                artifact_type=artifact_type,
                properties=properties,
                sagemaker_session=self.sagemaker_session.session,
            )
            # artifact.save()
            print(f"Created {artifact_name} artifact: {artifact.artifact_arn}\n")
        return artifact

    def associate_artifacts(
        self, artifacts, association_type, artifact_destination_arn
    ):
        """Asssociate two artifacts together

        Args:
            artifacts (list): List of Artifacts
            association_type (str): Type of association ('Produced', 'ContributedTo', etc...)
            artifact_destination_arn (str): Destination Artifact ARN
            sagemaker_session (SageMakerSession): SageMakerSession object

        Returns:
            None

        """
        if type(artifacts) != list:
            artifacts = [artifacts]
        for artifact in artifacts:
            if artifact == None:
                continue
            if type(artifact) == Artifact:
                source_arn = artifact.artifact_arn
            else:
                source_arn = artifact
            try:
                Association.create(
                    source_arn=source_arn,
                    destination_arn=artifact_destination_arn,
                    association_type=association_type,
                    sagemaker_session=self.sagemaker_session.session,
                )
                print(
                    f"Associated {artifact.artifact_arn} and {artifact_destination_arn} with association {association_type}\n"
                )
            except:
                print(
                    f"Association already exists between {source_arn} and {artifact_destination_arn}\n"
                )

    def create_training_job_artifacts(self, training_job_info, repo_links):
        """Create training job artifacts

        Args:
            training_job_info (dict): Training job information
            repo_links (list(tuples)): Repo links for processing and training code

        Returns:
            tuple of SageMaker Artifacts: code, training_data, and testing_data artifacts
        """
        # Create training job artifacts
        try:
            code_s3_uri = training_job_info["HyperParameters"][
                "sagemaker_submit_directory"
            ][1:-1]
        except:
            code_s3_uri = None
        training_data_s3_uri = training_job_info["InputDataConfig"][0]["DataSource"][
            "S3DataSource"
        ]["S3Uri"]
        testing_data_s3_uri = training_job_info["InputDataConfig"][1]["DataSource"][
            "S3DataSource"
        ]["S3Uri"]

        training_data_artifact = self.create_artifact(
            "TrainingData", training_data_s3_uri, "DataSet"
        )
        testing_data_artifact = self.create_artifact(
            "TestingData", testing_data_s3_uri, "DataSet"
        )
        if code_s3_uri:
            code_artifact = self.create_artifact("TrainingCode", code_s3_uri, "Code")
        else:
            code_artifact = None
        # Add git URL to code artifact
        if code_s3_uri and repo_links:
            for repo_link in repo_links:
                if repo_link[0] == "training_code":
                    code_artifact.properties = {"GitURL": repo_link[1]}
                    code_artifact.save()
        return code_artifact, training_data_artifact, testing_data_artifact

    def create_model_artifact(self, model_name, training_job_info):
        """Create a model artifact

        Args:
            model_name (str): Name of the SageMaker model
            training_job_info (dict): Training job information

        Returns:
            sagemaker.lineage.artifact.Artifact: Model artifact
        """
        if model_name:
            sagemaker_client = boto3.client(
                "sagemaker", region_name=self.sagemaker_session.region
            )
            trained_model_s3_uri = sagemaker_client.describe_model(
                ModelName=model_name
            )["PrimaryContainer"]["ModelDataUrl"]
        else:
            trained_model_s3_uri = training_job_info["ModelArtifacts"][
                "S3ModelArtifacts"
            ]
        # SageMaker automatically creates a model artifact so
        # we'll get an existing artifact back but it won't have
        # our custom attributes
        model_artifact = self.create_artifact(
            "Model",
            trained_model_s3_uri,
            "Model",
            properties={"SageMakerModelName": model_name},
        )
        # So set the custom attributes
        if model_artifact:
            model_artifact.artifact_name = "Model"
            model_artifact.source_uri = trained_model_s3_uri
            model_artifact.artifact_type = "Model"
            model_artifact.properties = {"SageMakerModelName": model_name}
            model_artifact.save()
        return model_artifact

    def create_sm_processing_job_artifacts(
        self, sagemaker_processing_job_description, repo_links
    ):
        """Create SageMaker Processing job artifacts

        Args:
            sagemaker_processing_job_description (dict): SageMaker Processing job information
            repo_links (list(tuples)): Repo links for processing and training code

        Returns:
            tuple of sagemaker.lineage.artifact.Artfiact: processing code, raw data, and processing job artifacts
        """
        try:
            processing_script_uri = sagemaker_processing_job_description[
                "ProcessingInputs"
            ][1]["S3Input"]["S3Uri"]
            processing_input_data_uri = sagemaker_processing_job_description[
                "ProcessingInputs"
            ][0]["S3Input"]["S3Uri"]
        except:
            processing_script_uri = sagemaker_processing_job_description[
                "ProcessingInputs"
            ][0]["S3Input"]["S3Uri"]
            processing_input_data_uri = sagemaker_processing_job_description[
                "AppSpecification"
            ]["ContainerArguments"][3]
        processing_job_arn = sagemaker_processing_job_description["ProcessingJobArn"]

        sagemaker_processing_code_artifact = self.create_artifact(
            "ProcessingCode", processing_script_uri, "Code"
        )
        # Add git URL to code artifact
        if repo_links:
            for repo_link in repo_links:
                if repo_link[0] == "processing_code":
                    sagemaker_processing_code_artifact.properties = {
                        "GitURL": repo_link[1]
                    }
                    sagemaker_processing_code_artifact.save()

        sagemaker_processing_input_data_artifact = self.create_artifact(
            "ProcessingInputData", processing_input_data_uri, "DataSet"
        )

        sagemaker_processing_job_artifact = self.create_artifact(
            "ProcessingJob", processing_job_arn, "ProcessingJob"
        )

        return (
            sagemaker_processing_code_artifact,
            sagemaker_processing_input_data_artifact,
            sagemaker_processing_job_artifact,
        )

    def create_ml_lineage(
        self,
        estimator_or_training_job_name,
        model_name=None,
        query=None,
        feature_group_names: list = None,
        sagemaker_processing_job_description=None,
        repo_links: list = None,
    ):
        """Do ML Lineage Tracking

        Args:
            estimator_or_training_job_name (sagemaker.Estimator or str): An estimator object (object you get after training a model) or the training job name
            model_name (str): Name of the model in SageMaker (SageMaker console --> Inference --> Models)
            query (str): The query you used to query the Feature Store and get your training and test data
            feature_group_names (list): An optional list of Feature Group names
            sagemaker_processing_job_description (dict): The job description of your SageMaker Processing job
            repo_links ([Tuples]): List of tuples containing repo links of processing and training code

        Returns:
            MLLineage
        """

        sagemaker_client = boto3.client(
            "sagemaker", region_name=self.sagemaker_session.region
        )
        if type(estimator_or_training_job_name) != str:
            training_job_info = (
                estimator_or_training_job_name.latest_training_job.describe()
            )
        else:
            training_job_info = sagemaker_client.describe_training_job(
                TrainingJobName=estimator_or_training_job_name
            )
        (
            code_artifact,
            training_data_artifact,
            testing_data_artifact,
        ) = self.create_training_job_artifacts(training_job_info, repo_links)

        model_artifact = self.create_model_artifact(model_name, training_job_info)

        input_artifacts = [training_data_artifact, testing_data_artifact, code_artifact]

        # Create Feature Store artifacts and query
        if query:
            # Base64 encode query string
            base64_encoded_query_bytes = base64.b64encode(query.encode("utf-8"))
            base64_encoded_query_string = str(base64_encoded_query_bytes, "utf-8")
            # Cut off
            if len(base64_encoded_query_string) > 256:
                base64_encoded_query_string = base64_encoded_query_string[0:256]

            training_data_artifact.properties = {
                "Base64FeatureStoreQueryString": base64_encoded_query_string
            }
            training_data_artifact.save()
            testing_data_artifact.properties = {
                "Base64FeatureStoreQueryString": base64_encoded_query_string
            }
            testing_data_artifact.save()

        if feature_group_names:
            feature_group_artifacts = []
            for feature_group_name in feature_group_names:
                feature_group_arn = sagemaker_client.describe_feature_group(
                    FeatureGroupName=feature_group_name
                )["FeatureGroupArn"]
                temp_artifact = self.create_artifact(
                    f"fg-{feature_group_name}", feature_group_arn, "FeatureGroup"
                )
                feature_group_artifacts.append(temp_artifact)

        # Create SageMaker Processing job artifacts
        if sagemaker_processing_job_description:
            sm_processing_job_artifacts = self.create_sm_processing_job_artifacts(
                sagemaker_processing_job_description, repo_links
            )
            sagemaker_processing_code_artifact = sm_processing_job_artifacts[0]
            sagemaker_processing_input_data_artifact = sm_processing_job_artifacts[1]
            sagemaker_processing_job_artifact = sm_processing_job_artifacts[2]

        # Get trial component which will contain metrics from the training job
        training_job_name = training_job_info["TrainingJobName"]
        trial_component = sagemaker_client.describe_trial_component(
            TrialComponentName=f"{training_job_name}-aws-training-job"
        )
        trial_component_arn = trial_component["TrialComponentArn"]

        # Associate input artifacts
        self.associate_artifacts(input_artifacts, "ContributedTo", trial_component_arn)
        self.associate_artifacts(
            trial_component_arn, "Produced", model_artifact.artifact_arn
        )

        # Associate Feature Group artifacts to training data artifact
        if feature_group_names and feature_group_artifacts:
            self.associate_artifacts(
                feature_group_artifacts,
                "ContributedTo",
                training_data_artifact.artifact_arn,
            )
            self.associate_artifacts(
                feature_group_artifacts,
                "ContributedTo",
                testing_data_artifact.artifact_arn,
            )

        # Associate SageMaker Processing job artifacts
        if sagemaker_processing_job_description:
            self.associate_artifacts(
                [
                    sagemaker_processing_code_artifact,
                    sagemaker_processing_input_data_artifact,
                ],
                "ContributedTo",
                sagemaker_processing_job_artifact.artifact_arn,
            )
            if feature_group_names and feature_group_artifacts:
                for feature_group_artifact in feature_group_artifacts:
                    self.associate_artifacts(
                        sagemaker_processing_job_artifact,
                        "ContributedTo",
                        feature_group_artifact.artifact_arn,
                    )
            else:  # Training data generated by processing job, not Feature Store data
                self.associate_artifacts(
                    sagemaker_processing_job_artifact,
                    "Produced",
                    training_data_artifact.artifact_arn,
                )

        self.sagemaker_model_name_or_model_s3_uri = model_name
        return self.get_ml_lineage()

    def delete_associations(self, arn):
        """Delete an artifact's associations.

        Args:
            arn (str): Artifact ARN
        """
        # Delete incoming associations
        incoming_associations = Association.list(destination_arn=arn)
        for summary in incoming_associations:
            assct = Association(
                source_arn=summary.source_arn,
                destination_arn=summary.destination_arn,
                sagemaker_session=self.sagemaker_session.session,
            )
            assct.delete()

        # Delete outgoing associations
        outgoing_associations = Association.list(source_arn=arn)
        for summary in outgoing_associations:
            assct = Association(
                source_arn=summary.source_arn,
                destination_arn=summary.destination_arn,
                sagemaker_session=self.sagemaker_session.session,
            )
            assct.delete()

    def delete_lineage_data(self):
        """Delete ALL lineage data in your account.
        """
        for summary in Context.list():
            print(f"Deleting context {summary.context_name}")
            self.delete_associations(summary.context_arn)
            ctx = Context(context_name=summary.context_name, sagemaker_session=self.sagemaker_session.session)
            ctx.delete()

        for summary in Action.list():
            print(f"Deleting action {summary.action_name}")
            self.delete_associations(summary.action_arn)
            actn = Action(action_name=summary.action_name, sagemaker_session=self.sagemaker_session.session)
            actn.delete()

        for summary in Artifact.list():
            print(f"Deleting artifact {summary.artifact_arn} {summary.artifact_name}")
            self.delete_associations(summary.artifact_arn)
            artfct = Artifact(
                artifact_arn=summary.artifact_arn,
                sagemaker_session=self.sagemaker_session.session,
            )
            artfct.delete(disassociate=True)


"""
120. Triangle
Medium

1106

120

Favorite

Share
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:

Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.
"""

"""
Runtime: 40 ms, faster than 95.68% of Python3 online submissions for Triangle.
Memory Usage: 14.4 MB, less than 13.26% of Python3 online submissions for Triangle.
"""
# 简单实现动态规划
# O(N^2)空间复杂度
class Solution:
    def minimumTotal(self, triangle) -> int:
        nr, nc = len(triangle), len(triangle)
        dp = [[triangle[0][0]] * nc for _ in range(nr)]
        #print(dp)
        for i in range(1, nr):
            dp[i][0] = dp[i - 1][0] + triangle[i][0]
            for j in range(1,i):
                dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1]) + triangle[i][j]
            dp[i][i]=dp[i-1][i-1]+triangle[i][i]
        res = dp[-1][0]
        for j in range(nc):
            res = min(res, dp[-1][j])
        #print(dp)
        return res

# O(N)复杂度解法
"""
Runtime: 40 ms, faster than 95.68% of Python3 online submissions for Triangle.
Memory Usage: 13.5 MB, less than 83.17% of Python3 online submissions for Triangle.
"""
# 关键是每行 列索引 从大到小！！！
class Solution2:
    def minimumTotal(self, triangle) -> int:
        dp=[0]*len(triangle)

        for i in range(len(triangle)):
            for j in range(len(triangle[i])-1,-1,-1):
                if j==0:
                    dp[j]=dp[j]+triangle[i][j]
                elif j==len(triangle[i])-1:
                    dp[j]=dp[j-1]+triangle[i][j]
                else:
                    dp[j]=min(dp[j-1],dp[j])+triangle[i][j]

        res=dp[0]
        for val in dp:
            res=min(res,val)
        #print(dp)
        return res

if __name__=='__main__':
    triangle=[[2],[3,4],[6,5,7],[4,1,8,3]]
    res=Solution2().minimumTotal(triangle)

    print(res)


'''
    페이지 교체 알고리즘 OPT 방식
    OPT : https://velog.io/@qweadzs/BOJ-1700-%EB%A9%80%ED%8B%B0%ED%83%AD-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81Python

    멀티탭을 모두 사용하고 있을 때, 어떤 코드를 뽑을 것인지 선택하는 문제
    - 앞으로 다시 사용하지 않을 코드
    위 조건을 만족하는 코드가 없다면
    - 가장 나중에 다시 사용할 코드
    먼저 사용할 것을 빼버리면, 다시 사용할 때 다시 꽂아야 함.
'''

from collections import deque

def solution(N, arr):
    use = []
    cnt = 0

    while(len(arr) > 0):
        # print(use ,arr)
        # 이미 사용하고 있다면 건너뜀
        if arr[0] in use:
            arr.popleft()
        else:
            if len(use) < N:
                use.append(arr.popleft())
            # 사용하고 있지 않다면, 하나를 뽑아야함
            else:
                max_idx = -1
                for idx, item in enumerate(use):
                    # 앞으로 사용하지 않을 것
                    if item not in arr:
                        use[idx] = arr.popleft()
                        cnt += 1
                        break
                    # 가장 나중에 사용할 것
                    else:
                        new_idx = arr.index(item)
                        if new_idx > max_idx:
                            max_idx = new_idx
                            max_item = item
                else:
                    use[use.index(max_item)] = arr.popleft()
                    cnt += 1
    # print(use ,arr)
    print(cnt)

if __name__ == "__main__":
    N, K = map(int, input().split())
    arr = deque(list(map(int, input().split())))

    solution(N, arr)


import os
import json

CONFIG_FILE = "config.json"
ROLEPLAY_DIR = "roleplay/"

def print_config(config):
    print(f"- Role: {config['role']}")
    print(f"- Memorable: {config['memorable']}")
    print(f"- Temperature: {config['temperature']}")
    print(f"- Presence Penalty: {config['presence_penalty']}")
    print(f"- Frequency Penalty: {config['frequency_penalty']}")
    print()

def init_config():
    oldConfig = {}
    if os.path.exists(CONFIG_FILE):
        oldConfig = load_config()

    def config_key(key, prompt, defaultValBackUp):
        defaultVal = oldConfig.get(key)
        if defaultVal is None:
            defaultVal = defaultValBackUp
        res = input(f"{prompt}(Default: {defaultVal}): ").strip()
        if not res:
            res = str(defaultVal)
        return res

    print("Configuring...")
    newConfig = {}

    newConfig["roleDir"] = config_key("roleDir", "Roleplay Content Directory", ROLEPLAY_DIR)

    roleFileList = os.listdir(ROLEPLAY_DIR)
    if roleFileList is None:
        roleFileList[0] = None
    roleListStr = " ".join(roleFile[:-4] for roleFile in roleFileList)
    newConfig["role"] = config_key("role", f"Role ({roleListStr})", roleFileList[0][:-4])

    newConfig["model"] = float(config_key("model", "Model", "gpt-3-turbo"))
    newConfig["memorable"] = config_key("memorable", "Memorable (y/n)", "n")
    newConfig["temperature"] = float(config_key("temperature", "Temperature", 1))
    newConfig["presence_penalty"] = float(config_key("presence_penalty", "Presence Penalty", 0))
    newConfig["frequency_penalty"] = float(config_key("frequency_penalty", "Frequency Penalty", 0))

    return newConfig

def load_config():
    return json.load(open(CONFIG_FILE))

def save_config(config):
    try:
        json.dump(
            config,
            open(CONFIG_FILE, mode="w", encoding="utf-8"),
            indent=4
        )
    except Exception as e:
        print(f"\nError: {e.__class__.__name__}. Failed to create config file.")
        exit(1)


"""
Authors: Bulelani Nkosi, Caryn Pialat
Main api to run chatbot
"""

# Imports

import os
import logging
from flask import Flask
from slack import WebClient
from slackeventsapi import SlackEventAdapter
from controllers.templates import QuestionAnswering

# Initialize a Flask app to host the events adapter
app = Flask(__name__)
slack_events_adapter = SlackEventAdapter(os.environ['SLACK_SIGNING_SECRET'], "/slack/events", app)
# slack_events_adapter.on()
# Initialize a Web API client
slack_web_client = WebClient(token=os.environ['SLACK_BOT_TOKEN'])
# Keep track of past messages
messages_received = {}

def answer_question(user_id: str, channel: str, question: str):
    """
    Function to answer questions
    """
    # Search for the answer
    question_answer = QuestionAnswering(channel, question)
    # Build answer payload
    answer = question_answer.get_answer_payload()

    # Post answer in slack
    response = slack_web_client.chat_postMessage(**answer)

    # Capture the timestamp of the message we've just posted so
    # we can use it to update the message after a user
    # has completed a task.
    question_answer.timestamp = response["ts"]

@slack_events_adapter.on("message")
def handle_message(payload):
    event = payload.get("event", {})
    text = event.get("text")
    print("++++++++++++++++++++++")
    print(event)
    # If the incoming message contains "hi", then respond with a "Hello" message
    if event.get("subtype") is None and "hi" == text:
        channel = event.get("channel")
        message = "Hello <@%s>! :tada:" % event.get("user")
        slack_web_client.chat_postMessage(channel=channel, text=message)

    elif event.get("subtype") is None and text.endswith("?"):
        event = payload.get("event", {})
        channel = event.get("channel")
        user = event.get("user")
        return answer_question(user, channel, text)

if __name__ == "__main__":
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logging.StreamHandler())
    app.run(port=3000)


# 첫번째 풀이
import heapq
def solution1(genres, plays):
    answer = []
    # 장르별 재생수에 따라 순위 설정
    genres_dict = {}
    for i in range(len(genres)):
        if genres[i] in genres_dict:
            genres_dict[genres[i]][0] += [i]
            genres_dict[genres[i]][1] += plays[i]
        else:
            genres_dict[genres[i]] = [[i], plays[i]]
    s_dict = sorted(genres_dict.values(), key = lambda x : x[1], reverse=True)
    # 장르안에서 1,2등 뽑아서 (곡이 1개일 경우 1개만 택) 리스트에 추가
    for i in s_dict:
        prime = []
        for j in i[0]:
            heapq.heappush(prime, (-plays[j], j))
        answer.append(heapq.heappop(prime)[1])
        if prime: answer.append(heapq.heappop(prime)[1])
    return answer

# 다른 풀이
def solution(genres, plays):
    genres_dict = {}
    genres_list = []
    for i in range(len(genres)):
        if genres[i] not in genres_dict:
            genres_dict[genres[i]] = []
        genres_dict[genres[i]].append([i, plays[i]])
    for g in genres_dict:
        genres_dict[g].sort(key=lambda x: x[1], reverse=True)
        genres_list.append([g, sum([play for _, play in genres_dict[g]])])
# 내 풀이와 다르게 genres_dict 에 각 노래의 재생수를 모두 담고
# 각 장르의 재생수 합을 다른 리스트에 담아서 풀이
    genres_list.sort(key=lambda x: x[1], reverse=True)

    answer = []
    for g, _ in genres_list:
        answer.extend([x[0] for x in genres_dict[g][:2]])
    return answer

print(solution(["classic", "pop", "classic", "classic", "pop"],[500, 600, 150, 800, 2500])) #[4,1,3,0]
print(solution(["classic","classic","classic","classic","pop"],[500,150,800,800,2500]))


from django.urls import path

from user import views

app_name = 'user'

urlpatterns = [
    path('', views.UserViewSet.as_view(), name='list'),
    path('manage/create/', views.CreateUserView.as_view(), name='create'),
    path('token/', views.CreateTokenView.as_view(), name='token'),
    path('manage/<int:user_id>/', views.ManageUserView.as_view(), name='user'),
]


class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        results = []
        def dfs(perm, remaining):
            if not remaining:
                results.append(perm.copy())
                return

            rest = remaining.copy()
            for num in remaining:
                rest.remove(num)
                perm.append(num)
                dfs(perm.copy(), rest)
                # * Backtrack:
                rest.append(num)
                perm = perm[:-1]
            return

        remaining = nums.copy()
        dfs([], remaining)
        return results


from fastapi import FastAPI, File, UploadFile, HTTPException, Query, Body
from fastapi.responses import RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
from app.s3_operations import upload_pdf, check_documents, initialize_s3_client, s3_object_exists, upload_file
from app.pdf_processing import process_pdf
from app.pinecone_ops import generate_and_store_embeddings
from app.chat import process_user_query, initialize_pinecone
from app.docx_processing import process_docx
from app.pptx_processing import process_pptx
from app.audio_processing import process_audio
from app.utils import initialize_openai
from typing import List, Dict, Union
import os
import uuid
import logging
import traceback

# Initialize Pinecone
initialize_pinecone()

client = initialize_openai()
if not client:
    logging.error("Failed to initialize OpenAI client.")

# Configure logging
logging.basicConfig(level=logging.INFO)

app = FastAPI()
# Configuración de CORS

origins = [

    "*"

]

app.add_middleware(

    CORSMiddleware,

    allow_origins=origins,

    allow_credentials=True,

    allow_methods=["*"],

    allow_headers=["*"],

)
# Fetch bucket name from environment variables
your_bucket_name = os.environ.get('YOUR_BUCKET_NAME')

# Initialize the S3 client once
s3 = initialize_s3_client()
if not s3:
    logging.error("Failed to initialize S3 client.")

@app.get("/", tags=["Root"])
def read_root() -> RedirectResponse:
    """Redirect to the API documentation."""
    return RedirectResponse(url="/docs/")

@app.get("/check-documents/", tags=["Documents"], response_description="Check if documents are loaded")
async def check_docs() -> Dict[str, Union[str, bool]]:
    """Check the current status of documents loaded in the system."""
    current_status = check_documents(your_bucket_name)
    return {"status": "Success", "message": "Documents loaded." if current_status else "No documents loaded."}

@app.post("/upload/", tags=["Documents"])
async def upload_pdf_route(file: UploadFile = File(..., description="A PDF file to be uploaded", example="example.pdf")) -> Dict[str, Union[str, bool]]:
    """
    Upload a single PDF file, process it, and store its embeddings.

    **Arguments**:
    - `file`: A PDF file to be uploaded.

    **Returns**:
    - A dictionary with the status, message, and filename.
    """
    logging.info("Starting upload process...")
    unique_filename = file.filename
    file_bytes = file.file.read()
    try:
        # Use upload_pdf function instead of s3.put_object directly
        upload_response = upload_pdf(file_bytes, unique_filename)
        if upload_response['status'] != "Success":
            return upload_response  # Return the error response directly if upload fails

        # Call process_pdf with all necessary arguments and capture the returned tuple
        data, temp_pdf_path = process_pdf(
            s3, your_bucket_name, unique_filename)

        # For debugging
        logging.info(f"Data type from process_pdf: {type(data)}, Data: {data}")
        logging.info(f"Temp PDF path: {temp_pdf_path}")

        # Call generate_and_store_embeddings instead of generate_embeddings and store_embeddings
        generate_and_store_embeddings(data, temp_pdf_path)

        return {
            "status": "Success",
            "message": "File uploaded, processed, and embeddings stored successfully",
            "filename": unique_filename
        }
    except Exception as e:
        # Added for debugging
        logging.error(f"Error type: {type(e)}, Error: {e}")
        return {"status": "Failed", "message": str(e)}

@app.post("/multipleupload/", tags=["Documents"])
async def multiple_upload_route(files: List[UploadFile] = File(..., description="A list of files to be uploaded", examples=[{"filename": "example1.pdf"}, {"filename": "example2.docx"}])) -> Dict[str, List[Dict[str, Union[str, bool]]]]:
    """
    Upload multiple files, process them, and store their embeddings.

    **Arguments**:
    - `files`: A list of files to be uploaded. The supported file types are PDF, DOCX, PPTX, MP3, M4A.

    **Returns**:
    - A dictionary with a list of result dictionaries, each containing the status, message, and filename.
    """
    results = []
    for file in files:
        unique_filename = file.filename
        file_bytes = file.file.read()
        file_extension = unique_filename.split(".")[-1].lower()

        try:
            # Upload the file to S3
            upload_response = upload_file(
                file_bytes, unique_filename)
            if upload_response['status'] != "Success":
                results.append({
                    "filename": unique_filename,
                    "status": "Failed",
                    "message": upload_response['message'],
                })
                continue  # Skip to the next file if the upload fails

            # Process the file depending on its extension
            if file_extension == "pdf":
                data, temp_path = process_pdf(
                    s3, your_bucket_name, unique_filename)
            elif file_extension == "docx":
                data, temp_path = process_docx(
                    s3, your_bucket_name, unique_filename)
            elif file_extension == "pptx":
                data, temp_path = process_pptx(
                    s3, your_bucket_name, unique_filename)
            elif file_extension in ["mp3", "m4a"]:
                # Assuming process_audio function exists and is imported
                data, temp_path = process_audio(
                    s3, your_bucket_name, unique_filename, client)
            else:
                raise HTTPException(
                    status_code=400, detail=f"Unsupported file type: {file_extension}")

            # Generate and store embeddings
            generate_and_store_embeddings(data, temp_path)

            results.append({
                "filename": unique_filename,
                "status": "Success",
                "message": "File uploaded, processed, and embeddings stored successfully",
            })
        except HTTPException as http_exception:
            raise http_exception
        except Exception as e:
            logging.error(f"Error processing file {unique_filename}: {e}")
            results.append({
                "filename": unique_filename,
                "status": "Failed",
                "message": str(e),
            })

    return {"results": results}

@app.post("/chat/", tags=["Chat"])
async def chat_endpoint(query: str = Query(..., description="The user's text query", examples="What is a PDF file?")) -> Dict[str, str]:
    """
    Handle a user's text query and return a response.

    **Arguments**:
    - `query`: A string containing the user's text query.

    **Returns**:
    - A dictionary with a response string.
    """
    try:
        response = process_user_query(query)
        return {"response": response}
    except Exception as e:
        print(traceback.format_exc())  # Print the full traceback
        raise HTTPException(status_code=500, detail=str(e))


from __future__ import print_function
from __future__ import division

import logging
logging.basicConfig(format='%(asctime)s | %(levelname)s : %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)

logger.info("Loading packages ...")

import os
import sys
import torch
import numpy as np
from tqdm import tqdm
import json
from datetime import datetime
import time
import traceback
import argparse
import warnings

# Project modules
import utils
import model
import training
import dataset

# Handle command line arguments
parser = argparse.ArgumentParser(description='Run a complete training pipeline. Optionally, a JSON configuration file can be used, to overwrite command-line arguments.')
# I/O
parser.add_argument('--config', dest='config_filepath',
                    help='Configuration .json file (optional). Overwrites existing command-line args!')
parser.add_argument('--output_dir', default='/users/gzerveas/data/gzerveas/pruned_med_img_seg/output',
                    help='Root output directory. Must exist. Time-stamped directories will be created inside.')
parser.add_argument('--data_dir', default='/gpfs/data/ceickhof/GAILA',
                    help='Data directory')
parser.add_argument('--load_model',
                    help='Path to pretrained model')
parser.add_argument('--debug_size', type=int,
                    help='For rapid testing purposes (e.g. debugging), limit training set to a small random sample')
parser.add_argument('--name', dest='experiment_name', default='',
                    help='A string identifier/name for the experiment to be run - it will be appended to the output directory name, before the timestamp')
parser.add_argument('--no_timestamp', action='store_true',
                    help='If set, a timestamp will not be appended to the output directory name')
parser.add_argument('--records_file', default='/gpfs/data/ceickhof/GAILA/records.xls',
                    help='Excel file keeping all records of experiments')
# Training
parser.add_argument('--gpu', type=str, default='0',
                    help='GPU index, -1 for CPU')
parser.add_argument('--num_workers', type=int, default=4,
                    help='dataloader threads. 0 for single-thread.')
parser.add_argument('--epochs', type=int, default=100,
                    help='Number of training epochs')
parser.add_argument('--lr', type=float, default=1.25e-4,
                             help='learning rate for batch size 32.')
parser.add_argument('--lr_step', type=str, default='90,120',
                             help='Comma separated string of epochs when to reduce learning rate by a factor of 10')
parser.add_argument('--batch_size', type=int, default=10,
                    help='Training batch size')
parser.add_argument('--img_size', type=int, default=128,
                    help='Final size of training images (after downsampling)')
parser.add_argument('--seed',
                    help='Seed used for splitting sets. None by default, set to an integer for reproducibility')

args = parser.parse_args()

NUM_CLASSES = 16

if args.task == '3d':
    heads = {'hm': NUM_CLASSES, 'dep': 1, 'rot': 8, 'dim': 3}
    if args.reg_bbox:
        heads.update({'wh': 2})
    if args.reg_offset:
        heads.update({'reg': 2})
elif args.task == 'localization':
    heads = {'hm': NUM_CLASSES, 'wh': 2}

def setup(args):
    """Prepare training session: read configuration from file (takes precedence), create directories.
    Input:
        args: arguments object from argparse
    Returns:
        config: configuration dictionary
    """

    config = args.__dict__  # configuration dictionary

    if args.config_filepath is not None:
        logger.info("Reading configuration ...")
        try:  # dictionary containing the entire configuration settings in a hierarchical fashion
            config.update(utils.load_config(args.config_filepath))
        except:
            logger.critical("Failed to load configuration file. Check JSON syntax and verify that files exist")
            traceback.print_exc()
            sys.exit(1)

    # Create output directory
    initial_timestamp = datetime.now()
    output_dir = config['output_dir']
    if not os.path.isdir(output_dir):
        raise IOError(
            "Root directory '{}', where the directory of the experiment will be created, must exist".format(output_dir))

    output_dir = os.path.join(output_dir, config['experiment_name'])

    formatted_timestamp = initial_timestamp.strftime("%Y-%m-%d_%H-%M-%S")
    config['initial_timestamp'] = formatted_timestamp
    if (not config['no_timestamp']) or (len(config['experiment_name']) == 0):
        output_dir += "_" + formatted_timestamp
    utils.create_dirs([output_dir])
    config['output_dir'] = output_dir

    # Save configuration as a (pretty) json file
    with open(os.path.join(output_dir, 'configuration.json'), 'w') as fp:
        json.dump(config, fp, indent=4, sort_keys=True)

    logger.info("Stored configuration file in '{}'".format(output_dir))

    return config

def main():

    start_time = time.time()

    config = setup(args)

    torch.manual_seed(config['seed'])

    device = torch.device('cuda' if (torch.cuda.is_available() and config['gpu'] != '-1') else 'cpu')

    # Create or load model
    # Fail early if something is amiss
    logger.info("Creating model ...")
    my_model = model.DCN_Detector(opt.heads, opt.head_conv)
    optimizer = torch.optim.Adam(my_model.parameters(), opt.lr)
    start_epoch = 0
    if args.load_model:
        my_model, optimizer, start_epoch = utils.load_model(my_model, args.load_model, optimizer, args.resume, args.lr, args.lr_step)
    my_model.to(device)

    # Initialize data generators
    logger.info("Loading and preprocessing data ...")
    my_dataset = dataset.ObjDetDataset()

    val_loader = torch.utils.data.DataLoader(
        my_dataset(opt, 'val'),
        batch_size=1,
        shuffle=False,
        num_workers=1,
        pin_memory=True
    )

    trainer = training.Trainer(opt, my_model, optimizer)
    trainer.set_device(opt.gpus, opt.chunk_sizes, opt.device)

    if opt.test: # Only evaluate and skip training
        _, preds = trainer.val(0, val_loader)
        val_loader.dataset.run_eval(preds, opt.save_dir)
        return

    train_loader = torch.utils.data.DataLoader(
        my_dataset(opt, 'train'),
        batch_size=opt.batch_size,
        shuffle=True,
        num_workers=opt.num_workers,
        pin_memory=True,
        drop_last=True
    )

    logger.info('Starting training...')
    best = 1e10
    metrics = []  # list of lists: for each epoch, stores metrics like accuracy, DICE,
    it = tqdm(range(1, config["epochs"] + 1), desc='Training', ncols=0)
    for epoch in range(start_epoch + 1, opt.num_epochs + 1):
        mark = epoch if opt.save_all else 'last'
        log_dict_train, _ = trainer.train(epoch, train_loader)
        logger.info('epoch: {} |'.format(epoch))
        for k, v in log_dict_train.items():
            logger.info('{} {:8f} | '.format(k, v))
        if opt.val_intervals > 0 and epoch % opt.val_intervals == 0:
            save_model(os.path.join(opt.save_dir, 'model_{}.pth'.format(mark)),
                        epoch, my_model, optimizer)
            with torch.no_grad():
                log_dict_val, preds = trainer.val(epoch, val_loader)
            for k, v in log_dict_val.items():
                logger.scalar_summary('val_{}'.format(k), v, epoch)
                logger.write('{} {:8f} | '.format(k, v))
            if log_dict_val[opt.metric] < best:
                best = log_dict_val[opt.metric]
                save_model(os.path.join(opt.save_dir, 'model_best.pth'),
                        epoch, my_model)
        else:
            save_model(os.path.join(opt.save_dir, 'model_last.pth'),
                        epoch, my_model, optimizer)
        if epoch in opt.lr_step:
            save_model(os.path.join(opt.save_dir, 'model_{}.pth'.format(epoch)), epoch, my_model, optimizer)
            lr = opt.lr * (0.1 ** (opt.lr_step.index(epoch) + 1))
            print('Drop LR to', lr)
            for param_group in optimizer.param_groups:
                param_group['lr'] = lr

        metrics.append([epoch, accuracy, dice, precision, recall])

        it.set_postfix(
            Accuracy='{:.3f}%'.format(accuracy * 100),
            Max_Accuracy='{:.3f}%'.format(max_acc * 100),
            Dice='{:.3f}%'.format(dice * 100),
            Max_Dice='{:.3f}%'.format(max_dice * 100),
            Precision='{:.3f}%'.format(precision * 100),
            Max_Precision='{:.3f}%'.format(max_prec * 100),
            Recall='{:.3f}%'.format(recall * 100),
            Max_Recall='{:.3f}%'.format(max_rec * 100),
            Last_pruning='{:2d}'.format(latest_pruning),
            Pruned='{:.2f}%'.format(pruned_ratio * 100))

    # Export evolution of metrics over epoch
    header = ["Epoch", "Accuracy", "DICE", "Precision", "Recall", "Pruned ratio"]
    metrics_filepath = os.path.join(config["output_dir"], "metrics_" + config["experiment_name"] + ".xls")
    book = utils.export_performance_metrics(metrics_filepath, metrics, header, sheet_name="metrics")

    # Export best metrics per pruning level
    header = ["Pruned ratio", "DICE", "Accuracy", "Precision", "Recall"]
    utils.export_performance_metrics(metrics_filepath, best_at_prune_level, header, book, sheet_name="best_metrics")

    # Export record metrics to a file accumulating records from all experiments
    utils.register_record(config["records_file"], config["initial_timestamp"], config["experiment_name"], metrics)

    logger.info('All Done!')

    total_runtime = time.time() - start_time
    logger.info(
        "Total runtime: {} hours, {} minutes, {} seconds\n".format(total_runtime // 3600, (total_runtime // 60) % 60,
                                                                   total_runtime % 60))

if __name__ == '__main__':

    main()


'''
백준 12970번 AB
그리디
'''
N, M = map(int, input().split())

ans = ''
tu_num = 0
A_num = 0

for i in range(N):
    if tu_num < M:
        if tu_num - A_num + N - i - 1 <= M:
            ans += 'A'
            tu_num += - A_num + N - i - 1
            A_num += 1
        else:
            ans += 'B'
    else:
        ans += 'B'

if M == tu_num:
    print(ans)
else:
    print(-1)


def solution(distance, rocks, n):
    rocks.sort()
    rocks.append(distance)
    left, right = 0, distance
    answer = 0
    while left <= right:
        prev = 0
        min_gap = distance
        removed_rocks = 0
        mid = (left+right)//2
        for rock in rocks:
            if rock - prev < mid:
                removed_rocks += 1
            else:
                # 징검다리 간격의 최소값을 기록한다
                min_gap = min(min_gap, rock-prev)
                prev = rock
        # 제한을 낮춰 삭제하는 돌의 갯수를 줄여야 한다
        if removed_rocks > n:
            right = mid-1
        # 제한을 높여 삭제하는 돌의 갯수를 늘려야 한다
        else:
            answer = min_gap
            left = mid + 1
    return answer

print(solution(25, [2, 14, 11, 21, 17], 2))


from datetime import datetime
import pandas as pd
import numpy as np
# import config
import yaml

# time_start = config.time_start
# time_end = config.time_end

with open("config.yaml", 'r') as stream:
    try:
        # print()
        content = yaml.load(stream)
        time_start = content['time_start']
        time_end = content['time_end']

    except yaml.YAMLError as exc:
        print(exc)

def Data_transform():

	df_author = pd.read_csv('group_member_list.csv')

	lan_id_list = df_author['LAN ID'].tolist()

	print(df_author.head())

	print(lan_id_list)
	print(time_start)
	print(time_end)

	df=pd.read_csv('filename.csv')

	df['month-date']=df['timestamp'].apply(lambda x: datetime.utcfromtimestamp(int(x/1000)).strftime('%b-%d'))

	print(df.head())

	df_new = df[(df['year_month_date']>=time_start) & (df['year_month_date']<=time_end) & (df['author_name'].isin(lan_id_list))]

	print(df_new.head())

	df_temp=df_new[['year_month_date','month-date','author_displayName','project','repo']].rename(columns={'author_displayName':'author'})

	df_temp.to_csv('BitBucketDashboardInput.csv',index=False)


# https://github.com/irishNoah/Algorithm-Study
# 11651번 / 좌표 정렬하기 2 / S5
# https://www.acmicpc.net/problem/11651

# 해당 문제 풀이는 lamda를 이용하여 해결하였음
# 참고 : https://haesoo9410.tistory.com/193

import sys
sys = sys.stdin.readline

N = int(input())

list_arr = []

while 1:
    if N == 0:
        break

    list_arr.append(list(map(int, input().split())))

    N = N - 1

# lamda를 이용
#  좌표를 y좌표가 증가하는 순으로, y좌표가 같으면 x좌표가 증가하는 순서로 정렬
list_arr.sort(key=lambda x:(x[1], x[0]))

for x, y in list_arr:
    print(x, y)

BAEKJOON(백준 온라인 저지)/10000번~/11651(좌표 정렬하기 2).py


#!/usr/bin/python
# encoding:utf-8

from __future__ import print_function
import json
import sxtwl
import damson
from damson.constraint import (Required, DataType, Between)

from flask import Flask, request

class Result(object):

    @staticmethod
    def fail(code, message):
        return Result(False, code, message)

    @staticmethod
    def sys_error(message=None):
        return Result(False, 'SYS_ERROR', message if message else u'系统繁忙，请稍后再试！')

    @staticmethod
    def params_error(message=None):
        return Result(False, 'PARAMS_ERROR', message if message else u'参数校验未通过！')

    @staticmethod
    def success(data):
        return Result(True, 'SUCCESS', 'ok', data=data)

    def __init__(self, success, code, message, data=None):
        self.success = success
        self.code = code,
        self.message = message
        self.data = data

    def __str__(self):
        result = {
            'success': self.success,
            'message': self.message,
            'code': self.code[0],
            'data': self.data
        }
        return json.dumps(result, ensure_ascii=False)

class OnionLunar:
    TIAN_GAN = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
    DI_ZHI = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
    SHX = ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"]
    CN_NUM = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"]
    JQMC = ["冬至", "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露",
            "秋分", "寒露", "霜降", "立冬", "小雪", "大雪"]
    YMC = ["冬月", "腊月", "正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月"]
    RMC = ["初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八",
           "十九", "二十", "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十", "卅一"]
    SHI_CHEN = {'子时': ('23:00', '00:59'),
                '丑时': ('01:00', '02:59'),
                '寅时': ('03:59', '04：59'),
                '卯时': ('05:00', '06:59'),
                '辰时': ('07:00', '08:59'),
                '巳时': ('09:00', '10:59'),
                '午时': ('11:00', '12:59'),
                '未时': ('13:00', '14:59'),
                '申时': ('15:00', '16:59'),
                '酉时': ('17:00', '18:59'),
                '戊时': ('19:00', '20:59'),
                '亥时': ('21:00', '22:59')
                }

    def __init__(self):
        self.lunar = sxtwl.Lunar()

    @damson.verify(hour=[DataType(int), Between(0, 24)], minute=[DataType(int), Between(0, 60)])
    def shichen(self, hour, minute):
        key = '%02d:%02d' % (hour, minute)
        for text in self.SHI_CHEN:
            interval = self.SHI_CHEN[text]
            begin = interval[0]
            end = interval[1]
            if begin < end:
                if begin >= key <= end:
                    return text
            else:
                if (begin <= key <= '23:59') or ('00:00' <= key <= end):
                    return text
        return None

    @damson.verify(year=[Required(), DataType(int), Between(1901, 2100, eopen=False)],
                   month=[Required(), DataType(int), Between(1, 12, eopen=False)],
                   day=[Required(), DataType(int), Between(1, 31, eopen=False)])
    def s2y_json(self, year, month, day):
        try:
            day = self.lunar.getDayBySolar(year, month, day)
            data = {
                'year': day.y,
                'gzYear': self.TIAN_GAN[day.Lyear2.tg] + self.DI_ZHI[day.Lyear2.dz],
                'month': day.m,
                'leapMonth': day.Lleap,
                'ymcMonth': self.YMC[day.Lmc],
                'gzMonth': self.TIAN_GAN[day.Lmonth2.tg] + self.DI_ZHI[day.Lmonth2.dz],
                'day': day.d,
                'rmcDay': self.RMC[day.Ldi],
                'gzDay': self.TIAN_GAN[day.Lday2.tg] + self.DI_ZHI[day.Lday2.dz]
            }
            result = Result.success(data)
        except Exception as e:
            result = Result.sys_error(e)
        return str(result)

    @damson.verify(year=[Required(), DataType(int), Between(1901, 2100, eopen=False)],
                   month=[Required(), DataType(int), Between(1, 12, eopen=False)],
                   day=[Required(), DataType(int), Between(1, 31, eopen=False)])
    def y2s_json(self, year, month, day):
        try:
            day = self.lunar.getDayByLunar(year, month, day, True)
            data = {
                'year': day.y,
                'gzYear': self.TIAN_GAN[day.Lyear2.tg] + self.DI_ZHI[day.Lyear2.dz],
                'month': day.m,
                'leapMonth': day.Lleap,
                'ymcMonth': self.YMC[day.Lmc],
                'gzMonth': self.TIAN_GAN[day.Lmonth2.tg] + self.DI_ZHI[day.Lmonth2.dz],
                'day': day.d,
                'rmcDay': self.RMC[day.Ldi],
                'gzDay': self.TIAN_GAN[day.Lday2.tg] + self.DI_ZHI[day.Lday2.dz]
            }
            result = Result.success(data)
        except Exception as e:
            result = Result.sys_error(e)
        return result

# +++++++++++++++++ Flask Web ++++++++++++++++++++

app = Flask(__name__)
onion = OnionLunar()

@app.route('/')
def home():
    html = """
    ©Copyright since 2019 xasync.com

Welcome to use Onion-Lunar!

—— Power by xasync.com

    """
    return html

@app.route('/solar2lunar', methods=['GET'])
def solar2lunar():
    try:
        year = int(request.args.get('year'))
        month = int(request.args.get('month'))
        day = int(request.args.get('day'))
        result = onion.s2y_json(year, month, day)
    except Exception as e:
        result = Result.sys_error(e)
    return str(result)

@app.route('/lunar2solar', methods=['GET'])
def lunar2solar():
    try:
        year = int(request.args.get('year'))
        month = int(request.args.get('month'))
        day = int(request.args.get('day'))
        result = onion.y2s_json(year, month, day)
    except Exception as e:
        result = Result.sys_error(e)
    return str(result)

if __name__ == '__main__':
    app.run('127.0.0.1', port=15954, debug=False)


# -*- coding: utf-8 -*-
"""
Course: CS 4365/5354 [Computer Vision]
Author: Jose Perez [ID: 80473954]
Assignment: Lab 1
Instructor: Olac Fuentes
"""
from timeit import default_timer as timer
from PIL import Image
from scipy.ndimage import filters
from numpy import *
from pylab import *

def get_hog_data(im_array, number_of_bars=12):
    from scipy.ndimage.filters import convolve
    convolution_matrix =  array([[-1, 0, 1], [-1,0,1], [-1, 0, 1]])

    imx = zeros(im_array.shape)
    imx = convolve(im_array, convolution_matrix)

    imy = zeros(im_array.shape)
    imy = convolve(im_array, transpose(convolution_matrix))

    magnitude = sqrt(imx**2+imy**2)
    direction = degrees( 2 * arctan2(1, 1/imx)-imy)

    # Put angles together depending on the number of bars
    angle_distance = 360 / number_of_bars
    angle_range = xrange(0, 360, angle_distance)
    bins = []
    for angle in angle_range:
        #current_bin = logical_and(direction>=angle, direction<angle+30)
        current_bin = logical_and(direction<angle, direction>=angle+30)
        bins.append(current_bin)

    integral_images = []
    for i in range(len(bins)):
        current_bin = bins[i]
        integral = magnitude
        integral[current_bin] = 0
        integral_images.append(integral)

    return integral

#def hog(im_array, bins, magnitude):
    #start_time = timer()

    # Sum the magnitudes of the angle bins
    #magnitude_sums = []

    #for i in range(len(bins)):
    #    magnitude_for_bin = magnitude[bins[i]]
     #   total_magnitude = magnitude_for_bin.sum()
     #   magnitude_sums.append(total_magnitude)

    #end_time = timer()
    #print('[HOG] Duration: ' + str(end_time - start_time))

    # Prepare the bar graph
    #fig = figure(2)
    #splot = fig.add_subplot(111)

    #splot.bar(range(len(magnitude_sums)), magnitude_sums, width=1)
    #xTickMarks = [str(i) + "-" + str(i + 30) for i in xrange(0, 360, 30)]

    #xTickMarks = [str(i) + "-" + str(i + angle_distance) for i in angle_range]
    #splot.set_xticklabels(xTickMarks)
    #splot.set_ylabel("Magnitude")
    #splot.set_xlabel("Angles")
    #show()

    #return array(magnitude_sums)
def similar_region_hog(interest_array, im_file, b, magnitude, stride=100):
    im_file_array = array(im_file)

    winning_region_array = []
    winning_distance = sys.maxsize

    start_time = timer()

    for x in range(0, im_file.width, stride):
        for y in range(0, im_file.height, stride):
            region_array = im_file_array[x:x+len(interest_array),y:y+len(interest_array[0])]

            if region_array.shape != interest_array.shape:
                continue

            color_distance = 0

            for i in range(3):
                color_hog = hog(interest_array[:,:,i])
                dist = sqrt((color_hog ** 2 - interest_array_hog ** 2).sum())
                color_distance = max(color_distance, dist)

            if color_distance < winning_distance:
                winning_distance = color_distance
                winning_region_array = region_array

    end_time = timer()
    print('[HOG] Duration: ' + str(end_time - start_time))
    return winning_region_array

def similar_region_pixel(interest_array, im_file, stride=100):
    im_file_array = array(im_file)

    winning_region_array = []
    winning_distance = sys.maxsize

    start_time = timer()
    for x in range(0, im_file.width, stride):
        for y in range(0, im_file.height, stride):
            region_array = im_file_array[x:x+len(interest_array),y:y+len(interest_array[0])]

            if region_array.shape != interest_array.shape:
                continue

            color_distance = 0

            for i in range(3):
                dist = sqrt((interest_array[:,:,i] - region_array[:,:,i]).sum())
                color_distance = max(color_distance, dist)

            if color_distance < winning_distance:
                winning_distance = color_distance
                winning_region_array = region_array

    end_time = timer()
    print('[Pixel] Duration: ' + str(end_time - start_time))
    return winning_region_array

im_file = Image.open("quijote1.jpg")
imshow(im_file)
#region_corners = array(ginput(2))
#top_left = region_corners[0]
#bottom_right = region_corners[1]
# 488, 756
top_left = array([2064, 918])
bottom_right = array([2552, 1674])

#difference_hog("quijote1.jpg", "quijote2.jpg", top_left, bottom_right)
filename1 = "quijote1.jpg"
filename2 = "quijote1.jpg"

# Open the file and crop it
#im_file = Image.open(filename1)
#region_of_interest = crop_image(im_file, top_left, bottom_right)
im_file_compare = Image.open(filename2)
arr = array(im_file_compare)

#integral_images[] = get_hog_data(arr[:,:,i])

#interest_array = array(region_of_interest)
#interest_array_hog = hog(interest_array[:,:,2])
#distance_hog = sys.maxsize
#for i in range(2):
#    color_hog = hog(interest_array[:,:,i])
#    dist = sqrt((color_hog ** 2 + interest_array_hog ** 2).sum())

#    if dist < distance_hog:
#        distance_hog = dist
 #       interest_array_hog = color_hog
#figure(3)
#imshow(similar_region_pixel(interest_array, im_file_compare))

#figure(4)
#imshow(similar_region_hog(interest_array, im_file_compare, b, magnitude))
#file_1_hog = hog_grayscale(filename1, top_left, bottom_right, 12)
#file_2_hog = hog_grayscale(filename2, top_left, bottom_right, 12)

#diff = sqrt(square(file_1_hog - file_2_hog).sum())
show()


fname = input("Enter file: ")
if len(fname) < 1:
    fname = "mbox-short.txt"

fhand = open(fname)

tmail = dict()
for line in fhand:
    line = line.rstrip()
    if not line.startswith('From '):
        continue
    times = line.split()[5]
    hour = times.split(':')[0]
    tmail[hour] = tmail.get(hour, 0) + 1

lst = list()
for k, v in list(tmail.items()):
    lst.append((k, v))

lst.sort()

for k, v in lst:
    print(k, v)

Python Things/Python For Everybody/Chapter 10/ex_10_01.py


import numpy as np

def solve():
    with open(file_name + ".in") as file:
        R, C, L, H = map(int, file.readline().split(" "))
        grid_bits = np.zeros(shape=(R, C), dtype=np.bool)
        for r in range(R):
            line = file.readline()[:C]
            for c in range(len(line)):
                grid_bits[r, c] = mushroom if line[c] == 'M' else tomato
    print(grid_bits.astype(np.int))

    best_cut_values = {}
    best_cuts = {}  # negative means it's a vertical cut, and not horizontal
    order = []
    for r in range(R):
        for c in range(C):
            for h in range(1, R - r + 1):
                for w in range(1, C - c + 1):
                    order.append((r, c, h, w))
    print(len(order))
    order.sort(key=lambda tup: tup[2] * tup[3])
    print("got sorted")
    for r, c, h, w in order:  # increasing order of size
        print(r, c, w, h)
        best_cut_values[r, c, h, w] = 0
        best_cuts[r, c, h, w] = 0  # zero: no cut
        c0, c1 = 0, 0
        for x in range(h):
            for y in range(w):
                if grid_bits[r + x, c + y]:
                    c1 += 1
                else:
                    c0 += 1
        if c0 < L or c1 < L:  # base case: impossible to cover
            continue
        if h * w <= H:  # base case: can be fully covered with a single slice
            best_cut_values[r, c, h, w] = w * h
            continue
        for cut in range(1, h):
            # val is a possible score, the score for this region for this cut
            val = best_cut_values[r, c, cut, w] + best_cut_values[
                r + cut, c, h - cut, w]
            if val > best_cut_values[r, c, h, w]:
                best_cut_values[r, c, h, w] = val
                best_cuts[r, c, h, w] = cut  # horizontal cut
        for cut in range(1, w):
            val = best_cut_values[r, c, h, cut] + best_cut_values[
                r, c + cut, h, w - cut]
            if val > best_cut_values[r, c, h, w]:
                best_cut_values[r, c, h, w] = val
                best_cuts[r, c, h, w] = -cut  # vertical cut

    sol = []  # retrieve solution

    stack = [(0, 0, R, C)]
    while stack:
        ssss = stack.pop()
        r, c, h, w = ssss
        cut = best_cuts[r, c, h, w]
        if cut == 0:  # this rectangle is valid
            if best_cut_values[r, c, h, w]:
                sol.append((r, c, h, w))
        elif cut > 0:
            stack.append((r, c, cut, w))
            stack.append((r + cut, c, h - cut, w))
        else:
            stack.append((r, c, h, -cut))
            stack.append((r, c - cut, h, w + cut))

    print(f"SCORE: {sum([h*w for (r, c, h, w) in sol])}")
    # for (r, c, h, w) in sol:

#
#
mushroom, tomato = True, False
np.set_printoptions(edgeitems=5)
file_name = ["example", "small", "medium", "big"][2]

solve()


"""
Simple sample of an autoregressive model compared to a RNN, focus is on data processsing and making the forcast correctly on a synthetic dataset. Predicting the next value based on T past values.
Out of the box RNN has too much flexibility, over parameterized for this case. It does perform slightly better on a more complex series though. But the dataset does not really have any long term dependencies as this is just a toy example. Also not that LSTMs will not perform much better than the simple RNN

Note that sklearn has LinearRegression/ForecasterAutoreg, but this sample is solely in pytorch
"""

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from model import SimpleRNN

# 0 for Autogeression and 1 for RNN
choose_model = 1

# Created the dataset
n = 500

#result without noise will perfect with the correct method, without we can still predict the periodically nature
#series = np.sin(0.05*np.arange(n)) + np.random.randn(n)*0.04  # normal sin wave plus noise,
series = np.sin((0.1*np.arange(n))**2) # more complex series

# Sanatity check by plotting
plt.plot(series)
plt.show()

# building the dataset
T = 10 # amount of past values to predict the next value
X = []
Y = []

# split the data into time-windows of 10 stride 1
for t in range(len(series)-T):
    x = series[t:t+T]
    X.append(x)
    y = series[t+T] # 11th value as label
    Y.append(y)

# Setting Device
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print(device)

if choose_model == 0:
    # Autoregressive model
    X = np.array(X).reshape(-1, T)
    model = nn.Linear(T, 1)

if choose_model == 1:
    X = np.array(X).reshape(-1, T, 1)
    model = SimpleRNN(num_inputs=1, num_hidden=5, num_layers=1, num_outputs=1)

model.to(device)
Y = np.array(Y).reshape(-1, 1)
N = len(X)
print("X Shape and Y Shape", X.shape, Y.shape)

loss_func = nn.MSELoss()
optim  = torch.optim.Adam(model.parameters(), lr = 0.01)

# training and test data split 50/50 no random splits
x_train = torch.from_numpy(X[:-(N//2)].astype(np.float32)).to(device)
y_train = torch.from_numpy(Y[:-(N//2)].astype(np.float32)).to(device)
x_test = torch.from_numpy(X[-(N//2):].astype(np.float32)).to(device)
y_test = torch.from_numpy(Y[-(N//2):].astype(np.float32)).to(device)

# train on the full dataset due to small size
def train(model, loss_func, optim, x_train, y_train, x_test,y_test, epochs=400):

    train_losses = np.zeros(epochs)
    test_losses = np.zeros(epochs)

    for it in range(epochs):
        optim.zero_grad()

        outputs = model(x_train)
        loss = loss_func(outputs, y_train)

        loss.backward()
        optim.step()

        train_losses[it] = loss.item()

        # test loss
        output_test = model(x_test)
        test_loss = loss_func(output_test, y_test)
        test_losses[it] = test_loss.item()

        print(f"Epoch {it+1}/{epochs}, Train loss: {loss.item():.4f}, Test loss: {test_loss.item():.4f}")

    return train_losses, test_losses

train_losses, test_losses = train(model, loss_func, optim, x_train, y_train, x_test,y_test, epochs=200)

# plot loss
plt.plot(train_losses, label='train_loss')
plt.plot(test_losses, label='test loss')
plt.legend()
plt.show()

# predictions
validation_predictions = []

# First entry of x_test
updated_x = x_test[0].view(T)

# POPULAR MISTAKE ONE STEP FORECAST: input_ = X_test[i].view(1, -1); i+=1; basically forcasting on the true targets

while len(validation_predictions) < len(y_test.tolist()):

    if choose_model == 0:
        _input = updated_x.view(1, -1)# reshape into 2D array, 1(sample) X T (features)
    if choose_model == 1:
        _input = updated_x.view(1, T, 1)

    p = model(_input) # index because model returns N x K (output node) (which here is 1x1), item to bring it back to python from tensor

    # update the predictions
    validation_predictions.append(p[0,0].item())

    # update input with latest forcast prediction
    updated_x = torch.cat((updated_x[1:], p[0]))

plt.plot(y_test.tolist(), label="true label")
plt.plot(validation_predictions, label="prediction")
plt.legend()
plt.show()

TimeSeries and Sequence Data/simple_timeseries_prediction.py


'''
	给定一个字符串 s，找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

    示例 1：

    输入: "babad"
    输出: "bab"
    注意: "aba" 也是一个有效答案。
    示例 2：

    输入: "cbbd"
    输出: "bb"
'''
import math
class Solution:
    def longestPalindrome(self, s):

        if len(s) < 1:
            return ''

        start = 0
        end = 0
        for i in range(len(s)):
            len1 = self.expandAroundCenter(s,i,i)
            len2 = self. expandAroundCenter(s,i,i+1)
            len_max = max(len1,len2)

            if len_max > end-start:
                start = i -int((len_max-1)/2)
                end = i + int(len_max/2)

        print(f"start:{start},end:{end}")
        return s[start:end+1]

    def expandAroundCenter(self,  s : str, left : int, right : int) -> int:
        L = left
        R = right
        while L>=0 and R<len(s) and s[L] == s[R]:
            L = L - 1
            R = R + 1

        return R - L -1

if __name__ == "__main__":
    solution = Solution()
    str1=solution.longestPalindrome("ABABADE")
    print(f"str1:{str1}")


from sense_hat import SenseHat

sense = SenseHat()

def tropisch_checker():
    # Haal de temperatuur op van de Sense HAT
    temp = sense.get_temperature()

    if temp >= 30:
        # Verander de achtergrondkleur van de Sense HAT naar rood
        sense.clear((255, 0, 0))
        print("Het is een tropische dag, denk eraan dat je genoeg drinkt!")
    else:
        # Verander de achtergrondkleur van de Sense HAT naar groen
        sense.clear((0, 255, 0))
        print("Het is geen tropische dag.")

# Roep de functie aan
tropisch_checker()


import threading
import  time
def thread1_job():
    print('T1 run')
    for i in range(10):
        time.sleep(0.1)
    print('t1 finish')
def thread2_job():
    print('T2 run')
    print('t2 finish')
def main():
    print("main run")
    threading1 = threading.Thread(target=thread1_job,name='T1')
    threading1.start()
    threading2 = threading.Thread(target=thread2_job, name='T2')
    threading2.start()
    threading1.join()
    print(('main finish'))
if __name__ == '__main__':
    main()


import logging
import threading
import timeit
import os
import psutil
import inspect
from pyramid.threadlocal import get_current_request

lock = threading.Lock()

"""Provides an ``includeme`` function that lets developers configure the
  package to be part of their Pyramid application with::

      config.include('pyramid_straw')

"""

logger = logging.getLogger(__name__)

def re_query(query, params):
    """Replace query params with actual parameters."""
    if type(params) is dict:
        for k, v in params.items():
            query = query.replace('%({})s'.format(k), str(v))
    if type(params) is tuple:
        for v in params:
            query = query.replace('?', str(v), 1)
    return query

def sizeof_fmt(num, suffix='B'):
    """Format the bytes."""

    for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
        if abs(num) < 1024.0:
            return '{:.1f}{}{}'.format(num, unit, suffix)
        num /= 1024.0
    return '{:.1f}{}{}'.format(num, 'Yi', suffix)

def humanize(lines, only_paths):
    """Yield dict about location of calling function"""
    for x in lines:
        (f, filename, at, function, l, i) = x
        # Will skip any lines that are not included
        if not any([x in filename for x in only_paths]):
            continue

        yield {
            'filename': filename.strip(),
            'at': at,
            'function': function.strip()
        }

def get_used_memory():
    """Gets the non-shared memory about process."""
    process = psutil.Process(os.getpid())
    return process.memory_info().rss

def to_toolbar(data):
    """Inject analyzed data in request."""
    request = get_current_request()

    if not request and not getattr(request, 'straw_data', None):
        return

    with lock:
        request.straw_data.append(data)

def includeme(config):
    settings = config.registry.settings

    # Custom sender for reports, needs to be callable
    dsn_factory = settings.get('pyramid_straw.report_hook', None)

    # Enable or disable saving data to request based on debugtoolbar presence
    debug_toolbar = len(settings.get('debugtoolbar.panels', []))

    # Control the inspection of query
    disable_inspect = settings.get('pyramid_straw.disable_inspect', False)

    # For example src/my_project_name/models
    only_paths = settings.get('pyramid_straw.only_paths', [])

    try:
        from sqlalchemy import event
        from sqlalchemy.engine.base import Engine

        def _before(conn, cursor, query, params, context, execmany):
            setattr(context, 'straw_start_timer', timeit.default_timer())
            setattr(context, 'straw_start_memory', get_used_memory())

        def _after(conn, cursor, query, params, context, execmany):
            stop_timer = timeit.default_timer()
            duration = (stop_timer - context.straw_start_timer) * 1000
            memory = get_used_memory() - context.straw_start_memory
            data = {
                'type': 'cursor',
                'duration': duration,
                'memory': sizeof_fmt(memory),
                'query': re_query(query, params),
                'parameters': params,
                'lines': []
            }
            if not disable_inspect:
                lines = inspect.getouterframes(inspect.currentframe())
                # NOTE: introspection takes a bit so it will add about .3-.5ms
                # to query timing
                data['lines'] = humanize(lines, only_paths)
            if dsn_factory and callable(dsn_factory):
                dsn_factory(data)
            if debug_toolbar:
                to_toolbar(data)
        if dsn_factory or debug_toolbar:
            event.listen(Engine, 'before_cursor_execute', _before)
            event.listen(Engine, 'after_cursor_execute', _after)

            # Flow events
            event.listen(Engine, 'begin', lambda x: to_toolbar({'type': 'begin'}))  # noqa
            event.listen(Engine, 'commit', lambda x: to_toolbar({'type': 'commit'}))  # noqa
            event.listen(Engine, 'rollback', lambda x: to_toolbar({'type': 'rollback'}))  # noqa

    except ImportError:
        logger.warning('SQLAlchemy is not installed.')


from pygbif import occurrences as occ
import json
from datetime import datetime

def latStat(left,right,top,bottom):
	latCheck = str(bottom) + "," + str(top)
	longCheck = str(left) + "," + str(right)
	return [latCheck,longCheck]

#IF NOT HOG FOUND FIND CLOSEST ONE

def hogSearch(left,right,top,bottom):
	l = left
	r = right
	t = top
	b = bottom
	coords = latStat(l,r,t,b)
	hog = occ.search(decimalLatitude = coords[0], decimalLongitude = coords[1], scientificName = 'Sus scrofa')
	print(hog['count'])
	dist = 0
	while hog['count'] == 0:
		dist += 0.7
		l -= .01
		r += .01
		t += .01
		b -= .01
		coords = latStat(l,r,t,b)
		print(coords[0],coords[1])
		hog = occ.search(decimalLatitude = coords[0], decimalLongitude = coords[1], scientificName = 'Sus scrofa')

	hogHistoric = []
	hog2019 = []
	nDic = hog['results']
	print(nDic)

	for sightings in nDic:
		si = sightings["eventDate"]
		hogHistoric.append(si)
		if '2019' in sightings["eventDate"]:
			hog2019.append(si)

	pigs2019 = []
	for hd in hog2019:
		nd = hd[:10]
		dd = nd.split('-',2)
		pigs2019.append(datetime(int(dd[0]),int(dd[1]),int(dd[2])))

	allpigs = []
	for allHog in hogHistoric:
		nd = allHog[:10]
		dd = nd.split('-',2)
		allpigs.append(datetime(int(dd[0]),int(dd[1]),int(dd[2])))

	if pigs2019 != []:
		dayDif = str(round((abs(datetime.now() - max(pigs2019)).total_seconds()) / 86400,2))
		bigDay = str(max(pigs2019))[:10]
	else:
		dayDif = str(round((abs(datetime.now() - max(allpigs)).total_seconds()) / 86400,2))
		bigDay = str(max(allpigs))[:10]

	print(dayDif)
	print(bigDay)

	str1 = "WARNING: WE FOUND " + str(len(hogHistoric)) + " HOG RECORDS IN YOUR AREA. "
	str2 = "MOST RECENT HOG WAS SEEN " + dayDif +" DAYS AGO. CHOOSE A MORE SPECIFIC LOCATION FOR BETTER RESULTS."
	pigPic = []
	for ss in nDic:
		print(ss["eventDate"][:10])
		if ss["eventDate"][:10] == str(bigDay):
			pigPic = ss["media"]
			break
	str3 = pigPic[0]['identifier']

	if dist != 0:
		totS = "WARNING: A HOG WAS SPOTTED " + str(dist) + " MILES FROM YOU " + dayDif + " DAYS AGO. "
	else:
		totS = str1+str2

	hogDic = [totS,str3]
	return hogDic


import pandas as pd
from unidecode import unidecode
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import re
import numpy as np

class ProductSearch:
    def __init__(self, database_csv: str):
        self.database = self.__load_bd(database_csv)
        self.stop_words = ['ml', 'und', 'g', '9', 'l', 'kg', 'unid', 'kilo', 'kilos', 'gramas', 'grama', 'und', 'unidades',
                      '%', 'de', 'a', 'o', 'que', 'e', 'do', 'da', 'em', 'um', 'para', 'e', 'com', 'nao', 'uma', 'os',
                      'no', 'se', 'na', 'por', 'mais', 'as', 'dos', 'como', 'mas', 'foi', 'ao', 'ele', 'das', 'tem',
                      'à', 'seu', 'sua', 'ou', 'ser', 'quando', 'muito', 'há', 'nos', 'já', 'está', 'eu', 'também',
                      'so', 'pelo', 'pela', 'ate', 'isso', 'ela', 'entre', 'era', 'depois', 'sem', 'mesmo', 'aos',
                      'ter', 'seus', 'quem', 'nas', 'me', 'esse', 'eles', 'estão', 'você', 'tinha', 'foram', 'essa',
                      'num', 'nem', 'suas', 'meu', 'as', 'minha', 'têm', 'numa', 'pelos', 'elas', 'havia', 'seja',
                      'qual', 'será', 'nós', 'tenho', 'lhe', 'deles', 'essas', 'esses', 'pelas', 'este', 'fosse',
                      'dele', 'tu', 'te', 'vocês', 'vos', 'lhes', 'meus', 'minhas', 'teu', 'tua', 'teus', 'tuas',
                      'nosso', 'nossa', 'nossos', 'nossas', 'dela', 'delas', 'esta', 'estes', 'estas', 'aquele',
                      'aquela', 'aqueles', 'aquelas', 'isto', 'aquilo', 'estou', 'esta', 'estamos', 'estao', 'estive',
                      'esteve', 'estivemos', 'estiveram', 'estava', 'estávamos', 'estavam', 'estivera', 'estiveramos',
                      'esteja', 'estejamos', 'estejam', 'estivesse', 'estivéssemos', 'estivessem', 'estiver',
                      'estivermos', 'estiverem', 'hei', 'ha', 'havemos', 'hao', 'houve', 'houvemos', 'houveram',
                      'houvera', 'houveramos', 'haja', 'hajamos', 'hajam', 'houvesse', 'houvéssemos', 'houvessem',
                      'houver', 'houvermos', 'houverem', 'houverei', 'houverá', 'houveremos', 'houverao', 'houveria',
                      'houveríamos', 'houveriam', 'sou', 'somos', 'são', 'era', 'eramos', 'eram', 'fui', 'foi', 'fomos',
                      'foram', 'fora', 'foramos', 'seja', 'sejamos', 'sejam', 'fosse', 'fossemos', 'fossem', 'for',
                      'formos', 'forem', 'serei', 'será', 'seremos', 'serão', 'seria', 'seríamos', 'seriam', 'tenho',
                      'tem', 'temos', 'tém', 'tinha', 'tínhamos', 'tinham', 'tive', 'teve', 'tivemos', 'tiveram',
                      'tivera', 'tivéramos', 'tenha', 'tenhamos', 'tenham', 'tivesse', 'tivéssemos', 'tivessem',
                      'tiver', 'tivermos', 'tiverem', 'terei', 'terá', 'teremos', 'terão', 'teria', 'teríamos',
                      'teriam']

    def __load_bd(self, database_csv: str):
        bd = pd.read_csv(database_csv, sep=";")

        bd['description'] = ' ' + bd['description'].astype(str)
        bd['description'] = bd['description'].str.lower()

        bd['description'] = bd['description'].apply(unidecode)
        bd['description'] = bd['description'].str.replace(r'\b\w{1,2}\b', '', regex=True)

        return bd

    def __reduce_bd_by_ocr_findeds(self, list_words_finded_by_ocr: list):
        pattern = ' | '.join(list_words_finded_by_ocr)
        aux = self.database['description'].str.extract(r'(' + pattern + ')')
        aux = self.database.iloc[aux.dropna().index.values]

        return list(aux.description), list(aux.id)

    def __find_ocr_string_product_in_bd_by_similarity(self, processed_bd: list, ocr_findeds: list):
        processed_bd.extend(ocr_findeds)

        tfidf_vectorizer = TfidfVectorizer(stop_words=self.stop_words)  # stopwords.words('portuguese'))
        tfidf_matrix = tfidf_vectorizer.fit_transform(processed_bd)
        cossine_sim = cosine_similarity(tfidf_matrix)

        return cossine_sim

    def __preprocess_product_description(self, product_description: str):
        product_description = product_description.split()
        preprocessed_product_description = []
        for word in product_description:
            word = re.sub(r'[^\w\s]', ' ', word)
            word = re.sub(r'\b\w{1,2}\b', '', word)
            word = unidecode(word.lower()).split()
            preprocessed_product_description.extend(word)
        return preprocessed_product_description

    def __call__(self, product_description: str, top_n: int = 1):
        product_description = self.__preprocess_product_description(product_description)
        description_candidates, id_candidates = self.__reduce_bd_by_ocr_findeds(product_description)
        if len(description_candidates) == 0:
            return [0], [''], ['']
        cossine_sim = self.__find_ocr_string_product_in_bd_by_similarity(description_candidates,
                                                                         [' '.join(product_description)])
        idx = (-cossine_sim[-1]).argsort()[1:top_n+1]
        similarities = cossine_sim[-1][idx]
        products = np.array(description_candidates)[idx]
        ids = np.array(id_candidates)[idx]
        return similarities, products, ids


import cv2
import glob
import re
import os
from datetime import datetime

img_array = []
numbers = re.compile(r"(\d+)")
path_to_image_directory = glob.glob('images/ESP32-CAM/*')
latest_image_directory = glob.glob(max(path_to_image_directory, key=os.path.getctime))[0]

def numericalSort(value):
    parts = numbers.split(value)
    parts[1::2] = map(int, parts[1::2])
    return parts

for filename in sorted(glob.glob(latest_image_directory + "/*.jpg") , key=numericalSort):
    img = cv2.imread(filename)
    height, width, layers = img.shape
    size = (width,height)
    img_array.append(img)

out = cv2.VideoWriter("videos/"+ str(datetime.now()) + ".mp4",cv2.VideoWriter_fourcc(*"DIVX"), 15, size)

for i in range(len(img_array)):
    out.write(img_array[i])

out.release()


from django.contrib import admin

# Register your models here.
from blog.models import Profile, Post, Tag

@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
    model = Profile

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    model = Tag

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    model = Post

    # In the list of all posts, only show the following information about each post
    list_display = (
        "id",
        "title",
        "subtitle",
        "slug",
        "publish_date",
        "published",
    )
    # Allow filtering the post list by posts that are published or unpublished.
    # Allow filtering posts by publish date.
    list_filter = (
        "published",
        "publish_date",
    )
    # Allow editing all the displayed fields, with the exception of the ID.
    list_editable = (
        "title",
        "subtitle",
        "slug",
        "publish_date",
        "published"
    )
    # Allow searching for posts using the title, subtitle, slug, and body.
    search_fields = (
        "title",
        "subtitle",
        "slug",
        "body"
    )
    # Prepopulate the slug field using the title and subtitle fields.
    prepopulated_fields = {
        "slug": (
            "title",
            "subtitle"
        )
    }
    # Use the publish date of all posts to create a browsable date hierarchy.
    date_hierarchy = "publish_date"
    # Show a button at the top of the list to save changes.
    save_on_top = True


import logging
from typing import AsyncGenerator

from geopy import Point, distance

from treasurehunt.game.event_backend import EventBackend
from treasurehunt.game.exceptions import (
    MailGatewayException,
    WinnerAlreadyExists,
)
from treasurehunt.game.mail_gateway import MailGateway
from treasurehunt.game.repository import TreasureHuntRepository
from treasurehunt.game.schemas import Hunter, HuntingEvent

logger = logging.getLogger("treasurehunt.treasurehunt")

class TreasureHunt:
    __winning_message_subject = """Hey, message from TreasureHunt"""
    __winning_message = """Hey, you’ve found a treasure, congratulations!
You are {nth} treasure hunter who has found the treasure.
Treasure location: {treasure_location}"""
    __hunting_event_message = (
        "{hunter_email} reached {location_str}. "
        "Hunter is {remaining_distance} meters away from the treasure."
    )

    def __init__(
        self,
        repository: TreasureHuntRepository,
        mail_gateway: MailGateway,
        event_backend: EventBackend,
    ):
        self.__repository = repository
        self.__mail_gateway = mail_gateway
        self.__event_backend = event_backend

    async def hunt(self, hunter: Hunter, hunter_location: Point) -> int:
        treasure_location = await self.__repository.read_treasure_location()
        remaining_distance = self.__mesure_distance_meters(
            hunter_location, treasure_location
        )
        is_winning = self.__hunter_is_winning(remaining_distance)
        if is_winning:
            try:
                await self.__repository.save_winner(hunter.email)
                await self.__inform_hunter_about_victory(
                    hunter, treasure_location=treasure_location
                )
            except WinnerAlreadyExists:
                pass
        await self.__event_backend.publish(
            HuntingEvent(
                hunter=hunter,
                location_str=hunter_location.format_unicode(),
                remaining_distance=remaining_distance,
                is_winning=is_winning,
            )
        )
        return remaining_distance

    def __mesure_distance_meters(self, distance_from: Point, distance_to: Point) -> int:
        return int(distance.distance(distance_from, distance_to).meters)

    def __hunter_is_winning(self, remaining_distance: int) -> bool:
        return remaining_distance <= 5

    async def __inform_hunter_about_victory(
        self, hunter: Hunter, treasure_location: Point
    ) -> None:
        winner_count = await self.__repository.read_winner_count()
        try:
            await self.__mail_gateway.send_mail(
                recipient=hunter.email,
                message=self.__winning_message.format(
                    nth=winner_count,
                    treasure_location=treasure_location.format_unicode(),
                ),
                subject=self.__winning_message_subject,
            )
        except MailGatewayException:
            logger.error(
                "Cannot inform a hunter about victory.",
                extra=dict(hunter_email=hunter.email),
                exc_info=True,
            )

    async def follow_hunt(self) -> AsyncGenerator[str, None]:
        async for event in self.__event_backend.subscribe():
            yield self.__hunting_event_message.format(
                hunter_email=event.hunter.email,
                location_str=event.location_str,
                remaining_distance=event.remaining_distance,
            )