"""
CLI package for the application.
"""

# pylint: disable=locally-disabled, multiple-statements, fixme, line-too-long, too-many-arguments, invalid-name, too-many-lines, protected-access, unused-argument, no-value-for-parameter, redefined-builtin

import click
from basedosdados.upload.base import Base
from basedosdados.upload.dataset import Dataset
from basedosdados.upload.table import Table
from basedosdados.upload.storage import Storage
from basedosdados.upload.metadata import Metadata

import basedosdados as bd
from basedosdados.exceptions import BaseDosDadosException
from ckanapi import CKANAPIError

@click.group()
@click.option("--templates", default=None, help="Templates path")
@click.option("--bucket_name", default=None, help="Project bucket name")
@click.option("--metadata_path", default=None, help="Folder to store metadata")
@click.version_option(package_name="basedosdados")
@click.pass_context
def cli(ctx, templates, bucket_name, metadata_path):
    """
    Function to define the CLI.
    """

    ctx.obj = dict(
        templates=templates,
        bucket_name=bucket_name,
        metadata_path=metadata_path,
    )

@click.group(name="dataset")
@click.pass_context
def cli_dataset(ctx):
    """
    Command to manage datasets.
    """

@cli_dataset.command(name="init", help="Initialize metadata files of dataset")
@click.argument("dataset_id")
@click.option(
    "--replace",
    is_flag=True,
    help="Whether to replace current metadata files",
)
@click.pass_context
def init_dataset(ctx, dataset_id, replace):
    """
    Initialize metadata files of dataset.
    """

    d = Dataset(dataset_id=dataset_id, **ctx.obj).init(replace=replace)

    click.echo(
        click.style(
            f"Dataset `{dataset_id}` folder and metadata were created at {d.metadata_path}",
            fg="green",
        )
    )

def mode_text(mode, verb, obj_id):
    """
    Returns a specific text for each mode.
    """

    if mode == "all":
        text = f"Datasets `{obj_id}` and `{obj_id}_staging` were {verb} in BigQuery"
    elif mode == "staging":
        text = f"Dataset `{obj_id}_stating` was {verb} in BigQuery"
    elif mode == "prod":
        text = f"Dataset `{obj_id}` was {verb} in BigQuery"

    return text

@cli_dataset.command(name="create", help="Create dataset on BigQuery")
@click.argument("dataset_id")
@click.option(
    "--mode", "-m", default="all", help="What datasets to create [prod|staging|all]"
)
@click.option(
    "--if_exists",
    default="raise",
    help="[raise|update|replace|pass] if dataset alread exists",
)
@click.option(
    "--dataset_is_public",
    default=True,
    help="Control if prod dataset is public or not. By default staging datasets like `dataset_id_staging` are not public.",
)
@click.option(
    "--location",
    default=None,
    help="Location of dataset data. List of possible region names locations: https://cloud.google.com/bigquery/docs/locations",
)
@click.pass_context
def create_dataset(ctx, dataset_id, mode, if_exists, dataset_is_public, location):
    """
    Create dataset on BigQuery.
    """

    Dataset(dataset_id=dataset_id, **ctx.obj).create(
        mode=mode,
        if_exists=if_exists,
        dataset_is_public=dataset_is_public,
        location=location,
    )

    click.echo(
        click.style(
            mode_text(mode, "created", dataset_id),
            fg="green",
        )
    )

@cli_dataset.command(name="update", help="Update dataset on BigQuery")
@click.argument("dataset_id")
@click.option(
    "--mode", "-m", default="all", help="What datasets to create [prod|staging|all]"
)
@click.pass_context
def update_dataset(ctx, dataset_id, mode, location):
    """
    Update dataset on BigQuery.
    """

    Dataset(dataset_id=dataset_id, **ctx.obj).update(mode=mode, location=location)

    click.echo(
        click.style(
            mode_text(mode, "updated", dataset_id),
            fg="green",
        )
    )

@cli_dataset.command(name="publicize", help="Make a dataset public")
@click.argument("dataset_id")
@click.option(
    "--dataset_is_public",
    default=True,
    help="Control if prod dataset is public or not. By default staging datasets like `dataset_id_staging` are not public.",
)
@click.pass_context
def publicize_dataset(ctx, dataset_id, dataset_is_public):
    """
    Publicize dataset.
    """

    Dataset(dataset_id=dataset_id, **ctx.obj).publicize(
        dataset_is_public=dataset_is_public
    )

    click.echo(
        click.style(
            f"Dataset `{dataset_id}` became public!",
            fg="green",
        )
    )

@cli_dataset.command(name="delete", help="Delete dataset")
@click.argument("dataset_id")
@click.option(
    "--mode", "-m", default="all", help="What datasets to create [prod|staging|all]"
)
@click.pass_context
def delete_dataset(ctx, dataset_id, mode):
    """
    Delete dataset.
    """

    if click.confirm(f"Are you sure you want to delete `{dataset_id}`?"):

        Dataset(dataset_id=dataset_id, **ctx.obj).delete(mode=mode)

    click.echo(
        click.style(
            mode_text(mode, "deleted", dataset_id),
            fg="green",
        )
    )

@click.group(name="table")
def cli_table():
    """
    Command to manage tables.
    """

@cli_table.command(name="init", help="Create metadata files")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--data_sample_path",
    default=None,
    help="Sample data used to pre-fill metadata",
    type=click.Path(exists=True),
)
@click.option(
    "--if_folder_exists",
    default="raise",
    help="[raise|replace|pass] actions if table folder exists",
)
@click.option(
    "--if_table_config_exists",
    default="raise",
    help="[raise|replace|pass] actions if table config files already exist",
)
@click.option(
    "--source_format",
    default="csv",
    help="Data source format. Only 'csv' is supported. Defaults to 'csv'.",
)
@click.option(
    "--force_columns",
    default=False,
    help="Overwrite columns with local columns.",
)
@click.option(
    "--columns_config_url_or_path",
    default=None,
    help="google sheets URL. Must be in the format https://docs.google.com/spreadsheets/d/<table_key>/edit#gid=<table_gid>. The sheet must contain the column name: 'coluna' and column description: 'descricao'.",
)
@click.pass_context
def init_table(
    ctx,
    dataset_id,
    table_id,
    data_sample_path,
    if_folder_exists,
    if_table_config_exists,
    source_format,
    force_columns,
    columns_config_url_or_path,
):
    """
    Initialize table metadata.
    """

    t = Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).init(
        data_sample_path=data_sample_path,
        if_folder_exists=if_folder_exists,
        if_table_config_exists=if_table_config_exists,
        source_format=source_format,
        force_columns=force_columns,
        columns_config_url_or_path=columns_config_url_or_path,
    )

    click.echo(
        click.style(
            f"Table `{table_id}` folder and metadata were created at {t.metadata_path}{dataset_id}",
            fg="green",
        )
    )

@cli_table.command(name="create", help="Create stagging table in BigQuery")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--path",
    "-p",
    type=click.Path(exists=True),
    default=None,
    help="Path of data folder or file.",
)
@click.option(
    "--if_table_exists",
    default="raise",
    help="[raise|replace|pass] actions if table exists",
)
@click.option(
    "--force_dataset",
    default=True,
    help="Whether to automatically create the dataset folders and in BigQuery",
)
@click.option(
    "--if_storage_data_exists",
    default="raise",
    help="[raise|replace|pass] actions if table data already exists at Storage",
)
@click.option(
    "--if_table_config_exists",
    default="raise",
    help="[raise|replace|pass] actions if table config files already exist",
)
@click.option(
    "--source_format",
    default="csv",
    help="Data source format. Only 'csv' is supported. Defaults to 'csv'.",
)
@click.option(
    "--force_columns",
    default=False,
    help="Overwrite columns with local columns.",
)
@click.option(
    "--columns_config_url_or_path",
    default=None,
    help="Path to the local architeture file or a public google sheets URL. Path only suports csv, xls, xlsx, xlsm, xlsb, odf, ods, odt formats. Google sheets URL must be in the format https://docs.google.com/spreadsheets/d/<table_key>/edit#gid=<table_gid>.",
)
@click.option(
    "--dataset_is_public",
    default=True,
    help="Control if prod dataset is public or not. By default staging datasets like `dataset_id_staging` are not public.",
)
@click.option(
    "--location",
    default=None,
    help="Location of dataset data. List of possible region names locations: https://cloud.google.com/bigquery/docs/locations",
)
@click.option(
    "--chunk_size",
    default=None,
    help="The size of a chunk of data whenever iterating (in bytes). This must be a multiple of 256 KB per the API specification.",
)
@click.pass_context
def create_table(
    ctx,
    dataset_id,
    table_id,
    path,
    if_table_exists,
    force_dataset,
    if_storage_data_exists,
    if_table_config_exists,
    source_format,
    force_columns,
    columns_config_url_or_path,
    dataset_is_public,
    location,
    chunk_size,
):

    """
    Create staging table in BigQuery.
    """

    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).create(
        path=path,
        if_table_exists=if_table_exists,
        force_dataset=force_dataset,
        if_storage_data_exists=if_storage_data_exists,
        if_table_config_exists=if_table_config_exists,
        source_format=source_format,
        force_columns=force_columns,
        columns_config_url_or_path=columns_config_url_or_path,
        dataset_is_public=dataset_is_public,
        location=location,
        chunk_size=chunk_size,
    )

    click.echo(
        click.style(
            f"Table `{dataset_id}_staging.{table_id}` was created in BigQuery",
            fg="green",
        )
    )

@cli_table.command(name="update", help="Update tables in BigQuery")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--mode",
    default="all",
    help="Choose a table from a dataset to update [prod|staging|all]",
)
@click.pass_context
def update_table(ctx, dataset_id, table_id, mode):
    """
    Update tables in BigQuery.
    """

    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).update(
        mode=mode,
    )

    click.echo(
        click.style(
            f"All tables `{dataset_id}*.{table_id}` were updated in BigQuery",
            fg="green",
        )
    )

@cli_table.command(
    name="update_columns", help="Update columns fields in tables_config.yaml "
)
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--columns_config_url_or_path",
    default=None,
    help="""\nFills columns in table_config.yaml automatically using a public google sheets URL or a local file. Also regenerate
        \npublish.sql and autofill type using bigquery_type.\n

    \nThe sheet must contain the columns:\n
        - name: column name\n
        - description: column description\n
        - bigquery_type: column bigquery type\n
        - measurement_unit: column mesurement unit\n
        - covered_by_dictionary: column related dictionary\n
        - directory_column: column related directory in the format <dataset_id>.<table_id>:<column_name>\n
        - temporal_coverage: column temporal coverage\n
        - has_sensitive_data: the column has sensitive data\n
        - observations: column observations\n
    \nArgs:\n
    \ncolumns_config_url_or_path (str): Path to the local architeture file or a public google sheets URL.\n
        Path only suports csv, xls, xlsx, xlsm, xlsb, odf, ods, odt formats.\n
        Google sheets URL must be in the format https://docs.google.com/spreadsheets/d/<table_key>/edit#gid=<table_gid>.\n
""",
)
@click.pass_context
def update_columns(ctx, dataset_id, table_id, columns_config_url_or_path):
    """
    Update columns fields in tables_config.yaml
    """

    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).update_columns(
        columns_config_url_or_path=columns_config_url_or_path,
    )

    click.echo(
        click.style(
            f"Columns from `{dataset_id}.{table_id}` were updated in table_config.yaml",
            fg="green",
        )
    )

@cli_table.command(name="publish", help="Publish staging table to prod")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--if_exists",
    default="raise",
    help="[raise|replace] actions if table exists",
)
@click.pass_context
def publish_table(ctx, dataset_id, table_id, if_exists):
    """
    Publish staging table to prod.
    """

    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).publish(
        if_exists=if_exists,
    )

    click.echo(
        click.style(
            f"Table `{dataset_id}.{table_id}` was published in BigQuery",
            fg="green",
        )
    )

@cli_table.command(name="delete", help="Delete BigQuery table")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option("--mode", help="Which table to delete [prod|staging]", required=True)
@click.pass_context
def delete_table(ctx, dataset_id, table_id, mode):
    """
    Delete BigQuery table.
    """

    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).delete(
        mode=mode,
    )

@cli_table.command(name="append", help="Append new data to existing table")
@click.argument("dataset_id")
@click.argument("table_id")
@click.argument("filepath", type=click.Path(exists=True))
@click.option("--partitions", help="Data partition as `value=key/value2=key2`")
@click.option(
    "--if_exists",
    default="raise",
    help="[raise|replace|pass] if file alread exists",
)
@click.option(
    "--chunk_size",
    default=None,
    help="The size of a chunk of data whenever iterating (in bytes). This must be a multiple of 256 KB per the API specification.",
)
@click.pass_context
def upload_table(
    ctx, dataset_id, table_id, filepath, partitions, if_exists, chunk_size
):

    """
    Upload data to BigQuery table.
    """
    Table(table_id=table_id, dataset_id=dataset_id, **ctx.obj).append(
        filepath=filepath,
        partitions=partitions,
        if_exists=if_exists,
        chunk_size=chunk_size,
    )

    click.echo(
        click.style(
            f"Data was added to `{dataset_id}.{table_id}`",
            fg="green",
        )
    )

@click.group(name="storage")
def cli_storage():
    """
    Commands for Google Cloud Storage.
    """

@cli_storage.command(name="init", help="Create bucket and initial folders")
@click.option("--bucket_name", default="basedosdados", help="Bucket name")
@click.option(
    "--replace",
    is_flag=True,
    help="Whether to replace current bucket files",
)
@click.option(
    "--very-sure/--not-sure",
    default=False,
    help="Are you sure that you want to replace current bucket files?",
)
@click.pass_context
def init_storage(ctx, bucket_name, replace, very_sure):
    """
    Initialize bucket and folders.
    """

    # TODO: Create config file to store bucket_name, etc...
    ctx.obj.pop("bucket_name")
    Storage(bucket_name=bucket_name, **ctx.obj).init(
        replace=replace, very_sure=very_sure
    )

    click.echo(
        click.style(
            f"Bucket `{bucket_name}` was created",
            fg="green",
        )
    )

@cli_storage.command(name="upload", help="Upload file to bucket")
@click.argument("dataset_id")
@click.argument("table_id")
@click.argument("filepath", type=click.Path(exists=True))
@click.option(
    "--mode",
    "-m",
    required=True,
    help="Where to save the file [raw|staging|header|auxiliary_files|architecture|all]",
)
@click.option("--partitions", help="Data partition as `value=key/value2=key2`")
@click.option(
    "--if_exists",
    default="raise",
    help="[raise|replace|pass] if file alread exists",
)
@click.option(
    "--chunk_size",
    default=None,
    help="The size of a chunk of data whenever iterating (in bytes). This must be a multiple of 256 KB per the API specification.",
)
@click.pass_context
def upload_storage(
    ctx, dataset_id, table_id, filepath, mode, partitions, if_exists, chunk_size
):

    '''
    Upload file to bucket.
    '''

    ctx.obj.pop("bucket_name")
    Storage(dataset_id, table_id, **ctx.obj).upload(
        filepath=filepath,
        mode=mode,
        partitions=partitions,
        if_exists=if_exists,
        chunk_size=chunk_size,
    )

    click.echo(
        click.style(
            "Data was added",
            fg="green",
        )
    )

@cli_storage.command(name="download", help="Download file from bucket")
@click.argument("dataset_id")
@click.argument("table_id")
@click.argument("savepath", type=click.Path(exists=True))
@click.option(
    "--filename",
    "-f",
    default="*",
    help="filename to download single file. If * downloads all files from bucket folder",
)
@click.option(
    "--mode",
    "-m",
    default="raw",
    help="Where to download data from [raw|staging|header|auxiliary_files|architecture]",
)
@click.option("--partitions", help="Data partition as `value=key/value2=key2`")
@click.option(
    "--if_not_exists",
    default="raise",
    help="[raise|pass] if file file not found at bucket folder",
)
@click.pass_context
def download_storage(
    ctx, dataset_id, table_id, filename, savepath, partitions, mode, if_not_exists
):
    """
    Download file from bucket.
    """
    Storage(dataset_id, table_id, **ctx.obj).download(
        filename, savepath, partitions, mode, if_not_exists
    )

    click.echo(
        click.style(
            f"Data was downloaded to `{savepath}`",
            fg="green",
        )
    )

@cli_storage.command(name="delete_table", help="Delete table from bucket")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--mode",
    "-m",
    required=True,
    default="staging",
    help="Where to delete the file from [raw|staging|header|auxiliary_files|architecture]",
)
@click.option(
    "--bucket_name",
    default=None,
    help="Bucket from which to delete data, you can change it to delete from a bucket other than yours",
)
@click.option("--not_found_ok", default=False, help="what to do if table not found")
@click.pass_context
def storage_delete_table(ctx, dataset_id, table_id, mode, not_found_ok, bucket_name):
    """
    Delete table from bucket.
    """
    Storage(dataset_id, table_id, **ctx.obj).delete_table(
        mode=mode, not_found_ok=not_found_ok, bucket_name=bucket_name
    )
    click.echo(
        click.style(
            f"Data was deleted from bucket `{bucket_name}`",
            fg="green",
        )
    )

@cli_storage.command(name="copy_table", help="Copy table to your bucket")
@click.argument("dataset_id")
@click.argument("table_id")
@click.option("--source_bucket_name", required=True, default="basedosdados")
@click.option(
    "--dst_bucket_name",
    default=None,
    help="Bucket where data will be copied to, defaults to your bucket",
)
@click.option(
    "--mode",
    "-m",
    default="staging",
    help="which bucket folder to get the table [raw|staging|header|auxiliary_files|architecture]",
)
@click.pass_context
def storage_copy_table(
    ctx, dataset_id, table_id, source_bucket_name, dst_bucket_name, mode
):
    """
    Copy table from another bucket.
    """
    Storage(dataset_id, table_id, **ctx.obj).copy_table(
        source_bucket_name=source_bucket_name,
        destination_bucket_name=dst_bucket_name,
        mode=mode,
    )

@click.group(name="list")
def cli_list():
    """
    CLI list commands.
    """

@cli_list.command(name="datasets", help="List datasets available at given project_id")
@click.option(
    "--project_id",
    default="basedosdados",
    help="The project which will be queried. You should have list/read permissions",
)
@click.option(
    "--filter_by",
    default=None,
    help="Filter your search, must be a string",
)
@click.option(
    "--with_description",
    default=False,
    help="[bool]Fetch short description for each dataset",
)
@click.pass_context
def cli_list_datasets(ctx, project_id, filter_by, with_description):
    """
    List datasets available at given project_id
    """
    bd.list_datasets(
        query_project_id=project_id,
        filter_by=filter_by,
        with_description=with_description,
    )

@cli_list.command(name="dataset_tables", help="List tables available at given dataset")
@click.argument("dataset_id")
@click.option(
    "--project_id",
    default="basedosdados",
    help="The project which will be queried. You should have list/read permissions",
)
@click.option(
    "--filter_by",
    default=None,
    help="Filter your search, must be a string",
)
@click.option(
    "--with_description",
    default=False,
    help="[bool]Fetch short description for each table",
)
@click.pass_context
def cli_list_dataset_tables(ctx, dataset_id, project_id, filter_by, with_description):
    """
    List tables available at given dataset.
    """
    bd.list_dataset_tables(
        dataset_id=dataset_id,
        query_project_id=project_id,
        filter_by=filter_by,
        with_description=with_description,
    )

@click.group(name="get")
def cli_get():
    """
    Get commands.
    """

@cli_get.command(
    name="dataset_description", help="Get the full description for given dataset"
)
@click.argument("dataset_id")
@click.option(
    "--project_id",
    default="basedosdados",
    help="The project which will be queried. You should have list/read permissions",
)
@click.pass_context
def cli_get_dataset_description(ctx, dataset_id, project_id):
    """
    Get the full description for given dataset
    """
    bd.get_dataset_description(
        dataset_id=dataset_id,
        query_project_id=project_id,
    )

@cli_get.command(
    name="table_description", help="Get the full description for given table"
)
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--project_id",
    default="basedosdados",
    help="The project which will be queried. You should have list/read permissions",
)
@click.pass_context
def cli_get_table_description(ctx, dataset_id, table_id, project_id):
    """
    Get the full description for given table
    """
    bd.get_table_description(
        dataset_id=dataset_id,
        table_id=table_id,
        query_project_id=project_id,
    )

@cli_get.command(
    name="table_columns",
    help="Get fields names,types and description for columns at given table",
)
@click.argument("dataset_id")
@click.argument("table_id")
@click.option(
    "--project_id",
    default="basedosdados",
    help="The project which will be queried. You should have list/read permissions",
)
@click.pass_context
def cli_get_table_columns(
    ctx,
    dataset_id,
    table_id,
    project_id,
):
    """
    Get fields names,types and description for columns at given table
    """
    bd.get_table_columns(
        dataset_id=dataset_id,
        table_id=table_id,
        query_project_id=project_id,
    )

@click.group(name="metadata")
def cli_metadata():
    """
    CLI metadata commands.
    """

@cli_metadata.command(name="create", help="Creates new metadata config file")
@click.argument("dataset_id")
@click.argument("table_id", required=False)
@click.option(
    "--if_exists",
    default="raise",
    help="[raise|replace|pass] if metadata config file alread exists",
)
@click.option(
    "--columns",
    default=[],
    help="Data columns. Example: --columns=col1,col2",
    callback=lambda _, __, x: x.split(",") if x else [],
)
@click.option(
    "--partition_columns",
    default=[],
    help="Columns that partition the data. Example: --partition_columns=col1,col2",
    callback=lambda _, __, x: x.split(",") if x else [],
)
@click.option(
    "--force_columns",
    default=False,
    help="Overwrite columns with local columns.",
)
@click.option(
    "--table_only",
    default=True,
    help=(
        "Force the creation of `table_config.yaml` file only if `dataset_conf"
        "ig.yaml` doesn't exist."
    ),
)
@click.pass_context
def cli_create_metadata(
    ctx,
    dataset_id,
    table_id,
    if_exists,
    columns,
    partition_columns,
    force_columns,
    table_only,
):
    """
    Creates new metadata config file.
    """

    m = Metadata(dataset_id, table_id, **ctx.obj).create(
        if_exists=if_exists,
        columns=columns,
        partition_columns=partition_columns,
        force_columns=force_columns,
        table_only=table_only,
    )

    click.echo(
        click.style(
            f"Metadata file was created at `{m.filepath}`",
            fg="green",
        )
    )

@cli_metadata.command(
    name="is_updated", help="Check if user's local metadata is updated"
)
@click.argument("dataset_id")
@click.argument("table_id", required=False)
@click.pass_context
def cli_is_updated_metadata(ctx, dataset_id, table_id):
    """
    Check if user's local metadata is updated.
    """
    m = Metadata(dataset_id, table_id, **ctx.obj)

    if m.is_updated():
        msg, color = "Local metadata is updated.", "green"
    else:
        msg = (
            "Local metadata is out of date. Please run `basedosdados metadata"
            " create` with the flag `if_exists=replace` to get the updated da"
            "ta."
        )
        color = "red"

    click.echo(click.style(msg, fg=color))

@cli_metadata.command(name="validate", help="Validate user's local metadata")
@click.argument("dataset_id")
@click.argument("table_id", required=False)
@click.pass_context
def cli_validate_metadata(ctx, dataset_id, table_id):
    """
    Validate user's local metadata.
    """
    m = Metadata(dataset_id, table_id, **ctx.obj)

    try:
        m.validate()
        msg, color = "Local metadata is valid.", "green"
    except BaseDosDadosException as e:
        msg = (
            f"Local metadata is invalid. Please check the traceback below for"
            f" more information on how to fix it:\n\n{repr(e)}"
        )
        color = "red"

    click.echo(click.style(msg, fg=color))

@cli_metadata.command(name="publish", help="Publish user's local metadata")
@click.argument("dataset_id")
@click.argument("table_id", required=False)
@click.option(
    "--all",
    default=False,
    help=(
        "Force the publishment of metadata specified in both `dataset_config."
        "yaml` and `table_config.yaml` at once."
    ),
)
@click.option(
    "--if_exists",
    default="raise",
    help=("Define what to do in case metadata already exists in CKAN."),
)
@click.option(
    "--update_locally",
    default=False,
    help=("Update local metadata with the new CKAN metadata on publish."),
)
@click.pass_context
def cli_publish_metadata(
    ctx,
    dataset_id,
    table_id,
    all,
    if_exists,
    update_locally,
):
    """
    Publish user's local metadata.
    """
    m = Metadata(dataset_id, table_id, **ctx.obj)

    try:
        m.publish(all=all, if_exists=if_exists, update_locally=update_locally)
        msg, color = "Local metadata has been published.", "green"
    except (CKANAPIError, BaseDosDadosException, AssertionError) as e:
        msg = (
            f"Local metadata couldn't be published due to an error. Pleas"
            f"e check the traceback below for more information on how to "
            f"fix it:\n\n{repr(e)}"
        )
        color = "red"

    click.echo(click.style(msg, fg=color))

@click.group(name="config")
def cli_config():
    """
    CLI config commands.
    """

@cli_config.command(name="init", help="Initialize configuration")
@click.option(
    "--overwrite",
    default=False,
    help="Wheteher to overwrite current config",
)
@click.pass_context
def init(ctx, overwrite):
    """
    Initialize configuration.
    """

    Base(overwrite_cli_config=overwrite, **ctx.obj)

@cli_config.command(name="refresh_template", help="Overwrite current templates")
@click.pass_context
def init_refresh_templates(ctx):
    """
    Initialize configuration.
    """

    Base(**ctx.obj)._refresh_templates()

# Allow anomalous backslash in string: '\ ' (it's used in gcloud sdk)
# pylint: disable=W1401
@click.command(
    name="download",
    help=(
        "Downloads data do SAVEPATH. SAVEPATH must point to a .csv file.\n\n"
        "Example: \n\n"
        'basedosdados download data.csv  --query="select * from basedosdados.br_ibge_pib.municipio limit 10" \ --billing_project_id=basedosdados-dev'
    ),
)
@click.argument(
    "savepath",
    type=click.Path(exists=False),
)
@click.option(
    "--query",
    default=None,
    help="A SQL Standard query to download data from BigQuery",
)
@click.option(
    "--dataset_id",
    default=None,
    help="Dataset_id, enter with table_id to download table",
)
@click.option(
    "--table_id",
    default=None,
    help="Table_id, enter with dataset_id to download table ",
)
@click.option(
    "--query_project_id",
    default=None,
    help="Which project the table lives. You can change this you want to query different projects.",
)
@click.option(
    "--billing_project_id",
    default=None,
    help="Project that will be billed. Find your Project ID here https://console.cloud.google.com/projectselector2/home/dashboard",
)
@click.option(
    "--limit",
    default=None,
    help="Number of rows returned",
)
@click.pass_context
def cli_download(
    ctx,
    savepath,
    query,
    dataset_id,
    table_id,
    query_project_id,
    billing_project_id,
    limit,
):
    """
    Download data from BigQuery.
    """

    bd.download(
        savepath=savepath,
        dataset_id=dataset_id,
        table_id=table_id,
        query=query,
        query_project_id=query_project_id,
        billing_project_id=billing_project_id,
        limit=limit,
    )

    click.echo(
        click.style(
            f"Table was downloaded to `{savepath}`",
            fg="green",
        )
    )

@click.command(
    name="reauth",
    help="Reauthorize credentials.",
)
def cli_reauth():
    """
    Reauthorize credentials.
    """

    bd.reauth()

cli.add_command(cli_dataset)
cli.add_command(cli_table)
cli.add_command(cli_storage)
cli.add_command(cli_config)
cli.add_command(cli_download)
cli.add_command(cli_reauth)
cli.add_command(cli_list)
cli.add_command(cli_get)
cli.add_command(cli_metadata)

if __name__ == "__main__":

    cli()


from configs.model_configs.skorch_config import skorch_config, skorch_config_default

config_random = {
    "model__module__activation": {
        "value": "reglu"
    },
    "model__module__token_bias": {
        "value": True
    },
    "model__module__prenormalization": {
        "value": True
    },
    "model__module__kv_compression": {
        "values": [True, False]
    },
    "model__module__kv_compression_sharing": {
        "values": ["headwise", 'key-value']
    },
    "model__module__initialization": {
        "value": "kaiming"
    },
    "model__module__n_layers": {
        "distribution": "q_uniform",
        "min": 1,
        "max": 6
    },
    "model__module__n_heads": {
        "value": 8,
    },
    "model__module__d_ffn_factor": {
        "distribution": "uniform",
        "min": 2. / 3,
        "max": 8. / 3
    },
    "model__module__ffn_dropout": {
        "distribution": "uniform",
        "min": 0,
        "max": 0.5
    },
    "model__module__attention_dropout": {
        "distribution": "uniform",
        "min": 0.0,
        "max": 0.5
    },
    "model__module__residual_dropout": {
        "distribution": "uniform",
        "min": 0.0,
        "max": 0.5
    },
    "model__lr": {
        "distribution": "log_uniform_values",
        "min": 1e-5,
        "max": 1e-3
    },
    "model__optimizer__weight_decay": {
        "distribution": "log_uniform_values",
        "min": 1e-6,
        "max": 1e-3
    },
    "d_token": { #modified in run_experiment.py
        "distribution": "q_uniform",
        "min": 64,
        "max": 512
    },
    "model__lr_scheduler": {
        "values": [True, False]
    },
    "use_gpu": {
        "value": True
    }
}

config_default = {
    "model__lr_scheduler": {
        "value": False
    },
    "model__module__activation": {
        "value": "reglu"
    },
    "model__module__token_bias": {
        "value": True
    },
    "model__module__prenormalization": {
        "value": True
    },
    "model__module__kv_compression": {
        "value": True
    },
    "model__module__kv_compression_sharing": {
        "value": "headwise"
    },
    "model__module__initialization": {
        "value": "kaiming"
    },
    "model__module__n_layers": {
        "value": 3
    },
    "model__module__n_heads": {
        "value": 8,
    },
    "model__module__d_ffn_factor": {
        "value": 4. / 3
    },
    "model__module__ffn_dropout": {
        "value": 0.1
    },
    "model__module__attention_dropout": {
        "value": 0.2
    },
    "model__module__residual_dropout": {
        "value": 0.0
    },
    "model__lr": {
        "value": 1e-4,
    },
    "model__optimizer__weight_decay": {
        "value": 1e-5,
    },
    "d_token": {
        "value": 192
    },
    "use_gpu": {
        "value": True
    }
}

config_regression = dict(config_random,
                                        **skorch_config,
                                        **{
                                            "model_name": {
                                                "value": "ft_transformer_regressor"
                                            },
                                        })

config_regression_default = dict(config_default,
                                        **skorch_config_default,
                                        **{
                                            "model_name": {
                                                "value": "ft_transformer_regressor"
                                            },
                                        })

config_classif = dict(config_random,
                                     **skorch_config,
                                     **{
                                         "model_name": {
                                             "value": "ft_transformer"
                                         },
                                     })

config_classif_default = dict(config_default,
                                     **skorch_config_default,
                                     **{
                                         "model_name": {
                                             "value": "ft_transformer"
                                         },
                                     })


"""Tests for Qiskti to Braket adapter."""
from unittest import TestCase

from braket.circuits import Circuit, FreeParameter, observables
import numpy as np
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

from qiskit_braket_provider.providers.adapter import (
    convert_qiskit_to_braket_circuit,
    qiskit_gate_name_to_braket_gate_mapping,
    qiskit_gate_names_to_braket_gates,
    qiskit_to_braket_gate_names_mapping,
    wrap_circuits_in_verbatim_box,
)

class TestAdapter(TestCase):
    """Tests adapter."""

    def test_mappers(self):
        """Tests mappers."""
        self.assertEqual(
            list(sorted(qiskit_to_braket_gate_names_mapping.keys())),
            list(sorted(qiskit_gate_names_to_braket_gates.keys())),
        )

        self.assertEqual(
            list(sorted(qiskit_to_braket_gate_names_mapping.values())),
            list(sorted(qiskit_gate_name_to_braket_gate_mapping.keys())),
        )

    def test_convert_parametric_qiskit_to_braket_circuit(self):
        """Tests convert_qiskit_to_braket_circuit works with parametric circuits."""

        theta = Parameter("θ")
        phi = Parameter("φ")
        lam = Parameter("λ")
        qiskit_circuit = QuantumCircuit(1, 1)
        qiskit_circuit.rz(theta, 0)
        qiskit_circuit.u(theta, phi, lam, 0)
        qiskit_circuit.u(theta, phi, np.pi, 0)
        braket_circuit = convert_qiskit_to_braket_circuit(qiskit_circuit)

        braket_circuit_ans = (
            Circuit()  # pylint: disable=no-member
            .rz(0, FreeParameter("θ"))
            .rz(0, FreeParameter("λ"))
            .rx(0, np.pi / 2)
            .rz(0, FreeParameter("θ"))
            .rx(0, -np.pi / 2)
            .rz(0, FreeParameter("φ"))
            .rz(0, np.pi)
            .rx(0, np.pi / 2)
            .rz(0, FreeParameter("θ"))
            .rx(0, -np.pi / 2)
            .rz(0, FreeParameter("φ"))
        )

        self.assertEqual(braket_circuit, braket_circuit_ans)

    def test_sample_result_type(self):
        """Tests sample result type with observables Z"""

        qiskit_circuit = QuantumCircuit(2, 2)
        qiskit_circuit.h(0)
        qiskit_circuit.cnot(0, 1)
        qiskit_circuit.measure(0, 0)
        braket_circuit = convert_qiskit_to_braket_circuit(qiskit_circuit)

        circuits = (
            Circuit()  # pylint: disable=no-member
            .h(0)
            .cnot(0, 1)
            .sample(observable=observables.Z(), target=0)
        )

        self.assertEqual(braket_circuit, circuits)

class TestVerbatimBoxWrapper(TestCase):
    """Test wrapping in Verbatim box."""

    def test_wrapped_circuits_have_one_instruction_equivalent_to_original_one(self):
        """Test circuits wrapped in verbatim box have correct instructions."""
        circuits = [
            Circuit().rz(1, 0.1).cz(0, 1).rx(0, 0.1),  # pylint: disable=no-member
            Circuit().cz(0, 1).cz(1, 2),  # pylint: disable=no-member
        ]

        wrapped_circuits = wrap_circuits_in_verbatim_box(circuits)

        # Verify circuits comprise of verbatim box
        self.assertTrue(
            all(
                wrapped.instructions[0].operator.name == "StartVerbatimBox"
                for wrapped in wrapped_circuits
            )
        )

        self.assertTrue(
            all(
                wrapped.instructions[-1].operator.name == "EndVerbatimBox"
                for wrapped in wrapped_circuits
            )
        )

        # verify that the contents of the verbatim box are identical
        # to the original circuit
        self.assertTrue(
            all(
                wrapped.instructions[1:-1] == original.instructions
                for wrapped, original in zip(wrapped_circuits, circuits)
            )
        )


i = 0
calculo = 0
quant_impares = 0
quant_pares = 0
while i < 10:
    n = int(input("digite um numero inteiro "))
    impar = n%2
    calculo += n
    if impar == 1:
        quant_impares += 1
    else:
        quant_pares += 1
    i += 1

print("O calculo de todos os numeros é {}".format(calculo))
print("Existem {} numeros impar".format(quant_impares))
print("Existem {} numeros pares".format(quant_pares))


# -*- coding: utf-8 -*-
# XXX I mean, is it right to import
# unicode_literals everywhere else but here?
# Even this comment looks ugly.
import os
import json
from contextlib import closing
from operator import itemgetter
from functools import partial
from itertools import imap, groupby, ifilter

import arrow
import click
import jinja2 as jinja2
import numpy
import pymongo
import rpy2.robjects
import shutil

from retorica.utils import slugify_to_ascii, read_rtf_text

def fix_speech_start_time(speech):
    speech[u'horaInicioDiscurso'] = speech[u'horaInicioDiscurso']['$date']
    return speech

def strip_suffix(value, suffix):
    if value.endswith(suffix):
        value = value[:-len(suffix)]
    return value

def normalize_name(name):
    name = slugify_to_ascii(name)
    name = name.encode('utf-8')
    name = strip_suffix(name, u'-presidente')
    return name

def normalize_author_name(speech):
    v = speech.get(u'nomeOrador')
    if v is not None:
        v = normalize_name(v)
    speech[u'nomeOradorNormalizado'] = v
    return speech

def read_document(path):
    with open(path, 'rb') as fp:
        return read_rtf_text(fp, errors='replace')

def load_speech_content(item, path):
    files = item[u'files']
    files = imap(itemgetter(u'path'), files)
    files = imap(partial(os.path.join, path), files)
    files = imap(read_document, files)
    item[u'conteudo'] = u'\n\n\n\n'.join(files)
    del item[u'files']
    return item

class DateParamType(click.ParamType):
    name = 'date'

    def convert(self, value, param, ctx):
        try:
            if value == u'today':
                value = arrow.get().datetime
            else:
                return arrow.get(value).datetime
        except Exception as exc:
            self.fail(u'{0} cannot be parsed as a date: {1}'.format(value, exc.message), param, ctx)

def convert_dates_to_iso(item):
    item[u'horaInicioDiscurso'] = item[u'horaInicioDiscurso'].isoformat()
    return item

def make_database_connection(host, port, database):
    client = pymongo.MongoClient(host, port)
    return getattr(client, database)

def capfirst(s):
    if not s:
        return s
    return s[0].capitalize() + s[1:]

def rpy2_source_retorica():
    here = os.path.abspath(os.path.join(os.path.dirname(__file__)))
    retorica = os.path.join(here, 'retorica.R')
    rpy2.robjects.r('setwd("{0}")'.format(os.getcwd()))
    rpy2.robjects.r('source("{0}", chdir=TRUE)'.format(retorica))

def escape_double_quotes(s):
    return s.replace('"', '\\"')

def prepare_arg(arg):
    if isinstance(arg, basestring):
        arg = u'"{0}"'.format(escape_double_quotes(arg))
    else:
        arg = repr(arg)
    return arg

def call_r_method_with_string_arguments(method, *args):
    args = map(prepare_arg, args)
    args = ', '.join(args)
    return rpy2.robjects.r('{0}({1})'.format(method, args))

@click.group()
def main():
    pass

def as_name_ide_tuple(item):
    nome = item.get(u'nome')
    if nome is not None:
        nome = normalize_name(nome)
    return nome, item.get(u'ide_cadastro')

def update_with(d1, d2):
    d3 = {}
    d3.update(d1)
    d3.update(d2)
    return d3

@main.command('import')
@click.option('-H', '--host', type=click.STRING, default=u'localhost')
@click.option('-P', '--port', type=click.INT, default=27017)
@click.option('-d', '--database', type=click.STRING, default=u'kingsnake')
@click.option('-s', '--start', type=DateParamType())
@click.option('-e', '--end', type=DateParamType())
# TODO should be an argument!!!! maybe we should just point to the kingsnake
# project and let people setup their connections there? that would be fking awesome!
@click.option('-i', '--ignore', type=click.Choice([u'AB', u'HO', u'PE', u'BC', u'OD', u'CG']),
              multiple=True, help=(u'Ignore speeches based on their session type (fase da sesão). '
                                   u'You can ignore multiple types by repeating the flag. '
                                   u'Eg.: -i AB -i OD.'))
@click.option('-f', '--files', type=click.Path(exists=True, file_okay=False),
              required=True, help=u'Where the files are stored.')
@click.argument('output', type=click.File(mode='w'))
def import_corpus(host, port, database, start, end, ignore, files, output):
    database = make_database_connection(host, port, database)

    lk = []
    if start:
        lk.append({u'horaInicioDiscurso': {u'$gt': start}})
    if end:
        lk.append({u'horaInicioDiscurso': {u'$lt': end}})
    if ignore:
        lk.append({u'faseSessao': {u'$not': {u'$in': ignore}}})

    if len(lk) == 1:
        lookup = lk[0]
    elif len(lk) == 0:
        lookup = {}
    else:
        lookup = {u'$and': lk}

    speeches = database.discursos.find(lookup)

    if not speeches.count():
        click.echo(u'No speech found')
        return 0

    # Ignorar discursos que não foram feitos por parlamentares
    speeches = [s for s in speeches if s[u'partidoOrador']]

    # Save this for later :D
    number_of_speeches = len(speeches)

    speeches = imap(normalize_author_name, speeches)

    deputados = database.deputados.find({u'nome_normalizado': None})
    number_of_deptuados = deputados.count()

    # remover _id
    deputados = imap(lambda i: i.pop(u'_id', None) and None or i, deputados)

    deputados_por_nome = dict()
    with click.progressbar(deputados, length=number_of_deptuados,
                           show_pos=True, label=u'Normalizing congressman names...') as deputados:
        for d in deputados:
            d[u'nome_normalizado'] = normalize_name(d[u'nome'])
            if d[u'nome_normalizado'] in deputados_por_nome:
                print(u'WARNING: Deputado repetido: {0} == {1}'.format(d, deputados_por_nome[d[u'nome_normalizado']]))
            deputados_por_nome[d[u'nome_normalizado']] = d

    speech_name_getter = itemgetter(u'nomeOradorNormalizado')

    # Filtrar os discursos, mantendo somente aqueles feitos por deputados presentes em `deputados_por_nome`
    speeches = ifilter(lambda s: speech_name_getter(s) in deputados_por_nome, speeches)

    with click.progressbar(speeches, length=number_of_speeches,
                           show_pos=True, label=u'Normalizing author names...') as speeches:

        speeches = sorted(speeches, key=speech_name_getter)
        valid_speeches = []

        for nome_orador, chunk in groupby(speeches, key=speech_name_getter):
            deputado = deputados_por_nome.get(nome_orador)
            if deputado is not None:
                chunk = list(chunk)
                if len(chunk) >= 2:
                    chunk = map(partial(update_with, deputado), chunk)
                    valid_speeches.extend(chunk)

        speeches = valid_speeches
        number_of_speeches = len(speeches)

    speeches = imap(convert_dates_to_iso, speeches)
    speeches = imap(partial(load_speech_content, path=files), speeches)

    # Strip object ids, which are not serializable by default
    speeches = imap(lambda i: i.pop(u'_id', None) and None or i, speeches)

    with click.progressbar(speeches, length=number_of_speeches,
                           show_pos=True, label=u'Extracting speech text...') as speeches:
        speeches = list(speeches)

        with closing(output) as fp:
            json.dump(speeches, fp, indent=2)

@main.command('process')
@click.option('-t', '--topics', type=click.INT, default=70, help=u'How many topics will be generated.')
@click.argument('corpus', type=click.Path(exists=True, dir_okay=False))
@click.argument('target', type=click.Path(dir_okay=False))
def process_corpus(corpus, target, topics):
    args = map(os.path.abspath, [corpus, target])
    args.append(topics)
    rpy2_source_retorica()
    call_r_method_with_string_arguments('processCorpus', *args)

def basename(path):
    return os.path.splitext(path)[0]

def open_p(path, *args, **kwargs):
    dn = os.path.dirname(path)
    if not os.path.exists(dn):
        os.makedirs(dn)
    return open(path, *args, **kwargs)

def as_rds_topics_tuple(path):
    base = basename(path)
    title = os.path.basename(base)
    return (title, (base + u'.json', path, base + u'.topics.txt'))

def _convert_str_vector(obj):
    arr = numpy.asarray(obj, dtype=object)
    mask = arr == rpy2.robjects.NA_Character
    if mask.any():
        arr[mask] = numpy.nan
    return arr

@main.command('build')
@click.option('-o', '--output', type=click.Path(exists=False, file_okay=False), default='./build')
@click.argument('rds-file', type=click.Path(exists=True, dir_okay=False), nargs=-1)
def compile_results(rds_file, output):
    rpy2_source_retorica()

    items = []
    inputs = imap(as_rds_topics_tuple, rds_file)
    outpath = lambda *x: os.path.abspath(os.path.join(output, *x))
    for title, args in inputs:
        url = u'{0}.html'.format(title)
        rel = u'data/{0}.json'.format(title)
        dest = outpath(rel)
        items.append(dict(title=title, url=url, data_url=rel, dest=dest, args=args))

    # First item should always be index.html
    if not items:
        raise RuntimeError(u'Especifique pelo menos um arquivo de entrada')
    else:
        items[0]['url'] = u'index.html'

    src = os.path.abspath(os.path.join(os.path.dirname(__file__), 'static'))

    shutil.copytree(src, output)

    env = jinja2.Environment(loader=jinja2.PackageLoader('retorica', 'templates'))

    for i in items:
        ctx = {}
        ctx.update(i)
        ctx['items'] = filter(lambda x: x is not i, items)

        rout = call_r_method_with_string_arguments('tagResults', *i['args'])
        rout = _convert_str_vector(rout)[0].encode('utf-8')
        with open_p(outpath(i['dest']), 'w') as out:
            out.write(rout)

        with open_p(outpath(i['url']), 'w') as out:
            out.write(env.get_template('page.html').render(**ctx).encode('utf-8'))


from typing import List, Tuple
from stock_trading_env import StockTradingEnv
from config import (
    FILENAME,
    STOCK_DATA_SAVE_DIR,
    TICKERS,
    TECHNICAL_INDICATORS,
    SEED,
    TRAIN_TEST_SPLIT_PERCENT,
    TRAINED_MODEL_DIR,
    TENSORBOARD_LOG_DIR,
)
from stable_baselines3 import PPO
from stable_baselines3.common.monitor import Monitor
from stable_baselines3.common.callbacks import BaseCallback
import numpy as np
from random import seed as random_seed
from torch import manual_seed
from pathlib import Path
from logger_config import benchmark_logger as log
import pandas as pd
from itertools import permutations
import re
from concurrent.futures import ProcessPoolExecutor
from time import perf_counter
from tqdm import tqdm

# TRY NOT TO MODIFY: seeding
random_seed(SEED)
np.random.seed(SEED)
manual_seed(SEED)
TECHNICAL_INDICATORS = []

class TensorboardCallback(BaseCallback):
    def __init__(self, save_freq: int, model_prefix: str, eval_env: Monitor):
        self.save_freq = save_freq
        self.model_prefix = model_prefix
        self.eval_env = eval_env
        super().__init__()

    def _on_step(self) -> bool:
        self.logger.record(
            key="train/holdings",
            value=self.locals["infos"][0]["holdings"],
        )
        self.logger.record(key="train/reward", value=self.locals["rewards"][0])
        self.logger.record(key="train/n_calls", value=self.n_calls)
        shares = self.locals["infos"][0]["shares"]
        for k, v in shares.items():
            self.logger.record(key=f"train/shares/{k}", value=v)

        if (self.n_calls > 0) and (self.n_calls % self.save_freq) == 0:
            obs, _ = self.eval_env.reset()
            done = False
            while not done:
                action, _ = self.model.predict(obs)
                obs, reward, done, truncated, info = self.eval_env.step(action)

            model_path = Path(TRAINED_MODEL_DIR) / self.model_prefix
            available_model_files = list(model_path.rglob("*.zip"))
            available_model_holdings = [
                int(f.stem.split("-")[-1]) for f in available_model_files
            ]
            available_model_holdings.sort()
            trade_holdings = int(info["holdings"])
            self.logger.record(key="trade/holdings", value=trade_holdings)

            if not available_model_holdings:
                model_filename = model_path / f"{trade_holdings}.zip"
                self.model.save(model_filename)
                log.info(f"Saving model checkpoint to {model_filename}")

            # elif len(available_model_holdings) < 5:
            #     model_filename = model_path / f"{trade_holdings}.zip"
            #     self.model.save(model_filename)
            #     log.info(f"Saving model checkpoint to {model_filename}")

            else:
                if trade_holdings > available_model_holdings[0]:
                    file_to_remove = model_path / f"{available_model_holdings[0]}.zip"
                    file_to_remove.unlink()
                    model_filename = model_path / f"{trade_holdings}.zip"
                    self.model.save(model_filename)
                    log.info(
                        f"Removed {file_to_remove} and Added {model_filename} file."
                    )
        return True

def load_df() -> pd.DataFrame:
    parquet_filename = Path(STOCK_DATA_SAVE_DIR) / FILENAME
    df = pd.read_parquet(parquet_filename, engine="fastparquet")[
        ["Date", "Ticker", "Close"]
    ]
    log.info(f"Data loaded successfully into Dataframe\n{df.head().to_markdown()}")
    return df

def add_features(df: pd.DataFrame, experiment: dict) -> pd.DataFrame:
    if not experiment["technical_indicators"]:
        log.info(f"No features to add to Dataframe\n{df.head().to_markdown()}")
        return df
    features_df = df.copy()
    for ticker in TICKERS:
        features_df[features_df["Ticker"] == ticker]["Close"].values
        filter = features_df["Ticker"] == ticker
        for technical_indicator in experiment["technical_indicators"]:
            period = re.search(r"\d+", technical_indicator).group()
            features_df.loc[filter, technical_indicator] = features_df[filter][
                "Close"
            ].shift(-int(period))

    log.info(f"Addded features to Dataframe\n{features_df.head().to_markdown()}")
    return features_df

def clean_df(df: pd.DataFrame) -> pd.DataFrame:
    cleaned_df = df.copy().dropna(axis=0).reset_index(drop=True)
    cleaned_df.index = cleaned_df["Date"].factorize()[0]
    cleaned_df["Buy/Sold/Hold"] = 0.0
    cleaned_df = cleaned_df.loc[
        cleaned_df.groupby(level=0).count()["Date"] == len(TICKERS)
    ]
    log.info(f"Cleaned Dataframe \n{cleaned_df.head().to_markdown()}")
    return cleaned_df

def split_train_test(
    df: pd.DataFrame, technical_indicators: List[str]
) -> Tuple[np.array, np.array]:
    train_size = df.index.values[-1] - int(
        df.index.values[-1] * TRAIN_TEST_SPLIT_PERCENT
    )
    train_df = df.loc[:train_size]
    trade_df = df.loc[train_size + 1 :]

    train_arrays = np.array(
        train_df[["Close"] + technical_indicators + ["Buy/Sold/Hold"]]
        .groupby(train_df.index)
        .apply(np.array)
        .values.tolist(),
        dtype=np.float32,
    )
    trade_arrays = np.array(
        trade_df[["Close"] + technical_indicators + ["Buy/Sold/Hold"]]
        .groupby(trade_df.index)
        .apply(np.array)
        .values.tolist(),
        dtype=np.float32,
    )
    log.info(f"Technical Indicators used {technical_indicators}")
    log.info(
        f"Successfully split the DataFrame into {len(train_arrays)} ({(1-TRAIN_TEST_SPLIT_PERCENT)*100}%) training data and {len(trade_arrays)} ({TRAIN_TEST_SPLIT_PERCENT*100}%) trading data."
    )
    return train_arrays, trade_arrays

def generate_past_hours_permutation(start: int, end: int) -> List[dict]:
    items = []
    for item in permutations(range(start, end), 2):
        if item[0] < item[1]:
            features = [f"PAST_{abs(i)}_HOUR" for i in range(item[0], item[1] + 1)]
            items.append(
                {
                    "id": f"close-price-past-hours-({item[0]})-end-({item[1]})",
                    "value": item,
                    "technical_indicators": features,
                }
            )
    return items

def train(df: pd.DataFrame, experiment: dict, model_name: str):
    start_time = perf_counter()
    full_df = df.copy()
    full_df = add_features(full_df, experiment)
    full_df = clean_df(full_df)
    train_arrays, trade_arrays = split_train_test(
        full_df, technical_indicators=experiment["technical_indicators"]
    )
    train_env = Monitor(
        StockTradingEnv(train_arrays, TICKERS, experiment["technical_indicators"])
    )
    trade_env = Monitor(
        StockTradingEnv(trade_arrays, TICKERS, experiment["technical_indicators"])
    )
    identifier = "-".join(experiment["technical_indicators"])
    identifier = "close-price" if not identifier else identifier
    MODEL_PREFIX = f"{model_name}/{identifier}"
    TOTAL_TIMESTAMP = 50_000
    tensorboard_log = Path(f"{TENSORBOARD_LOG_DIR}/{model_name}")
    model = PPO("MlpPolicy", train_env, verbose=0, tensorboard_log=tensorboard_log)
    model.learn(
        total_timesteps=TOTAL_TIMESTAMP,
        callback=TensorboardCallback(
            save_freq=4096, model_prefix=MODEL_PREFIX, eval_env=trade_env
        ),
        tb_log_name=f"ppo-{TOTAL_TIMESTAMP}-{identifier}",
    )
    obs, _ = trade_env.reset()
    for i in range(len(trade_arrays)):
        action, _ = model.predict(obs)
        obs, reward, done, truncated, info = trade_env.step(action)
    print(info)
    log.info(f"Training took {perf_counter() - start_time: .2f} seconds.")

def benchmark():
    Path(TRAINED_MODEL_DIR).mkdir(parents=True, exist_ok=True)
    model_name = "ppo"
    EXPERIMENTS = [{"id": "close-price", "value": "Close", "technical_indicators": []}]
    past_hours_experiments = generate_past_hours_permutation(start=-6, end=0)
    EXPERIMENTS.extend(past_hours_experiments)
    df = load_df()
    with ProcessPoolExecutor(max_workers=3) as e:
        tqdm([e.submit(train, df, exp, model_name) for exp in EXPERIMENTS])

if __name__ == "__main__":
    # train()
    benchmark()


from tkinter import *
import tkinter.messagebox
from tkinter import ttk
from tkinter import font
import sqlite3
from patient_form import Patient
from room_form import Room
from employee_form import Employee
from appointment_form import Appointment
from billing_form import Billing

conn=sqlite3.connect("HospitalDB.db")

print("DATABASE CONNECTION SUCCESSFUL")

#Class For Menu
class Menu:
    def __init__(self,master):
        self.master = master
        self.master.title("HOSPITAL MANAGEMENT SYSTEM")
        self.master.geometry("800x600+0+0")
        self.master.config(bg="cadet blue")
        self.frame = Frame(self.master,bg="cadet blue")
        self.frame.pack()

        self.lblTitle = Label(self.frame,text = "MAIN MENU", font="Helvetica 20 bold",bg="cadet blue")
        self.lblTitle.grid(row =0 ,column = 0,columnspan=2,pady=50)

        self.LoginFrame = Frame(self.frame,width=400,height=80,relief="ridge",bg="cadet blue",bd=20)
        self.LoginFrame.grid(row=1,column=0)
        #===========BUTTONS=============
        self.button1 = Button(self.LoginFrame,text = "1.PATIENT REGISTRATION", width =30,font="Helvetica 14 bold",bg="cadet blue",command=self.Patient_Reg)
        self.button1.grid(row=1,column=0,pady=10)

        self.button2 = Button(self.LoginFrame, text="2.ROOM ALLOCATION",width =30,font="Helvetica 14 bold",bg="cadet blue",command=self.Room_Allocation)
        self.button2.grid(row=3,column=0,pady=10)

        self.button3 = Button(self.LoginFrame, text="3.EMPLOYEE REGISTRATION",width =30,font="Helvetica 14 bold",bg="cadet blue",command=self.Employee_Reg)
        self.button3.grid(row=5,column=0,pady=10)

        self.button4 = Button(self.LoginFrame, text="4.BOOK APPOINTMENT",width =30,font="Helvetica 14 bold",bg="cadet blue",command=self.Appointment_Form)
        self.button4.grid(row=7,column=0,pady=10)

        self.button5 = Button(self.LoginFrame, text="5.PATIENT BILL",width =30,font="Helvetica 14 bold",bg="cadet blue",command=self.Billing_Form)
        self.button5.grid(row=9,column=0,pady=10)

        self.button6 = Button(self.LoginFrame, text="6.EXIT",width =30,font="Helvetica 14 bold",bg="cadet blue",command = self.Exit)
        self.button6.grid(row=11,column=0,pady=10)

    #Function to Exit Menu Window
    def Exit(self):
        self.master.destroy()

    #Function to open Patient Registration Window
    def Patient_Reg(self):
        self.newWindow = Toplevel(self.master)
        self.app = Patient(self.newWindow)

    #Function to open Room Allocation Window

    def Room_Allocation(self):
        self.newWindow = Toplevel(self.master)
        self.app = Room(self.newWindow)

    #Function to open Employee Registration Window
    def Employee_Reg(self):
        self.newWindow = Toplevel(self.master)
        self.app = Employee(self.newWindow)

    #Function to open Appointment Window
    def Appointment_Form(self):
        self.newWindow = Toplevel(self.master)
        self.app = Appointment(self.newWindow)

    #Function to open Billing Window
    def Billing_Form(self):
        self.newWindow = Toplevel(self.master)
        self.app = Billing(self.newWindow)


'''
    6.1-6, 6.11-19, 6.23 lecture

    Author: Sam Sit
'''
import arrays
import random
import time
import searches
import arrays_utils
import turtle
import math

def making_arrays():
    print(arrays.Array(5))
    print(arrays.Array(1,0))
    print(arrays.Array(10,""))
    print(arrays.Array(20, False))

def while_fill(an_array):
    i = 0
    while i < len(an_array):
        an_array[i] = i
        i += 1
    print(an_array)

def for_fill(an_array):
    count = 0
    for i in range(len(an_array)):
        an_array[i] = count
        count += 1
    print(an_array)

def roll_the_die(sides):
    number = random.randint(1, int(sides))
    print("rolling a " + str(sides) + "-sided die: " + str(number))

def linear_search_timer(an_array, target):
    begin = time.perf_counter()

    searches.linear_search(an_array, target)

    end = time.perf_counter()
    elapsed = end - begin
    print("elapsed time:", elapsed)

def linear_timer():
    array = arrays_utils.range_array(1, 10000000)
    linear_search_timer(array, 1)
    linear_search_timer(array, 5000000)
    linear_search_timer(array, 10000000)

def print_odds(an_array):
    count = 0
    for i in range(len(an_array)):
        if(an_array[count] % 2):
            print(an_array[count], end=" ")
        count += 1

def print_odds_rec(an_array, index=0):
    if index < len(an_array):
        if(an_array[index] % 2):
            print(an_array[index], end=" ")
        print_odds_rec(an_array, index+1)

def countdown(n):
    result = 0

    if n >= 0:
        print(n)
        result = n + countdown(n-1)

    return result

def factorial(n):
    result = 0

    if n > 1:
        result = n*factorial(n-1)

    if n == 1 or 0:
        result = 1

    return result

def circles(radius, depth):
    result = 0
    if depth == 0:
        return 0
    if depth == 1:
        turtle.circle(radius)
        return (math.pi*radius*2)

    if depth > 1:
        for i in range(4):
            turtle.circle(radius, 90)
            circles(radius/2, depth-1)
        result = math.pi*radius*2 + circles(radius/2, depth-1)
        return result

def count_up(n):
    result = 0

    if n >= 0:
        result = n + count_up(n-1)
        print(n)

    return result

def binary_search_timer(an_array, target):
    begin = time.perf_counter()

    searches.binary_search(an_array, target)

    end = time.perf_counter()
    elapsed = end - begin
    print("elapsed time:", elapsed)

def binary_timer():
    array = arrays_utils.range_array(1, 10000000)
    binary_search_timer(array, 1)
    binary_search_timer(array, 5000000)
    binary_search_timer(array, 10000000)

def main():

    #making_arrays()
    '''
    an_array = arrays.Array(10)
    print(an_array)
    while_fill(an_array)
    for_fill(an_array)

    random.seed(1)

    for i in range(10):
        roll_the_die(6)
    '''
    #array = arrays_utils.range_array(1,100)
    #target = 50
    #linear_search_timer(array, target)
    #linear_timer()
    #print_odds(array)
    #print_odds_rec(array)
    #print("sum:", countdown(5))
    #print(factorial(10))
    #print(factorial(100))
    #print(factorial(2000))
    #print("sum:", countdown(100000))
    #turtle.tracer(0)
    #print(circles(200, 2))
    #input("enter to cont...")
    print("sum:", count_up(5))
    print("linear")
    linear_timer()
    print("binary")
    binary_timer()

main()


from django import template
from movies.models import Category, Movie

register = template.Library()

@register.simple_tag()
def get_categories():
    """Все категории фильмов"""
    return Category.objects.all()

@register.inclusion_tag('movies/tags/lust_movie.html')
def get_lust_movie():
    """Последние добавленные фильмы"""
    movies = Movie.objects.filter(draft=False).order_by('-id')[:5]
    return {'lust_movies': movies}


from __future__ import print_function

import argparse
import os

from torch.utils.data import DataLoader
import torchvision
from torchvision import datasets, transforms
import torch
import numpy as np

import random

from diffusion import diffusor
from classification import classification

# ===========================================================
# Training settings
# ===========================================================
parser = argparse.ArgumentParser(description='PyTorch classification')
# hyper-parameters
parser.add_argument('--epoch', type=int, default=200, help='number of epochs to train for')
parser.add_argument('--lr', type=float, default=0.01, help='Learning Rate. Default=0.0001')
parser.add_argument('--gpu', type=int, default=0)
parser.add_argument('--log', type=int, default=1)
parser.add_argument('--batchSize', type=int, default=32, help='training batch size')
parser.add_argument('--decay', type=float, default=0.0001, help='Weight Decay. Default=0.0001')
parser.add_argument('--seed', type=int, default=123, help='random seed to use. Default=123')

# model configuration
parser.add_argument('--model', '-m', type=str, default='diffusion', help='choose which model is going to use')
parser.add_argument('--size', type=int, default=224, help='image size for resize')
parser.add_argument('--diffusion', type=str, default='anisotropic', help='which diffusion')
parser.add_argument('--diffusionCoeff', '--dc', type=float, default=0.001, help='diffusion coefficient')
parser.add_argument('--classifier', type=str, default='ResNet18', help='which classifier')
parser.add_argument('--num_classes', type=int, default=10, help='number of classes')
parser.add_argument('--kernel_size', type=int, default=11, help='kernel size for gaussian kernel')
parser.add_argument('--std', type=float, default=10.5, help='std for gaussian kernel')
parser.add_argument('--title', type=str, default='anisotropic_diffusion')

args = parser.parse_args()

class main:
    def __init__(self):
        self.model = None
        self.train_loader = None
        self.val_loader = None

    def dataLoad(self):
        # ===========================================================
        # Set train dataset & test dataset
        # ===========================================================
        print('===> Loading datasets')

        def seed_worker(self):
            np.random.seed(args.seed)
            random.seed(args.seed)

        transform = transforms.Compose([
            transforms.Resize(args.size),
            transforms.ToTensor(),
            # transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
        ])

        trainset = torchvision.datasets.STL10(root='../../../dataset/stl10', split='train', download=True, transform=transform)
        testset = torchvision.datasets.STL10(root='../../../dataset/stl10', split='test', download=True, transform=transform)

        self.train_loader = torch.utils.data.DataLoader(trainset, batch_size=args.batchSize, shuffle=True, num_workers=2)
        self.val_loader = torch.utils.data.DataLoader(testset, batch_size=args.batchSize, shuffle=False, num_workers=2)

    def modelCall(self):

        if args.model == 'diffusion':          # anisotropic and isotropic diffusion auto-encoder
            self.model = diffusor(args, self.train_loader, self.val_loader, args.diffusion)

        elif args.model == 'classification':   # classification
            self.model = classification(args, self.train_loader, self.val_loader, args.diffusion)

if __name__ == '__main__':
    os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
    os.environ["CUDA_VISIBLE_DEVICES"]=str(args.gpu)
    # os.environ["CUDA_VISIBLE_DEVICES"]="0"

    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)
    torch.cuda.manual_seed_all(args.seed)
    np.random.seed(args.seed)
    random.seed(args.seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

    main = main()
    main.dataLoad()
    main.modelCall()

    main.model.runner()


from earl_nema import NemaRC
import pydicom
from pydicom.tag import Tag
import os
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

class NemaPlots():

    def __init__(self, RCs, recons, title):

        ''' Recovery coefficient plots including EARL guidelines
            using RCs generated from NemaRC class

        Parameters:
        RCs (list): List containing NemaRC objects from each recon
        recons (list): List containing strngs with the recon parameters of each element in RCs
        title (str): Title of plot (recon parameters to be compared)
        '''
        self.RCs = RCs
        self.recons = recons
        self.title = title

        # EARL ranges

        #EARL 1
        self.RC_max_1 = np.array([[0.95,1.16],[0.91,1.13],[0.83,1.09],
                                        [0.73,1.01],[0.59,0.85],[0.31,0.49]])
        self.RC_mean_1 = np.array([[0.76,0.89],[0.72,0.85],[0.63,0.78],
                                        [0.57,0.73],[0.44,0.60],[0.27,0.38]])

        #EARL 2
        self.RC_max_2 = np.array([[1.05,1.29],[1.01,1.26],[1.01,1.32],
                                        [1.00,1.38],[0.85,1.22],[0.52,0.88]])
        self.RC_mean_2 = np.array([[0.85,1.0],[0.82,0.97],[0.8,0.99],
                                        [0.76,0.97],[0.63,0.86],[0.39,0.61]])
        self.SUV_peak_2 = np.array([[0.99,1.07],[0.95,1.07],[0.9,1.09],[0.75,0.99],[0.45,0.69],[0.27,0.41]])

        self.spheres = np.array([37, 28, 22, 17, 13, 10])

        sns.set_palette("husl")

    def RCmean(self):

        x = [10,37]
        y = [1,1]

        sns.set_style("white")
        #sns.set_palette("husl")
        plt.figure(figsize=(8,7))
        for RC,recon in zip(self.RCs,self.recons):
            plt.plot(self.spheres,RC.spheres['RC_mean'],marker='o', linestyle = '--',label=recon)

        plt.fill_between(self.spheres, self.RC_mean_1[:,0], self.RC_mean_1[:,1],
                                color='lightblue', alpha=0.2, label='EARL 1')
        plt.fill_between(self.spheres, self.RC_mean_2[:,0], self.RC_mean_2[:,1],
                                color='C4', alpha=0.2, label='EARL 2')
        plt.xlabel('Sphere Diameter (mm)',fontsize = 16)
        plt.plot(x,y,linestyle='--',color='gray')
        plt.ylabel('RC Mean', fontsize = 16)
        plt.ylim(0.4,1.2)
        plt.legend(fontsize = 12,loc='lower right')
        plt.title(self.title + ' RC Mean',fontsize=16)
        plt.show()

    def RCmax(self):

        x = [10,37]
        y = [1,1]

        sns.set_style("white")
        #sns.set_palette("husl")
        plt.figure(figsize=(8,7))
        for RC,recon in zip(self.RCs,self.recons):
            plt.plot(self.spheres,RC.spheres['RC_max'],marker='o', linestyle = '--',label=recon)

        plt.fill_between(self.spheres, self.RC_max_1[:,0], self.RC_max_1[:,1],
                                color='lightblue', alpha=0.2, label='EARL 1')
        plt.fill_between(self.spheres, self.RC_max_2[:,0], self.RC_max_2[:,1],
                                color='C4', alpha=0.2, label='EARL 2')
        plt.xlabel('Sphere Diameter (mm)',fontsize = 16)
        plt.plot(x,y,linestyle='--',color='gray')
        plt.ylabel('RC Max', fontsize = 16)
        plt.ylim(0.4,1.6)
        plt.legend(fontsize = 12,loc='lower right')
        plt.title(self.title + ' RC Max',fontsize=16)
        plt.show()

    def COV(self,xaxis,axis_title):

        sns.set_style("white")
        #sns.set_palette("husl")
        plt.figure(figsize=(8,7))

        for RC,recon,x in zip(self.RCs,self.recons,xaxis):
            plt.errorbar(x,RC.COV,yerr=RC.COV_error, marker='o',label=recon)

        threshold = [0.15,0.15,0.15,0.15,0.15,0.15]
        plt.plot(xaxis,threshold,linestyle = 'dashed',color='gray',label='limit')
        plt.ylabel('COV',fontsize=16)
        plt.xlabel(axis_title,fontsize=16)
        plt.legend(fontsize = 12)
        plt.title(self.title + ' Background COV',fontsize=16)
        plt.show()

    def noise(self):
        cov = []
        for RC in self.RCs:
            cov.append(RC.COV*100)

        return cov

    def bias(self): #1 - SUV mean Mean contrast recovery
        mcr_bias = []
        for RC in self.RCs:
            #mcr_bias.append(np.abs(1-RC.MCR_mean)*100)
            mcr_bias.append((RC.MCR_mean-1)*100)
        return mcr_bias

    def max_bias(self): #1 - SUV mean Mean contrast recovery
        mcr_max_bias = []
        for RC in self.RCs:
            #mcr_max_bias.append(np.abs(1-RC.MCR_max)*100)
            mcr_max_bias.append((RC.MCR_max-1)*100)

        return mcr_max_bias

    # Noise vs Bias plots

    def cov_v_mcr(self): #nvb = CoV (noise) vs MCR (average SUV mean bias of all 6 spheres)

        cov = []
        mcr_bias = []
        for RC in self.RCs:
            cov.append(RC.COV)
            mcr_bias.append(np.abs(1-RC.MCR_mean))

        sns.set_style("white")
        plt.figure(figsize=(8,7))

        plt.plot(cov,mcr_bias)

        plt.xlabel(r'Noise (\% COV)',fontsize=16)
        plt.ylabel(r'Bias (mean \% recovery)',fontsize=16)
        plt.title(self.title + ' MCR bias vs COV',fontsize=16)
        plt.show()

    def cov_v_max_mcr(self):

        cov = []
        mcr_bias = []
        for RC in self.RCs:
            cov.append(RC.COV)
            mcr_bias.append(1-RC.MCR_max)

        sns.set_style("white")
        plt.figure(figsize=(8,7))

        plt.plot(cov,mcr_bias)

        plt.xlabel('Noise (COV)',fontsize=16)
        plt.ylabel('Bias (SUV max)',fontsize=16)
        plt.title(self.title + ' MCR max bias vs COV',fontsize=16)
        plt.show()

    def cov_time(self):

        cov_time = []
        scan_duration = [19,38,75,150,300,600]
        for RC in self.RCs:
            cov_time.append(RC.COV)

        sns.set_style("white")
        plt.figure(figsize=(8,7))

        plt.plot(cov_time,scan_duration)

        plt.xlabel('Scan Duration (s)',fontsize=16)
        plt.ylabel('COV',fontsize=16)
        plt.title(self.title + ' MCR max bias vs COV',fontsize=16)
        plt.show()
        return cov_time