"""Modify existing ArcGIS Online content (mostly hosted feature services). Contains classes for updating hosted feature
service data, modifying the attachments on a hosted feature service, or modifying map symbology.
"""

import json
import logging
import shutil
import sys
import warnings
from datetime import datetime
from pathlib import Path
from tempfile import TemporaryDirectory

import arcgis
import geopandas as gpd
import numpy as np
import pandas as pd
import pyogrio
from arcgis.features import GeoAccessor, GeoSeriesAccessor

from palletjack import utils

logger = logging.getLogger(__name__)

class FeatureServiceUpdater:
    """Update an AGOL Feature Service with data from a pandas DataFrame.

    This class represents the feature layer that will be updated and stores a reference to the feature layer and it's
    containing gis. It contains four methods for updating the layer's data: add_features, remove_features,
    update_features, and truncate_and_load_features.

    It is the client's responsibility to separate out the new data into these different steps. If the extract/transform
    stages result in separate groups of records that need to be added, deleted, and updated, the client must call the
    three different methods with dataframes containing only the respective records for each operation.

    The method used to upload the data to AGOL saves the updated data as a new layer named upload in working_dir/upload.
    gdb, zips the gdb, uploads it to AGOL, and then uses this as the source data for a call to the feature layer's
    .append() method. The geodatabase upload.gdb will be created in working_dir if it doesn't already exist. Ideally,
    working_dir should be a TemporaryDirectory unless persistent access to the gdb is desired.
    """

    def __init__(self, gis, feature_service_itemid, working_dir=None, layer_index=0):
        self._class_logger = logging.getLogger(__name__).getChild(self.__class__.__name__)
        self.gis = gis
        self.feature_service_itemid = feature_service_itemid
        self.feature_layer = arcgis.features.FeatureLayer.fromitem(gis.content.get(feature_service_itemid))
        self.working_dir = working_dir if working_dir else None
        self.layer_index = layer_index

    def add_features(self, dataframe):
        """Adds new features to existing hosted feature layer from new dataframe.

        The new dataframe must have a 'SHAPE' column containing geometries of the same type as the live data.

        The new dataframe's columns and data must match the existing data's fields (with the exception of generated
        fields like shape area and length) in name, type, and allowable length. Live fields that are not nullable and
        don't have a default value must have a value in the new data; missing data in these fields will raise an error.

        Args:
            dataframe (pd.DataFrame.spatial): Spatially enabled dataframe of data to be added

        Raises:
            ValueError: If the new field and existing fields don't match, the SHAPE field is missing or has an
                incompatible type, the new data contains null fields, the new data exceeds the existing field
                lengths, or a specified field is missing from either new or live data.

        Returns:
            int: Number of features added
        """

        self._class_logger.info(
            'Adding items to layer `%s` in itemid `%s` in-place', self.layer_index, self.feature_service_itemid
        )
        fields = FeatureServiceUpdater._get_fields_from_dataframe(dataframe)
        self._class_logger.debug('Using fields %s', fields)

        #: Field checks to prevent various AGOL errors
        utils.FieldChecker.check_fields(self.feature_layer.properties, dataframe, fields, add_oid=False)

        #: Upload
        append_count = self._upload_data(
            dataframe,
            upsert=False,
        )
        return append_count

    def remove_features(self, delete_oids):
        """Deletes features from a hosted feature layer based on comma-separated string of Object IDs

        This is a wrapper around the arcgis.FeatureLayer.delete_features method that adds some sanity checking. The
        delete operation is rolled back if any of the features fail to delete using (rollback_on_failure=True). This
        function will raise a RuntimeError as well after delete_features() returns if any of them fail.

        The sanity checks will raise errors or warnings as appropriate if any of them fail.

        Args:
            delete_oids (list[int]): List of OIDs to delete

        Raises:
            ValueError: If delete_string can't be split on `,`
            TypeError: If any of the items in delete_string can't be cast to ints
            ValueError: If delete_string is empty
            UserWarning: If any of the Object IDs in delete_string don't exist in the live data
            RuntimeError: If any of the OIDs fail to delete

        Returns:
            int: The number of features deleted
        """

        self._class_logger.info(
            'Deleting features from layer `%s` in itemid `%s`', self.layer_index, self.feature_service_itemid
        )
        self._class_logger.debug('Delete string: %s', delete_oids)

        #: Verify delete list
        oid_numeric = utils.DeleteUtils.check_delete_oids_are_ints(delete_oids)
        utils.DeleteUtils.check_for_empty_oid_list(oid_numeric, delete_oids)
        delete_string = ','.join([str(oid) for oid in oid_numeric])
        num_missing_oids = utils.DeleteUtils.check_delete_oids_are_in_live_data(
            delete_string, oid_numeric, self.feature_layer
        )

        #: Note: apparently not all services support rollback:
        #: https://developers.arcgis.com/rest/services-reference/enterprise/delete-features.htm
        deletes = utils.retry(
            self.feature_layer.delete_features,
            deletes=delete_string,
            rollback_on_failure=True,
        )

        failed_deletes = [result['objectId'] for result in deletes['deleteResults'] if not result['success']]
        if failed_deletes:
            raise RuntimeError(f'The following Object IDs failed to delete: {failed_deletes}')

        #: The REST API still returns success: True on missing OIDs, so we have to track this ourselves
        actual_delete_count = len(deletes['deleteResults']) - num_missing_oids

        return actual_delete_count

    def update_features(self, dataframe, update_geometry=True):
        """Updates existing features within a hosted feature layer using OBJECTID as the join field.

        The new dataframe's columns and data must match the existing data's fields (with the exception of generated
        fields like shape area and length) in name, type, and allowable length. Live fields that are not nullable and
        don't have a default value must have a value in the new data; missing data in these fields will raise an error.

        Uses the OBJECTID field to determine which features should be updated by the underlying FeatureLayer.append()
        method. The most robust way to do this is to load the live data as a dataframe, subset it down to the desired
        rows, make your edits based on a separate join id, and then pass that dataframe to this method.

        The new data can have either attributes and geometries or only attributes based on the update_geometry flag. A
        combination of updates from a source with both attributes & geometries and a source with attributes-only must
        be done with two separate calls. The geometries must be provided in a SHAPE column and be the same type as the
        live data.

        Args:
            dataframe (pd.DataFrame.spatial): Spatially enabled dataframe of data to be updated
            update_geometry (bool): Whether to update attributes and geometry (True) or just attributes (False).
                Defaults to False.

        Raises:
            ValueError: If the new field and existing fields don't match, the SHAPE field is missing or has an
                incompatible type, the new data contains null fields, the new data exceeds the existing field
                lengths, or a specified field is missing from either new or live data.

        Returns:
            int: Number of features updated
        """

        self._class_logger.info(
            'Updating layer `%s` in itemid `%s` in-place', self.layer_index, self.feature_service_itemid
        )

        fields = FeatureServiceUpdater._get_fields_from_dataframe(dataframe)
        self._class_logger.debug('Updating fields %s', fields)

        #: Add null geometries if update_geometry==False so that we can create a featureset from the dataframe
        #: (geometries will be ignored by upsert call)
        if not update_geometry:
            self._class_logger.debug('Attribute-only update; inserting null geometries')
            dataframe['SHAPE'] = utils.get_null_geometries(self.feature_layer.properties)

        #: Field checks to prevent various AGOL errors
        utils.FieldChecker.check_fields(self.feature_layer.properties, dataframe, fields, add_oid=True)

        #: Upload data
        append_count = self._upload_data(
            dataframe,
            upsert=True,
            upsert_matching_field='OBJECTID',
            append_fields=fields,  #: Apparently this works if append_fields is all the fields, but not a subset?
            update_geometry=update_geometry
        )
        return append_count

    def truncate_and_load_features(self, dataframe, save_old=False):
        """Overwrite a hosted feature layer by truncating and loading the new data

        When the existing dataset is truncated, a copy is kept in memory as a spatially-enabled dataframe. If
        save_old is set, this is saved as a layer in self.working_dir/backup.gdb with the layer name
        {featurelayer.name}_{todays_date}.json (foobar_2022-12-31.json).

        The new dataframe must have a 'SHAPE' column containing geometries of the same type as the live data. New
        OBJECTIDs will be automatically generated.

        The new dataframe's columns and data must match the existing data's fields (with the exception of generated
        fields like shape area and length) in name, type, and allowable length. Live fields that are not nullable and
        don't have a default value must have a value in the new data; missing data in these fields will raise an error.

        Args:
            dataframe (pd.DataFrame.spatial): Spatially enabled dataframe of new data to be loaded
            save_old (bool, optional): Save existing data to backup.gdb in working_dir. Defaults to False

        Returns:
            int: Number of features loaded
        """

        self._class_logger.info(
            'Truncating and loading layer `%s` in itemid `%s`', self.layer_index, self.feature_service_itemid
        )
        start = datetime.now()

        #: Save the data to disk if desired
        if save_old:
            self._class_logger.info('Saving existing data to %s', self.working_dir)
            saved_layer_path = utils.save_feature_layer_to_gdb(self.feature_layer, self.working_dir)

        fields = FeatureServiceUpdater._get_fields_from_dataframe(dataframe)

        #: Field checks to prevent various AGOL errors
        utils.FieldChecker.check_fields(self.feature_layer.properties, dataframe, fields, add_oid=False)

        self._class_logger.info('Truncating existing features...')
        self._truncate_existing_data()

        try:
            self._class_logger.info('Loading new data...')
            append_count = self._upload_data(dataframe, upsert=False)
            self._class_logger.debug('Total truncate and load time: %s', datetime.now() - start)
        except Exception:
            if save_old:
                self._class_logger.error('Append failed. Data saved to %s', saved_layer_path)
                raise
            self._class_logger.error('Append failed. Old data not saved (save_old set to False)')
            raise

        return append_count

    @staticmethod
    def _get_fields_from_dataframe(dataframe):
        """Get the fields from a dataframe, excluding Shape_Area and Shape_Length

        Args:
            dataframe (pd.DataFrame): Dataframe to get fields from

        Returns:
            list[str]: List of the columns of the dataframe, excluding Shape_Area and Shape_Length
        """

        fields = list(dataframe.columns)
        for auto_gen_field in ['Shape_Area', 'Shape_Length']:
            try:
                fields.remove(auto_gen_field)
            except ValueError:
                continue

        return fields

    def _upload_data(self, dataframe, **append_kwargs):
        """Append a spatially-enabled dataframe to a feature layer by uploading it as a zipped file gdb

        We first save the new dataframe as a layer in an empty geodatabase, then zip it and upload it to AGOL as a
        standalone item. We then call append on the target feature layer with this item as the source for the append,
        using upsert where appropriate to update existing data using OBJECTID as the join field. Afterwards, we delete
        the gdb item and the zipped gdb.

        Args:
            dataframe (pd.DataFrame.spatial): A spatially-enabled dataframe containing data to be added or upserted to
            the feature layer. The fields must match the live fields in name, type, and length (where applicable). The
            dataframe must have a SHAPE column containing geometries of the same type as the live data.

        Raises:
            ValueError: If the field used as a key for upsert matching is not present in either the new or live data
            RuntimeError: If the append operation fails

        Returns:
            int: The number of records upserted
        """
        try:
            if append_kwargs['upsert'] \
                and (
                        append_kwargs['upsert_matching_field'] not in append_kwargs['append_fields']
                        or
                        append_kwargs['upsert_matching_field'] not in dataframe.columns
                    ):
                raise ValueError(
                    f'Upsert matching field {append_kwargs["upsert_matching_field"]} not found in either append fields or existing fields.'
                )
        except KeyError:
            pass
        self._class_logger.debug('Saving data to gdb and zipping...')
        zipped_gdb_path = self._save_to_gdb_and_zip(dataframe)

        self._class_logger.debug('Uploading gdb to AGOL...')
        gdb_item = self._upload_gdb(zipped_gdb_path)

        self._class_logger.debug('Appending data from gdb to feature layer...')
        try:
            result, messages = utils.retry(
                self.feature_layer.append,
                item_id=gdb_item.id,
                upload_format='filegdb',
                source_table_name='upload',
                return_messages=True,
                rollback=True,
                **append_kwargs
            )
            if not result:
                raise RuntimeError('Append failed but did not error')
        except Exception as error:
            raise RuntimeError('Failed to append data from gdb, changes should have been rolled back') from error

        self._cleanup(gdb_item, zipped_gdb_path)

        return messages['recordCount']

    def _save_to_gdb_and_zip(self, dataframe):
        """Save a spatially-enabled dataframe to a gdb, zip it, and return path to the zipped file.

        Requires self.working_dir to be set. Uses pyogrio to save the dataframe to the gdb, then uses
        shutil.make_archive to zip the gdb. The zipped gdb is saved in self.working_dir.

        Args:
            dataframe (pd.DataFrame.spatial): The input dataframe to be saved. Must contain geometries in the SHAPE
            field

        Raises:
            ValueError: If self.working_dir is not set or the empty upload.gdb doesn't exist in it

        Returns:
            pathlib.Path: The path to the zipped GDB
        """

        try:
            gdb_path = Path(self.working_dir) / 'upload.gdb'
        except TypeError as error:
            raise AttributeError('working_dir not specified on FeatureServiceUpdater') from error

        gdf = utils.sedf_to_gdf(dataframe)

        try:
            gdf.to_file(gdb_path, layer='upload', engine='pyogrio', driver='OpenFileGDB')
        except pyogrio.errors.DataSourceError as error:
            raise ValueError(
                f'Error writing layer to {gdb_path}. Verify {self.working_dir} exists and is writable.'
            ) from error
        try:
            zipped_gdb_path = shutil.make_archive(gdb_path, 'zip', root_dir=gdb_path.parent, base_dir=gdb_path.name)
        except OSError as error:
            raise ValueError(f'Error zipping {gdb_path}') from error

        return zipped_gdb_path

    def _upload_gdb(self, zipped_gdb_path):
        """Add a zipped gdb to AGOL as an item to self.gis

        Args:
            zipped_gdb_path (str or Path-like): Path to the zipped gdb

        Raises:
            RuntimeError: If there is an error uploading the gdb to AGOL

        Returns:
            arcgis.gis.Item: Reference to the resulting Item object in self.gis
        """

        try:
            gdb_item = utils.retry(
                self.gis.content.add,
                item_properties={
                    'type': 'File Geodatabase',
                    'title': 'Temporary gdb upload',
                    'snippet': 'Temporary gdb upload from palletjack'
                },
                data=zipped_gdb_path
            )
        except Exception as error:
            raise RuntimeError(f'Error uploading {zipped_gdb_path} to AGOL') from error
        return gdb_item

    def _cleanup(self, gdb_item, zipped_gdb_path):
        """Remove the zipped gdb from disk and the gdb item from AGOL

        Args:
            gdb_item (arcgis.gis.Item): Reference to the gdb item in self.gis
            zipped_gdb_path (str or Path-like): Path to the gdb on disk

        Raises:
            RuntimeError: If there are errors deleting the gdb item or the zipped gdb
        """

        try:
            gdb_item.delete()
        except Exception as error:
            warnings.warn(f'Error deleting gdb item {gdb_item.id} from AGOL')
            warnings.warn(repr(error))

        try:
            Path(zipped_gdb_path).unlink()
        except Exception as error:
            warnings.warn(f'Error deleting zipped gdb {zipped_gdb_path}')
            warnings.warn(repr(error))

    def _truncate_existing_data(self):
        """Remove all existing features from the live dataset

        Raises:
            RuntimeError: If the truncate fails

        Returns:
            pd.DataFrame: The feature layer's data as a spatially-enabled dataframe prior to truncating
        """

        self._class_logger.debug('Truncating...')
        truncate_result = utils.retry(self.feature_layer.manager.truncate, asynchronous=True, wait=True)
        self._class_logger.debug(truncate_result)
        if truncate_result['status'] != 'Completed':
            raise RuntimeError(
                f'Failed to truncate existing data from layer id {self.layer_index} in itemid {self.feature_service_itemid}'
            )

class FeatureServiceAttachmentsUpdater:
    """Add or overwrite attachments in a feature service using a dataframe of the desired "new" attachments.

    Updates the attachments based on a dataframe containing two columns: a join key present in the live data (the
    dataframe column name must match the feature service field name) and the path of the file to attach to the feature.
    While AGOL supports multiple attachments, this only accepts a single file as input.

    If a matching feature in AGOl doesn't have an attachment, the file referred to by the dataframe will be uploaded.
    If it does have an attachment, it checks the existing filename with the referenced file. If they are different, the
    file from the dataframe will be updated. If they are the same, nothing happens.
    """

    def __init__(self, gis):
        """
        Args:
            gis (arcgis.gis.GIS): The AGOL organization's gis object
        """

        self.gis = gis
        self._class_logger = logging.getLogger(__name__).getChild(self.__class__.__name__)
        self.failed_dict = {}

    def _get_live_oid_and_guid_from_join_field_values(self, live_features_as_df, attachment_join_field, attachments_df):
        """Get the live Object ID and guid from the live features for only the features in attachments_df

        Args:
            live_features_as_df (pd.DataFrame): Spatial dataframe of all the feature layer's live data from AGOL
            attachment_join_field (str): Column in attachments_df to use as a join key with live data
            attachments_df (pd.DataFrame): New attachment data, including the join key and a path to the "new"
                attachment

        Returns:
            pd.DataFrame: Attachments dataframe with corresponding live OIDs and GUIDs.
        """

        self._class_logger.debug('Using %s as the join field between live and new data', attachment_join_field)
        subset_df = live_features_as_df.reindex(columns=['OBJECTID', 'GlobalID', attachment_join_field])
        merged_df = subset_df.merge(attachments_df, on=attachment_join_field, how='inner')
        self._class_logger.debug('%s features common to both live and new data', len(merged_df.index))

        return merged_df

    def _get_current_attachment_info_by_oid(self, live_data_subset_df):
        """Merge the live attachment data using the ObjectID as the join

        Args:
            live_data_subset_df (pd.DataFrame): Live data with 'OBJECTID', 'GlobalID', and new attachment data

        Returns:
            pd.DataFrame: Live and new attachment data in one dataframe
        """

        live_attachments_df = pd.DataFrame(self.feature_layer.attachments.search())
        live_attachments_subset_df = live_attachments_df.reindex(columns=['PARENTOBJECTID', 'NAME', 'ID'])
        merged_df = live_data_subset_df.merge(
            live_attachments_subset_df, left_on='OBJECTID', right_on='PARENTOBJECTID', how='left'
        )
        #: Cast ID field to nullable int to avoid conversion to float for np.nans
        merged_df['ID'] = merged_df['ID'].astype('Int64')

        return merged_df

    def _create_attachment_action_df(self, attachment_eval_df, attachment_path_field):
        """Create a dataframe containing the action needed for each feature resulting from the attachment join.

        If the live feature doesn't have an attachment, add the attachment. If it does, compare the file names and only
        attach if they are different. Otherwise, leave null.

        Args:
            attachment_eval_df (pd.DataFrame): DataFrame of live attachment data, subsetted to features that matched
                the join key in the new attachments
            attachment_path_field (str): The column that holds the attachment path

        Returns:
            pd.DataFrame: attachment_eval_df with 'operation' and 'new_filename' columns added
        """

        #: Get the file name from the full path
        attachment_eval_df['new_filename'] = attachment_eval_df[attachment_path_field].apply(
            lambda path: Path(path).name
        )

        #: Overwrite if different names, add if no existing name, do nothing if names are the same
        attachment_eval_df['operation'] = np.nan
        attachment_eval_df.loc[attachment_eval_df['NAME'] != attachment_eval_df['new_filename'],
                               'operation'] = 'overwrite'
        attachment_eval_df.loc[attachment_eval_df['NAME'].isna(), 'operation'] = 'add'

        value_counts = attachment_eval_df['operation'].value_counts(dropna=False)
        for operation in ['add', 'overwrite', np.nan]:
            if operation not in value_counts:
                value_counts[operation] = 0
        self._class_logger.debug(
            'Calculated attachment operations: adds: %s, overwrites: %s, none: %s', value_counts['add'],
            value_counts['overwrite'], value_counts[np.nan]
        )

        return attachment_eval_df

    def _add_attachments_by_oid(self, attachment_action_df, attachment_path_field):
        """Add attachments using the feature's OID based on the 'operation' field of the dataframe

        Args:
            attachment_action_df (pd.DataFrame): A dataframe containing 'operation', 'OBJECTID', and
                attachment_path_field columns
            attachment_path_field (str): The column that holds the attachment path

        Returns:
            int: The number of features that successfully have attachments added.
        """

        adds_dict = attachment_action_df[attachment_action_df['operation'] == 'add'].to_dict(orient='index')
        adds_count = 0

        for row in adds_dict.values():
            target_oid = row['OBJECTID']
            filepath = row[attachment_path_field]

            self._class_logger.debug('Add %s to OID %s', filepath, target_oid)
            try:
                result = self.feature_layer.attachments.add(target_oid, filepath)
            except Exception:
                self._class_logger.error('AGOL error while adding %s to OID %s', filepath, target_oid, exc_info=True)
                self.failed_dict[target_oid] = ('add', filepath)
                continue

            self._class_logger.debug('%s', result)
            if not result['addAttachmentResult']['success']:
                warnings.warn(f'Failed to attach {filepath} to OID {target_oid}')
                self.failed_dict[target_oid] = ('add', filepath)
                continue

            adds_count += 1

        return adds_count

    def _overwrite_attachments_by_oid(self, attachment_action_df, attachment_path_field):
        """Overwrite attachments using the feature's OID based on the 'operation' field of the dataframe

        Args:
            attachment_action_df (pd.DataFrame): A dataframe containing 'operation', 'OBJECTID', 'ID', 'NAME', and
                attachment_path_field columns
            attachment_path_field (str): The column that holds the attachment path

        Returns:
            int: The number of features that successfully have their attachments overwritten.
        """

        overwrites_dict = attachment_action_df[attachment_action_df['operation'] == 'overwrite'].to_dict(orient='index')
        overwrites_count = 0

        for row in overwrites_dict.values():
            target_oid = row['OBJECTID']
            filepath = row[attachment_path_field]
            attachment_id = row['ID']
            old_name = row['NAME']

            self._class_logger.debug(
                'Overwriting %s (attachment ID %s) on OID %s with %s', old_name, attachment_id, target_oid, filepath
            )
            try:
                result = self.feature_layer.attachments.update(target_oid, attachment_id, filepath)
            except Exception:
                self._class_logger.error(
                    'AGOL error while overwriting %s (attachment ID %s) on OID %s with %s',
                    old_name,
                    attachment_id,
                    target_oid,
                    filepath,
                    exc_info=True
                )
                self.failed_dict[target_oid] = ('update', filepath)
                continue

            if not result['updateAttachmentResult']['success']:
                warnings.warn(
                    f'Failed to update {old_name}, attachment ID {attachment_id}, on OID {target_oid} with {filepath}'
                )
                self.failed_dict[target_oid] = ('update', filepath)
                continue

            overwrites_count += 1

        return overwrites_count

    @staticmethod
    def _check_attachment_dataframe_for_invalid_column_names(attachment_dataframe, invalid_names):
        invalid_names_index = pd.Index(invalid_names)
        intersection = attachment_dataframe.columns.intersection(invalid_names_index)
        if not intersection.empty:
            raise RuntimeError(f'Attachment dataframe contains the following invalid names: {list(intersection)}')

    def update_attachments(
        self, feature_layer_itemid, attachment_join_field, attachment_path_field, attachments_df, layer_number=0
    ):
        """Update a feature layer's attachments based on info from a dataframe of desired attachment file names

        Depends on a dataframe populated with a join key for the live data and the downloaded or locally-available
        attachments. If the name of the "new" attachment is the same as an existing attachment for that feature, it is
        not updated. If it is different or there isn't an existing attachment, the "new" attachment is attached to that
        feature.

        Args:
            feature_layer_itemid (str): The AGOL Item ID of the feature layer to update
            attachment_join_field (str): The field containing the join key between the attachments dataframe and the
                live data
            attachment_path_field (str): The field containing the desired attachment file path
            attachments_df (pd.DataFrame): A dataframe of desired attachments, including a join key and the local path
                to the attachment
            layer_number (int, optional): The layer within the Item ID to update. Defaults to 0.

        Returns:
            (int, int): Tuple of counts of successful overwrites and adds.
        """

        self._class_logger.info('Updating attachments...')
        #: These names are present in the live attachment data downloaded from AGOL. Because we merge the dataframes
        #: later, we need to make sure they're not the same. There may be better ways of handling this that allows the
        #: client names to be preserved, but for now force them to fix this.
        self._check_attachment_dataframe_for_invalid_column_names(
            attachments_df, invalid_names=['OBJECTID', 'PARENTOBJECTID', 'NAME', 'ID']
        )
        self._class_logger.debug('Using layer %s from item ID %s', layer_number, feature_layer_itemid)
        self.feature_layer = self.gis.content.get(feature_layer_itemid).layers[layer_number]
        live_features_as_df = pd.DataFrame.spatial.from_layer(self.feature_layer)
        live_data_subset_df = self._get_live_oid_and_guid_from_join_field_values(
            live_features_as_df, attachment_join_field, attachments_df
        )
        #: TODO: Make sure layer supports attachments so we don't get an arcgis error.
        #: Check out the feature layer .properties and FeatureLayerManager.add_to_definition to check/enable?
        attachment_eval_df = self._get_current_attachment_info_by_oid(live_data_subset_df)
        attachment_action_df = self._create_attachment_action_df(attachment_eval_df, attachment_path_field)

        overwrites_count = self._overwrite_attachments_by_oid(attachment_action_df, attachment_path_field)
        adds_count = self._add_attachments_by_oid(attachment_action_df, attachment_path_field)
        self._class_logger.info('%s attachments added, %s attachments overwritten', adds_count, overwrites_count)

        return overwrites_count, adds_count

    @staticmethod
    def build_attachments_dataframe(input_dataframe, join_column, attachment_column, out_dir):
        """Create an attachments dataframe by subsetting down to just the two fields and dropping any rows
           with null/empty attachments

        Args:
            input_dataframe (pd.DataFrame): Input data containing at least the join and attachment filename columns
            join_column (str): Unique key joining attachments to live data
            attachment_column (str): Filename for each attachment
            out_dir (str or Path): Output directory, will be used to build full path to attachment

        Returns:
            pd.DataFrame: Dataframe with join key, attachment name, and full attachment paths
        """

        input_dataframe[attachment_column].replace('', np.nan, inplace=True)  #: pandas doesn't see empty strings as NAs
        attachments_dataframe = input_dataframe[[join_column, attachment_column]] \
                                            .copy().dropna(subset=[attachment_column])
        #: Create the full path by prepending the output directory using .apply and a lambda function
        attachments_dataframe['full_file_path'] = attachments_dataframe[attachment_column] \
                                                    .apply(lambda filename: str(Path(out_dir, filename)))

        return attachments_dataframe

class ColorRampReclassifier:
    """Updates the interval ranges on a webmap's layer's classification renderer based on the layer's current data.

    Manually edits the JSON definition to change a layer's color ramp values based on a simple unclassed scheme similar
    to AGOL's unclassed ramp. The minimum value is the dataset minimum, the max is the mean value plus one standard
    deviation.
    """

    def __init__(self, webmap_item, gis):
        """
        Args:
            webmap_item (arcgis.mapping.WebMap): The webmap item in the AGOL organization
            gis (arcgis.gis.GIS): The AGOL organization as a gis object
        """

        self.webmap_item = webmap_item
        self.gis = gis
        self._class_logger = logging.getLogger(__name__).getChild(self.__class__.__name__)

    def _get_layer_dataframe(self, layer_name, feature_layer_number=0):
        """Create a dataframe from layer_name in self.webmap_item

        Args:
            layer_name (str): The exact name of the layer
            feature_layer_number (int): The number of the layer with the feature service to update. Defaults to 0.

        Returns:
            spatially-enabled data frame: The layer's data, including geometries.
        """

        self._class_logger.info('Getting dataframe from `%s` on `%s`', layer_name, self.webmap_item.title)
        webmap_object = arcgis.mapping.WebMap(self.webmap_item)
        layer = webmap_object.get_layer(title=layer_name)
        feature_layer = self.gis.content.get(layer['itemId'])
        layer_dataframe = pd.DataFrame.spatial.from_layer(feature_layer.layers[feature_layer_number])

        return layer_dataframe

    def _get_layer_id(self, layer_name):
        """Get the ID number of layer_name in self.webmap_item

        Args:
            layer_name (str): The exact name of the layer

        Raises:
            ValueError: If the layer is not found in the webmap

        Returns:
            int: The index (0-based) of the the layer in the web map
        """

        data = self.webmap_item.get_data()
        for layer_id, layer in enumerate(data['operationalLayers']):
            if layer['title'] == layer_name:
                self._class_logger.debug('Layer `%s` has id `%s`', layer_name, layer_id)
                return layer_id

        #: If we haven't matched the title and returned a valid id, raise an error.
        raise ValueError(f'Could not find "{layer_name}" in {self.webmap_item.title}')

    @staticmethod
    def _calculate_new_stops(dataframe, column, stops):
        """Calculate new stop values for an AGOL color ramp using what appears to be AGOL's method for unclassed ramps.

        Args:
            dataframe (pd.DataFrame): Data being classified
            column (str): Column to classify
            stops (int, optional): Number of stops to create.

        Returns:
            List: New stops cast as ints
        """

        if column not in dataframe.columns:
            raise ValueError(f'Column `{column}` not in dataframe')
        minval = dataframe[column].min()
        mean = dataframe[column].mean()
        std_dev = dataframe[column].std()
        upper = mean + std_dev  #: AGOL's default upper value for unclassed ramps seems to be mean + 1 std dev

        new_stops = np.linspace(minval, upper, stops)
        new_stops_ints = [int(stop) for stop in new_stops]

        return new_stops_ints

    def _update_stop_values(self, layer_number, new_stops):
        """Update the stop values of an (un)classified polygon renderer in an AGOL Webmap

        Args:
            layer_number (int): The index for the layer to be updated
            new_stops (List): New values for the existing stops

        Returns:
            Bool: Success or failure of update operation
        """

        #: Get short reference to the stops dictionary from the webmap's data json
        data = self.webmap_item.get_data()
        renderer = data['operationalLayers'][layer_number]['layerDefinition']['drawingInfo']['renderer']
        stops = renderer['visualVariables'][0]['stops']

        #: Overwrite the value, update the webmap item
        for stop, new_value in zip(stops, new_stops):
            stop['value'] = new_value
        self._class_logger.info(
            'Updating stop values on layer number `%s` in `%s`', layer_number, self.webmap_item.title
        )
        result = self.webmap_item.update(item_properties={'text': json.dumps(data)})
        self._class_logger.debug('Update result: %s', result)

        return result

    def update_color_ramp_values(self, layer_name, column_name, stops=5):
        """Update the color ramp ranges for layer_name in self.webmap_item.

        Does not alter colors or introduce additional stops; only overwrites the values for existing breaks.

        Args:
            layer_name (str): The exact name of the layer to be updated
            column_name (str): The name of the attribute being displayed as an (un)classified range
            stops (int, optional): The number of stops to calculate. Must match existing stops. Defaults to 5.

        Returns:
            Bool: Success or failure of update operation
        """
        layer_id = self._get_layer_id(layer_name)
        dataframe = self._get_layer_dataframe(layer_name)
        new_stops = self._calculate_new_stops(dataframe, column_name, stops)
        result = self._update_stop_values(layer_id, new_stops)

        return result


#!/usr/bin/env python

import rospy
import math
import time
from std_msgs.msg import Float32

def almostEqual(x,y, epsilon=1*10**(-8)):
   return abs(x-y) <= epsilon

def callback(data):
   global target
   target = round(data.data, 2)

def smoother():
   global target
   global curr
   target = 0
   curr = 0
   rospy.init_node("smoother", anonymous=True)
   rospy.Subscriber("command", Float32, callback)
   while not rospy.is_shutdown():
      if almostEqual(curr, target) == False:
         if curr < target:
            curr += 0.01
         elif curr > target:
            curr -= 0.01
      time.sleep(0.1)

if __name__ == '__main__':
   smoother()


import re
from signals.Signal import signal
from fastapi import FastAPI, Request, Form, params
from starlette.responses import RedirectResponse
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from populate_db.update_coin_price import update_coin_price
from fastapi.middleware.cors import CORSMiddleware
# from tortoise.contrib.fastapi import register_tortoise
import sqlite3 as sql
import config
import signals as sn
import json

from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import json_item

# Read row as dictionary in sql3
def dict_factory(cursor, row):
    dictionary = {}
    for idx, col in enumerate(cursor.description):
        dictionary[col[0]] = row[idx]
    return dictionary

app = FastAPI()
templates = Jinja2Templates(directory="templates")

# update coin_price
update_coin_price()

origins = [
    'http://localhost:3000'
]
# add middleware and specify cors
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# register_tortoise(
#     app,
#     db_url='sqlite://database.sqlite3',
#     modules={'models': ['models']},
#     generate_schemas = True,
#     add_exception_handlers = True
# )

@app.get("/")
async def index(request: Request):

    #create db conn
    conn = sql.connect(config.DB_URL)
    conn.row_factory = sql.Row
    cursor = conn.cursor()
    symbols = []

    params_dict = request.query_params._dict
    if 'signal_filter' not in params_dict:
        conn = sql.connect(config.DB_URL)
        conn.row_factory = sql.Row
        cursor = conn.cursor()

        select_query = '''SELECT DISTINCT symbol from coin_pair '''
        cursor.execute(select_query)
        rows = cursor.fetchall()
        for row in rows:
            symbols.append(row)
    elif params_dict['signal_filter'] == 'All':
        conn = sql.connect(config.DB_URL)
        conn.row_factory = sql.Row
        cursor = conn.cursor()

        select_query = '''SELECT DISTINCT symbol from coin_pair '''
        cursor.execute(select_query)
        rows = cursor.fetchall()
        for row in rows:
            symbols.append(row)

    else:
        signal_name = params_dict['signal_filter']
        signal = getattr(sn, signal_name)
        select_query = '''SELECT DISTINCT symbol from coin_pair '''
        cursor.execute(select_query)
        rows = cursor.fetchall()
        for row in rows:
            is_over = signal.signal(row['symbol'])
            if is_over:
                symbols.append(row)

    select_query = '''SELECT DISTINCT name from signal '''
    cursor.execute(select_query)
    rows = cursor.fetchall()
    signal_list = []
    for row in rows:
        signal_list.append(row['name'])

    conn.commit()
    conn.close()

    return templates.TemplateResponse("index.html", {"request": request, "symbols": symbols, "signal_list": signal_list})

@app.get("/coin/")
async def coin(request: Request):
    symbol = request.query_params['symbol']
    #create db conn
    conn = sql.connect(config.DB_URL)
    conn.row_factory = sql.Row
    cursor = conn.cursor()

    #create list exist symbol
    # get symbol interval
    select_query = '''SELECT open_time, open, high, low, close, close_time, volume
                      from coin_pair a inner join coin_price b on (a.id = b.id_coin_pair)
                      where symbol = ? '''
    cursor.execute(select_query, (symbol,))
    rows = cursor.fetchall()
    templates = Jinja2Templates(directory="templates")

    # # select strategy
    # select_query = '''SELECT name from strategy'''
    # cursor.execute(select_query)
    # strategy = cursor.fetchall()

    conn.commit()
    conn.close()

    templates = Jinja2Templates(directory="templates")

    return templates.TemplateResponse("coin_detail.html", {"request": request, "rows": rows, "symbol":symbol})

@app.post("/apply_strategy")
async def apply_strategy(strategy_id: int = Form(...), id_symbol: int = Form(...)):

    #create db conn
    conn = sql.connect('app.db')
    conn.row_factory = sql.Row
    cursor = conn.cursor()
    templates = Jinja2Templates(directory="templates")

    #create list exist symbol
    # get symbol interval
    cursor.execute('''Select distinct symbol from coin_pair where id = ?''', (id_symbol,))
    result = cursor.fetchone()
    symbol = result['symbol']

    select_query = '''INSERT INTO coin_strategy(symbol, id_strategy) VALUES(?,?)'''
    cursor.execute(select_query, (symbol,strategy_id))

    conn.commit()
    conn.close()

    return RedirectResponse(url = f'/strategy/?strategy_id={strategy_id}', status_code = 303)

"""
Back-End React start from here!
"""

#indicator api

@app.get("/indicator/{id_signal}")
async def get_indicators(id_signal: int):
    if id_signal == 0:
        return json.dumps([])
    #create db conn
    conn = sql.connect('app.db')
    conn.row_factory = dict_factory
    cursor = conn.cursor()

    select_query = """SELECT DISTINCT id_signal, id_indicator, interval
                    FROM indicator
                    WHERE id_signal = ? """

    cursor.execute(select_query, (id_signal, ))
    rows = cursor.fetchall()

    result = list()

    for row in rows:
        id_signal = row['id_signal']
        id_indicator = row['id_indicator']
        interval = row['interval']

        select_query = """WITH RECURSIVE
                      indicator_distinct AS (
                      SELECT DISTINCT id_indicator, id_signal, interval
                      FROM indicator
                      )
                      SELECT params_name, value
                      FROM indicator_distinct NATURAL JOIN indicator
                      WHERE id_signal = ? and id_indicator = ? and interval = ?"""

        cursor.execute(select_query, (id_signal, id_indicator, interval,))
        result_rows = cursor.fetchall()
        temp_dict = dict()
        for params in result_rows:
            temp_dict[params['params_name']] = params['value']
        temp_dict["id_indicator"] = id_indicator
        result.append(temp_dict)

    return json.dumps(result)

#Signal api
@app.get("/signal")
async def get_all_signal(request: Request):
    #create db conn
    conn = sql.connect('app.db')
    conn.row_factory = dict_factory
    cursor = conn.cursor()

    #create list exist symbol
    # get symbol interval
    cursor.execute('''Select * from signal ''')
    rows = cursor.fetchall()

    result = []
    # add result to lis of dict
    for row in rows:
        result.append(row)

    conn.commit()
    conn.close()

    return json.dumps(result)

#get symbol by signal
@app.get("/signal/{id_signal}")
async def get_signal_filter(request: Request, id_signal: int):
    #update coin_price
    # update_coin_price()
    #create db conn
    conn = sql.connect(config.DB_URL)
    conn.row_factory = dict_factory
    cursor = conn.cursor()
    symbols = []

    #get signal_name
    select_query = '''SELECT * from signal where id = ? '''
    cursor.execute(select_query, (id_signal, ))
    row = cursor.fetchone()

    if row == None:
        print('Im in!')
        select_query = '''SELECT DISTINCT symbol from coin_pair '''
        cursor.execute(select_query)
        rows = cursor.fetchall()
        for row in rows:
            symbols.append(row)

    else:
        signal_name = row['name']
        signal = getattr(sn, signal_name)
        select_query = '''SELECT DISTINCT symbol from coin_pair '''
        cursor.execute(select_query)
        rows = cursor.fetchall()
        for row in rows:
            is_over = signal.signal(row['symbol'])
            if is_over:
                symbols.append(row)

    conn.commit()
    conn.close()

    return json.dumps(symbols)

# Strategy
@app.get("/strategy")
async def get_all_strategy(request: Request):
    #connect to DB
    conn = sql.connect(config.DB_URL)
    conn.row_factory = dict_factory
    cursor = conn.cursor()

    selected_query = """ Select distinct name from strategy"""
    cursor.execute(selected_query)

    rows = cursor.fetchall()
    result = []

    for row in rows:
        result.append(row)

    return json.dumps(result)

# Test
import sys
sys.path.append("./backtrader")
from backtrader_plotting import Bokeh
from backtrader_plotting.schemes import Tradimo
from bokeh.io import output_file, show, save
from bokeh.plotting import figure
from bokeh.embed import components
from backtrader_plotting import Bokeh
import backtrader as bt
import datetime
from datafeed import create_coin_price_datafeed
from bokeh.embed import file_html, json_item

@app.get("/test")
async def get_test(request: Request):

    class TestStrategy(bt.Strategy):
        params = (
            ('buydate', 21),
            ('holdtime', 6),
        )

        def next(self):
            if len(self.data) == self.p.buydate:
                self.buy(self.datas[0], size=None)

            if len(self.data) == self.p.buydate + self.p.holdtime:
                self.sell(self.datas[0], size=None)

    cerebro = bt.Cerebro()

    cerebro.addstrategy(TestStrategy, buydate=3)

    data = create_coin_price_datafeed(symbol = 'BTCUSDT', interval = '1d')
    cerebro.adddata(data)

    cerebro.run()

    b = Bokeh(style='bar', tabs='multi', scheme=Tradimo(),
    toolbar_location='right', show = False, output_mode = 'save')
    test = cerebro.plot(b)
    tabs = test[0][0].model

    result = json.dumps(json_item(tabs, "myplot"))

    return result

@app.get("/test_strategy")
async def get_test_strategy(request: Request):

    class SmaCross(bt.Strategy):
    # list of parameters which are configurable for the strategy
        params = dict(
            pfast=50,  # period for the fast moving average
            pslow=200   # period for the slow moving average
        )

        def __init__(self):
            self.order = None
            sma1 = bt.ind.EMA(period=self.p.pfast)  # fast moving average
            sma2 = bt.ind.EMA(period=self.p.pslow)  # slow moving average
            self.crossover = bt.ind.CrossOver(sma1, sma2)  # crossover signal

        def log(self, txt, dt=None):
            ''' Logging function fot this strategy'''
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))

        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Buy/Sell order submitted/accepted to/by broker - Nothing to do
                return

            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.log(
                        'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                        (order.executed.price,
                        order.executed.value,
                        order.executed.comm))

                    self.buyprice = order.executed.price
                    self.buycomm = order.executed.comm
                else:  # Sell
                    self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                            (order.executed.price,
                            order.executed.value,
                            order.executed.comm))

            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.log('Order Canceled/Margin/Rejected')

            # Write down: no pending order
            self.order = None

        def notify_trade(self, trade):
            if not trade.isclosed:
                return

            self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                    (trade.pnl, trade.pnlcomm))

        def next(self):
            if not self.position:  # not in the market
                if self.crossover > 0:  # if fast crosses slow to the upside
                    limit_price = self.datas[0].close
                    size = float()
                    self.buy()  # enter long

            elif self.crossover < 0:  # in the market & cross to the downside
                self.close()  # close long position

    cerebro = bt.Cerebro()

    cerebro.addstrategy(SmaCross)

    data = create_coin_price_datafeed(symbol = 'BTCUSDT', interval = '1d')
    cerebro.adddata(data)

    cerebro.run()

    b = Bokeh(style='bar', tabs='multi', scheme=Tradimo(),
    toolbar_location='right', show = False, output_mode = 'save')
    test = cerebro.plot(b)
    tabs = test[0][0].model

    result = json.dumps(json_item(tabs, "myplot"))

    return result


#!/usr/bin/env python

from .._externals.srm import SRM
from .procrustes import procrustes
import numpy as np
from .format_data import format_data as formatter
from .._shared.helpers import memoize
import warnings

@memoize
def align(data, align='hyper', normalize=None, ndims=None, method=None,
          format_data=True):
    """
    Aligns a list of arrays

    This function takes a list of high dimensional arrays and 'hyperaligns' them
    to a 'common' space, or coordinate system following the approach outlined by
    Haxby et al, 2011. Hyperalignment uses linear transformations (rotation,
    reflection, translation, scaling) to register a group of arrays to a common
    space. This can be useful when two or more datasets describe an identical
    or similar system, but may not be in same coordinate system. For example,
    consider the example of fMRI recordings (voxels by time) from the visual
    cortex of a group of subjects watching the same movie: The brain responses
    should be highly similar, but the coordinates may not be aligned.

    Haxby JV, Guntupalli JS, Connolly AC, Halchenko YO, Conroy BR, Gobbini
    MI, Hanke M, and Ramadge PJ (2011)  A common, high-dimensional model of
    the representational space in human ventral temporal cortex.  Neuron 72,
    404 -- 416. (used to implement hyperalignment, see https://github.com/PyMVPA/PyMVPA)

    Brain Imaging Analysis Kit, http://brainiak.org. (used to implement Shared Response Model [SRM], see https://github.com/IntelPNI/brainiak)

    Parameters
    ----------
    data : numpy array, pandas df, or list of arrays/dfs
        A list of Numpy arrays or Pandas Dataframes

    align : str or dict
        If str, either 'hyper' or 'SRM'.  If 'hyper', alignment algorithm will be
        hyperalignment. If 'SRM', alignment algorithm will be shared response
        model.  You can also pass a dictionary for finer control, where the 'model'
        key is a string that specifies the model and the params key is a dictionary
        of parameter values (default : 'hyper').

    format_data : bool
        Whether or not to first call the format_data function (default: True).

    normalize : None
        Deprecated argument.  Please use new analyze function to perform
        combinations of transformations

    ndims : None
        Deprecated argument.  Please use new analyze function to perform
        combinations of transformations

    Returns
    ----------
    aligned : list
        An aligned list of numpy arrays

    """

    # if model is None, just return data
    if align is None:
        return data
    elif isinstance(align, dict):
        if align['model'] is None:
            return data
    else:
        if method is not None:
            warnings.warn('The method argument will be deprecated.  Please use align. See the API docs for more info: http://hypertools.readthedocs.io/en/latest/hypertools.tools.align.html#hypertools.tools.align')
            align = method

        if align is True:
            warnings.warn("Setting align=True will be deprecated.  Please specify the \
                          type of alignment, i.e. align='hyper'. See API docs for more info: http://hypertools.readthedocs.io/en/latest/hypertools.tools.align.html#hypertools.tools.align")
            align = 'hyper'

        # common format
        if format_data:
            data = formatter(data, ppca=True)

        if len(data) == 1:
            warnings.warn('Data in list of length 1 can not be aligned. '
                 'Skipping the alignment.')

        if data[0].shape[1] >= data[0].shape[0]:
            warnings.warn('The number of features exceeds number of samples. This can lead \
                 to overfitting.  We recommend reducing the dimensionality to be \
                 less than the number of samples prior to hyperalignment.')

        if (align == 'hyper') or (method == 'hyper'):

            ##STEP 0: STANDARDIZE SIZE AND SHAPE##
            sizes_0 = [x.shape[0] for x in data]
            sizes_1 = [x.shape[1] for x in data]

            #find the smallest number of rows
            R = min(sizes_0)
            C = max(sizes_1)

            m = [np.empty((R,C), dtype=np.ndarray)] * len(data)

            for idx,x in enumerate(data):
                y = x[0:R,:]
                missing = C - y.shape[1]
                add = np.zeros((y.shape[0], missing))
                y = np.append(y, add, axis=1)
                m[idx]=y

            ##STEP 1: TEMPLATE##
            for x in range(0, len(m)):
                if x==0:
                    template = np.copy(m[x])
                else:
                    next = procrustes(m[x], template / (x + 1))
                    template += next
            template /= len(m)

            ##STEP 2: NEW COMMON TEMPLATE##
            #align each subj to the template from STEP 1
            template2 = np.zeros(template.shape)
            for x in range(0, len(m)):
                next = procrustes(m[x], template)
                template2 += next
            template2 /= len(m)

            #STEP 3 (below): ALIGN TO NEW TEMPLATE
            aligned = [np.zeros(template2.shape)] * len(m)
            for x in range(0, len(m)):
                next = procrustes(m[x], template2)
                aligned[x] = next
            return aligned

        elif (align == 'SRM') or (method == 'SRM'):
            data = [i.T for i in data]
            srm = SRM(features=np.min([i.shape[0] for i in data]))
            fit = srm.fit(data)
            return [i.T for i in srm.transform(data)]


T = int(input())

def valid_Bracet(brackets):
    stack = []

    for token in brackets:
        if not stack:
            if token == "(":
                stack.append(token)
            else:
                return "NO"
        else:
            if token == "(":
                stack.append(token)
            else:
                stack.pop()

    if len(stack) == 0:
        return "YES"

    return "NO"

res = []
for _ in range(T):
    brackets = str(input())
    res.append(valid_Bracet(brackets))

for i in res:
    print(i)


def lcm(a, b):
    if a > b:
        greater = a
    else:
        greater = b
    while(True):
        if(greater % a == 0 and greater % b == 0):
            return greater
        greater += 1

def main():
    n1 = int(input("Enter 1st Number:\n"))
    n2 = int(input("Enter 2nd Number:\n"))
    print(f"The Lcm of {n1} and {n2} is {lcm(n1, n2)}.")

main()


#####import some libraries
import tkinter as tk
from tkinter import messagebox
from Player import Player
import random
import os
from os import path
from QuizQuestion import QuizQuestion
import pickle
#from tkinter.ttk import Separator,Progressbar
#from tkinter.messagebox import showinfo

class QuizGame():
# mainly a class to host all user interface objects + some data (questions, players)
    def __init__(self):

        # setting up the root of the user interface = menuWindow
        self._MenuWindow()
        # initialize these as None or compiler will complain the class doesn't have these as attributes
        self._nameTextField = None  # need ref to this to get content & to destroy
        self._nameButton = None  # need ref to this to destroy
        self._pWindow = None  # need ref to this to destroy
        self._mainWindow = None  # need ref to this to add new Qframe
        self._Qframe = None  # put widgets of mainwindow inside a frame together for easy destroy

        # fill with players in record.. maybe link to .txt file to keep player info even when closing game:
        # creating game loads player file into _players and saving game overwrites player file with new info

    def run(self):  # starting window of user interface is launched
        self._menuWindow.mainloop()

    def _MenuWindow(self):
        self._menuWindow = tk.Tk()
        self._menuWindow.title("VUB QUIZ APP")
        self._menuWindow.geometry('500x300') #600x400
        self._menuWindow.resizable(0, 0)  # not resizable: change?
        self._menuWindow.configure(bg="black")

        # start screen: 4 buttons: new player, select player, view stats, save game
        # title at top of every window
        title = tk.Label(self._menuWindow, text='VUB QUIZ APP', font=(
            'impact', 20, 'italic'), justify=tk.CENTER, bg='dark slate blue', fg='white', name='app_title', height=2)
        title.pack(side=tk.TOP, fill=tk.BOTH)

        # make these buttons prettier?:
        newPButton = tk.Button(master=self._menuWindow,
                               text="Create new player", command=self._EnterName, width = 30,bg='slate blue',fg='black',activebackground='light grey',height=2)
        newPButton.place(x=130, y=120)

        selectPButton = tk.Button(master=self._menuWindow,
                                text="Select a player", command = self._SelectPWindow, width = 30,bg='slate blue',fg='black',activebackground='light grey',height=2)
        selectPButton.place(x = 130, y = 180)

        viewStatsButton = tk.Button(master=self._menuWindow,
                                text="View stats",command=self._viewStat, width = 30,bg='slate blue',fg='black',activebackground='light grey',height=2)

        viewStatsButton.place(x=130, y=240)

    #this fxn destroys all the widgets except the app_title widget in the frame
    def _destroyFrame(self):
        for widget in self._menuWindow.winfo_children(): #travers through all the widget in the frme
            if str(widget) != '.app_title':
                widget.destroy()

    def _EnterName(self):  # called by "new player" button: open new entry and button on menuwindow
        self._nameWindow = tk.Toplevel(master = self._menuWindow) # new window
        self._nameWindow.geometry('500x300')
        self._nameWindow.resizable(0,0) # not resizable: change this?
        self._nameWindow.title('SELECT A PLAYER')
        self._nameWindow.config(bg ='black')
        title = tk.Label(self._nameWindow,text='VUB QUIZ APP', font=(
            'impact', 20, 'italic'), justify=tk.CENTER, bg='dark slate blue', fg='white', name='app_title', height=2)
        title.pack(side = tk.TOP, fill = tk.BOTH)
        self._txt_box = tk.Label(
            self._nameWindow, text="Enter your Name: ", font="30",fg='white',bg='black')
        self._txt_box.place(x=30, y=130)
        self._nameTextField = tk.Entry(
            self._nameWindow, text="player name", width=40)
        self._nameTextField.place(x=200, y=133)

        self._nameButton = tk.Button(
            self._nameWindow, text="Confirm", command=self._AddPlayer, width=20, bg='slate blue',fg='black',activebackground='light grey' ) #calls the AddPlayer fxn when buttons is clicked
        self._nameButton.place(x=200, y=220)

    #function to add player and save the player object locally after creating
    def _AddPlayer(self):  # called by Confirm button, create new player instance + put inside local list of players
        name = self._nameTextField.get()
        self._checkPlayerExist(name)
        if len(name) == 0:
            messagebox.showinfo("Error", "Enter your Name") #make sure the name field is not empty
        else:
            playerObj = Player(name)
            self._savePlayerInfo(playerObj) # save the player object locally
            #self._startPlaying() #start the quiz game
            self._nameWindow.destroy() #destroy the current add player window

    #function that shows the list of players and choose the player to start the quiz
    def _SelectPWindow(self):
        self._pWindow = tk.Toplevel(master = self._menuWindow) # new window
        self._pWindow.geometry('500x300')
        self._pWindow.resizable(0,0) # not resizable: change this?
        self._pWindow.title('SELECT A PLAYER')
        self._pWindow.config(bg ='black')
        title = tk.Label(self._pWindow,text='VUB QUIZ APP', font=(
            'impact', 20, 'italic'), justify=tk.CENTER, bg='dark slate blue', fg='white', name='app_title', height=2)
        title.pack(side = tk.TOP, fill = tk.BOTH)

        players_list = self._loadPlayers() #load players from the pickled object
        for players in players_list: #traverse through the list of players object and create button for each
            tk.Button(self._pWindow,
                      text=players.getName(),
                      width = 40,
                      bg='black',
                      fg='slate blue',
                      activebackground='light grey',
                      command=lambda players=players: self._startPlaying(players)).pack(padx=5, pady=5)

    #view the stats of the player in the
    def _viewStat(self):
        self._statWindow = tk.Toplevel(master = self._menuWindow) # new window
        self._statWindow.geometry('500x300')
        self._statWindow.resizable(0,0) # not resizable: change this?
        self._statWindow.title('View Players Stats')
        self._statWindow.config(bg ='black')
        title = tk.Label(self._statWindow,text='VUB QUIZ APP', font=(
            'impact', 20, 'italic'), justify=tk.CENTER, bg='dark slate blue', fg='white', name='app_title', height=2)
        title.pack(side = tk.TOP, fill = tk.BOTH)
        #load the picked list of players saved locally
        players_list = self._loadPlayers()
        for players in players_list: #traverse through the list of players object and create button for each
            tk.Button(self._statWindow,
                      text=players.getName(),
                      width = 60,
                      bg='black',
                      fg='slate blue',
                      activebackground='light grey',
                      command=lambda players=players: self._viewScore(players)).pack(padx=5, pady=5)

    #show the score of the players in a message box when the button is clicked from the list of players
    def _viewScore(self, players):
        self._scoreWindow = tk.Toplevel(master = self._statWindow) # new window
        self._scoreWindow.geometry('500x300')
        self._scoreWindow.resizable(0,0) # not resizable: change this?
        self._scoreWindow.title('Individual Player Stats')
        self._scoreWindow.config(bg ='black')
        title = tk.Label(self._scoreWindow,text='VUB QUIZ APP', font=(
            'impact', 20, 'italic'), justify=tk.CENTER, bg='dark slate blue', fg='white', name='app_title', height=2)
        title.pack(side = tk.TOP, fill = tk.BOTH)
        score = players.getFinalScore()
        name = players.getName()

        self._txt_box = tk.Label(
            self._scoreWindow, text="Score for " + name + " are as follows:", font= 14 ,fg='white',bg='black')
        self._txt_box.place(x=30, y=80)

        x_cord = 30
        y_cord = 120

        for key, value in score.items():
            scrText = key.title() + " : " + str(value)
            tk.Label(self._scoreWindow, text= scrText).place(x = x_cord , y = y_cord)
            #x_cord += 50
            y_cord += 40

    #this fxn loads the pickled players and passes the players objects as a list
    def _loadPlayers(self):
        prev_cwd = os.getcwd()
        os.chdir('players_data')
        cwd = os.getcwd()
        players = []
        for files in os.listdir(cwd):
            with open(files, 'rb') as openfile:
                players.append(pickle.load(openfile))
        os.chdir(prev_cwd)
        return players

    #this fxn shows the starting window for the quiz game
    def _startPlaying(self, players):
        self._destroyFrame()
        players.clearScore()
        currentP = players
        txt_box = tk.Label(self._menuWindow,
                           text="Choose the category " +
                               currentP.getName(),
                           font="24", fg='white', bg='black')
        txt_box.place(x=50, y=80)

        #gets the chosen questions category and passes the index of the chosen questions and the category
        def choseCategory():
            categoryQuestions = []
            questionsIndex = []
            if categoryVar.get() == '-1' : #checks if radio button is clicked or not
                messagebox.showinfo("Error", 'Choose a category!' )
            else:
                selectedCategory = categoryVar.get()

                for questions in quiz_questions:
                    if selectedCategory in questions:
                        categoryQuestions.append(questions)
                        questionsIndex.append(quiz_questions.index(questions))
                self._playingWindow(questionsIndex, selectedCategory, currentP)

        categoryVar = tk.StringVar()
        categoryVar.set(-1)

        R1 = tk.Radiobutton(self._menuWindow, text= 'History',variable= categoryVar, indicatoron=0, value = 'history', width = 20, height=1, fg='black',bg='slate blue', activebackground='light grey')
        R1.place(x=50, y=130)

        R2 = tk.Radiobutton(self._menuWindow, text= 'Computer Science', variable= categoryVar,indicatoron=0, value = 'computer science', width = 20,fg='black',height=1,bg='slate blue', activebackground='light grey')
        R2.place(x=300, y=130)

        R3 = tk.Radiobutton(self._menuWindow, text= 'Economics', variable= categoryVar,indicatoron=0, value = 'economics', width = 20,fg='black',height=1,bg='slate blue', activebackground='light grey')
        R3.place(x=50, y=170)

        R4 = tk.Radiobutton(self._menuWindow, text= 'Politics', variable= categoryVar,indicatoron=0, value = 'politics', width = 20,fg='black',height=1,bg='slate blue', activebackground='light grey')
        R4.place(x=300, y=170)

        b = tk.Button(master=self._menuWindow, text='Start Quiz', width=20, bg='slate blue',fg='black',activebackground='light grey',
                      command= choseCategory)

        b.place(x=175, y=240)

    #save the info of the current player as a pickle object locally
    def _savePlayerInfo(self, player):
        player_name = player.getName()
        prev_dir=os.getcwd()
        filename = player_name + '.pk1'
        os.chdir('players_data')
        with open(filename, 'wb') as output:
            pickle.dump(player, output, pickle.HIGHEST_PROTOCOL)
        os.chdir(prev_dir)

    #check if the player exists or not and throw error if exists
    def _checkPlayerExist(self, player_name):
        prev_dir=os.getcwd()
        os.chdir('players_data')
        filename = player_name+ '.pk1'
        if path.exists(filename):
            messagebox.showinfo("Error", 'User Already Exists!' )
        os.chdir(prev_dir)

    #this fxn displays the final window to the player with the score after completing the quiz
    def _finalWindow(self, selectedCategory, currentP):
        ltst_score = currentP.getScore()
        currentP.updateFinalScore(selectedCategory, ltst_score)
        self._savePlayerInfo(currentP)
        #show the points in the message box
        def showPoints():
            result = "Your final score for " + selectedCategory + " is " + str(ltst_score)
            messagebox.showinfo("Result", result)

        self._destroyFrame()
        exit_label = tk.Label(self._menuWindow,
                      text= 'Congratulations you have finished your quiz!!!' , font='10', fg='white', bg='black')
        exit_label.place(x=80, y=110)

        btnShowAnswer = tk.Button(master=self._menuWindow, text='View Score', width=20, bg='dark slate blue',fg='black',activebackground='light grey',
                      command= showPoints)
        btnShowAnswer.place(x=180, y=200)

    #shows the playing window i.e the frame where the questions are loaded
    def _playingWindow(self, questionsIndex, selectedCategory, currentP):
        self._destroyFrame()
        no_of_ques = 5
        #random index of the question to show
        #index = randomQuestions(no_of_ques, (len(categoryQuestions) -1))
        index = random.sample(questionsIndex, no_of_ques)
        for i in range(no_of_ques):
            question, index = getQuestions(i, index)
            self._showQuestions(question, index, i, currentP)
            self._destroyFrame()
            if i == (no_of_ques - 1): #check if the current question is the final question
                self._finalWindow(selectedCategory, currentP)

    #show the question and thei options one at a time through the for loop in the playingWindow function
    def _showQuestions(self, question, index, i, currentP):
        q_label = str(i + 1) +'. ' + question
        question_label = tk.Label(self._menuWindow,
                      text= q_label , font='10', fg='white', bg='black')
        question_label.place(x=20, y=80)

        answers = getAnswersOnTheList(index, i) #get answer of the current question

        #gets the value from the radio button selected and checks
        def checkAnswer():
            if radioVar.get() == '-1' : #checks if radio button is clicked or not
                messagebox.showinfo("Error", 'Choose a option' )
            else:
                selection = radioVar.get()
                correct_ans = getCorrectAnswer(index, i)
                if selection == correct_ans:
                    currentP.addScore()
                else:
                    currentP.subScore()
                var2.set(1)

        radioVar = tk.StringVar()
        radioVar.set(-1)

        #initialized a variable for python to wait until it's value is changed
        var2 = tk.IntVar()

        R1 = tk.Radiobutton(self._menuWindow, text= answers[0],variable= radioVar, indicatoron=0, value = answers[0], width = 20, height=2, fg='black',bg='slate blue', activebackground='light grey')
        R1.place(x=50, y=110)

        R2 = tk.Radiobutton(self._menuWindow, text= answers[1], variable= radioVar,indicatoron=0, value = answers[1], width = 20,fg='black',height=2,bg='slate blue', activebackground='light grey')
        R2.place(x=300, y=110)

        R3 = tk.Radiobutton(self._menuWindow, text= answers[2], variable= radioVar,indicatoron=0, value = answers[2], width = 20,fg='black',height=2,bg='slate blue', activebackground='light grey')
        R3.place(x=50, y=200)

        R4 = tk.Radiobutton(self._menuWindow, text= answers[3], variable= radioVar,indicatoron=0, value = answers[3], width = 20,fg='black',height=2,bg='slate blue', activebackground='light grey')
        R4.place(x=300, y=200)

        next_button =  tk.Button(self._menuWindow, text = 'Next Question', command =checkAnswer,fg='black',bg='dark slate blue',activebackground='light grey')
        next_button.place(x=210, y = 260)

        next_button.wait_variable(var2)

# *******************************************************************
questions_obj = []

game = QuizGame()

quiz_questions = [
    ['Question No. 0: When did the WW1 start?', '1', ['28 july 1914', '28 june 1914', '28 july 1916', '28 june 1916'],'history'],
    ['Question No. 1: Who invented the first Vaccine', '2', ['Louis pasteur', 'Edward jener', 'Alexander Fleming', 'james Phipps'],'history'],
    ['Question No. 2: Who invented the first Vaccine', '2', ['Louis pasteur', 'Edward jener', 'Alexander Fleming', 'james Phipps'],'history'],
    ['Question No. 3: Which vaccine was the first invented?', '3', ['Covid-19', 'turbercolisis', 'Small pox', 'meningitis'],'history'],
    ['Question No. 4: When did the WW2 end?', '4', ['1949', '1955', '1947', '1945'],'history'],
    ['Question No. 5: I joined the EU most recently?', '5', ['Swahili', 'Croatia', 'Bulgaria', 'Slovenia'],'history'],
    ['Question No. 6: Which country is not a former European colony?', '6', ['Namibia', 'Congo', 'Ethiopia', 'Kenya'],'history'],
    ['Question No. 7: Who was Ivory Coast first president?', '7', ['Julius Nyerere', 'Alix Mazrui', 'Kwame Nkrumah', 'felix Houphouet Boigny'],'history'],
    ['Question No. 8: Which country was known as Dahomey', '8', ['Benin', 'Senegal', 'Botswana', 'Dahomie'],'history'],
    ['Question No. 9: When did prince Philippe became king of Belgium', '9', ['2011', '2013', '2014', '2012'],'history'],
    ['Question No. 10: Who was the first president of the US?', '2', ['Donald Trump', 'George Washington', 'Barack Obama', 'John Adams'],'history'],
    ['Question No. 11: Positive Liberty is advocated by', '2', ['J.S.Mill', 'T.H.Green', 'Isiah Berlin', 'Locke'],'politics'],
    ['Question No. 12: Who wrote the book “Social Contract”?', '3', ['Thompson Mill', 'Mill', 'J.J Rousseau', 'John Locke'],'politics'],
    ['Question No. 13: Term “Historicism” was made popular by', '4', ['Foster', 'Sabine', 'Locke', 'Popper'],'politics'],
    ['Question No. 14: Who has described politics as” acting in concert”?', '5', ['Aristotle', 'Hannah Arendt', 'Aristole', 'Karl Marx'],'politics'],
    ['Question No. 15: Who is the father of Political Science?', '6', ['Karl Max', 'Plato', 'Aristotle', 'Austin'],'politics'],
    ['Question No. 16: Who called political Science as the Master Science?', '7', ['Cicero', 'Hobbes', 'Socrates', 'Aristotle'],'politics'],
    ['Question No. 17: Permanent Revolution was propounded by', '8', ['Trostky', 'Marx', 'Lenin', 'Engens'],'politics'],
    ['Question No. 18: "Physiocrats” were:', '9', ['Russian proletarians', 'French economist', 'German nazis', 'Dutch physicist'],'politics'],
    ['Question No. 19: Which of the following is the example of unwritten constitution?', '1', ['Saudi Arabia', 'England', 'Japan', 'France'],'politics'],
    ['Question No. 20: Who wrote the book “Republic”?', '2', ['Locke', 'Plato', 'Cicero', 'Bodin'],'politics'],
    ['Question No. 21: Which general term refer to harmful software?', '3', ['virus', 'trojan horse', 'malware', 'spyware'],'computer science'],
    ['Question No. 22: Who is the primary creator of java Language?', '4', ['Brian Kernighan', 'Domain squatters', 'Bryan Kerningham', 'james Gosling'],'computer science'],
    ['Question No. 23:What was the name of the first calculator', '5', ['abacus', 'Pascaline', 'calculus1', 'lening'],'computer science'],
    ['Question No. 24:What is the date and time of the Unix epoch', '6', ['00:00, December 31,1970', '00:00,january 1,1980', '00:00,january 1,1970', '00:00, December 31,1980'],'computer science'],
    ['Question No. 25: 1 Gigabyte equals:', '7', ['1000Mb', '1220Mb', '1200Mb', '1024Mb'],'computer science'],
    ['Question No. 26: A web address is usually known as:', '8', ['URL', 'WWW', 'URI', 'UWL'],'computer science'],
    ['Question No. 27:Who invented the first microchip?', '9', ['Jack Dorsey', 'Jack Kilby', 'Greg Chesson', 'Ronald Rider'],'computer science'],
    ['Question No. 28: Which among the folowing connects two networks?', '1', ['gateway', 'highway', 'http', 'bus'],'computer science'],
    ['Question No. 29:The technology is used to record cryptocurrency transactions is?', '2', ['digital wallet', 'Blockchain', 'token', 'mining'],'computer science'],
    ['Question No. 30:What is the binary representation of 45/8?', '3', ['110.101', '100.11', '100.101', '10.011'],'computer science'],
    ['Question No. 31:Who wrote the book “ Principles of Economics”?', '4', ['Pele', 'Carl Menger', 'Leon Walras', 'Alfred Marshall'],'economics'],
    ['Question No. 32:Who is known as the father of modern economics?', '5', ['David Ricardo', 'Adam Smith', 'W robert', 'Quesnay'],'economics'],
    ['Question No. 33:The critical minimum effort theory is due to', '6', ['D.Ricardo', 'W.A.Lewis', 'H.Liebenstein', 'Hirschman'],'economics'],
    ['Question No. 34:Who has given the theory of “Big Push”?', '7', ['Lewis', 'Harvey', 'Smith', 'P N. Rosentein-Rodan'],'economics'],
    ['Question No. 35:The IS-LM model was developed by', '8', ['John Hicks', 'J.Romer', 'Lucas', 'JP.hagen'],'economics'],
    ['Question No. 36:Relational Expectation Theory is associated with', '9', ['None of the above', 'Lucas', 'Hicks', 'Romer'],'economics'],
    ['Question No. 37:“Money is what money does”. Who said this?', '6', ['Hagen', 'Roberto', 'Walker', 'Smith'],'economics'],
    ['Question No. 38:Lorenz curve enables one to study which of the following', '7', ['Average', 'Tax rates', 'Correlation', 'Dispersion'],'economics'],
    ['Question No. 39:Input-Output Technique was invented by', '8', ['Wassily Leontief', 'Romero', 'Robert Solo', 'Lucas'],'economics'],
    ['Question No. 40:At the break-even point, the total cost equals', '9', ['Total lost', 'Total Revenue', 'Total capital', 'Total profit'],'economics']
    ]

#create questions objects and initialize them with the value from the list
for items in quiz_questions:
    questions_obj.append(QuizQuestion(items[0], items[1], items[2], items[3]))

def getQuestions(i, index):
    return questions_obj[index[i]].getQuestion(), index

def getAnswersOnTheList(index, i):
    return questions_obj[index[i]].getAnswersList()

def getCorrectAnswer(index, i):
    return questions_obj[index[i]].getAnswer()

game.run()


from math import sqrt
def ipotenuza(a, b):
    ip=a*a+b*b
    return sqrt (ip)

b=int(input())
g=open("triplete_pitagoreice.txt","w")
for a in range(1,b):
    ip= ipotenuza(a,b)
    if int(ip)==ip:
        ip=int(ip)
        g.write("("+str(a)+","+str(b)+","+str(ip)+") ")
g.close()

AN 1 SEM 1/Programarea algoritmilor/lab4/1.py


import torch
import time
from utils.time_log import time_since
import os
import sys
from utils.string_helper import *

def preprocess_beam_search_result(beam_search_result, idx2word, vocab_size, oov_lists, eos_idx, unk_idx, replace_unk, src_str_list):
    batch_size = beam_search_result['batch_size']
    predictions = beam_search_result['predictions']
    scores = beam_search_result['scores']
    attention = beam_search_result['attention']
    assert len(predictions) == batch_size
    pred_list = []  # a list of dict, with len = batch_size
    for pred_n_best, score_n_best, attn_n_best, oov, src_word_list in zip(predictions, scores, attention, oov_lists, src_str_list):
        # attn_n_best: list of tensor with size [trg_len, src_len], len=n_best
        pred_dict = {}
        sentences_n_best = []
        for pred, attn in zip(pred_n_best, attn_n_best):
            sentence = prediction_to_sentence(pred, idx2word, vocab_size, oov, eos_idx, unk_idx, replace_unk, src_word_list, attn)
            sentences_n_best.append(sentence)
        pred_dict['sentences'] = sentences_n_best  # a list of list of word, with len [n_best, out_seq_len], does not include tbe final <EOS>
        pred_dict['scores'] = score_n_best  # a list of zero dim tensor, with len [n_best]
        pred_dict['attention'] = attn_n_best  # a list of FloatTensor[output sequence length, src_len], with len = [n_best]
        pred_list.append(pred_dict)
    return pred_list

def predict_with_beam_search(generator, one2many_data_loader, opt, delimiter_word='<sep>'):
    # file for storing the predicted keyphrases
    if opt.pred_file_prefix == "":
        pred_output_file = open(os.path.join(opt.pred_path, "predictions.txt"), "w")
    else:
        pred_output_file = open(os.path.join(opt.pred_path, "%s_predictions.txt" % opt.pred_file_prefix), "w")
    # debug
    interval = 1000
    start_time_prediction = time.time()
    with torch.no_grad():
        start_time = time.time()
        for batch_i, batch in enumerate(one2many_data_loader):
            if (batch_i + 1) % interval == 0:
                print("Batch %d: Time for running beam search on %d batches : %.1f" % (batch_i+1, interval, time_since(start_time)))
                sys.stdout.flush()
                start_time = time.time()
            src, src_lens, src_mask, src_oov, oov_lists, src_str_list, trg_str_2dlist, _, _, _, _, original_idx_list = batch
            """
            src: [batch_size, max_src_len], a LongTensor containing the word indices of source sentences, , with oov words replaced by unk idx
            src_lens: [batch_size], a list containing the length of src sequences for each batch
            src_mask: [batch, max_src_len], a FloatTensor
            src_oov: [batch, max_src_len], a LongTensor containing the word indices of source sentences, contains the index of oov words (used by copy)
            oov_lists: [batch_size], a list of oov words for each src, 2dlist
            """
            src = src.to(opt.device)
            src_mask = src_mask.to(opt.device)
            src_oov = src_oov.to(opt.device)

            beam_search_result = generator.beam_search(src, src_lens, src_oov, src_mask, oov_lists, opt.word2idx, opt.max_eos_per_output_seq)
            pred_list = preprocess_beam_search_result(beam_search_result, opt.idx2word, opt.vocab_size, oov_lists, opt.word2idx[pykp.io.EOS_WORD], opt.word2idx[pykp.io.UNK_WORD], opt.replace_unk, src_str_list)
            # list of {"sentences": [], "scores": [], "attention": []}

            # recover the original order in the dataset
            seq_pairs = sorted(zip(original_idx_list, src_str_list, trg_str_2dlist, pred_list, oov_lists),
                               key=lambda p: p[0])
            original_idx_list, src_str_list, trg_str_2dlist, pred_list, oov_lists = zip(*seq_pairs)

            # Process every src in the batch
            for src_str, trg_str_list, pred, oov in zip(src_str_list, trg_str_2dlist, pred_list, oov_lists):
                # src_str: a list of words; trg_str: a list of keyphrases, each keyphrase is a list of words
                # pred_seq_list: a list of sequence objects, sorted by scores
                # oov: a list of oov words
                pred_str_list = pred['sentences']  # predicted sentences from a single src, a list of list of word, with len=[beam_size, out_seq_len], does not include the final <EOS>

                all_keyphrase_list = []  # a list of word list contains all the keyphrases in the top max_n sequences decoded by beam search
                for word_list in pred_str_list:
                    all_keyphrase_list += split_word_list_by_delimiter(word_list, delimiter_word)
                #pred_str_list = [word_list for word_list, is_keep in zip(all_keyphrase_list, not_duplicate_mask) if is_keep]
                pred_str_list = all_keyphrase_list

                # output the predicted keyphrases to a file
                pred_print_out = ''
                for word_list_i, word_list in enumerate(pred_str_list):
                    if word_list_i < len(pred_str_list) - 1:
                        pred_print_out += '%s;' % ' '.join(word_list)
                    else:
                        pred_print_out += '%s' % ' '.join(word_list)
                pred_print_out += '\n'
                pred_output_file.write(pred_print_out)

    pred_output_file.close()
    total_time_prediction = time_since(start_time_prediction)
    print("done!")
    print(total_time_prediction)


def say_hello(cool):
  print("Hi", cool)

def respond(nome):
    res = input("can I ask you some questions?")
    if res == "yes":
        print("Ok lets get started!")
        green = input("what is your favorite animal?")
        print ("No way!", green, "is my favorite animal as well!")
    else:
        print("Bye", nome)

def main():
  answer = input("what's your name?")
  say_hello(answer)
  respond(answer)

if __name__ == "__main__":
    main()


"""Criar um programa que calcule o IMC
de uma pessoa com as seguintes condições
* abaixo de 18.5: abaixo do peso
* entre de 18.5 e 25: peso ideal
* entre de 25 e 30: sobrepeso
* entre de 30 e 40: obesidade
* acima de 40: obesidade mórbida"""

weight = float(input("Digite seu peso:  "))
height = float(input("Digite sua altura:  "))

imc = weight/height**2

if imc <= 18.5:
    print('O IMC foi de {:.1f}\n'
          'ABAIXO DO PESO'.format(imc))
elif 18.5 < imc <= 25:
    print('O IMC foi de {:.1f}\n'
          'PESO IDEAL'.format(imc))
elif 25 < imc <= 30:
    print('O IMC foi de {:.1f}\n'
          'SOBREPESO'.format(imc))
elif 30 < imc <= 40:
    print('O IMC foi de {:.1f}\n'
          'OBESIDADE'.format(imc))
elif imc > 40:
    print('O IMC foi de {:.1f}\n'
          'OBESIDADE MÓRBIDA'.format(imc))


from django.urls import path,include
from watchlist_app.api.views import WatchListAV,WatchListDetailAV,StreamPlatformListAV,StreamPlatformDetailAV,ReviewList,ReviewDetail,ReviewCreate

urlpatterns = [
	path('list/',WatchListAV.as_view(),name="movie-list"),
    path('<int:pk>',WatchListDetailAV.as_view(),name='movie-detail'),

    path('stream/',StreamPlatformListAV.as_view(),name="stream-platofrmlist"),
    path('stream/<int:pk>',StreamPlatformDetailAV.as_view(),name="stream-platofrmdetail"),

    # path('stream/<int:pk>/review',StreamPlatformDetailAV.as_view(),name="stream-platofrmdetail"),
    # path('stream/review/<int:pk>',ReviewDetail.as_view(),name='review-detail'),
    path('review',ReviewList.as_view(),name='review-list'),
    path('review/<int:pk>/review-create',ReviewCreate.as_view(),name='review-create'),
    path('review/<int:pk>',ReviewDetail.as_view(),name='review-detail'),

]


import csv
import matplotlib.pyplot as plt
'''
next()는 두 가지 포맷으로 사용된다.
function 구조로 사용되면 header만 리턴한다.
consumer 구조로 사용되면 data에서 header를 제거한다.

data: [] = list()는 list 타입의 data를 list()로 초기화 시키는 것이다.
단,한 메소드 내에서만 사용하면 로컬에서 초기화한다. 예제는 다음과 같다.
data: [] = None
def save_highest_temperature(self):
    data = list()
그러나 여러메소드에서 사용하면 필드에서 초기화한다. 예제는 다음과 같다.
data:[] = list()

row[날짜,지점,평균기온(℃),최저기온(℃),최고기온(℃)] 최고기온은 -1(뒤에서 첫번째, =4)이다.
'''

class ChangeTemperatureOnMyBirthday():
    data: [] = list()
    highest_temperature: [] = list()
    lowest_temperature: [] = list()

    def processing(self):
        self.read_data()
        self.highest_temperature()
        self.visualize_data()
        self.extract_date_data()

    def read_data(self):
        data = csv.reader(open('data/seoul.csv', 'rt', encoding='UTF-8'))
        next(data)
        # print([i for i in data])
        self.data = data

    def show_highest_temperature(self):
        # print([i[-1] for i in self.data])
        return [i[-1] for i in self.data]

    def save_highest_temperature(self):
        [(self.highest_temperature.append(float(i[-1]))) for i in self.data if i[-1] != '']
        print(f' 총 {len(self.highest_temperature)}개')

    def highest_temperature_my_birthday(self):
        high = []  # 최고기온
        low = []
        for i in self.data:
            if i[-1] != '' and i[-2] != '':
                if 1995 <= int(i[0].split('-')[0]):
                    if i[0].split('-')[1] == '03' and i[0].split('-')[2] == '10':
                        high.append(float(i[-1]))
                        low.append(float(i[-2]))

        plt.rc('font', family='Malgun Gothic')
        plt.rcParams['axes.unicode_minus'] = False
        plt.title("내 생일의 기온 변화 그래프")
        plt.plot(high, 'hotpink',label='high')
        plt.plot(low, 'skyblue', label='low')
        plt.legend()
        plt.show()

    def visualize_data(self):
        plt.plot(self.high, 'hotpink')
        plt.plot(self.low, 'skyblue')
        plt.show()

    def extract_date_data(self):
        '''
        for i in self.data:
            if i[-1] != '':
                if i[0].split('-')[1] == '03' and i[0].split('-')[2] == '10':
                    self.highest_temperature.append(float(i[-1]))
        '''

        [(self.highest_temperature.append(float(i[-1]))) for i in self.data if i[-1] != ''
         if i[0].split('-')[1] == '03' and i[0].split('-')[2] == '10']
        [(self.lowest_temperature.append(float(i[-2]))) for i in self.data if i[-2] != ''
         if i[0].split('-')[1] == '03' and i[0].split('-')[2] == '10']
        print(self.highest_temperature)

if __name__ == '__main__':
    this = ChangeTemperatureOnMyBirthday()
    this.read_data()
    # print([i for i in this.data])
    # this.show_highest_temperature()
    # this.save_highest_temperature()
    this.highest_temperature_my_birthday()
    # this.visualize_data()
    # this.extract_date_data()
    # this.visualize_highest_data()
    # this.visualize_lowest_data()


import pandas as pd
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import make_scorer
from sklearn.metrics import roc_auc_score
import time
import datetime

def ReadData():
    features = pd.read_csv('./features.csv', index_col='match_id')
    radiant_win = features['radiant_win']

    print("ReadData: X.shape =", features.shape)

    return features, radiant_win

def PrepareData(features):
    # Removing data from future.
    features = features.drop(
        ['barracks_status_dire',
            'barracks_status_radiant',
            'tower_status_dire',
            'tower_status_radiant',
            'radiant_win',
            'duration'],
        axis=1)

    ###
    print("EGOR EGOR EOGR EGOR EGOR EGOR EOGR EGOR EGOR EGOR EOGR EGOR ", features.shape)
    # print(features.head())
    # print(features.columns.tolist())
    ###

    columns_with_missings = features.shape[0] - features.count(axis=0)
    # This is columns with missings.
    print(columns_with_missings[columns_with_missings > 0])

    # TODO: try to replace with large value.
    features.fillna(0, inplace = True)

    return features#, radiant_win

def DropCategorialFeatures(features):
    features = features.drop(
        ['lobby_type',
            'r1_hero',
            'r2_hero',
            'r3_hero',
            'r4_hero',
            'r5_hero',
            'd1_hero',
            'd2_hero',
            'd3_hero',
            'd4_hero',
            'd5_hero'],
        axis=1)

    return features

def DoRegression(X_train, y_train):
    kfold =  KFold(
        n_splits = 5,
        shuffle = True,
        random_state = 241)

    iter = 1
    best_c = 0.000001
    max_score = 0.
    for c in np.power(10.0, np.arange(-5, 6)):
        start_time = datetime.datetime.now()
        score = cross_val_score(
                estimator = LogisticRegression(
                    C=c,
                    random_state = 241),
                X=X_train,
                y=y_train,
                scoring=make_scorer(roc_auc_score),
                cv=kfold)

        print("\nC =", c,"Time elapsed:",datetime.datetime.now() - start_time)
        print("\nIter =",iter,"score =",score.mean(),"\n")

        if score.mean() > max_score:
            best_c = c
            max_score = score.mean()

        iter+=1

    print("Best score =",max_score,", best c =",best_c)

def GetHeroesAmount(X):
    heroes_values = np.unique(X[['r1_hero',
            'r2_hero',
            'r3_hero',
            'r4_hero',
            'r5_hero',
            'd1_hero',
            'd2_hero',
            'd3_hero',
            'd4_hero',
            'd5_hero']].values)

    print("Heroes", heroes_values)
    print("Heroes amount =", heroes_values.argmax())
    print("Heroes amount =", heroes_values.shape[0])
    return heroes_values.shape[0]

def BagOfWords(X, N):
    print("Before X.shape =",X.shape)
    categorial_features_names = [
            'r1_hero',
            'r2_hero',
            'r3_hero',
            'r4_hero',
            'r5_hero',
            'd1_hero',
            'd2_hero',
            'd3_hero',
            'd4_hero',
            'd5_hero']
    categorial_features = X[categorial_features_names]
    print("categorial_features.shape = ", categorial_features.shape)

    X_pick = np.zeros((X.shape[0], N))
    print("X_pick.shape =",X_pick.shape)
    print("type(X_pick) =",type(X_pick))

    for i, match_id in enumerate(X.index):
        for p in range(5):
            X_pick[i, X.ix[match_id, 'r%d_hero' % (p+1)]-5] = 1
            X_pick[i, X.ix[match_id, 'd%d_hero' % (p+1)]-5] = -1

    X = X.drop(
            categorial_features_names,
            axis=1)
    print("135",X.shape)
    # result = pd.concat([X, pd.DataFrame(X_pick)], axis = 1, ignore_index=True)
    result = pd.DataFrame(np.concatenate((X.as_matrix(), X_pick), axis=1))

    # i = 0
    # for name in categorial_features:
    #     X[name] = X_pick[:,i]
    #     i+=1

    result.to_csv("categorial_features.csv")
    print("After X.shape =",result.shape)
    return X

def ReaTestdData():
    features = pd.read_csv('./features_test.csv', index_col='match_id')
    print("ReadData: X.shape =", features.shape)
    features.fillna(0, inplace = True)

    return features

def DoFinalPredict(X_train, y_train, X_test):
    print("X_train.shape =",X_train.shape)
    print("y_train.shape =", y_train.shape)
    print("X_test.shape =", X_test.shape)
    logistic_regression = LogisticRegression(
            C=0.01,
            random_state = 241)
    logistic_regression.fit(X_train, y_train)
    result = logistic_regression.predict_proba(X_test);
    print("result.shape =",result.shape)
    return result[:,1]

def main():
    X_original, y = ReadData()
    X_test = ReaTestdData()

    X = PrepareData(X_original)

    GetHeroesAmount(X)

    scaler = StandardScaler()
    # X_scaled = scaler.fit_transform(X.as_matrix())
    # print(X_scaled)

    # DoRegression(X_scaled, y)
    # Best score = 0.653792271116 , best c = 10.0

    X_without_categorial_features = DropCategorialFeatures(X)
    X_without_categorial_features_scaled = scaler.fit_transform(
            X_without_categorial_features.as_matrix())

    X_test_without_categorial_features = DropCategorialFeatures(X_test)
    X_test_without_categorial_features_scaled = scaler.fit_transform(
            X_test_without_categorial_features.as_matrix())
    # DoRegression(X_without_categorial_features_scaled, y)
    # Best score = 0.65396795402 , best c = 0.01

    # X_with_bag_of_words = BagOfWords(X, GetHeroesAmount(X))
    # DoRegression(
    #         scaler.fit_transform(
    #             X_with_bag_of_words.as_matrix()),
    #         y)
    # Best score = 0.653813650883 , best c = 0.01

    result = DoFinalPredict(
            X_without_categorial_features_scaled,
            y,
            X_test_without_categorial_features_scaled)

    print("Min predict result =", result.min())
    print("Max predict result =", result.max())

    print("X_test_without_categorial_features_scaled.shape =", X_test_without_categorial_features_scaled.shape)
    match_id = X_test.index.values
    print(match_id)
    print("match_id.shape =", match_id.shape)
    print("result.shape =", result.shape)
    result = result.reshape((result.shape[0],1))
    match_id = np.around(match_id.reshape((result.shape[0],1)),decimals = 1)
    final_result = np.concatenate(
            (match_id,
            result),
            axis = 1)
    # final_result.tofile("result.csv", sep="\n")

    # with open('result.csv', 'w') as f:
    #     for line in final_result:
    #         print(type(line))
    #         print(line)
    #         f.write(str(line[0] + line[1] + "\n"))

    np.savetxt('result.csv', final_result, fmt='%d, %lf')

if __name__ == "__main__":
    main()


MAX_NUMBER_OF_ACTIVE_DB_SESSIONS = 50
TIME_OUT_FOR_QUERY = 60*5
MLFLOW_CLEANUP = 60*60
NUMBER_OF_ITEMS_TO_DRAIN = 300
EXECUTION_CAP_FOR_MAINMENUURLS = 5
BASE_URL = "http://bigpanda.cern.ch"
TIMEOUT_WHEN_DB_LOADED = 5
URL_WITH_BIG_TASKS = '/tasks/?site=ORNL_Titan_MCORE&status=running&json'
URL_WITH_ES_TASKS = '/tasks/?eventservice=eventservice&json'
HARVESTER_LIST_URL = '/harvester/instances/?days=7&json'
LOG_PATH = "/tmp/cachecontroller.log"
PANDA_LOGGER_PATH = "/cephfs/atlpan/filebrowser"
MAX_LOG_AGE_DAYS = 3
PANDA_LOGGER_PATH_ANALYTICS = "/cephfs/atlpan/pandajoblogs"
MAX_LOG_AGE_DAYS_ANALYTICS = 14