import json

from flask import request
from pymongo.errors import DuplicateKeyError
from werkzeug.urls import url_encode

from biocontainers.biomongo.helpers import InsertContainers

from biocontainers.common.models import MongoTool, _CONSTANT_TOOL_CLASSES, MongoToolVersion, MongoWorkflow, SimilarTool
from biocontainers_flask.server.controllers.utils import transform_dic_tool_class, \
    transform_tool_version, transform_mongo_tool_dict, transform_mongo_tool, get_facets

from biocontainers_flask.server.models.file_wrapper import FileWrapper  # noqa: E501
from biocontainers_flask.server.models.stat import Stat
from biocontainers_flask.server.models.tool import Tool  # noqa: E501
from biocontainers_flask.server.models.tool_version import ToolVersion  # noqa: E501
from biocontainers_flask.server.models.workflow import Workflow

def facets_get(id=None, alias=None, tool_class=None, registry=None, organization=None, name=None, toolname=None,
               description=None, author=None, checker=None, facets=None,
               all_fields_search=None):  # noqa: E501
    """Facets all the properties from tools

    This endpoint returns all facets properties available or a filtered subset using  metadata query parameters.  # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param alias: Support for this parameter is optional for tool registries that support aliases. If provided will only return entries with the given alias.
    :type alias: str
    :param tool_class: Filter tools by the name of the subclass (#/definitions/ToolClass)
    :type tool_class: str
    :param registry: The image registry that contains the image.
    :type registry: str
    :param organization: The organization in the registry that published the image.
    :type organization: str
    :param name: The name of the image.
    :type name: str
    :param toolname: The name of the tool.
    :type toolname: str
    :param description: The description of the tool.
    :type description: str
    :param author: The author of the tool (TODO a thought occurs, are we assuming that the author of the CWL and the image are the same?).
    :type author: str
    :param checker: Return only checker workflows.
    :type checker: bool
    :param offset: Start index of paging. Pagination results can be based on numbers or other values chosen by the registry implementor (for example, SHA values). If this exceeds the current result set return an empty set.  If not specified in the request, this will start at the beginning of the results.
    :type offset: str
    :param limit: Amount of records to return in a given page.
    :type limit: int

    :rtype: List[Facet]
    """
    tools = []
    is_all_field_search = False
    license = None
    tool_tags = None
    if all_fields_search is not None:
        id = license = tool_tags = alias = organization = name = toolname = description = author = all_fields_search
        is_all_field_search = True

    facets_dic = {}
    if facets is not None:
        facets_list = facets.split(",")
        for facet in facets_list:
            value_list = facet.split(":")
            if value_list[0] not in facets_dic:
                facets_dic[value_list[0]] = []
                facets_dic[value_list[0]].append(value_list[1])
            else:
                facets_dic[value_list[0]].append(value_list[1])

    resp = MongoTool.get_tools(id=id, alias=alias, registry=registry, organization=organization, name=name,
                               toolname=toolname, description=description, author=author,
                               checker=checker, license=license, tool_tags=tool_tags, facets=facets_dic, offset=0,
                               limit=100000, is_all_field_search=is_all_field_search)

    if resp is None:
        return None

    mongo_tools = resp.tools
    facets = get_facets(mongo_tools)
    return facets

def tool_classes_get():  # noqa: E501
    """List all tool types

    This endpoint returns all tool-classes available.  # noqa: E501

    :rtype: List[ToolClass]
    """
    tool_classes = []
    for key in _CONSTANT_TOOL_CLASSES:
        tool_classes.append(transform_dic_tool_class(_CONSTANT_TOOL_CLASSES[key]))

    return tool_classes

def tools_get(id=None, alias=None, tool_class=None, registry=None, organization=None, name=None, toolname=None,
              description=None, author=None, checker=None, facets=None, offset=None, limit=None,
              all_fields_search=None, sort_field=None, sort_order=None):  # noqa: E501
    """List all tools

    This endpoint returns all tools available or a filtered subset using metadata query parameters.  # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param alias: Support for this parameter is optional for tool registries that support aliases. If provided will only return entries with the given alias.
    :type alias: str
    :param tool_class: Filter tools by the name of the subclass (#/definitions/ToolClass)
    :type tool_class: str
    :param registry: The image registry that contains the image.
    :type registry: str
    :param organization: The organization in the registry that published the image.
    :type organization: str
    :param name: The name of the image.
    :type name: str
    :param toolname: The name of the tool.
    :type toolname: str
    :param description: The description of the tool.
    :type description: str
    :param author: The author of the tool
    (TODO a thought occurs, are we assuming that the author of the CWL and the image are the same?).
    :type author: str
    :param checker: Return only checker workflows.
    :type checker: bool
    :param offset: Start index of paging. Pagination results can be based on numbers or other values chosen by the registry implementor (for example, SHA values). If this exceeds the current result set return an empty set.  If not specified in the request, this will start at the beginning of the results.
    :type offset: str
    :param limit: Amount of records to return in a given page.
    :type limit: int
    :param all_fields_search: Search by all fields.
    :param sort_field: field to sort the results
    :param sort_order: sort order, asc or desc
    :rtype: List[Tool]
    """

    is_all_field_search = False
    license = None
    tool_tags = None

    if offset is None:
        offset = 0
    else:
        offset = int(offset)

    if all_fields_search is not None:
        id = license = tool_tags = alias = organization = name = toolname = description = author = all_fields_search
        is_all_field_search = True

    facets_dic = {}
    if facets is not None:
        facets_list = facets.split(",")
        if (len(facets_list) > 0):
            for facet in facets_list:
                value_list = facet.split(":")
                if (len(value_list) == 2):
                    if value_list[0] not in facets_dic:
                        facets_dic[value_list[0]] = []
                        facets_dic[value_list[0]].append(value_list[1])
                    else:
                        facets_dic[value_list[0]].append(value_list[1])

    resp = tools_get_common(id=id, alias=alias, registry=registry, organization=organization, name=name,
                            toolname=toolname, toolclass=tool_class, description=description, author=author,
                            checker=checker, license=license, tool_tags=tool_tags, facets=facets_dic, offset=offset,
                            limit=limit, is_all_field_search=is_all_field_search,
                            sort_field=sort_field, sort_order=sort_order)

    if resp is None:
        return None

    next_page = None
    if resp.next_offset is not None:
        args_next_page = request.args.copy()
        args_next_page['offset'] = resp.next_offset
        args_next_page['limit'] = limit
        next_page = '{}?{}'.format(request.base_url, url_encode(args_next_page))

    args_last_page = request.args.copy()
    args_last_page['offset'] = resp.last_page_offset
    args_last_page['limit'] = limit
    last_page = '{}?{}'.format(request.base_url, url_encode(args_last_page))

    return resp.tools, 200, {'next_page': next_page, 'last_page': last_page,
                             'self_link': request.url, 'current_offset': offset,
                             'current_limit': limit}

def tools_get_common(id=None, alias=None, registry=None, organization=None, name=None, toolname=None, toolclass=None,
                     description=None, author=None, checker=None, license=None, tool_tags=None, facets=None, offset=0,
                     limit=1000, is_all_field_search=False,
                     sort_field=None, sort_order=None):
    tools = []
    resp = MongoTool.get_tools(id=id, alias=alias, registry=registry, organization=organization, name=name,
                               toolname=toolname, toolclass=toolclass, description=description, author=author,
                               checker=checker, license=license, tool_tags=tool_tags, facets=facets, offset=offset,
                               limit=limit, is_all_field_search=is_all_field_search,
                               sort_field=sort_field, sort_order=sort_order)

    if resp is None:
        return None

    mongo_tools = resp.tools
    if mongo_tools is not None:
        for mongo_tool in mongo_tools:
            if ('anchor_tool' in mongo_tool) and (len(mongo_tool['anchor_tool']) > 0):
                continue  # don't include tools that have 'anchor_tool' field
            # Transform the mongo tool to API tool
            tool = transform_mongo_tool_dict(mongo_tool)
            tools.append(tool)
    # If the checker is provided, we filter for checker tools.
    if checker is not None:
        new_tools = []
        for tool in tools:
            if tool.has_checker == checker:
                new_tools.append(tool)
        tools = new_tools
    resp.tools = tools
    return resp

def tools_id_get(id):  # noqa: E501
    """List one specific tool, acts as an anchor for self references

    This endpoint returns one specific tool (which has ToolVersions nested inside it). # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str

    :rtype: Tool
    """
    resp = tools_get_common(id="^" + id + "$")  # regex to search for exact id
    if resp is not None and resp.tools is not None and len(resp.tools) > 0:
        return resp.tools[0]

    return None

def tools_id_versions_get(id):  # noqa: E501
    """List versions of a tool

    Returns all versions of the specified tool. # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str

    :rtype: List[ToolVersion]
    """
    mongo_tool = MongoTool.get_tool_by_id(id)
    anchor_tools = MongoTool.get_tool_with_anchor_tool_field(id)
    tool_versions = []
    if mongo_tool is not None:
        mongo_tool_versions = mongo_tool.get_tool_versions()
        if (anchor_tools is not None) and (len(anchor_tools) > 0):
            for a_tool in anchor_tools:
                mongo_tool_versions.extend(a_tool.get_tool_versions())
        for mongo_tool_version in mongo_tool_versions:
            tool_versions.append(transform_tool_version(mongo_tool_version, mongo_tool.id))

    return tool_versions

def tools_id_versions_version_id_containerfile_get(id, version_id):  # noqa: E501
    """Get the container specification(s) for the specified image.

    Returns the container specifications(s) for the specified image. For example, a CWL CommandlineTool can be associated with one specification for a container, a CWL Workflow can be associated with multiple specifications for containers. # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version for this particular tool registry, for example `v1`.
    :type version_id: str

    :rtype: List[FileWrapper]
    """
    return 'No yet implemented!'

def tools_id_versions_version_id_get(id, version_id):  # noqa: E501
    """List one specific tool version, acts as an anchor for self references

    This endpoint returns one specific tool version. # noqa: E501

    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version, scoped to this registry, for example `v1`. We recommend that versions use semantic versioning https://semver.org/spec/v2.0.0.html  (For example, `1.0.0` instead of `develop`)
    :type version_id: str

    :rtype: ToolVersion
    """
    mongo_tool = MongoTool.get_tool_by_id(id)
    tool_versions = []
    if mongo_tool is not None:
        mongo_tool_versions = mongo_tool.get_tool_versions()
        for mongo_tool_version in mongo_tool_versions:
            tool_versions.append(transform_tool_version(mongo_tool_version, mongo_tool.id))

    for tool_version in tool_versions:
        if tool_version.id == version_id:
            return tool_version

    return None

def tools_id_versions_version_id_type_descriptor_get(type, id, version_id):  # noqa: E501
    """Get the tool descriptor for the specified tool

    Returns the descriptor for the specified tool (examples include CWL, WDL, Nextflow, or Galaxy documents). # noqa: E501

    :param type: The output type of the descriptor. Plain types return the bare descriptor while the \"non-plain\" types return a descriptor wrapped with metadata. Allowable values include \"CWL\", \"WDL\", \"NFL\", \"GALAXY\", \"PLAIN_CWL\", \"PLAIN_WDL\", \"PLAIN_NFL\", \"PLAIN_GALAXY\".
    :type type: str
    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version, scoped to this registry, for example `v1`.
    :type version_id: str

    :rtype: FileWrapper
    """
    return 'Not yet Implemented!'

def tools_id_versions_version_id_type_descriptor_relative_path_get(type, id, version_id, relative_path):  # noqa: E501
    """Get additional tool descriptor files relative to the main file

    Descriptors can often include imports that refer to additional descriptors. This returns additional descriptors for the specified tool in the same or other directories that can be reached as a relative path. This endpoint can be useful for workflow engine implementations like cwltool to programmatically download all the descriptors for a tool and run it. This can optionally include other files described with FileWrappers such as test parameters and containerfiles. # noqa: E501

    :param type: The output type of the descriptor. If not specified, it is up to the underlying implementation to determine which output type to return. Plain types return the bare descriptor while the \"non-plain\" types return a descriptor wrapped with metadata. Allowable values are \"CWL\", \"WDL\", \"NFL\", \"GALAXY\", \"PLAIN_CWL\", \"PLAIN_WDL\", \"PLAIN_NFL\", \"PLAIN_GALAXY\".
    :type type: str
    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version for this particular tool registry, for example `v1`.
    :type version_id: str
    :param relative_path: A relative path to the additional file (same directory or subdirectories), for example 'foo.cwl' would return a 'foo.cwl' from the same directory as the main descriptor. 'nestedDirectory/foo.cwl' would return the file  from a nested subdirectory.  Unencoded paths such 'sampleDirectory/foo.cwl' should also be allowed.
    :type relative_path: str

    :rtype: FileWrapper
    """
    return 'Not yet implemented!'

def tools_id_versions_version_id_type_files_get(type, id, version_id, format=None):  # noqa: E501
    """Get a list of objects that contain the relative path and file type

    Get a list of objects that contain the relative path and file type. The descriptors are intended for use with the /tools/{id}/versions/{version_id}/{type}/descriptor/{relative_path} endpoint. Returns a zip file of all files when format=zip is specified. # noqa: E501

    :param type: The output type of the descriptor. Examples of allowable values are \"CWL\", \"WDL\", \"NFL\", \"GALAXY\".
    :type type: str
    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version for this particular tool registry, for example `v1`.
    :type version_id: str
    :param format: Returns a zip file of all files when format=zip is specified.
    :type format: str

    :rtype: List[ToolFile]
    """
    return 'Not Yet Implemented'

def tools_id_versions_version_id_type_tests_get(type, id, version_id):  # noqa: E501
    """Get a list of test JSONs

    Get a list of test JSONs (these allow you to execute the tool successfully) suitable for use with this descriptor type. # noqa: E501

    :param type: The type of the underlying descriptor. Allowable values include \"CWL\", \"WDL\", \"NFL\", \"GALAXY\", \"PLAIN_CWL\", \"PLAIN_WDL\", \"PLAIN_NFL\", \"PLAIN_GALAXY\". For example, \"CWL\" would return an list of ToolTests objects while \"PLAIN_CWL\" would return a bare JSON list with the content of the tests.
    :type type: str
    :param id: A unique identifier of the tool, scoped to this registry, for example `123456`.
    :type id: str
    :param version_id: An identifier of the tool version for this particular tool registry, for example `v1`.
    :type version_id: str

    :rtype: List[FileWrapper]
    """
    return 'Not Yet Implemented'

def stats():
    """
    This method returns a list of stats for the API
    :return:
    """

    tools = MongoTool.get_all_tools()

    stats = []
    stats.append(Stat('num_tools', str(len(tools))))

    tool_versions = MongoToolVersion.get_all_tool_versions()
    stats.append(Stat('num_versions', str(len(tool_versions))))

    num_containers = 0
    num_docker = 0
    num_conda = 0
    for key in tool_versions:
        num_containers = num_containers + len(key.image_containers)
        for container in key.image_containers:
            if (container.container_type == 'DOCKER'):
                num_docker = num_docker + 1
            elif container.container_type == 'CONDA':
                num_conda = num_conda + 1
    stats.append(Stat('num_containers', str(num_containers)))
    stats.append(Stat('num_conda_containers', str(num_conda)))
    stats.append(Stat('num_docker_containers', str(num_docker)))

    return stats

def tools_get_similars(id=None):
    similar_tool = SimilarTool.get_similars_by_id(id)
    ids = []
    for similar in similar_tool.similars:
        ids.append(similar.id)

    tools = MongoTool.get_all_tools_by_id(ids)
    result_tools = []
    if tools is not None:
        for mongo_tool in tools:
            if ('anchor_tool' in mongo_tool) and (len(mongo_tool.anchor_tool) > 0):
                continue  # don't include tools that have 'anchor_tool' field
            score = 0
            for similar in similar_tool.similars:
                if similar.id == mongo_tool.id:
                    score = similar.score
            tool = transform_mongo_tool(mongo_tool)
            tool.similar_score = score
            result_tools.append(tool)
    # If the checker is provided, we filter for checker tools.
    return result_tools

def wokflows_get(name=None, description=None, author=None, license=None, type=None, container=None,
                 offset=0, limit=1000, all_fields_search=None, sort_field='name', sort_order='asc'):
    """List all workflows

       This endpoint returns all workflows available or a filtered subset using metadata query parameters.

       :param name: The name of the workflow.
       :type name: str
       :param description: The description of the workflow.
       :type description: str
       :param author: The author of the workflow
       :type author: str
       :param license: license of the workflow
       :type license: str
       :param type: type of the workflow
       :type type: str
       :param container: container used by the workflow
       :type container: str
       :param offset: Start index of paging.
       :type offset: int
       :param limit: Amount of records to return in a given page.
       :type limit: int
       :param all_fields_search: Search by all fields.
       :param sort_field: field to sort the results
       :param sort_order: sort order, asc or desc
       :rtype: List[Worflow]
       """

    is_all_field_search = False

    if all_fields_search is not None:
        name = description = author = license = type = container = all_fields_search
        is_all_field_search = True

    resp = MongoWorkflow.get_workflows(name, description, author, license, type, container,
                                       offset, limit, is_all_field_search, sort_field, sort_order)

    if resp is None:
        return None

    tools = []
    if tools is not None:
        for tool in resp.tools:
            tool = Workflow.from_dict(tool)
            tools.append(tool)

    next_page = None
    if resp.next_offset is not None:
        args_next_page = request.args.copy()
        args_next_page['offset'] = resp.next_offset
        args_next_page['limit'] = limit
        next_page = '{}?{}'.format(request.base_url, url_encode(args_next_page))

    args_last_page = request.args.copy()
    args_last_page['offset'] = resp.last_page_offset
    args_last_page['limit'] = limit
    last_page = '{}?{}'.format(request.base_url, url_encode(args_last_page))

    return tools, 200, {'next_page': next_page, 'last_page': last_page,
                        'self_link': request.url, 'current_offset': offset,
                        'current_limit': limit}

def wokflow_post():
    request_dict = request.get_json()
    mongo_workflow = MongoWorkflow()
    mongo_workflow.name = request_dict["name"]
    mongo_workflow.git_repo = request_dict["git-repo"]
    mongo_workflow.description = request_dict.get("description")
    mongo_workflow.author = request_dict.get("author")
    mongo_workflow.license = request_dict.get("license")
    mongo_workflow.type = request_dict.get("type")
    mongo_workflow.containers = request_dict.get("containers")

    try:
        mongo_workflow.save()
    except DuplicateKeyError as error:
        return "Duplicate record: name or git-repo already exists", 409
    except Exception as e:
        return str(e), 400

    try:
        InsertContainers.annotate_workflow(mongo_workflow, "/tmp/bioconda-recipes/")
    except Exception as e:
        print("Error in getting containers from : " + mongo_workflow.git_repo)
        return "Failed to retrieve containers. But, Workflow registered successfully ", 201

    return "Workflow registered successfully", 201

def tools_container_type_container_tag_get(container_type, container_tag):
    """Get the container using container_type and container_tag.

    :param container_type: container_type, for example: conda, docker, singulariry etc.,
    :type container_type: str
    :param container_tag: container_tag, for example: im-pipelines:1.0.0--pyh9f0ad1d_0
    :type container_tag: str

    :rtype: string
    """

    tool_name = container_tag.split(":")[0]
    tool_version = container_tag.split(":")[1]
    tools_list = MongoToolVersion.get_tool_version_by_name(tool_name)
    if tools_list is None:
        return None

    container_type = container_type.upper()
    for tool in tools_list:
        containers = tool.image_containers
        for container in containers:
            if container.container_type == container_type:
                if container_type == "CONDA" and container.tag == "conda:" + tool_version:
                    return container.full_tag
                if container.tag == tool_version:  # this case works for quay images
                    return container.full_tag
                try:
                    tag_replace = container.tag.replace("'", '"')
                    tag_replace = tag_replace.replace("None", "null")
                    tag_replace = tag_replace.replace("True", "true")
                    tag_replace = tag_replace.replace("False", "false")
                    tag_obj = json.loads(tag_replace)  # this case works for docker images
                    if tag_obj['name'] == tool_version:
                        return container.full_tag
                except ValueError as e:
                    continue


with open("weather2018.csv", "r", encoding="utf-8") as w:
	aTotal = []
	maxCold = 15.1;
	maxColdDay = "31.08.2018";
	maxHat = 15.1;
	maxHatDay = "31.08.2018";
	aDay = [];
	day = 31;
	aDays = [];
	rainDays = 1;
	totalDay = 0;
	for string in w:
		if(string[0] == "#"):
			continue
		else:

			string1 = string.split(';')
			if float(string1[0][1:3]) == day:
				aDay.append(float(string1[1][1:-1]))
			else:
				if "".join(string1[23]).find("Осадков нет") != -1:
					rainDays += 1
				if "".join(string1[23]).find(" ") != -1:
					rainDays += 1
				totalDay += 1
				day = float(string1[0][1:3])
				aDays.append(round(sum(aDay),2)/len(aDay))
				aDay = []
			if  float(string1[1][1:3]) < maxCold:
				maxCold = float(string1[1][1:3])
				maxColdDay = string1[0][1:11]
			if  float(string1[1][1:3]) > maxHat:
				maxHat = float(string1[1][1:3])
				maxHatDay = string1[0][1:11]

	aTotal = sum(aDays)/len(aDays)
	print(round(aTotal,2), maxColdDay, maxHatDay, rainDays, totalDay)
input()


import logging
import os
import shutil
import tempfile
import unittest

from telemetry.core import util
from telemetry.core import exceptions
from telemetry import decorators
from telemetry.internal.browser import browser as browser_module
from telemetry.internal.browser import browser_finder
from telemetry.internal.platform import gpu_device
from telemetry.internal.platform import gpu_info
from telemetry.internal.platform import system_info
from telemetry.internal.util import path
from telemetry.testing import browser_test_case
from telemetry.testing import options_for_unittests
from telemetry.timeline import tracing_config

import mock

class IntentionalException(Exception):
  pass

class BrowserTest(browser_test_case.BrowserTestCase):
  def testBrowserCreation(self):
    self.assertEquals(1, len(self._browser.tabs))

    # Different browsers boot up to different things.
    assert self._browser.tabs[0].url

  @decorators.Enabled('has tabs')
  def testNewCloseTab(self):
    existing_tab = self._browser.tabs[0]
    self.assertEquals(1, len(self._browser.tabs))
    existing_tab_url = existing_tab.url

    new_tab = self._browser.tabs.New()
    self.assertEquals(2, len(self._browser.tabs))
    self.assertEquals(existing_tab.url, existing_tab_url)
    self.assertEquals(new_tab.url, 'about:blank')

    new_tab.Close()
    self.assertEquals(1, len(self._browser.tabs))
    self.assertEquals(existing_tab.url, existing_tab_url)

  def testMultipleTabCalls(self):
    self._browser.tabs[0].Navigate(self.UrlOfUnittestFile('blank.html'))
    self._browser.tabs[0].WaitForDocumentReadyStateToBeInteractiveOrBetter()

  def testTabCallByReference(self):
    tab = self._browser.tabs[0]
    tab.Navigate(self.UrlOfUnittestFile('blank.html'))
    self._browser.tabs[0].WaitForDocumentReadyStateToBeInteractiveOrBetter()

  @decorators.Enabled('has tabs')
  def testCloseReferencedTab(self):
    self._browser.tabs.New()
    tab = self._browser.tabs[0]
    tab.Navigate(self.UrlOfUnittestFile('blank.html'))
    tab.Close()
    self.assertEquals(1, len(self._browser.tabs))

  @decorators.Enabled('has tabs')
  def testForegroundTab(self):
    # Should be only one tab at this stage, so that must be the foreground tab
    original_tab = self._browser.tabs[0]
    self.assertEqual(self._browser.foreground_tab, original_tab)
    new_tab = self._browser.tabs.New()
    # New tab shouls be foreground tab
    self.assertEqual(self._browser.foreground_tab, new_tab)
    # Make sure that activating the background tab makes it the foreground tab
    original_tab.Activate()
    self.assertEqual(self._browser.foreground_tab, original_tab)
    # Closing the current foreground tab should switch the foreground tab to the
    # other tab
    original_tab.Close()
    self.assertEqual(self._browser.foreground_tab, new_tab)

  # This test uses the reference browser and doesn't have access to
  # helper binaries like crashpad_database_util.
  @decorators.Enabled('linux')
  def testGetMinidumpPathOnCrash(self):
    tab = self._browser.tabs[0]
    with self.assertRaises(exceptions.AppCrashException):
      tab.Navigate('chrome://crash', timeout=5)
    crash_minidump_path = self._browser.GetMostRecentMinidumpPath()
    self.assertIsNotNone(crash_minidump_path)

  def testGetSystemInfo(self):
    if not self._browser.supports_system_info:
      logging.warning(
          'Browser does not support getting system info, skipping test.')
      return

    info = self._browser.GetSystemInfo()

    self.assertTrue(isinstance(info, system_info.SystemInfo))
    self.assertTrue(hasattr(info, 'model_name'))
    self.assertTrue(hasattr(info, 'gpu'))
    self.assertTrue(isinstance(info.gpu, gpu_info.GPUInfo))
    self.assertTrue(hasattr(info.gpu, 'devices'))
    self.assertTrue(len(info.gpu.devices) > 0)
    for g in info.gpu.devices:
      self.assertTrue(isinstance(g, gpu_device.GPUDevice))

  def testGetSystemInfoNotCachedObject(self):
    if not self._browser.supports_system_info:
      logging.warning(
          'Browser does not support getting system info, skipping test.')
      return

    info_a = self._browser.GetSystemInfo()
    info_b = self._browser.GetSystemInfo()
    self.assertFalse(info_a is info_b)

  def testGetSystemTotalMemory(self):
    self.assertTrue(self._browser.memory_stats['SystemTotalPhysicalMemory'] > 0)

  @decorators.Disabled('cros-chrome-guest', 'system-guest',  # chromeos guest
                       'chromeos')  # crbug.com/628836.
  def testIsTracingRunning(self):
    tracing_controller = self._browser.platform.tracing_controller
    if not tracing_controller.IsChromeTracingSupported():
      return
    self.assertFalse(tracing_controller.is_tracing_running)
    config = tracing_config.TracingConfig()
    config.enable_chrome_trace = True
    tracing_controller.StartTracing(config)
    self.assertTrue(tracing_controller.is_tracing_running)
    tracing_controller.StopTracing()
    self.assertFalse(tracing_controller.is_tracing_running)

class CommandLineBrowserTest(browser_test_case.BrowserTestCase):
  @classmethod
  def CustomizeBrowserOptions(cls, options):
    options.AppendExtraBrowserArgs('--user-agent=telemetry')

  def testCommandLineOverriding(self):
    # This test starts the browser with --user-agent=telemetry. This tests
    # whether the user agent is then set.
    t = self._browser.tabs[0]
    t.Navigate(self.UrlOfUnittestFile('blank.html'))
    t.WaitForDocumentReadyStateToBeInteractiveOrBetter()
    self.assertEquals(t.EvaluateJavaScript('navigator.userAgent'),
                      'telemetry')

class DirtyProfileBrowserTest(browser_test_case.BrowserTestCase):
  @classmethod
  def CustomizeBrowserOptions(cls, options):
    options.profile_type = 'small_profile'

  @decorators.Disabled('chromeos')  # crbug.com/243912
  def testDirtyProfileCreation(self):
    self.assertEquals(1, len(self._browser.tabs))

class BrowserLoggingTest(browser_test_case.BrowserTestCase):
  @classmethod
  def CustomizeBrowserOptions(cls, options):
    options.logging_verbosity = options.VERBOSE_LOGGING

  @decorators.Disabled('chromeos', 'android')
  def testLogFileExist(self):
    self.assertTrue(
       os.path.isfile(self._browser._browser_backend.log_file_path))

def _GenerateBrowserProfile(number_of_tabs):
  """ Generate a browser profile which browser had |number_of_tabs| number of
  tabs opened before it was closed.
      Returns:
        profile_dir: the directory of profile.
  """
  profile_dir = tempfile.mkdtemp()
  options = options_for_unittests.GetCopy()
  options.browser_options.output_profile_path = profile_dir
  browser_to_create = browser_finder.FindBrowser(options)
  with browser_to_create.Create(options) as browser:
    browser.platform.SetHTTPServerDirectories(path.GetUnittestDataDir())
    blank_file_path = os.path.join(path.GetUnittestDataDir(), 'blank.html')
    blank_url = browser.platform.http_server.UrlOf(blank_file_path)
    browser.foreground_tab.Navigate(blank_url)
    browser.foreground_tab.WaitForDocumentReadyStateToBeComplete()
    for _ in xrange(number_of_tabs - 1):
      tab = browser.tabs.New()
      tab.Navigate(blank_url)
      tab.WaitForDocumentReadyStateToBeComplete()
  return profile_dir

class BrowserCreationTest(unittest.TestCase):
  def setUp(self):
    self.mock_browser_backend = mock.MagicMock()
    self.mock_platform_backend = mock.MagicMock()

  def testCleanedUpCalledWhenExceptionRaisedInBrowserCreation(self):
    self.mock_platform_backend.platform.FlushDnsCache.side_effect = (
        IntentionalException('Boom!'))
    with self.assertRaises(IntentionalException):
      browser_module.Browser(
         self.mock_browser_backend, self.mock_platform_backend,
         credentials_path=None)
    self.assertTrue(self.mock_platform_backend.WillCloseBrowser.called)

  def testOriginalExceptionNotSwallow(self):
    self.mock_platform_backend.platform.FlushDnsCache.side_effect = (
        IntentionalException('Boom!'))
    self.mock_platform_backend.WillCloseBrowser.side_effect = (
        IntentionalException('Cannot close browser!'))
    with self.assertRaises(IntentionalException) as context:
      browser_module.Browser(
         self.mock_browser_backend, self.mock_platform_backend,
         credentials_path=None)
    self.assertIn('Boom!', context.exception.message)

class BrowserRestoreSessionTest(unittest.TestCase):

  @classmethod
  def setUpClass(cls):
    cls._number_of_tabs = 4
    cls._profile_dir = _GenerateBrowserProfile(cls._number_of_tabs)
    cls._options = options_for_unittests.GetCopy()
    cls._options.browser_options.AppendExtraBrowserArgs(
        ['--restore-last-session'])
    cls._options.browser_options.profile_dir = cls._profile_dir
    cls._browser_to_create = browser_finder.FindBrowser(cls._options)

  @decorators.Enabled('has tabs')
  @decorators.Disabled('chromeos', 'win', 'mac')
  # TODO(nednguyen): Enable this test on windowsn platform
  def testRestoreBrowserWithMultipleTabs(self):
    with self._browser_to_create.Create(self._options) as browser:
      # The number of tabs will be self._number_of_tabs + 1 as it includes the
      # old tabs and a new blank tab.
      expected_number_of_tabs = self._number_of_tabs + 1
      try:
        util.WaitFor(lambda: len(browser.tabs) == expected_number_of_tabs, 10)
      except:
        logging.error('Number of tabs is %s' % len(browser.tabs))
        raise
      self.assertEquals(expected_number_of_tabs, len(browser.tabs))

  @classmethod
  def tearDownClass(cls):
    shutil.rmtree(cls._profile_dir)

class TestBrowserOperationDoNotLeakTempFiles(unittest.TestCase):

  @decorators.Enabled('win', 'mac', 'linux')
  @decorators.Isolated
  def testBrowserNotLeakingTempFiles(self):
    options = options_for_unittests.GetCopy()
    browser_to_create = browser_finder.FindBrowser(options)
    self.assertIsNotNone(browser_to_create)
    before_browser_run_temp_dir_content = os.listdir(tempfile.tempdir)
    with browser_to_create.Create(options) as browser:
      tab = browser.tabs.New()
      tab.Navigate('about:blank')
      self.assertEquals(2, tab.EvaluateJavaScript('1 + 1'))
    after_browser_run_temp_dir_content = os.listdir(tempfile.tempdir)
    self.assertEqual(before_browser_run_temp_dir_content,
                     after_browser_run_temp_dir_content)


import pytest
from cleanlab.datalab.internal.data import Data
from cleanlab.datalab.internal.data_issues import DataIssues

class TestDataIssues:
    labels = ["B", "A", "B"]
    label_name = "labels"

    @pytest.fixture
    def data_issues(self):
        data = Data(data={self.label_name: self.labels}, label_name=self.label_name)
        data_issues = DataIssues(data=data)
        yield data_issues

    def test_data_issues_init(self, data_issues):
        assert hasattr(data_issues, "issues")
        assert hasattr(data_issues, "issue_summary")
        assert hasattr(data_issues, "info")

    def test_statistics(self, data_issues):
        stats = data_issues.statistics

        assert stats == data_issues.info["statistics"]
        assert stats["num_examples"] == 3, f"Incorrect number of examples: {stats['num_examples']}"
        assert stats["class_names"] == ["A", "B"], f"Incorrect class names: {stats['class_names']}"
        assert stats["num_classes"] == 2, f"Incorrect number of classes: {stats['num_classes']}"
        assert stats["multi_label"] is False
        assert (
            stats["health_score"] is None
        ), f"Health score should initially be None, but is {stats['health_score']}"

    def test_get_info(self, data_issues):
        with pytest.raises(ValueError):
            data_issues.get_info("nonexistent_issue")
        assert data_issues.get_info("statistics") == data_issues.info["statistics"]

    def test_get_info_label(self, data_issues):
        data_issues.info["label"] = {"given_label": [0, 1, 1], "predicted_label": [1, 0, 1]}
        info = data_issues.get_info("label")

        label_format_error_message = (
            "get_info('label') should return the given label formatted with the class names"
        )
        assert info.get("given_label").tolist() == ["A", "B", "B"], label_format_error_message
        assert info.get("predicted_label").tolist() == self.labels, label_format_error_message


import sys
import os
import time
from multiprocessing import Pool, cpu_count
import numpy as np
import matplotlib.pyplot as plt
from torch import nn

sys.path.insert(0, 'graph_enc_dec')
from graph_enc_dec import data_sets
from graph_enc_dec import graph_clustering as gc
from graph_enc_dec import architecture
from graph_enc_dec.model import Model
from graph_enc_dec.standard_architectures import ConvAutoencoder

SEED = 15
N_CPUS = cpu_count()

def estimate_signals(i, G_params, eps_c, eps_d, n_samples, L, nodes_enc, nodes_dec,
                     ups, feat_enc, feat_dec, feat_only_conv):
    # Create graphs
    Gx, Gy = data_sets.perturbated_graphs(G_params, eps_c, eps_d, seed=SEED)

    # Create graph signals
    data = data_sets.LinearDS2GS(Gx, Gy, n_samples, L, 3*G_params['k'],
                                    median=True)
    data.to_unit_norm()
    print('Median Diff between Y and X:', np.median(np.linalg.norm((data.train_X-data.train_Y)**2,1)))

    X = data.train_X
    Beta = np.linalg.pinv(X.T.dot(X)).dot(X.T).dot(data.train_Y)
    test_Y = data.test_Y
    est_Y_test = data.test_X.dot(Beta)
    test_err = np.sum((est_Y_test-test_Y)**2, axis=1)/np.linalg.norm(data.test_Y)**2
    print('Linear model: mean err: {} - median: {}'.format(np.mean(test_err), np.median(test_err)))

    data.to_tensor()

    # Obtein clusters
    cluster_x = gc.MultiResGraphClustering(Gx, nodes_enc, k=4, up_method=None)
    cluster_y = gc.MultiResGraphClustering(Gy, nodes_dec, k=4, up_method=ups)

    # Standar ConvAutoenc
    net = architecture.GraphEncoderDecoder(feat_enc, [Gx.N]*7,
                                           cluster_x.Ds, feat_dec,
                                           [Gx.N]*7, cluster_y.Us,
                                           feat_only_conv, As_dec=cluster_y.As,
                                           last_act_fn=nn.Tanh(), act_fn=nn.Tanh())
    model = Model(net, decay_rate=.9, epochs=25, batch_size=100, learning_rate=0.05,
                  verbose=True, eval_freq=1, max_non_dec=5)
    print('Model parameters: ', model.count_params())
    model.fit(data.train_X, data.train_Y, data.val_X, data.val_Y)
    mean_err, median_err, mse = model.test(data.test_X, data.test_Y)
    print('Autoencoder: Graph {}: N: {} Mean MSE: {} - Mean Err: {} - Median Err: {}'
          .format(i, Gx.N, mse, mean_err, median_err))

    # Graph Autoenc
    net = architecture.GraphEncoderDecoder(feat_enc, cluster_x.sizes,
                                           cluster_x.Ds, feat_dec,
                                           cluster_y.sizes, cluster_y.Us,
                                           feat_only_conv, As_dec=cluster_y.As,
                                           last_act_fn=nn.Tanh(), act_fn=nn.Tanh())
    model = Model(net, decay_rate=.9, epochs=25, batch_size=100, learning_rate=0.05,
                  verbose=True, eval_freq=1, max_non_dec=5)
    print('Model parameters: ', model.count_params())
    model.fit(data.train_X, data.train_Y, data.val_X, data.val_Y)
    mean_err, median_err, mse = model.test(data.test_X, data.test_Y)
    print('GRAPH ENC-DEC Graph {}: N: {} Mean MSE: {} - Mean Err: {} - Median Err: {}'
            .format(i, Gx.N, mse, mean_err, median_err))
    return mean_err, mse, model.count_params()

if __name__ == '__main__':
    np.random.seed(SEED)
    # TODO: test hier_A function
    # TODO: test build_arch

    # Signal parameters
    L = 5
    n_samples = [5000, 1000, 1000]

    # Graph parameters
    n_graphs = 1  # 25

    G_params = {}
    G_params['type'] = data_sets.SBM # SBM or ER
    G_params['N'] = N = 64  # 256
    G_params['k'] = k = 4
    G_params['p'] = 0.6  # 0.15
    G_params['q'] = 0.01  # 0.01/4
    G_params['type_z'] = data_sets.RAND
    eps_c = 0.0005
    eps_d = 0.05

    # Model parameters
    feat_enc = [1, 5, 5, 10, 10, 15, 20]
    nodes_enc = [N, 32, 32, 16, 16, 8, k]  # [N, 128, 64, 32, 16, 8, k]
    feat_dec = [20, 15, 10, 10, 5, 5, 5]
    nodes_dec = [k, 8, 16, 16, 32, 32, N]  # [k, 8, 16, 32, 64, 124, N]
    feat_only_conv = [5, 5, 1]
    ups = gc.WEI

    start_time = time.time()
    mean_err = np.zeros(n_graphs)
    mse = np.zeros(n_graphs)
    if n_graphs > 1:
        with Pool(processes=N_CPUS) as pool:
            results = []
            for i in range(n_graphs):
                results.append(pool.apply_async(estimate_signals,
                        args=[i, G_params, eps_c, eps_d, n_samples, L, nodes_enc,
                              nodes_dec, ups, feat_enc, feat_dec, feat_only_conv]))
            for i in range(n_graphs):
                mean_err[i], mse[i], params = results[i].get()
    else:
        mean_err[0], mse[0], params = estimate_signals(0,G_params,eps_c, eps_d,
                                            n_samples,L,nodes_enc,nodes_dec,ups,
                                            feat_enc,feat_dec,feat_only_conv)

    end_time = time.time()
    print('Time: {} minutes'.format((end_time-start_time)/60))
    print('Mean MSE: {} - Mean Err: {} - Median Err: {} - STD: {}'
          .format(np.mean(mse), np.mean(mean_err), np.median(mean_err), np.std(mean_err)))


import argparse

import matplotlib.pyplot as plt

from model_sir import Simulation
from model_animation import Animation, LineAnimation
from model_plot import plot_simulation

from model_animation import get_ylim

def main(*args):
    """Command line entry point.

    $ python runsim_model.py                        # show animation on screen
    $ python runsim_model.py --file=video.mp4       # save animation to video
    $ python runsim_model.py --plot                 # show plot on screen
    $ python runsim_model.py --plot --file=plot.pdf # save plot to pdf

    """
    #
    # Use argparse to handle parsing the command line arguments.
    #
    parser = argparse.ArgumentParser(description='Animate an epidemic')
    parser.add_argument('--size', metavar='N', type=int, default=100,
                        help='Use a N x N simulation grid')
    parser.add_argument('--duration', metavar='T', type=int, default=365,
                        help='Simulate for T days')
    parser.add_argument('--recovery', metavar='P', type=float, default=0.02,
                        help='Probability of recovery (per day)')
    parser.add_argument('--infection', metavar='P', type=float, default=0.03,
                        help='Probability of infecting a neighbour (per day)')
    parser.add_argument('--death', metavar='P', type=float, default=0.002,
                        help='Probability of dying when infected (per day)')
    parser.add_argument('--deathCap', metavar='P', type=float, default=0.005,
                        help='Probability of dying when healthcare capacity is reached')
    parser.add_argument('--infectionCap', metavar='P', type=float, default=0.05,
                        help='Probability of infection when healthcare capacity is reached')
    parser.add_argument('--population', metavar='N', type=int, default=6000,
                        help='The size of the population')
    parser.add_argument('--cases', metavar='N', type=int, default=2,
                        help='Number of initial infected people')
    parser.add_argument('--capacity', metavar='N', type=int, default=2000,
                        help='Hospitals healthcare capacity. 0 if unlimited capacity.')
    parser.add_argument('--lockdown', metavar='N', type=int, default=600,
                        help='Lockdown when cases reach a value. 0 if no lockdown at all.')
    parser.add_argument('--plot', action='store_true',
                        help='Generate plots instead of an animation')
    parser.add_argument('--file', metavar='N', type=str, default=None,
                        help='Filename to save to instead of showing on screen')
    args = parser.parse_args(args)

    # Set up the simulation
    simulation = Simulation(args.size, args.size,
                            args.recovery, args.infection, args.death,
                            args.capacity, args.lockdown,
                            args.infectionCap, args.deathCap)
    simulation.population(args.population)
    simulation.infect_randomly(args.cases)

    # Obtain value args.population to be used in model_animation.py
    get_ylim(args.population)

    # Plot or animation?
    if args.plot:
        fig = plot_simulation(simulation, args.duration)

        if args.file is None:
            #  python model_runsim.py --plot
            plt.show()
        else:
            #  python model_runsim.py --plot --file=plot.pdf
            fig.savefig(args.file)
    else:
        animation = Animation(simulation, args.duration)

        if args.file is None:
            #  python model_runsim.py
            animation.show()
        else:
            #  python model_runsim.py --file=animation.mp4
            #
            # NOTE: this needs ffmpeg to be installed.
            animation.save(args.file)

if __name__ == "__main__":
    #
    # CLI entry point. The main() function can also be imported and called
    # with string arguments.
    #
    import sys
    main(*sys.argv[1:])


n = int(input())
welfare = input().split(' ')
welfare = list(map(int, welfare ))
max_value = max(welfare)
min_burles = 0

for i in range(n):
	temp = max_value - welfare[i]
	min_burles += temp

print(min_burles)


import time

def getYesterday():
  day = int(time.strftime("%d"))
  month = time.strftime("%b")
  year = int(time.strftime("%Y"))

  if day!=1:
    return day-1, month, year

  #El dia uno no va hacia atrás
  else:
    list_month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    list_num_m = [31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

    pos_m = int(time.strftime("%m")) # Month as a number from 1 to 12
    pos_m = pos_m-1 # Month as a number from 0 to 11

    # 1st of january
    if pos_m==0:
      # Last day of past year
      return 31, 'Dec', year-1

    # 1st of march
    elif pos_m==2:
      # February has two different cases
      if year%4==0 and year%100!=0 or year%400==0:
        day=29 # Bisiesto
      else:
        day=28 # Normal
      return day, 'Feb', year

    # Others 1st
    else:
      month = list_month[pos_m-1]
      day = list_num_m[pos_m-1]
      return day, month, year


import os
import sys
import requests
from bs4 import BeautifulSoup
import colorama

tabs = []
tags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a', 'ul', 'li', 'head', 'title', "div"]
# write your code here
while True:
    text = input()
    dots = [pos for pos, char in enumerate(text) if char == '.']
    if len(dots) > 0:
        end_dot_index = dots[-1]
        filename = text[:end_dot_index]

    args = sys.argv
    directory = args[1]

    if not os.path.exists(directory):
        os.mkdir(directory)
    else:
        pass

    if text[:8].lower() != "https://" and text != "exit" and text != "back":
        text = "https://" + text

    if len(dots) == 0 and text != "back" and text != "exit":
        print("This URL contains an error")

    elif text == 'back':
        if len(tabs) > 1:
            tabs.pop()
            print(tabs.pop())
        else:
            print("Error: No websites visited")

    elif text == 'exit':
        break

    else:
        site = requests.get(text)
        soup = BeautifulSoup(site.text, 'html.parser')
        page = soup.find_all(tags)
        tabs.append(page)
        with open(directory + "/" + filename + ".txt", 'w') as f:
            for tag in page:
                if tag.string is None:
                    pass
                elif tag.name == "a":
                    print(colorama.Fore.BLUE + tag.string)
                    print(colorama.Style.RESET_ALL)
                    f.write(tag.string + '\n')
                else:
                    print(tag.string)
                    f.write(tag.string + '\n')


import contextlib
import enum
import functools
import hashlib
import math
import re
from datetime import datetime
from functools import cache, lru_cache, wraps
from typing import Tuple

import redis
from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.db.models import Prefetch
from django.http import JsonResponse
from django.utils import timezone
from spoilr.core.api.hints import (
    get_hints_enabled,
    get_max_open_hints,
    get_solves_before_hint_unlock,
)
from spoilr.core.api.hunt import is_site_over
from spoilr.core.models import TeamType
from spoilr.hints.models import Hint
from spoilr.utils import json

from puzzles.celery import celery_app
from puzzles.models import Puzzle, PuzzleAccess, Team
from puzzles.models.story import StoryCard, StoryCardAccess

def normalize_answer(answer):
    normalized = ""
    for c in answer:
        code = ord(c)
        if code >= ord("a") and code <= ord("z"):
            # uppercase any lowercase alpha
            normalized += c.upper()
        elif code >= ord("A") and code <= ord("Z"):
            # pass-through any uppercase alpha
            normalized += c
        elif code <= 255:
            # ignore all other ASCII characters
            pass
        else:
            # pass everything else
            normalized += c
    return normalized

def login_required(function=None):
    def decorator(view_func):
        @wraps(view_func)
        def _wrapped_view(request, *args, **kwargs):
            if request.user.is_authenticated:
                return view_func(request, *args, **kwargs)
            return JsonResponse({}, status=401)

        return _wrapped_view

    if function:
        return decorator(function)
    return decorator

class RateLimiter:
    def __init__(self, interval):
        self.interval = interval
        self.last_guess = 0

    def is_allowed(self):
        now = time.time()
        if now - self.last_guess > self.interval:
            self.last_guess = now
            return True
        return False

@lru_cache(maxsize=32768)
def get_rate_limiter_for(key, limit=1):
    return RateLimiter(12)

def is_unlocked(*, puzzle_slug=None, story_slug=None, user=None, team_id=None):
    if team_id is None:
        team_id = user.team_id
    team = Team.objects.filter(id=team_id)
    if puzzle_slug:
        team = team.prefetch_related(
            Prefetch(
                "puzzleaccess_set",
                queryset=PuzzleAccess.objects.filter(puzzle__slug=puzzle_slug),
            )
        )
    if story_slug:
        team = team.prefetch_related(
            Prefetch(
                "storycardaccess_set",
                queryset=StoryCardAccess.objects.filter(story_card__slug=story_slug),
            )
        )

    team = team.first()
    if not team:
        return False
    if puzzle_slug and team.puzzleaccess_set.first():
        return True
    if story_slug and team.storycardaccess_set.first():
        return True
    if team.is_internal or team.is_public:
        return (puzzle_slug and Puzzle.objects.filter(slug=puzzle_slug).exists()) or (
            story_slug and StoryCard.objects.filter(slug=story_slug).exists()
        )
    return False

@cache
def get_encryption_key(slug):
    "Get symmetric key for encrypting/decrypting a js file."
    m = hashlib.sha256()
    m.update(slug.encode())
    hashname = m.hexdigest()[:8]
    m = hashlib.sha256()
    m.update((hashname + settings.JS_ENCRYPTION_KEY).encode())
    key = m.hexdigest()
    return {hashname: key}

def get_encryption_keys(names):
    "Get key dict for a list of names."
    d = {}
    # encryption keys not used for the archive site
    if not settings.IS_POSTHUNT:
        for kv in map(get_encryption_key, names):
            d.update(kv)
    return d

@cache
def get_redis_handle():
    return redis.Redis(
        host=settings.REDIS_HOST,
        port=settings.REDIS_PORT,
        db=settings.REDIS_DATABASE_ENUM.REDIS_CLIENT.value,
    )

def redis_lock(*args, timeout=settings.REDIS_LONG_TIMEOUT, **kwargs):
    """
    Multiprocess lock using redis.

    You almost certainly want to use this as a context manager.
    Lock is automatically released after 60 seconds by default.
    """
    if settings.IS_PYODIDE:
        # skip lock when running in pyodide locally
        class FakeLock(contextlib.nullcontext):
            # provide noop handler for .acquire(), .release(), etc
            def __getattr__(self, k):
                return lambda *args, **kwargs: None

        return FakeLock()
    redis_handle = get_redis_handle()
    return redis_handle.lock(*args, timeout=timeout, **kwargs)

def throttleable_task(func):
    """
    Decorator to make a task that can be throttled.

    Send a task to Celery, throttled at most once per interval.
    Other calls will be dropped.

    func.throttle(key, interval, args=args, kwargs=kwargs)

    key: All tasks with the same key will be throttled together
    interval: seconds to wait between function calls
    """

    def get_lock_key(key):
        return f"throttle_task_lock:{key}"

    def get_state_key(key):
        return f"throttle_task_state:{key}"

    @functools.wraps(func)
    def wrapper(*args, _throttle_data=None, **kwargs):
        if not settings.IS_PYODIDE and _throttle_data is not None:
            key, prev_serialized, timeout = _throttle_data
            state_key = get_state_key(key)
            now = datetime.utcnow().timestamp()
            with redis_lock(get_lock_key(key), timeout=timeout):
                serialized = get_redis_handle().get(state_key)
                if serialized is not None:
                    serialized = serialized.decode()
                if serialized == prev_serialized:
                    now = datetime.utcnow().timestamp()
                    new_state = json.dumps([now, False])
                    get_redis_handle().set(state_key, new_state, px=int(timeout * 1000))
        return func(*args, **kwargs)

    task = celery_app.task(wrapper)

    def throttle(key, interval, *, args=None, kwargs=None):
        if args is None:
            args = ()
        if kwargs is None:
            kwargs = {}
        if settings.IS_PYODIDE:
            task(*args, **kwargs)
            return
        timeout = settings.REDIS_FAST_TIMEOUT + interval
        with redis_lock(get_lock_key(key), timeout=timeout):
            state_key = get_state_key(key)
            now = datetime.utcnow().timestamp()
            ts = -math.inf
            pending = False
            state = get_redis_handle().get(state_key)
            if state is not None:
                ts, pending = json.loads(state)
            if pending:
                # throttled
                pass
            else:
                next_state_serialized = json.dumps([ts, True])
                get_redis_handle().set(
                    state_key, next_state_serialized, px=int(timeout * 1000)
                )
                countdown = max(0, ts + interval - now)
                task.apply_async(
                    args=args,
                    kwargs={
                        **kwargs,
                        "_throttle_data": (key, next_state_serialized, timeout),
                    },
                    countdown=countdown,
                    expires=countdown + settings.REDIS_FAST_TIMEOUT,
                )

    task.throttle = throttle
    return task

def get_puzzle_solve_count(puzzle):
    """Number of teams who solved this puzzle."""
    return (
        PuzzleAccess.objects.exclude(team__type=TeamType.INTERNAL)
        .filter(solved=True, solved_time__isnull=False, puzzle_id=puzzle.id)
        .count()
    )

class HintVisibility(enum.IntEnum):
    LOCKED = 0
    CAN_VIEW = 1
    CAN_REQUEST = 2

def num_open_hints(team):
    """Number of hints by this team that are still open."""
    return Hint.all_requiring_response().filter(team=team).count()

def hint_availability(puzzle, team) -> Tuple[HintVisibility, str]:
    if not team:
        return HintVisibility.LOCKED, "You must be signed in to request a hint."

    if team.is_public:
        return HintVisibility.LOCKED, "Public access team cannot request hints."

    if not get_hints_enabled():
        return HintVisibility.LOCKED, "Hints have not been released yet."

    if is_site_over():
        return (
            HintVisibility.CAN_VIEW,
            "HQ has closed and is no longer accepting hints.",
        )

    if puzzle.canonical_puzzle:
        puzzle = puzzle.canonical_puzzle
        if puzzle.canonical_puzzle:
            raise RuntimeError(f"Canonical puzzle chain longer than 2: {puzzle.slug}")

    if not (
        puzzle.override_hint_unlocked
        or get_puzzle_solve_count(puzzle) >= get_solves_before_hint_unlock()
    ):
        return HintVisibility.LOCKED, "Hints are not yet available for this puzzle."

    num_open = num_open_hints(team)
    if num_open >= get_max_open_hints():
        hints = "hint request" if num_open == 1 else "hint requests"
        return (
            HintVisibility.CAN_VIEW,
            f"You have {num_open} outstanding {hints}. Please wait for us to get back to you before requesting more hints.",
        )
    return HintVisibility.CAN_REQUEST, "Hints are available for this puzzle!"

def random_permutation(count):
    """
    Generate a pseudorandom permutation of range(count).

    Uses numpy when available since we did so during Hunt but fallback to the
    random module with Pyodide.
    """
    if settings.IS_PYODIDE:
        import random

        arr = list(range(count))
        random.shuffle(arr)
        return arr
    else:
        import numpy as np

        return np.random.permutation(count)


import streamlit as st
import sklearn
import joblib
model = joblib.load('Coursera Review Sentiment')
st.title('Coursera Sentiment')
ip = st.text_input('Enter your review')
op = model.predict([ip])
if st.button('Predict'):
  temp = op[0]
  if temp==5:
    st.title("Loved It! (Rating: 5)")
  elif temp==4:
    st.title("It's Good (Rating: 4)")
  elif temp==3:
    st.title("It's Neutral (Rating: 3)")
  elif temp==2:
    st.title("It's Bad (Rating: 2)")
  elif temp==1:
    st.title("It's the worst course User have ever seen! (Rating: 1)")


import csv
import sys
import json

import os

cwd = os.getcwd()

def minsToDegrees (minsString, isLongitude):
    parts = minsString.split("-")

    parts[0] = float(parts[0])
    parts[1] = float(parts[1])
    parts[2] = float(parts[2][:parts[2].find(" ")])

    degrees = parts[0] + (parts[1] / 60) + (parts[2] / 3600)

    # If this is a longitude value, it is x degrees West, so it needs to be multiplied by negative one, since West values are negative
    # Note: automatically applying this multiplication on any longitude value only works because all of the values we are processing are in North America, so all values are West of the prime meridian. If we were processing values worldwide, we would have to actually read the "W" or "E" character and apply the multiplication only for if the char read is "W"
    if isLongitude:
        degrees *= -1

    return degrees

# for each directory in the cwd
for name in os.listdir(cwd):
    if os.path.isdir(name) and name != '.git':
        fixes = list()

        # for each file in this directory
        for filename in os.listdir(name):
            # open the file
            with open(cwd + '/' + name + '/' + filename, 'r+') as file:
                fixes = fixes + json.load(file)

        outfilename = name + ".csv"
        with open(outfilename, 'w') as csvFile:
            headers = ['name', 'latitude', 'longitude', 'state', 'artcc', 'type', 'updated']
            writer = csv.DictWriter(csvFile, fieldnames=headers)

            writer.writeheader()

            for row in fixes:
                # convert the lat and long coordinates to degrees
                row['latitude'] = minsToDegrees(row['latitude'], False)
                row['longitude'] = minsToDegrees(row['longitude'], True)

                writer.writerow(row)


# 16 다이나믹 프로그래밍 - 못생긴 수
# Solved Date: 22.06.23.

import sys
read = sys.stdin.readline

def solve(n):
    dp = [0 for _ in range(n)]
    dp[0] = 1
    index_2, index_3, index_5 = 0, 0, 0
    next_2, next_3, next_5 = 2, 3, 5
    for index in range(1, n):
        dp[index] = min(next_2, next_3, next_5)
        if next_2 == dp[index]:
            index_2 += 1
            next_2 = dp[index_2] * 2
        if next_3 == dp[index]:
            index_3 += 1
            next_3 = dp[index_3] * 3
        if next_5 == dp[index]:
            index_5 += 1
            next_5 = dp[index_5] * 5
    return dp[n-1]

if __name__ == '__main__':
    n = int(read().rstrip())
    print(solve(n))


import codecs
import os
import re
import json
from collections import defaultdict

from nlplingo.annotation.ace import AceAnnotation
from nlplingo.annotation.serif import to_lingo_doc
from nlplingo.text.text_theory import Document
from nlplingo.annotation.idt import process_idt_file
from nlplingo.annotation.enote import process_enote_file
from nlplingo.annotation.spannotator import process_span_file
#from nlplingo.annotation.stanford_corenlp import process_corenlp_file
#from nlplingo.annotation.srl_column import process_srl_file
from nlplingo.embeddings.word_embeddings import DocumentContextualEmbeddings
from nlplingo.common import io_utils
import numpy as np
import logging

logger = logging.getLogger(__name__)

class InputFileType(object):
    def __init__(self):
        self.text_file = None
        self.idt_file = None
        self.enote_file = None
        self.acetext_file = None  # ACE text file, where we only care about texts not within xml-tags
        self.apf_file = None  # ACE xml file
        self.span_file = None  # similar to Spannotator format
        self.corenlp_file = None
        self.srl_file = None
        self.serif_file = None
        self.lingo_file = None
        self.embedding_file = None

def parse_filelist_line(line):
    """
    :return: InputFileType
    """
    input_file_type = InputFileType()
    for file in line.strip().split():
        if file.startswith('TEXT:'):
            input_file_type.text_file = file[len('TEXT:'):]
        elif file.startswith('IDT:'):
            input_file_type.idt_file = file[len('IDT:'):]
        elif file.startswith('ENOTE:'):
            input_file_type.enote_file = file[len('ENOTE:'):]
        elif file.startswith('ACETEXT:'):
            input_file_type.acetext_file = file[len('ACETEXT:'):]
        elif file.startswith('APF:'):
            input_file_type.apf_file = file[len('APF:'):]
        elif file.startswith('SPAN:'):
            input_file_type.span_file = file[len('SPAN:'):]
        elif file.startswith('CORENLP'):
            input_file_type.corenlp_file = file[len('CORENLP:'):]
        elif file.startswith('SRL'):
            input_file_type.srl_file = file[len('SRL:'):]
        elif file.startswith('SERIF'):
            input_file_type.serif_file = file[len('SERIF:'):]
        elif file.startswith('LINGO'):
            input_file_type.lingo_file = file[len('LINGO:'):]
        elif file.startswith('EMBEDDING'):
            input_file_type.embedding_file = file[len('EMBEDDING:'):]

    if input_file_type.text_file is None and input_file_type.acetext_file is None and input_file_type.serif_file is None and input_file_type.lingo_file is None:
        raise ValueError('TEXT, ACETEXT, SERIF, or LINGO must be present!')
    return input_file_type

def read_doc_annotation(filelists, params):
    """
    :type filelists: list[str]
    Returns:
        list[nlplingo.text.text_theory.Document]
    """
    docs = []
    """:type docs: list[text.text_theory.Document]"""
    for file_index, line in enumerate(filelists):
        input_file_type = parse_filelist_line(line)

        # TODO: we probably want to restrict having only text_file, serif_file, or acetext_file
        if input_file_type.text_file is not None:
            docid = os.path.basename(input_file_type.text_file)
            text_f = codecs.open(input_file_type.text_file, 'r', encoding='utf-8')
            all_text = text_f.read()
            text_f.close()
            doc = Document(docid, all_text.strip())

        if input_file_type.serif_file is not None:
            doc = to_lingo_doc(input_file_type.serif_file,
                               params.get('add_serif_entity_mentions', True),
                               params.get('add_serif_event_mentions', False),
                               params.get('add_serif_event_event_relation_mentions', False),
                               params.get('add_serif_entity_entity_relation_mentions', False),
                               params.get('add_serif_prop_adj', False),
                               params.get('add_serif_dep_graph', False),
                               params.get('allow_anchor_as_event_argument', False)
                               )

        if input_file_type.acetext_file is not None:
            docid = re.match(r'(.*).sgm', os.path.basename(input_file_type.acetext_file)).group(1)
            text_list = AceAnnotation.process_ace_textfile_to_list(input_file_type.acetext_file)
            # sometimes, e.g. for ACE, we need to keep the sentence strings separate. In ACE sgm files, it contains
            # things like '&' which Spacy normalizes to a single character '&' and Spacy thus changed the original
            # character offsets. This is bad for keeping correspondences with the .apf file for character offsets.
            # So we let it affect 1 sentence or 1 paragraph, but not the rest of the document.

            # Some ACE text files have words that end with a dash e.g. 'I-'. This presents a problem. The annotation
            # file annotates 'I', but Spacy keeps it as a single token 'I-', and then I won't be able to find
            # Spacy tokens to back the Anchor or EntityMention. To prevent these from being dropped, we will replace
            # all '- ' with '  '.
            text_list = [s.replace(r'- ', '  ') for s in text_list]
            text_list = [s.replace(r' ~', '  ') for s in text_list]
            text_list = [s.replace(r'~ ', '  ') for s in text_list]
            text_list = [s.replace(r' -', '  ') for s in text_list]
            text_list = [s.replace(r'.-', '. ') for s in text_list] # e.g. 'U.S.-led' => 'U.S. led', else Spacy splits to 'U.S.-' and 'led'
            text_list = [s.replace(r'/', ' ') for s in text_list]

            doc = Document(docid, text=None, sentence_strings=text_list)

        if input_file_type.lingo_file is not None:
            with codecs.open(input_file_type.lingo_file, 'r', encoding='utf-8') as f:
                doc = Document.from_json(json.load(f))

        if input_file_type.idt_file is not None:
            doc = process_idt_file(doc, input_file_type.idt_file)  # adds entity mentions
        if input_file_type.enote_file is not None:
            doc = process_enote_file(doc, input_file_type.enote_file, auto_adjust=True)  # adds events
        if input_file_type.apf_file is not None:
            doc = AceAnnotation.process_ace_xmlfile(doc, input_file_type.apf_file)
        if input_file_type.span_file is not None:
            doc = process_span_file(doc, input_file_type.span_file)
        if input_file_type.corenlp_file is not None:
            doc = process_corenlp_file(doc, input_file_type.corenlp_file)
        if input_file_type.srl_file is not None:
            doc = process_srl_file(doc, input_file_type.srl_file)
        if input_file_type.embedding_file is not None:
            if os.path.exists(input_file_type.embedding_file):
                npz_data = np.load(input_file_type.embedding_file, allow_pickle=True)
                DocumentContextualEmbeddings.load_embeddings_into_doc(doc, npz_data, params.get('average_embeddings', False))
            else:
                logger.critical("Embedding npz file {} does not exist".format(input_file_type.embedding_file))

        if doc is not None:
            docs.append(doc)

        if (file_index % 20) == 0:
            print('Read {} input documents out of {}'.format(str(file_index+1), str(len(filelists))))
    return docs

def count_annotations_in_docs(documents):
    number_anchors = 0
    number_args = 0
    number_assigned_anchors = 0
    number_assigned_args = 0
    number_assigned_multiword_anchors = 0
    event_type_count = defaultdict(int)
    for doc in documents:
        for event in doc.events:
            number_anchors += event.number_of_anchors()
            number_args += event.number_of_arguments()
            event_type_count[event.label] += 1
        for sent in doc.sentences:
            for event in sent.events:
                number_assigned_anchors += event.number_of_anchors()
                number_assigned_args += event.number_of_arguments()
                for anchor in event.anchors:
                    if len(anchor.tokens) > 1:
                        number_assigned_multiword_anchors += 1
    print('In %d documents, #anchors=%d #assigned_anchors=%d #assigned_multiword_anchors=%d, #args=%d #assigned_args=%d' % \
          (len(documents), number_anchors, number_assigned_anchors, number_assigned_multiword_anchors, number_args, number_assigned_args))

def populate_doc_sentences_with_embeddings_and_annotations(docs, params, word_embeddings):

    if 'override_event_argument_annotation' in params:
        override_event_argument_annotation(docs, params['override_event_argument_annotation'])

    # apply Spacy for sentence segmentation and tokenization, using Spacy tokens to back Anchor and EntityMention
    #spacy_en = spacy.load('en')
    for index, doc in enumerate(docs):
        # if len(doc.sentences) == 0:
        #     doc.annotate_sentences(word_embeddings, spacy_en,
        #                            fuzzy_match_entitymention=params.get('entitymention.fuzzy_token_backing', False),
        #                            fuzzy_match_anchor=params.get('anchor.fuzzy_token_backing', False))
        # else:
        #     # read_doc_annotation above has already done sentence splitting and tokenization to construct
        #     # Sentence objects, e.g. using output from CoreNLP
        doc.annotate_sentences(word_embeddings, model=None,
                                   fuzzy_match_entitymention=params.get('entitymention.fuzzy_token_backing', False),
                                   fuzzy_match_anchor=params.get('anchor.fuzzy_token_backing', False))

        if (index % 20) == 0:
            print('Prepared {} input documents out of {}'.format(str(index+1), str(len(docs))))

    # em_labels = set()
    # for doc in docs:
    #     for sentence in doc.sentences:
    #         for em in sentence.entity_mentions:
    #             em_labels.add(em.label)
    # for label in sorted(em_labels):
    #     print('EM-LABEL {}'.format(label))
    # print(len(em_labels))

    # print('In train_test.prepare_docs')
    # for doc in docs:
    #     for sent in doc.sentences:
    #         for event in sent.events:
    #             for arg in event.arguments:
    #                 if len(arg.entity_mention.tokens) > 1:
    #                     print('Multiword argument: {} {}'.format(arg.entity_mention.label, ' '.join(token.text for token in arg.entity_mention.tokens)))
    # exit(0)

    count_annotations_in_docs(docs)

    #print('Event type counts:')
    #for et in sorted(event_type_count.keys()):
    #    print('#{}: {}'.format(et, event_type_count[et]))

def prepare_docs(filelists, word_embeddings, params):
    """
    :rtype: list[nlplingo.text.text_theory.Document]
    """
    # read IDT and ENote annotations
    docs = read_doc_annotation(io_utils.read_file_to_list(filelists), params)
    print('num# docs = %d' % (len(docs)))
    populate_doc_sentences_with_embeddings_and_annotations(docs, params, word_embeddings)
    return docs

def override_event_argument_annotation(docs, override_file):
    """
    :type docs: list[nlplingo.text.text_theory.Document]
    :type override_file: str
    """

    doc_annotations = defaultdict(list)
    with codecs.open(override_file, 'r', encoding='utf-8') as f:
        datas = json.load(f)
    for data in datas:
        doc_annotations[data['docid']].append(data)

    for doc in docs:
        docid = doc.docid
        if docid in doc_annotations:
            annotations = doc_annotations[docid]

            unfound_annotation_count = 0
            for annotation in annotations:
                target_event_type = annotation['trigger']['event_type']
                target_start = annotation['argument']['start']
                target_end = annotation['argument']['end']
                target_role = annotation['argument']['role']    # NOTE: if target_role == 'None', we remove the argument

                found = False
                for event in doc.events:
                    event_type = event.label
                    for arg in event.arguments:
                        start = arg.start_char_offset()
                        end = arg.end_char_offset()

                        if target_event_type == event_type and target_start == start and target_end == end:
                            arg.label = target_role
                            found = True
                            break

                if not found:
                    unfound_annotation_count += 1

            print('ingestion.override_event_argument_annotation: docid={} {}/{} annotations not found'.format(docid, str(unfound_annotation_count), str(len(annotations))))

        # we now remove arguments which have been tagged as 'None'
        for event in doc.events:
            new_arguments = []
            for arg in event.arguments:
                if arg.label != 'None':
                    new_arguments.append(arg)
            event.arguments = new_arguments

    role_counts = defaultdict(int)
    for doc in docs:
        for event in doc.events:
            for arg in event.arguments:
                role_counts[arg.label] += 1

    for role in sorted(role_counts):
        print('ingestion.override_event_argument_annotation: #{} = {}'.format(role, str(role_counts[role])))


__author__ = 'Xing'
class Solution(object):
    def countAndSay(self, n):
        """
        :type n: int
        :rtype: str
        """
        if n==1:return '1'
        pre=self.countAndSay(n-1)
        result,index,count,length='',0,0,len(pre)
        while index<length:
            count+=1
            if index+1<length and pre[index]==pre[index+1]:
                index+=1
            else:
                result+=str(count)+pre[index]
                count=0
                index+=1
        return result

s=Solution()
# print(s.countAndSay(1))
for n in range(5):
    print(s.countAndSay(n))


"""
Изменить реализацию функции рекурсивного поиска элемента в словаре (из предыдущего задания)
следующим образом:
- функция должна находить ПЕРВОЕ соответствие имени и возвращать результат в виде словаря:

    {'val': found_value, 'parent': found_value_parent, 'deep': found_value_deep}

"""

def recursive_search(source_dict, lookup_value, deep=-1, parent=None):
    """ Your code is here """

    if len(source_dict) == 0:
        return 0

    lst = {}
    for k, v in source_dict.items():
        if isinstance(v, dict):
            deep += 1
            recursive_search(source_dict(source_dict[k], lookup_value, deep))
        if isinstance(v, list):
            deep += 1
            for i in v:
                if i == lookup_value:
                    return {'val': lookup_value, 'parent': k, 'deep': deep}
        if isinstance(v, str) and v == lookup_value:
            deep +=1
            return {'val': lookup_value, 'parent': k, 'deep': deep}

""" Source dict """
def get_source_dict():
    return {
        "key1": "John",  # deep 0
        'key2': {
            'key3': 'Alex',  # deep 1
            'key4': {
                'key5': ['Kate', 'Mary'],  # deep 2
                'key6': {
                    'key7': [
                        'Bob',  # deep 3
                        'Duke',
                        {
                            'key8': {  # deep 4
                                'key9': [  # deep 5
                                    'Lisa',
                                    {
                                        'key10': ['Mark']  # deep 6
                                    }
                                ]
                            }
                        }
                    ]
                },
            },
            'key8': 'Robert'  # deep 1
        }
    }

""" Test """
source_dict = get_source_dict()
values = [
    ("Alex", {'val': 'Alex', 'parent': 'key3', 'deep': 1}),
    ("Mary", {'val': 'Mary', 'parent': 'key5', 'deep': 2}),
    ('Duke', {'val': 'Duke', 'parent': 'key7', 'deep': 3}),
    ('Mark', {'val': 'Mark', 'parent': 'key10', 'deep': 6})
]

for lookup_value, expected_result in values:
    result = recursive_search(source_dict, lookup_value)
    #assert result == expected_result, f"{result} != {expected_result}"
    print(result)


# LEnet 网络
################################
# linear  input
# linear  16
# linear  64
# linear  100
###################################
import torch.nn as nn

# 定义网络结构
class Net(nn.Module):
    def __init__(self, input, output):
        super(Net, self).__init__()
        self.l1 = nn.Sequential(
            nn.Linear(input, 64),
            nn.ReLU()
        )
        self.l2 = nn.Sequential(
            nn.Linear(64, 512),
            nn.ReLU()
        )
        self.l3 = nn.Linear(512, output)

    # 定义前向传播过程，输入为x
    def forward(self, x):
        x = self.l1(x)
        x = self.l2(x)
        x = self.l3(x)
        return x


"""
Michael Miller and Kurt Tuohy
CS 598 Deep Learning for Healthcare - University of Illinois
Final project - Paper Results Verification
4/4/2022

Reproduce the k-Nearest Neighbors models in the paper below.
Do both classification and regression.

Paper: "Natural language processing for cognitive therapy: Extracting schemas from thought records"
by Franziska Burger, Mark A. Neerincx, and Willem-Paul Brinkman
DOI: https://doi.org/10.1371/journal.pone.0257832
Data repo: https://github.com/mich1eal/cs598_dl4hc
For dependencies, and data acquisition instructions, please see this repository's readme
"""

# To track RAM and CPU usage
import time
import psutil
# Store initial RAM usage to help tare final usage
init_ram_used = psutil.virtual_memory()[3]

import pandas as pd
import numpy as np
import torch
from torchtext.vocab import GloVe
# kNN models
from sklearn.neighbors import NearestNeighbors
# To compute distance in kNN model
from scipy.spatial import distance
# To help perform hyperparameter grid search
#from skorch import NeuralNetClassifier
#from sklearn.model_selection import GridSearchCV
# Statistical mode for kNN classifier
from scipy.stats import mode
# To compute model goodness-of-fit
from scipy.stats import spearmanr

# Local imports to help make scripts more modular.
import cog_globals as GLOB
import cog_prep_data as prep

### Arguments for kNNs

# Desired embedding level: 'token' or 'utterance'
embed_level = 'utterance'
# Desired values of k for classification and regression models.
# Using the paper's best-performing settings
classification_k = 4
regression_k = 5

### Set seed. Copied from HW3 RNN notebook.

#random.seed(GLOB.seed)
np.random.seed(GLOB.seed)
torch.manual_seed(GLOB.seed)
#os.environ["PYTHONHASHSEED"] = str(GLOB.seed)

###### Model definition

### Distance metric for kNN

def cosine_dist(x, y):
    return distance.cosine(x,y)

### kNN classification

class kNNClassification():
    '''
    Implementation of the researchers' kNN classifier.
    Since the paper used multi-class labels, they used
    custom evaluation logic rather than using the basic
    sklearn KNeighborsClassifier.
    '''

    def __init__(self, k, distance_metric):
        self.k = k
        self.metric = distance_metric
        self.model = NearestNeighbors(n_neighbors=self.k, metric=self.metric)

    def fit(self, train_x):
        self.model.fit(train_x)

    def test(self, test_x, train_y, test_y):
        '''
        For the given test utterances, identify the nearest neighbors
        from the training set. Take the statistical mode of the class
        labels from those neighbors.
        '''
        # Array to store all predicted class labels
        test_y_hat = np.zeros(test_y.shape)

        # Loop over test utterances
        for i, utterance in enumerate(test_x):
            #print(f"utterance: {utterance}")
            # Predict k nearest training neighbors of the given utterance
            neighbors = self.model.kneighbors([utterance], self.k, return_distance=False)[0]
            #print(f'neighbors: {neighbors}')
            # Array to store predicted neighbors' class labels
            k_pred_class = np.zeros((self.k, test_y.shape[1]))

            # Get class labels of nearest training neighbors
            for j, neighbor in enumerate(neighbors):
                k_pred_class[j,:] = train_y[neighbor,:]

            # Take statistical mode of the predicted neighbors' class labels.
            # This gives predicted class labels for the given utterance.
            #print(f'k_pred_classes for neighbors: {k_pred_class}')
            test_y_hat[i,:] = mode(k_pred_class, nan_policy='omit')[0]
            #print(f'Mode of k_pred_classes: {test_y_hat[i,:]}')

        return test_y_hat

### kNN Regression

class kNNRegression():
    '''
    Implementation of the researchers' kNN regression model.
    Since the paper used multi-class labels, they used
    custom evaluation logic rather than using the basic
    sklearn KNeighborsRegressor.
    '''

    def __init__(self, k, distance_metric):
        self.k = k
        self.metric = distance_metric
        self.model = NearestNeighbors(n_neighbors=self.k, metric=self.metric)

    def fit(self, train_x):
        self.model.fit(train_x)

    def test(self, test_x, train_y, test_y):
        '''
        For the given test utterances, identify the nearest neighbors
        from the training set. Take the average of the class
        labels from those neighbors.
        '''
        # Array to store all predicted class labels
        test_y_hat = np.zeros(test_y.shape)

        # Loop over test utterances
        for i, utterance in enumerate(test_x):
            #print(f"utterance: {utterance}")
            # Predict k nearest training neighbors of the given utterance
            neighbors = self.model.kneighbors([utterance], self.k, return_distance=False)[0]
            #print(f'neighbors: {neighbors}')
            # Array to store predicted neighbors' class labels
            k_pred_class = np.zeros(test_y.shape[1])

            # Sum class labels of nearest training neighbors
            for neighbor in neighbors:
                k_pred_class += train_y[neighbor,:]

            #print(f'k_pred_classes for neighbors: {k_pred_class}')
            # Take average of class labels.
            test_y_hat[i,:] = np.divide(k_pred_class, self.k)
            #print(f'Average of k_pred_classes: {test_y_hat[i,:]}')

        return test_y_hat

###### Evaluation routine

def spearman_r(X, Y):
    '''
    Computes Spearman rank-order correlation between
    model predictions and ground truth.
    Code taken from researchers' original Jupyter notebook.

    Inputs:
        X: matrix of predictions, with one column per schema.
        Y: matrix of ground-truth labels, with one column per schema.
    Output:
        rho: pandas dataframe with three columns:
                "schema" for schema name,
                "estimate" for r values, and
                "p" for p-values.
    '''

    # Initialize arrays of corr coefficients and p values
    rho_array = np.zeros(X.shape[1])
    p_array = np.zeros(X.shape[1])

    # Compute coefficient over each schema and save
    for schema in range(len(GLOB.SCHEMAS)):
        # print(f'Spearman correlation for schema {schema}:')
        # true_vs_pred_labels = pd.DataFrame({
        #     'true': X[:, schema],
        #     'pred': Y[:, schema]})
        # print(true_vs_pred_labels.to_string(index=False))
        rho, p_val = spearmanr(X[:, schema], Y[:, schema])
        rho_array[schema] = rho
        p_array[schema] = p_val

    return pd.DataFrame({
        "schema": GLOB.SCHEMAS,
        "estimate": rho_array.tolist(),
        "p": p_array.tolist()})

###### Load data

in_frame = prep.read_data(process_mode=None)

###### Preprocess data

# Tokenize
in_frame = prep.tokenize(in_frame)

# Split dataset into training / validation / test sets.
# Note that the validation set is only needed to find the best value of k,
# which we're not doing here. Instead we use the researchers' best k values.

train_frame, val_frame, test_frame = prep.split_data(in_frame,
                                                    train_fraction=GLOB.train_fraction,
                                                    val_fraction=GLOB.val_fraction,
                                                    test_fraction=GLOB.test_fraction)

###### Prepare data

# Load GLoVe embeddings
embedding_glove = GloVe(name='6B', dim=GLOB.glove_embed_dim)

# Create training, validation and test datasets

### Get training, validation and test datasets for the chosen schema.
# Choose dataset class based on the embedding level.

if embed_level == 'token':

    train_set = prep.TokenDataset(train_frame,
                                  schemas=GLOB.SCHEMAS,
                                  max_len=GLOB.max_utt_length,
                                  vocab_size=GLOB.max_vocab_size,
                                  vocab=None,
                                  embeddings=embedding_glove)

    val_set = prep.TokenDataset(val_frame,
                                schemas=GLOB.SCHEMAS,
                                max_len=GLOB.max_utt_length,
                                vocab_size=GLOB.max_vocab_size,
                                vocab=train_set.vocab,
                                embeddings=embedding_glove)

    test_set = prep.TokenDataset(test_frame,
                                 schemas=GLOB.SCHEMAS,
                                 max_len=GLOB.max_utt_length,
                                 vocab_size=GLOB.max_vocab_size,
                                 vocab=train_set.vocab,
                                 embeddings=embedding_glove)

elif embed_level == 'utterance':

    train_set = prep.UtteranceDataset(train_frame,
                                      schemas=GLOB.SCHEMAS,
                                      max_len=GLOB.max_utt_length,
                                      vocab_size=GLOB.max_vocab_size,
                                      vocab=None,
                                      tfidf_tokenizer=None,
                                      embeddings=embedding_glove)

    val_set = prep.UtteranceDataset(val_frame,
                                    schemas=GLOB.SCHEMAS,
                                    max_len=GLOB.max_utt_length,
                                    vocab_size=GLOB.max_vocab_size,
                                    vocab=train_set.vocab,
                                    tfidf_tokenizer=train_set.tfidf_tokenizer,
                                    embeddings=embedding_glove)

    test_set = prep.UtteranceDataset(test_frame,
                                     schemas=GLOB.SCHEMAS,
                                     max_len=GLOB.max_utt_length,
                                     vocab_size=GLOB.max_vocab_size,
                                     vocab=train_set.vocab,
                                     tfidf_tokenizer=train_set.tfidf_tokenizer,
                                     embeddings=embedding_glove)

# Create dataloaders for our three datasets.

# For classical models, return all data at once. No batching needed
train_loader = prep.create_dataloader(train_set, batch_size=len(train_set), shuffle=True)
val_loader = prep.create_dataloader(val_set, batch_size=len(val_set), shuffle=False)
# For test_set, return entire set from dataloader
test_loader = prep.create_dataloader(test_set, batch_size=len(test_set), shuffle=False)

# Get data from loaders

train_x, train_y = next(iter(train_loader))
val_x, val_y = next(iter(val_loader))
test_x, test_y = next(iter(test_loader))

# Convert datasets to numpy arrays

train_x = train_x.numpy()
train_y = train_y.numpy()
val_x = val_x.numpy()
val_y = val_y.numpy()
test_x = test_x.numpy()
test_y = test_y.numpy()

###### Run models ######

# Start timer for training and testing
train_start_time = time.time()

# kNN classifier
knn_classifier = kNNClassification(k=classification_k, distance_metric=cosine_dist)
knn_classifier.fit(train_x)
classifier_test_y_hat = knn_classifier.test(test_x, train_y, test_y)

# kNN regressor
knn_regressor = kNNRegression(k=regression_k, distance_metric=cosine_dist)
knn_regressor.fit(train_x)
regressor_test_y_hat = knn_regressor.test(test_x, train_y, test_y)

# Training & testing time in seconds for training
train_time = time.time() - train_start_time

# Get and display goodness of fit for each schema

classifier_test_gof = spearman_r(test_y, classifier_test_y_hat)
print("\nkNN Classification Test Set Goodness of Fit (Spearman r) Per Schema:")
print(classifier_test_gof.to_string(index=False))

regressor_test_gof = spearman_r(test_y, regressor_test_y_hat)
print("\nkNN Regression Test Set Goodness of Fit (Spearman r) Per Schema:")
print(regressor_test_gof.to_string(index=False))

# Display training time in seconds
print(f"\nTraining + Test time: {train_time} seconds")

# Compute and display RAM usage
final_ram_used = psutil.virtual_memory()[3]
script_ram_k_used = (final_ram_used - init_ram_used) / 1024
print(f"\nRAM used by script: {script_ram_k_used} K")