"""
This module provides python bindings for the Firecloud API.
For more details see https://software.broadinstitute.org/firecloud/

To see how the python bindings map to the RESTful endpoints view
the README at https://pypi.python.org/pypi/firecloud.
"""

from __future__ import print_function
import json
import sys
import os
import io
import logging
import subprocess
try:
    from collections.abc import Iterable
except ImportError:
    from collections import Iterable

from six.moves.urllib.parse import urlencode, urljoin
from six import string_types

import inspect

import google.auth
from google.auth.exceptions import DefaultCredentialsError, RefreshError
from google.auth.transport.requests import AuthorizedSession, Request
from google.oauth2 import id_token

from firecloud.errors import FireCloudServerError
from firecloud.fccore import __fcconfig as fcconfig
from firecloud.__about__ import __version__

FISS_USER_AGENT = "FISS/" + __version__

# Set Global Authorized Session
__SESSION = None
__USER_ID = None

# Suppress warnings about project ID
logging.getLogger('google.auth').setLevel(logging.ERROR)

#################################################
# Utilities
#################################################
def _set_session():
    """ Sets global __SESSION and __USER_ID if they haven't been set """
    global __SESSION
    global __USER_ID

    if __SESSION is None:
        # determine if clock_skew_in_seconds is a parameter for id_token.verify_oauth2_token()
        try:  # PY3
            argspec = inspect.getfullargspec(id_token.verify_oauth2_token)
        except AttributeError:  # PY2
            argspec = inspect.getargspec(id_token.verify_oauth2_token)

        try:
            __SESSION = AuthorizedSession(google.auth.default(['https://www.googleapis.com/auth/userinfo.profile',
                                                               'https://www.googleapis.com/auth/userinfo.email'])[0])
            health()
            # google.auth 2.1.0 introduced a restrictive clock skew that was unmodifiable until 2.3.2
            if 'clock_skew_in_seconds' in argspec.args:
                __USER_ID = id_token.verify_oauth2_token(__SESSION.credentials.id_token,
                                                         Request(session=__SESSION),
                                                         clock_skew_in_seconds=10)['email']
            else:
                __USER_ID = id_token.verify_oauth2_token(__SESSION.credentials.id_token,
                                                         Request(session=__SESSION))['email']
        except AttributeError:
            __USER_ID = __SESSION.credentials.service_account_email
        except (DefaultCredentialsError, RefreshError) as gae:
            if os.getenv('SERVER_SOFTWARE', '').startswith('Google App Engine/'):
                raise
            logging.warning("Unable to determine/refresh application credentials")
            try:
                subprocess.check_call(['gcloud', 'auth', 'application-default',
                                       'login', '--no-launch-browser'])
                __SESSION = AuthorizedSession(google.auth.default(['https://www.googleapis.com/auth/userinfo.profile',
                                                                   'https://www.googleapis.com/auth/userinfo.email'])[0])
            except subprocess.CalledProcessError as cpe:
                if cpe.returncode < 0:
                    logging.exception("%s was terminated by signal %d",
                                      cpe.cmd, -cpe.returncode)
                else:
                    logging.exception("%s returned %d", cpe.cmd,
                                      cpe.returncode)
                raise gae

def _fiss_agent_header(headers=None):
    """ Return request headers for fiss.
        Inserts FISS as the User-Agent.
        Initializes __SESSION if it hasn't been set.

    Args:
        headers (dict): Include additional headers as key-value pairs

    """
    _set_session()

    fiss_headers = {"User-Agent" : FISS_USER_AGENT}
    if headers is not None:
        fiss_headers.update(headers)
    return fiss_headers

def __get(methcall, headers=None, root_url=None, **kwargs):
    if not headers:
        headers = _fiss_agent_header()
    if root_url is None:
        root_url = fcconfig.root_url
    r = __SESSION.get(urljoin(root_url, methcall), headers=headers, **kwargs)
    if fcconfig.verbosity > 1:
        print('FISSFC call: %s' % r.url, file=sys.stderr)
    return r

def __patch(methcall, headers=None, root_url=None, **kwargs):
    if not headers:
        headers = _fiss_agent_header({"Content-type":  "application/json"})
    if root_url is None:
        root_url = fcconfig.root_url
    r = __SESSION.patch(urljoin(root_url, methcall), headers=headers, **kwargs)
    if fcconfig.verbosity > 1:
        info = r.url
        data = kwargs.get("data", None)
        if data:
            info += " \n(data=%s) " % data
        json = kwargs.get("json", None)
        if json:
            info += " \n(json=%s) " % json
        print('FISSFC call: PATCH %s' % info, file=sys.stderr)
    return r

def __post(methcall, headers=None, root_url=None, **kwargs):
    if not headers:
        headers = _fiss_agent_header({"Content-type":  "application/json"})
    if root_url is None:
        root_url = fcconfig.root_url
    r = __SESSION.post(urljoin(root_url, methcall), headers=headers, **kwargs)
    if fcconfig.verbosity > 1:
        info = r.url
        json = kwargs.get("json", None)
        if json:
            info += " \n(json=%s) " % json
        print('FISSFC call: POST %s' % info, file=sys.stderr)
    return r

def __put(methcall, headers=None, root_url=None, **kwargs):
    if not headers:
        headers = _fiss_agent_header()
    if root_url is None:
        root_url = fcconfig.root_url
    r = __SESSION.put(urljoin(root_url, methcall), headers=headers, **kwargs)
    if fcconfig.verbosity > 1:
        info = r.url
        json = kwargs.get("json", None)
        if json:
            info += " \n(json=%s) " % json
        print('FISSFC call: PUT %s' % info, file=sys.stderr)
    return r

def __delete(methcall, headers=None, root_url=None):
    if not headers:
        headers = _fiss_agent_header()
    if root_url is None:
        root_url = fcconfig.root_url
    r = __SESSION.delete(urljoin(root_url, methcall), headers=headers)
    if fcconfig.verbosity > 1:
        print('FISSFC call: DELETE %s' % r.url, file=sys.stderr)
    return r

def _check_response_code(response, codes):
    """
    Throws an exception if the http response is not expected. Can check single
    integer or list of valid responses.

    Example usage:
        >>> r = api.get_workspace("broad-firecloud-testing", "Fake-Bucket")
        >>> _check_response_code(r, 200)
         ... FireCloudServerError ...
    """
    if type(codes) == int:
        codes = [codes]
    if response.status_code not in codes:
        try:
            msg = json.dumps(response.json())
        except:
            msg = response.text
        raise FireCloudServerError(response.status_code, msg)

def whoami():
    """ Return __USER_ID """
    _set_session()
    return __USER_ID

##############################################################
# 1. Orchestration API calls, see https://api.firecloud.org/
##############################################################

##################
### 1.1 Entities
##################

def get_entities_with_type(namespace, workspace):
    """List entities in a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Entities/getEntitiesWithType
    """
    uri = "workspaces/{0}/{1}/entities_with_type".format(namespace, workspace)
    return __get(uri)

def list_entity_types(namespace, workspace):
    """List the entity types present in a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Entities/getEntityTypes
    """
    headers = _fiss_agent_header({"Content-type":  "application/json"})
    uri = "workspaces/{0}/{1}/entities".format(namespace, workspace)
    return __get(uri, headers=headers)

def upload_entities(namespace, workspace, entity_data, model='firecloud',
                    delete_empty=False):
    """Upload entities from tab-delimited string.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        entity_data (str): TSV string describing entites
        model (str): Data Model type. "firecloud" (default) or "flexible"
        delete_empty (bool): Values left blank in the TSV will be deleted if set to true

    Swagger:
        https://api.firecloud.org/#!/Entities/importEntities
        https://api.firecloud.org/#!/Entities/flexibleImportEntities
    """
    params = {"deleteEmptyValues": delete_empty}
    body = urlencode({"entities" : entity_data})
    headers = _fiss_agent_header({
        'Content-type':  "application/x-www-form-urlencoded"
    })
    endpoint = 'importEntities'
    if model == 'flexible':
        endpoint = 'flexibleImportEntities'
    uri = "workspaces/{0}/{1}/{2}".format(namespace, workspace, endpoint)
    return __post(uri, headers=headers, data=body, params=params)

def upload_entities_tsv(namespace, workspace, entities_tsv, model='firecloud'):
    """Upload entities from a tsv loadfile.

    File-based wrapper for api.upload_entities().
    A loadfile is a tab-separated text file with a header row
    describing entity type and attribute names, followed by
    rows of entities and their attribute values.

        Ex:
            entity:participant_id   age   alive
            participant_23           25       Y
            participant_27           35       N

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        entities_tsv (file): FireCloud loadfile, see format above
        model (str): Data Model type. "firecloud" (default) or "flexible"
    """
    if isinstance(entities_tsv, string_types):
        with open(entities_tsv, "r") as tsv:
            entity_data = tsv.read()
    elif isinstance(entities_tsv, io.StringIO):
        entity_data = entities_tsv.getvalue()
    else:
        raise ValueError('Unsupported input type.')
    return upload_entities(namespace, workspace, entity_data, model)

def copy_entities(from_namespace, from_workspace, to_namespace,
                  to_workspace, etype, enames, link_existing_entities=False):
    """Copy entities between workspaces

    Args:
        from_namespace (str): project (namespace) to which source workspace belongs
        from_workspace (str): Source workspace name
        to_namespace (str): project (namespace) to which target workspace belongs
        to_workspace (str): Target workspace name
        etype (str): Entity type
        enames (list(str)): List of entity names to copy
        link_existing_entities (boolean): Link all soft conflicts to the entities that already exist.

    Swagger:
        https://api.firecloud.org/#!/Entities/copyEntities
    """

    uri = "workspaces/{0}/{1}/entities/copy".format(to_namespace, to_workspace)
    body = {
        "sourceWorkspace": {
            "namespace": from_namespace,
            "name": from_workspace
        },
        "entityType": etype,
        "entityNames": enames
    }

    return __post(uri, json=body, params={'linkExistingEntities':
                                          str(link_existing_entities).lower()})

def get_entities(namespace, workspace, etype):
    """List entities of given type in a workspace.

    Response content will be in JSON format.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type

    Swagger:
        https://api.firecloud.org/#!/Entities/getEntities
    """
    uri = "workspaces/{0}/{1}/entities/{2}".format(namespace, workspace, etype)
    return __get(uri)

def get_entities_tsv(namespace, workspace, etype, attrs=None, model="firecloud"):
    """List entities of given type in a workspace as a TSV.

    Similar to get_entities(), but the response is a TSV.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type
        attrs (list): list of ordered attribute names to be in downloaded tsv
        model (str): Data Model type. "firecloud" (default) or "flexible"

    Swagger:
        https://api.firecloud.org/#!/Entities/downloadEntitiesTSV
    """
    params = {'model': model}
    if attrs is not None:
        params['attributeNames'] = ','.join(attrs)
    uri = "workspaces/{0}/{1}/entities/{2}/tsv".format(namespace,
                                                workspace, etype)
    return __get(uri, params=params)

def get_entity(namespace, workspace, etype, ename):
    """Request entity information.

    Gets entity metadata and attributes.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type
        ename (str): The entity's unique id

    Swagger:
        https://api.firecloud.org/#!/Entities/getEntity
    """
    uri = "workspaces/{0}/{1}/entities/{2}/{3}".format(namespace,
                                            workspace, etype, ename)
    return __get(uri)

def delete_entities(namespace, workspace, json_body):
    """Delete entities in a workspace.

    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        json_body:
        [
          {
            "entityType": "string",
            "entityName": "string"
          }
        ]

    Swagger:
        https://api.firecloud.org/#!/Entities/deleteEntities
    """

    uri = "workspaces/{0}/{1}/entities/delete".format(namespace, workspace)
    return __post(uri, json=json_body)

def delete_entity_type(namespace, workspace, etype, ename):
    """Delete specified entities of a single type in a workspace.

    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type
        ename (str, or iterable of str): unique entity id(s)

    Swagger:
        https://api.firecloud.org/#!/Entities/deleteEntities
    """

    uri = "workspaces/{0}/{1}/entities/delete".format(namespace, workspace)
    if isinstance(ename, string_types):
        body = [{"entityType":etype, "entityName":ename}]
    elif isinstance(ename, Iterable):
        body = [{"entityType":etype, "entityName":i} for i in ename]

    return __post(uri, json=body)

def delete_entities_of_type(namespace, workspace, etype):
    """Delete all entities of a type in a workspace.

    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type

    Swagger:
        https://api.firecloud.org/#/Entities/deleteEntitiesOfType
    """

    uri = "workspaces/{0}/{1}/entityTypes/{2}".format(namespace, workspace,
                                                      etype)
    return __delete(uri)

def delete_participant(namespace, workspace, name):
    """Delete participant in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): participant_id(s)
    """
    return delete_entity_type(namespace, workspace, "participant", name)

def delete_participant_set(namespace, workspace, name):
    """Delete participant set in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): participant_set_id(s)
    """
    return delete_entity_type(namespace, workspace, "participant_set", name)

def delete_sample(namespace, workspace, name):
    """Delete sample in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): sample_id(s)
    """
    return delete_entity_type(namespace, workspace, "sample", name)

def delete_sample_set(namespace, workspace, name):
    """Delete sample set in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): sample_set_id(s)
    """
    return delete_entity_type(namespace, workspace, "sample_set", name)

def delete_pair(namespace, workspace, name):
    """Delete pair in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): pair_id(s)
    """
    return delete_entity_type(namespace, workspace, "pair", name)

def delete_pair_set(namespace, workspace, name):
    """Delete pair set in a workspace.

    Convenience wrapper for api.delete_entity().
    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        name (str, or iterable of str): pair_set_id(s)
    """
    return delete_entity_type(namespace, workspace, "pair_set", name)

def get_entities_query(namespace, workspace, etype, page=1, page_size=100,
                       sort_direction="asc", filter_terms=None,
                       filter_operator=None, fields=None):
    """Paginated version of get_entities_with_type.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype (str): Entity type
        page (int): Page number, 1-indexed
        page_size (int): Number of entities to return per page
        sort_direction (str): "asc" or "desc"
        filter_terms (str): Space-separated list to text match
        filter_operator (str): "and" or "or" - determines if one or all
                              filter_terms must have a match. Default: "and"
        fields (str): Comma-separated list of which fields to populate

    Swagger:
        https://api.firecloud.org/#!/Entities/entityQuery

    """

    # Initial parameters for pagination
    params = {
        "page" : page,
        "pageSize" : page_size,
        "sortDirection" : sort_direction
    }
    if filter_terms:
        params['filterTerms'] = filter_terms
    if filter_operator:
        params['filterOperator'] = filter_operator
    if fields:
        params['fields'] = fields

    uri = "workspaces/{0}/{1}/entityQuery/{2}".format(namespace,workspace,etype)
    return __get(uri, params=params)

def rename_entity(namespace, workspace, etype, ename, name):
    """Rename entity in a workspace

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype     (str): Entity type
        ename     (str): Entity name
        name      (str): New entity name

    Swagger:
        https://api.firecloud.org/#/Entities/renameEntity
    """

    body = {"name": name}
    uri = "workspaces/{0}/{1}/entities/{2}/{3}/rename".format(namespace,
                                                              workspace, etype,
                                                              ename)
    return __post(uri, json=body)

def rename_entity_type(namespace, workspace, etype, name):
    """Rename entity type in a workspace

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype     (str): Entity type
        name      (str): New entity type name

    Swagger:
        https://api.firecloud.org/#/Entities/renameEntityType
    """

    body = {"newName": name}
    uri = "workspaces/{0}/{1}/entityTypes/{2}".format(namespace, workspace,
                                                      etype)
    return __patch(uri, json=body)

def rename_entity_type_attribute(namespace, workspace, etype, attr, name):
    """Change the attribute name for an entity type in a workspace. If the old
       name doesn't exist the update will fail. If the new name already exists,
       the update will fail, and no records will be updated.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype     (str): Entity type
        attr      (str): Attribute
        name      (str): New attribute name

    Swagger:
        https://api.firecloud.org/#/Entities/rename_entity_attributes
    """
    body = {"newAttributeName": name}
    uri = "workspaces/{0}/{1}/entityTypes/{2}/attributes/{3}".format(namespace,
                                                                     workspace,
                                                                     etype,
                                                                     attr)
    return __patch(uri, json=body)

def update_entity(namespace, workspace, etype, ename, updates):
    """ Update entity attributes in a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        etype     (str): Entity type
        ename     (str): Entity name
        updates   (list(dict)): List of updates to entity from _attr_set, e.g.

    Swagger:
        https://api.firecloud.org/#!/Entities/update_entity
    """
    headers = _fiss_agent_header({"Content-type":  "application/json"})
    uri = "{0}workspaces/{1}/{2}/entities/{3}/{4}".format(fcconfig.root_url,
                                            namespace, workspace, etype, ename)

    # FIXME: create __patch method, akin to __get, __delete etc
    return __SESSION.patch(uri, headers=headers, json=updates)

###############################
### 1.2 Method Configurations
###############################

def list_workspace_configs(namespace, workspace, allRepos=False):
    """List method configurations in workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        allRepos (bool): list configs for all repos, not just Agora

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/listWorkspaceMethodConfigs
        DUPLICATE: https://api.firecloud.org/#!/Workspaces/listWorkspaceMethodConfigs
    """
    uri = "workspaces/{0}/{1}/methodconfigs".format(namespace, workspace)
    return __get(uri, params={'allRepos': allRepos})

def create_workspace_config(namespace, workspace, body):
    """Create method configuration in workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        body (json) : a filled-in JSON object for the new method config
                      (e.g. see return value of get_workspace_config)

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/postWorkspaceMethodConfig
        DUPLICATE: https://api.firecloud.org/#!/Workspaces/postWorkspaceMethodConfig
    """

    #json_body = {
    #    "namespace"      : mnamespace,
    #    "name"           : method,
    #    "rootEntityType" : root_etype,
    #    "inputs" : {},
    #    "outputs" : {},
    #    "prerequisites" : {}
    #}
    uri = "workspaces/{0}/{1}/methodconfigs".format(namespace, workspace)
    return __post(uri, json=body)

def delete_workspace_config(namespace, workspace, cnamespace, config):
    """Delete method configuration in workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        mnamespace (str): Method namespace
        method (str): Method name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/deleteWorkspaceMethodConfig
    """
    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}".format(namespace,
                                            workspace, cnamespace, config)
    return __delete(uri)

def get_workspace_config(namespace, workspace, cnamespace, config):
    """Get method configuration in workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        cnamespace (str): Config namespace
        config (str): Config name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/getWorkspaceMethodConfig
    """

    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}".format(namespace,
                                            workspace, cnamespace, config)
    return __get(uri)

def overwrite_workspace_config(namespace, workspace, cnamespace, configname, body):
    """Add or overwrite method configuration in workspace.

    Args:
        namespace  (str): project to which workspace belongs
        workspace  (str): Workspace name
        cnamespace (str): Configuration namespace
        configname (str): Configuration name
        body      (json): new body (definition) of the method config

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/overwriteWorkspaceMethodConfig
    """
    headers = _fiss_agent_header({"Content-type": "application/json"})
    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}".format(namespace,
                                        workspace, cnamespace, configname)
    return __put(uri, headers=headers, json=body)

def update_workspace_config(namespace, workspace, cnamespace, configname, body):
    """Update method configuration in workspace.

    Args:
        namespace  (str): project to which workspace belongs
        workspace  (str): Workspace name
        cnamespace (str): Configuration namespace
        configname (str): Configuration name
        body      (json): new body (definition) of the method config

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/updateWorkspaceMethodConfig
    """
    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}".format(namespace,
                                        workspace, cnamespace, configname)
    return __post(uri, json=body)

def validate_config(namespace, workspace, cnamespace, config):
    """Get syntax validation for a configuration.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        cnamespace (str): Configuration namespace
        config (str): Configuration name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/validate_method_configuration
    """
    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}/validate".format(namespace,
                                                    workspace, cnamespace, config)
    return __get(uri)

def rename_workspace_config(namespace, workspace, cnamespace, config,
                                            new_namespace, new_name):
    """Rename a method configuration in a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        mnamespace (str): Config namespace
        config (str): Config name
        new_namespace (str): Updated config namespace
        new_name (str): Updated method name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/renameWorkspaceMethodConfig
    """

    body = {
        "namespace" : new_namespace,
        "name"      : new_name,
        # I have no idea why this is required by FC, but it is...
        "workspaceName" : {
            "namespace" : namespace,
            "name"      : workspace
        }
    }
    uri = "workspaces/{0}/{1}/method_configs/{2}/{3}/rename".format(namespace,
                                                workspace, cnamespace, config)
    return __post(uri, json=body)

def copy_config_from_repo(namespace, workspace, from_cnamespace,
                          from_config, from_snapshot_id, to_cnamespace,
                          to_config):
    """Copy a method config from the methods repository to a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        from_cnamespace (str): Source configuration namespace
        from_config (str): Source configuration name
        from_snapshot_id (int): Source configuration snapshot_id
        to_cnamespace (str): Target configuration namespace
        to_config (str): Target configuration name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/copyFromMethodRepo
        DUPLICATE: https://api.firecloud.org/#!/Method_Repository/copyFromMethodRepo
    """

    body = {
        "configurationNamespace"  : from_cnamespace,
        "configurationName"       : from_config,
        "configurationSnapshotId" : from_snapshot_id,
        "destinationNamespace"    : to_cnamespace,
        "destinationName"         : to_config
    }
    uri = "workspaces/{0}/{1}/method_configs/copyFromMethodRepo".format(
                                                        namespace, workspace)
    return __post(uri, json=body)

def copy_config_to_repo(namespace, workspace, from_cnamespace,
                        from_config, to_cnamespace, to_config):
    """Copy a method config from a workspace to the methods repository.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        from_cnamespace (str): Source configuration namespace
        from_config (str): Source configuration name
        to_cnamespace (str): Target configuration namespace
        to_config (str): Target configuration name

    Swagger:
        https://api.firecloud.org/#!/Method_Configurations/copyToMethodRepo
        DUPLICATE: https://api.firecloud.org/#!/Method_Repository/copyToMethodRepo
    """

    body = {
        "configurationNamespace" : to_cnamespace,
        "configurationName"      : to_config,
        "sourceNamespace"        : from_cnamespace,
        "sourceName"             : from_config
    }
    uri = "workspaces/{0}/{1}/method_configs/copyToMethodRepo".format(
                                                    namespace, workspace)
    return __post(uri, json=body)

###########################
### 1.3 Method Repository
###########################

def list_repository_methods(namespace=None, name=None, snapshotId=None):
    """List method(s) in the methods repository.

    Args:
        namespace (str): Method Repository namespace
        name (str): method name
        snapshotId (int): method snapshot ID

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/listMethodRepositoryMethods
    """
    params = {k:v for (k,v) in locals().items() if v is not None}
    return __get("methods", params=params)

def list_repository_configs(namespace=None, name=None, snapshotId=None):
    """List configurations in the methods repository.

    Args:
        namespace (str): Method Repository namespace
        name (str): config name
        snapshotId (int): config snapshot ID

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/listMethodRepositoryConfigurations
    """
    params = {k:v for (k,v) in locals().items() if v is not None}
    return __get("configurations", params=params)

def get_config_template(namespace, method, version):
    """Get the configuration template for a method.

    The method should exist in the methods repository.

    Args:
        namespace (str): Method's namespace
        method (str): method name
        version (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/createMethodTemplate
    """

    body = {
        "methodNamespace" : namespace,
        "methodName"      : method,
        "methodVersion"   : int(version)
    }
    return __post("template", json=body)

def get_inputs_outputs(namespace, method, snapshot_id):
    """Get a description of the inputs and outputs for a method.

    The method should exist in the methods repository.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        snapshot_id (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/getMethodIO
    """

    body = {
        "methodNamespace" : namespace,
        "methodName"      : method,
        "methodVersion"   : snapshot_id
    }
    return __post("inputsOutputs", json=body)

def get_repository_config(namespace, config, snapshot_id):
    """Get a method configuration from the methods repository.

    Args:
        namespace (str): Methods namespace
        config (str): config name
        snapshot_id (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/getMethodRepositoryConfiguration
    """
    uri = "configurations/{0}/{1}/{2}".format(namespace, config, snapshot_id)
    return __get(uri)

def get_repository_method(namespace, method, snapshot_id, wdl_only=False):
    """Get a method definition from the method repository.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        version (int): snapshot_id of the method
        wdl_only (bool): Exclude metadata

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/get_api_methods_namespace_name_snapshotId
    """
    uri = "methods/{0}/{1}/{2}?onlyPayload={3}".format(namespace, method,
                                                       snapshot_id,
                                                       str(wdl_only).lower())
    return __get(uri)

def update_repository_method(namespace, method, synopsis, wdl, doc=None,
                             comment=""):
    """Create/Update workflow definition.

    FireCloud will create a new snapshot_id for the given workflow.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        synopsis (str): short (<80 char) description of method
        wdl (file): Workflow Description Language file
        doc (file): (Optional) Additional documentation
        comment (str): (Optional) Comment specific to this snapshot

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/post_api_methods

    """
    with open(wdl, 'r') as wf:
        wdl_payload = wf.read()
    if doc is not None:
        with open (doc, 'r') as df:
            doc = df.read()

    body = {
        "namespace": namespace,
        "name": method,
        "entityType": "Workflow",
        "payload": wdl_payload,
        "documentation": doc,
        "synopsis": synopsis,
        "snapshotComment": comment
    }

    return __post("methods",
                  json={key: value for key, value in body.items() if value})

def delete_repository_method(namespace, name, snapshot_id):
    """Redacts a method and all of its associated configurations.

    The method should exist in the methods repository.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        snapshot_id (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/delete_api_methods_namespace_name_snapshotId
    """
    uri = "methods/{0}/{1}/{2}".format(namespace, name, snapshot_id)
    return __delete(uri)

def delete_repository_config(namespace, name, snapshot_id):
    """Redacts a configuration and all of its associated configurations.

    The configuration should exist in the methods repository.

    Args:
        namespace (str): configuration namespace
        configuration (str): configuration name
        snapshot_id (int): snapshot_id of the configuration

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/delete_api_configurations_namespace_name_snapshotId
    """
    uri = "configurations/{0}/{1}/{2}".format(namespace, name, snapshot_id)
    return __delete(uri)

def get_repository_method_acl(namespace, method, snapshot_id):
    """Get permissions for a method.

    The method should exist in the methods repository.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        version (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/getMethodACL
    """
    uri = "methods/{0}/{1}/{2}/permissions".format(namespace,method,snapshot_id)
    return __get(uri)

def update_repository_method_acl(namespace, method, snapshot_id, acl_updates):
    """Set method permissions.

    The method should exist in the methods repository.

    Args:
        namespace (str): Methods namespace
        method (str): method name
        snapshot_id (int): snapshot_id of the method
        acl_updates (list(dict)): List of access control updates

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/setMethodACL
    """

    uri = "methods/{0}/{1}/{2}/permissions".format(namespace,method,snapshot_id)
    return __post(uri, json=acl_updates)

def get_repository_config_acl(namespace, config, snapshot_id):
    """Get configuration permissions.

    The configuration should exist in the methods repository.

    Args:
        namespace (str): Configuration namespace
        config (str): Configuration name
        snapshot_id (int): snapshot_id of the method

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/getConfigACL
    """
    uri = "configurations/{0}/{1}/{2}/permissions".format(namespace,
                                                    config, snapshot_id)
    return __get(uri)

def update_repository_config_acl(namespace, config, snapshot_id, acl_updates):
    """Set configuration permissions.

    The configuration should exist in the methods repository.

    Args:
        namespace (str): Configuration namespace
        config (str): Configuration name
        snapshot_id (int): snapshot_id of the method
        acl_updates (list(dict)): List of access control updates

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/setConfigACL
    """

    uri = "configurations/{0}/{1}/{2}/permissions".format(namespace,
                                                config, snapshot_id)
    return __post(uri, json=acl_updates)

def get_method_configurations(namespace, method_name):
    """Get method configurations.

    Given the namespace/name of a method, returns all configurations in
    the repository that reference that method.

    Args:
        namespace (str): Method namespace
        method_name (str): Method name

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/get_api_methods_namespace_name_configurations

    """

    uri = "methods/{0}/{1}/configurations".format(namespace, method_name)

    return __get(uri)

def get_api_methods_definitions():
    """List method definitions.

    List method definitions - i.e. unique namespace/name pairs -
    with counts of snapshots and associated configurations

    Swagger:
        https://api.firecloud.org/#!/Method_Repository/get_api_methods_definitions

    """

    uri = "methods/definitions"

    return __get(uri)

#################
### 1.4 Profile
#################

def list_billing_projects():
    """list billing projects for a user.

    Swagger:
        https://api.firecloud.org/#/BillingV2/listUserBillingProjectsV2
    """
    return __get("billing/v2")

def get_proxy_group(email=None):
    """Returns the proxy group email for the current user

    Args:
        email (str): User email whose proxy group to retrieve
                     if None uses current user email

    Swagger:
    https://api.firecloud.org/#/Profile/getProxyGroup
    """
    if email is None:
        email = whoami()
    uri = "proxyGroup/{}".format(email)
    return __get(uri)

################
### 1.5 Status
################

def get_status():
    """Request the status of FireCloud services.

    Swagger:
        https://api.firecloud.org/#!/Status/status
    """
    root_url = fcconfig.root_url.rpartition("api")[0]
    return __get("status", root_url=root_url)

def health():
    """Health of FireCloud API.

    Swagger:
        https://api.firecloud.org/#!/Status/health
    """
    root_url = fcconfig.root_url.rpartition("api")[0]
    return __get("health", root_url=root_url)

######################
### 1.6 Submissions
######################

def list_submissions(namespace, workspace):
    """List submissions in FireCloud workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Submissions/listSubmissions
    """
    uri = "workspaces/{0}/{1}/submissions".format(namespace, workspace)
    return __get(uri)

def create_submission(wnamespace, workspace, cnamespace, config,
                      entity=None, etype=None, expression=None,
                      use_callcache=True, delete_intermediate_output_files=False,
                      use_reference_disks=False, memory_retry_multiplier=0,
                      workflow_failure_mode="", user_comment=""):
    """Submit job in FireCloud workspace.

    Args:
        wnamespace (str): project to which workspace belongs
        workspace (str): Workspace name
        cnamespace (str): Method configuration namespace
        config (str): Method configuration name
        entity (str): Entity to submit job on. Should be the same type as
            the root entity type of the method config, unless an
            expression is used
        etype (str): Entity type of root_entity
        expression (str): Instead of using entity as the root entity,
            evaluate the root entity from this expression.
        use_callcache (bool): use call cache if applicable (default: true)
        delete_intermediate_output_files (bool): Whether or not to delete
            intermediate output files when the workflow completes. See Cromwell
            docs (https://cromwell.readthedocs.io/en/develop/wf_options/Google)
            for more information
        use_reference_disks (bool): Whether or not to use pre-built disks for
            common genome references
        memory_retry_multiplier (float): If a task fails due to running out of
            memory and the task has maxRetries in its runtime attributes, then
            it will be retried with its memory multiplied by this amount. See
            Cromwell docs
            (https://cromwell.readthedocs.io/en/develop/cromwell_features/RetryWithMoreMemory)
            for more information
        workflow_failure_mode (str): What happens after a task fails. Choose from
            ContinueWhilePossible and NoNewCalls. Defaults to NoNewCalls if not
            specified. See Cromwell docs
            (https://cromwell.readthedocs.io/en/develop/execution/ExecutionTwists/#failure-modes)
            for more information.
        user_comment: Freeform user defined description, optional (max length
            1000 characters)

    Swagger:
        https://api.firecloud.org/#!/Submissions/createSubmission
    """

    uri = "workspaces/{0}/{1}/submissions".format(wnamespace, workspace)
    body = {
        "methodConfigurationNamespace" : cnamespace,
        "methodConfigurationName" : config,
         "useCallCache" : use_callcache
    }

    if etype:
        body['entityType'] = etype

    if entity:
        body['entityName'] = entity

    if expression:
        body['expression'] = expression

    if delete_intermediate_output_files:
        body['deleteIntermediateOutputFiles'] = delete_intermediate_output_files

    if use_reference_disks:
        body['useReferenceDisks'] = use_reference_disks

    if memory_retry_multiplier:
        body['memoryRetryMultiplier'] = memory_retry_multiplier

    if workflow_failure_mode:
        body['workflowFailureMode'] = workflow_failure_mode

    if user_comment:
        body['userComment'] = user_comment

    return __post(uri, json=body)

def abort_submission(namespace, workspace, submission_id):
    """Abort running job in a workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        submission_id (str): Submission's unique identifier

    Swagger:
        https://api.firecloud.org/#!/Submissions/deleteSubmission
    """
    uri = "workspaces/{0}/{1}/submissions/{2}".format(namespace,
                                        workspace, submission_id)
    return __delete(uri)

def get_submission(namespace, workspace, submission_id):
    """Request submission information.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        submission_id (str): Submission's unique identifier

    Swagger:
        https://api.firecloud.org/#!/Submissions/monitorSubmission
    """
    uri = "workspaces/{0}/{1}/submissions/{2}".format(namespace,
                                            workspace, submission_id)
    return __get(uri)

def get_workflow_metadata(namespace, workspace, submission_id, workflow_id):
    """Request the metadata for a workflow in a submission.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        submission_id (str): Submission's unique identifier
        workflow_id (str): Workflow's unique identifier.

    Swagger:
        https://api.firecloud.org/#!/Submissions/workflowMetadata
    """
    uri = "workspaces/{0}/{1}/submissions/{2}/workflows/{3}".format(namespace,
                                            workspace, submission_id, workflow_id)
    return __get(uri)

def get_workflow_outputs(namespace, workspace, submission_id, workflow_id):
    """Request the outputs for a workflow in a submission.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        submission_id (str): Submission's unique identifier
        workflow_id (str): Workflow's unique identifier.

    Swagger:
        https://api.firecloud.org/#!/Submissions/workflowOutputsInSubmission
    """
    uri = "workspaces/{0}/{1}/".format(namespace, workspace)
    uri += "submissions/{0}/workflows/{1}/outputs".format(submission_id,
                                                            workflow_id)
    return __get(uri)

def get_workflow_cost(namespace, workspace, submission_id, workflow_id):
    """Request the cost for a workflow in a submission.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        submission_id (str): Submission's unique identifier
        workflow_id (str): Workflow's unique identifier.

    Swagger:
        https://api.firecloud.org/#!/Submissions/workflowCostInSubmission
    """
    uri = "workspaces/{0}/{1}/".format(namespace, workspace)
    uri += "submissions/{0}/workflows/{1}/cost".format(submission_id,
                                                         workflow_id)
    return __get(uri)

def get_submission_queue():
    """ List workflow counts by queueing state.

    Swagger:
        https://api.firecloud.org/#!/Submissions/workflowQueueStatus
    """
    return __get("submissions/queueStatus")

#####################
### 1.7 Workspaces
#####################

def list_workspaces(fields=None):
    """Request list of FireCloud workspaces.

    Args:
        fields (str): a comma-delimited list of values that limits the
            response payload to include only those keys and exclude other
            keys (e.g., to include {"workspace": {"attributes": {...}}},
            specify "workspace.attributes").

    Swagger:
        https://api.firecloud.org/#!/Workspaces/listWorkspaces
    """
    if fields is None:
        return __get("workspaces")
    else:
        return __get("workspaces", params={"fields": fields})

def get_storage_cost(namespace, workspace):
    """Request average monthly storage cost for workspace.
    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
    Swagger:
        https://api.firecloud.org/#/Workspaces/getStorageCostEstimate
    """
    uri = "workspaces/{0}/{1}/storageCostEstimate".format(namespace, workspace)
    return __get(uri)

def get_bucket_usage(namespace, workspace):
    """Request google bucket usage for workspace.
    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
    Swagger:
        https://api.firecloud.org/#!/Workspaces/getBucketUsage
    """
    uri = "workspaces/{0}/{1}/bucketUsage".format(namespace, workspace)
    return __get(uri)

def create_workspace(namespace, name, authorizationDomain="", attributes=None,
                     noWorkspaceOwner=False, bucketLocation=""):
    """Create a new FireCloud Workspace.

    Args:
        namespace (str): The namespace (billing project) the workspace belongs to
        name (str): The name of the workspace
        authorizationDomain (str|list): The list of groups in the Authorization
                                        Domain (empty if no AD is set)
        attributes (dict): Workspace attributes as key value pairs
        noWorkspaceOwner (bool): Optional, false if not specified. If true, the
                                 workspace is created with a Billing Project
                                 owner but no workspace owner. Requires being a
                                 Billing Project owner.
        bucketLocation (str): Region (NOT multi-region) in which bucket
                              attached to the workspace should be created. If
                              not provided, the bucket will be created in the
                              'US' multi-region.

    Swagger:
        https://api.firecloud.org/#!/Workspaces/createWorkspace
    """

    if not attributes:
        attributes = dict()

    body = {
        "namespace": namespace,
        "name": name,
        "attributes": attributes
    }

    if authorizationDomain:
        if isinstance(authorizationDomain, string_types):
            authDomain = [{"membersGroupName": authorizationDomain}]
        else:
            authDomain = [{"membersGroupName": groupName} for groupName in authorizationDomain]
    else:
        authDomain = []

    body["authorizationDomain"] = authDomain

    if noWorkspaceOwner:
        body["noWorkspaceOwner"] = noWorkspaceOwner

    if bucketLocation:
        body["bucketLocation"] = bucketLocation

    return __post("workspaces", json=body)

def delete_workspace(namespace, workspace):
    """Delete FireCloud Workspace.

    Note: This action is not reversible. Be careful!

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Workspaces/deleteWorkspace
    """
    uri = "workspaces/{0}/{1}".format(namespace, workspace)
    return __delete(uri)

def get_workspace(namespace, workspace, fields=None):
    """Request FireCloud Workspace information.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        fields (str): a comma-delimited list of values that limits the
            response payload to include only those keys and exclude other
            keys (e.g., to include {"workspace": {"attributes": {...}}},
            specify "workspace.attributes").

    Swagger:
        https://api.firecloud.org/#!/Workspaces/getWorkspace
    """
    uri = "workspaces/{0}/{1}".format(namespace, workspace)
    if fields is None:
        return __get(uri)
    else:
        return __get(uri, params={"fields": fields})

def get_workspace_acl(namespace, workspace):
    """Request FireCloud access aontrol list for workspace.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Workspaces/getWorkspaceAcl
    """
    uri = "workspaces/{0}/{1}/acl".format(namespace, workspace)
    return __get(uri)

def update_workspace_acl(namespace, workspace, acl_updates, invite_users_not_found=False):
    """Update workspace access control list.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        acl_updates (list(dict)): Acl updates as dicts with two keys:
            "email" - Firecloud user email
            "accessLevel" - one of "OWNER", "READER", "WRITER", "NO ACCESS"
            Example: {"email":"user1@mail.com", "accessLevel":"WRITER"}
        invite_users_not_found (bool): true to invite unregistered users, false to ignore

    Swagger:
        https://api.firecloud.org/#!/Workspaces/updateWorkspaceACL
    """
    uri = "{0}workspaces/{1}/{2}/acl?inviteUsersNotFound={3}".format(fcconfig.root_url,
                                                namespace, workspace, str(invite_users_not_found).lower())
    headers = _fiss_agent_header({"Content-type":  "application/json"})
    # FIXME: create __patch method, akin to __get, __delete etc
    return __SESSION.patch(uri, headers=headers, data=json.dumps(acl_updates))

def clone_workspace(from_namespace, from_workspace, to_namespace, to_workspace,
                    authorizationDomain="", copyFilesWithPrefix=None):
    """Clone a FireCloud workspace.

    A clone is a shallow copy of a FireCloud workspace, enabling
    easy sharing of data, such as TCGA data, without duplication.

    Args:
        from_namespace (str):  project (namespace) to which source workspace belongs
        from_workspace (str): Source workspace's name
        to_namespace (str):  project to which target workspace belongs
        to_workspace (str): Target workspace's name
        authorizationDomain: (str) required authorization domains
        copyFilesWithPrefix: (str) prefix of bucket objects to copy to the destination workspace

    Swagger:
        https://api.firecloud.org/#!/Workspaces/cloneWorkspace
    """

    if authorizationDomain:
        if isinstance(authorizationDomain, string_types):
            authDomain = [{"membersGroupName": authorizationDomain}]
        else:
            authDomain = [{"membersGroupName": authDomain} for authDomain in authorizationDomain]
    else:
        authDomain = []

    body = {
        "namespace": to_namespace,
        "name": to_workspace,
        "attributes": dict(),
        "authorizationDomain": authDomain,
    }

    if copyFilesWithPrefix is not None:
        body["copyFilesWithPrefix"] = copyFilesWithPrefix

    uri = "workspaces/{0}/{1}/clone".format(from_namespace, from_workspace)
    return __post(uri, json=body)

def lock_workspace(namespace, workspace):
    """Lock FireCloud workspace, making it read-only.

    This prevents modifying attributes or submitting workflows
    in the workspace. Can be undone with unlock_workspace()

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Workspaces/lockWorkspace
    """
    uri = "workspaces/{0}/{1}/lock".format(namespace, workspace)
    return __put(uri)

def unlock_workspace(namespace, workspace):
    """Unlock FireCloud workspace.

    Enables modifications to a workspace. See lock_workspace()

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name

    Swagger:
        https://api.firecloud.org/#!/Workspaces/unlockWorkspace
    """
    uri = "workspaces/{0}/{1}/unlock".format(namespace, workspace)
    return __put(uri)

def update_workspace_attributes(namespace, workspace, attrs):
    """Update or remove workspace attributes.

    Args:
        namespace (str): project to which workspace belongs
        workspace (str): Workspace name
        attrs (list(dict)): List of update operations for workspace attributes.
            Use the helper dictionary construction functions to create these:

            _attr_set()       : Set/Update attribute
            _attr_rem()       : Remove attribute
            _attr_ladd()      : Add member to list attribute
            _attr_lrem()      : Remove member from list attribute
            _attr_vlcreate()  : Create a value-list attribute
            _attr_erlcreate() : Create an entity-reference list attribute

    Swagger:
        https://api.firecloud.org/#!/Workspaces/updateAttributes
    """
    headers = _fiss_agent_header({"Content-type":  "application/json"})
    uri = "{0}workspaces/{1}/{2}/updateAttributes".format(fcconfig.root_url,
                                                        namespace, workspace)
    body = json.dumps(attrs)

    # FIXME: create __patch method, akin to __get, __delete etc
    return __SESSION.patch(uri, headers=headers, data=body)

# Helper functions to create attribute update dictionaries

def _attr_set(attr, value):
    """Create an 'update' dictionary for update_workspace_attributes() and
    update_entity()"""
    return {
        "op"                 : "AddUpdateAttribute",
        "attributeName"      : attr,
        "addUpdateAttribute" : value
   }

def _attr_rem(attr):
    """Create a 'remove' dictionary for update_workspace_attributes() and
    update_entity()"""
    return {
        "op"             : "RemoveAttribute",
        "attributeName"  : attr
    }

def _attr_ladd(attr, value):
    """Create a 'list add' dictionary for update_workspace_attributes() and
    update_entity()"""
    return {
        "op"                : "AddListMember",
        "attributeListName" : attr,
        "newMember"         : value
    }

def _attr_lrem(attr, value):
    """Create a 'list remove' dictionary for update_workspace_attributes() and
    update_entity()"""
    return {
        "op"                : "RemoveListMember",
        "attributeListName" : attr,
        "removeMember"      : value,
    }

def _attr_vlcreate(attr):
    """Create a 'value-list create' dict for update_workspace_attributes() and
    update_entity()"""
    return {
        "op"            : "CreateAttributeValueList",
        "attributeName" : attr
    }

def _attr_erlcreate(attr):
    """Create a 'entity-reference list create' dictionary for
       update_workspace_attributes() and update_entity()"""
    return {
        "op"                : "CreateAttributeEntityReferenceList",
        "attributeListName" : attr
    }

#####################
### 1.8 Groups
#####################

def get_groups():
    """Get the list of the groups that the caller is a member of

    Swagger:
        https://api.firecloud.org/#!/Groups/getGroups
    """
    return __get('groups')

def get_group(group):
    """View the members in a group (must be an Admin of the group)

    Args:
        group (str): Group name

    Swagger:
        https://api.firecloud.org/#!/Groups/getGroup
    """
    uri = "groups/{0}".format(group)
    return __get(uri)

def delete_group(group):
    """Delete a group that the caller owns

    Args:
        group (str): Group name

    Swagger:
        https://api.firecloud.org/#!/Groups/deleteGroup
    """
    uri = "groups/{0}".format(group)
    return __delete(uri)

def create_group(group):
    """Create a new group

    Args:
        group (str): Group name

    Swagger:
        https://api.firecloud.org/#!/Groups/createGroup
    """
    uri = "groups/{0}".format(group)
    return __post(uri)

def add_user_to_group(group, role, email):
    """Add a user to a group the caller owns

    Args:
        group (str): Group name
        role (str) : Role of user for group; either 'member' or 'admin'
        email (str): Email of user or group to add

    Swagger:
        https://api.firecloud.org/#!/Groups/addUserToGroup
    """
    uri = "groups/{0}/{1}/{2}".format(group, role, email)
    return __put(uri)

def remove_user_from_group(group, role, email):
    """Remove a user from a group the caller owns

    Args:
        group (str): Group name
        role (str) : Role of user for group; either 'member' or 'admin'
        email (str): Email of user or group to remove

    Swagger:
        https://api.firecloud.org/#!/Groups/removeUserFromGroup
    """
    uri = "groups/{0}/{1}/{2}".format(group, role, email)
    return __delete(uri)

def request_access_to_group(group):
    """Request access to a group

    Args:
        group (str): Group name

    Swagger:
        https://api.firecloud.org/#!/Groups/requestAccessToGroup
    """
    uri = "groups/{0}/requestAccess".format(group)
    return __post(uri)


from tinkoff.invest import Quotation
from datetime import timedelta
from dataclasses import asdict
from typing import Union
import math
import numpy as np

def qtof(q: Union[Quotation, dict]) -> float:
    if type(q) is dict:
        return float(q['units'] + q['nano'] * 10**-9)
    elif type(q) is Quotation:
        return float(q.units + q.nano * 10**-9)
    else:
        raise NotImplementedError

def ftoq(f: float) -> Quotation:
    return Quotation(*math.modf(f))

def ema(data, window):
    # numpy_ewma_vectorized_v2
    # thx to Divakar on stackoverflow
    alpha = 2 / (window + 1.)
    alpha_rev = 1 - alpha
    n = data.shape[0]

    pows = alpha_rev**(np.arange(n+1))

    scale_arr = 1 / pows[:-1]
    offset = data[0] * pows[1:]
    pw0 = alpha * alpha_rev**(n-1)

    mult = data * pw0 * scale_arr
    cumsums = mult.cumsum()
    out = offset + cumsums * scale_arr[::-1]
    return out

def ema_new_val(new_val, prev_ema, window):
    alpha = 2 / (window + 1.)
    alpha_rev = 1 - alpha
    return new_val * alpha + prev_ema * alpha_rev

def process_candle(candle):
    try:
        candle = asdict(candle)
        for attr in ['open', 'high', 'low', 'close']:
            candle[attr] = qtof(candle[attr])
        candle['time'] += timedelta(hours=3)  # converting to Moscow time
        return candle
    except Exception:
        return None


"""
Create all table that the project needs
"""
import os
from flask import Flask, render_template, request
from sqlalchemy import create_engine, text
from sqlalchemy.orm import scoped_session, sessionmaker
from env_init import database_flask_env_init

database_flask_env_init()

# Set up database
engine = create_engine(os.getenv("DATABASE_URL"))
db = scoped_session(sessionmaker(bind=engine))

def user_init():
    db.execute(text('drop table if exists users'))
    sql = text('''create table users(
            id serial primary key,
            username varchar not null unique,
            password varchar not null
        );''')
    db.execute(sql)
    db.commit()

def book_init():
    db.execute(text('drop table if exists books'))
    sql = text('''create table books(
            id serial primary key,
            isbn varchar not null unique,
            title varchar not null,
            author varchar not null,
            year varchar not null
        );''')
    db.execute(sql)
    db.commit()

def review_init():
    db.execute(text('drop table if exists reviews'))
    sql = text('''create table reviews(
            id serial primary key,
            username varchar references users(username) not null,
            isbn varchar references books(isbn) not null,
            comment varchar not null,
            star varchar not null
        );''')
    db.execute(sql)
    db.commit()

def main():
    review_init()

if __name__ == "__main__":
    main()


from django.db import models
from student.models import Student

class Applyleave(models.Model):
    id=models.AutoField(primary_key=True)
    studentId = models.ForeignKey(
        Student,
        on_delete=models.CASCADE,
    )
    startDate= models.DateField()
    endDate= models.DateField()
    status= models.BooleanField()


#！ --*--coding:utf-8--*--
import socket,threading
from multiprocessing import Process

# def work(sock,addr):
# 	print(sock)
# 	print(addr)
# 	while True:
# 		pass

# def tcplink(sock,addr):
# 	print("Accept new connection from %s:%s" % addr)
# 	sock.send(b"welcome")
# 	while True:
# 		data = sock.recv(1024)
# 		time.sleep(1)
# 		if not data or data.decode("utf-8")=="exit":
# 			print("exit")
# 			break
# 		sock.send("Hello ,%s" % data.decode("utf-8").encode("utf-8"))
# 	sock.close()

# client message(utf-8)   server message(unicode) message.decode(utf-8)

# main_process
# sub_thread sub_process
# 异步 ： in parallel 并行
# 同步 ： in queue 串行

# gui

def recive(sock):
	while True:
		rm = sock.recv(1024)
		print("\n"+str(rm)+"\n")
		print("                              input__:")

def send(sock):
	while True:
		instr = raw_input("                              input__:")
		sock.send(instr)

def session(sock):
		#print(sock.recv(1024))
		#sock.send(b"welcome")
	t1 = threading.Thread(target=recive,args=(sock,))
	t2= threading.Thread(target=send,args=(sock,))
	t1.start()
	t2.start()

if __name__=="__main__":
	s = socket.socket()
	# 127.0.0.1 ---- 规定127.0.0.1代表本机ip
	s.bind(("127.0.0.1",700))
	# 监听
	s.listen(5)
	while True:
		sock,addr = s.accept()
		#print(sock.recv(1024))
		#sock.send(b"welcome")
		#p = Process(target=session,args=(sock,addr))
		#p.start()
		t = threading.Thread(target=session,args=(sock,))
		t.start()

# ctrl + alt + delete

# pickle.PicklingError: Can't pickle
# pickle 无法将一个socket对象进行序列化 这是为什么？
# 这是由window操作系统导致
# 多进程 通过os.fork() 就可以在linux当中启动一个子进程，但是在window中
# 这个方式是不行的
# 怎么在windows中解决这个bug  ---- 将多进程变成多线程

# pickle 序列化  --- 将一个python的对象，进行序列化之后，写到内存里，
# 或者写到二进制文件里，或者在网络中进行传输
# dict 字典 d = dict() tcp：面向字节流的一种传输协议
# 怎样将一个内存对象变成字节流 ：pickle进行序列化 14节课


from odoo import api, fields, models, _

class PurchaseOrder(models.Model):
    _inherit = 'purchase.order'

    status_settling = fields.Selection(string='Status of Settling',
                                       selection=[
                                           ('without_filing', 'Without Filing'),
                                           ('partially_settled',
                                               'Partially Settled'),
                                           ('settled', 'Settled')
                                       ], required=True, default='without_filing')


from setuptools import setup, find_packages

# setup metainfo
libinfo_content = open('__init__.py', 'r').readlines()
version_line = [l.strip() for l in libinfo_content if l.startswith('__version__')][0]
exec(version_line)  # produce __version__

setup(
    name='zerotasq',
    version=__version__,
    description='Stream tasks to multiple worker processes using ZeroMQ.',
    url='https://github.com/chschock/zerotasq',
    long_description=open('README.md', 'r').read(),
    long_description_content_type='text/markdown',
    author='Christoph Schock',
    author_email='chschock@gmail.com',
    license='MIT',
    packages=find_packages(),
    zip_safe=False,
    install_requires=[
        'pyzmq>=17.1.0',
        'termcolor>=1.1'
    ],
    classifiers=(
        'Programming Language :: Python :: 3.6',
        'License :: MIT License',
        'Operating System :: Linux',
        'Topic :: Scientific/Engineering',
    ),
    keywords='multiprocessing stream processing zeromq load balancer',
)


#Eric Hurchey
#Programming Assignment 4
#03/29/2022

import random
FILE='''I am typing a bit of nonsense in order to test my text justification program. The goal is to split a file into paragraphs using a unique separator, and then for each paragraph, split it into lines and add spaces so the each line prints the same length.

We are ignoring punctuation and carriage returns. The only caveat is that if the last line only has one word in it, it should be left justified.

The goal of the program is to be a primitive simulation of one of the functions of a word processor, the most ubiquitous of software applications which used to have a vibrant ecosystem of variants, but which has collapsed into a monopoly governed by Microsoft word. Other parts of word processing require paging files into buffers, spell checking, display management and keyboard processing.'''

def printpara(words,length): #call a function to print out the paragraphs in order
    line = []
    spaces = []
    while len(words) != 0:
        line.append(getline(words,length))
    for i in range(len(line)):
        for x in range(len(line[i])):
            spaces=[[1]*len(line[x]) for x in range(len(line))]
    for q in range(len(spaces)):
        spaces[q][len(spaces[q]) - 1]=0
    spaces = addspaces(line,spaces,length)
    printline(line,spaces)

def printline(line,spaces): #call a function to print out the desired line length with no spaces
    for i in range(len(line)): #the length of each line will print out the needed spaces
        print(" ")
        for x in range(len(line[i])): #here, each line will make sure that the lines are even
            print(line[i][x] + (" " * spaces[i][x]), end='')

def addspaces(line, spaces, length): #call a function to print out the amount of spaces that will be printed within each line
    total_line = 0
    for i in range(len(line)):
        total_line = 0
        for e in range(len(line[i])):
            total_line += 1 + len(line[i][e])
        total_line -= 1
        while length > total_line:
            if i != len(line) - 1:
                m = random.randint(0, len(spaces[i]) - 2)
                spaces[i][m] += 1
                total_line += 1
            elif i == len(line) - 1:
                spaces[i][len(spaces[i]) - 1] = length - total_line
                length = total_line
    return spaces

def getline(words,length): #a given call function
    ans = []
    total = 0
    while (length>total) and 0 != len(words):
        word = words.pop(0)
        total += len(word) + 1 #add 1 for the space
        ans.append(word)
 #now we are one word too long
    if total > length:
        words.insert(0,ans.pop())
    return ans

def main(FILE,length): #calling a main function
    test = FILE.split('\n\n')
    for i in test:
        printpara(i.split(), length)
        print() #this is very important that this is within the for loop because if it's not, then there will not be any spaces between the paragraphs

main(FILE, 40) #calling back a wanted length
print() #just wanting to clearly seperate one file length from another, this is not needed, but just for clarity sake and to visual see the difference
main(FILE, 60) #calling back a wanted length


import json
import os

def name_to_var(lang_name):
    return (
        lang_name.upper()
        .replace(" ", "_")
        .replace("(", "")
        .replace(")", "")
        .replace("-", "_")
        .replace("'", "")
    )

def load_json(name):
    filepath = os.path.join(os.path.dirname(__file__), "langs_coverage", f"{name}.json")
    return json.loads(open(filepath).read())

auto_gen_comment = f"# Auto-generated. Do not modify, use {__file__} instead.\n"

name2json = {}

for name in ["m2m100", "mbart50", "nllb200"]:
    name2json[name] = lang2code = load_json(name)

    with open(f"./dl_translate/lang/{name}.py", "w") as f:
        f.write(auto_gen_comment)
        for lang, code in lang2code.items():
            f.write(f'{name_to_var(lang)} = "{lang}"\n')

with open("./dl_translate/_pairs.py", "w") as f:
    f.write(auto_gen_comment)

    for name, lang2code in name2json.items():
        f.write(f"_PAIRS_{name.upper()} = {tuple(lang2code.items())}\n")


from typing import Callable
import tensorflow as tf
import numpy as np

def hard_swish(x: tf.Tensor):
  with tf.name_scope('hard_swish'):
    return x * tf.nn.relu6(x + np.float32(3)) * np.float32(1. / 6.)

class FuncAsLayer:
    """
    This wrapper is a common denominator for function activations and layer activations. In tensorflow we have
    the activation PReLU which doesn't have function counterpart
    """
    def __init__(self, func: Callable):
        self.func = func

    def __call__(self):
        return tf.keras.layers.Activation(self.func)

ACTIVATION_FUNCS = dict(
    re=tf.keras.layers.ReLU,
    r6=FuncAsLayer(tf.nn.relu6),
    pre=tf.keras.layers.PReLU,
    hs=FuncAsLayer(hard_swish),
    sw=FuncAsLayer(tf.nn.swish)
)


# -*- coding: utf-8 -*-
"""
Created on Fri Dec  2 09:27:39 2022

@author: schmi
"""
# A B C = ROCK PAPER SCISSORS
# X Y Z = ROCK PAPER SCISSORS

import numpy as np

# First index is my choice, second is enemy choice
return_points = np.array([[4,1,7],[8,5,2],[3,9,6]])

cast = {'A':0,'B':1,'C':2, 'X':0,'Y':1,'Z':2}

file = open('input.txt', 'r')
score = 0
for play in file.readlines():
    enemy = play[0]
    me = play[2]
    score = score + return_points[cast[me],cast[enemy]]
print(score)

# Part two
# A B C = ROCK PAPER SCISSORS
# X Y Z = LOSE DRAW  WIN

# First index is enemy choice, second is my choice
return_points = np.array([[3,4,8],[1,5,9],[2,6,7]])

file = open('input.txt', 'r')
score = 0
for play in file.readlines():
    enemy = play[0]
    me = play[2]
    score = score + return_points[cast[enemy],cast[me]]
print(score)


### Goal: Read in an Image an autoamtically detect the Scale used:
## Caveats: Maybe we will need manual input of scale,
##Do we perform this per Image or once provided the person doesn't change the zoom

####
#### Read in all Images and save their respective paths and filenames
def Images_list(path_to_images):
  import os as os
  PureNames = []
  Image_names = []
  for root, dirs, files in os.walk(path_to_images, topdown=False):
    #print(dirs, files)
    for name in files:
      #print(os.path.join(root, name))
      Image_names.append(os.path.join(root, name))
  for x in range(0,len(Image_names)):
    PureNames.append(Image_names[x].split("/")[-1])
  return Image_names, PureNames

def getLineLength(Image_names):
  ## Gaussiaun blur and image read ##
  # Detects lines used for scale, always
  # counts the saves the shortest scale
  # in image
  # Input: image names in folder
  # Output: line coordinates and length
  ###################################

  import cv2 as cv2
  import numpy as np
  from PIL import Image
  import matplotlib.pyplot as plt

  list_of_lengths = []
  list_of_images = []
  line_Coors = []
  list_of_lines = []
  for x in range(len(Image_names)):
      img = cv2.imread(Image_names[x])

      gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
      #thresh, gray_thresh = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV)
      #thresh = cv2.threshold()

      ## why should we blur? Image lines should be sharp -> thresholding should be the most effective
      ## In thresholding we need to consider colors but should be managable if we make an OTSU and
      ## hard cut off for white backgrounds with white scales
      kernel_size = (5,1) # We do not wnt to blur the x axis as we need the length information
      blur_gray = cv2.GaussianBlur(gray,(kernel_size),0)

      low_threshold = 200
      high_threshold = 300
      edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

      #### Set HoughLinesP Parameters

      rho = 1 # distance resolution in pixels of the Hough grid
      theta = np.pi / 180  # angular resolution in radians of the Hough grid
      threshold = 10  # minimum number of votes (intersections in Hough grid cell)
      min_line_length = 100 # minimum number of pixels making up a line
      max_line_gap = 0  # maximum gap in pixels between connectable line segments
      line_image = np.copy(img) * 0  # creating a blank to draw lines on
      # Run Hough on edge detected image
      # Output "lines" is an array containing endpoints of detected line segments

      lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),min_line_length, max_line_gap)

      if lines is None: ## If no lines are found
        list_of_lengths.append(0)
        line_Coors.append(0)
      else: # if there are lines
        print(lines)
        for line in lines:
          x1, y1, x2, y2 = line[0]
          cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 1)

        combined, Coordinates = group_lines(lines) ## combine close lines

        plt.clf()
        plt.imshow(line_image, cmap = "gray")
        plt.show()

        # Find the index of the min value so we can sort the line coordinates
        Idx = combined.index(min(combined))
        Correct_Coor = Coordinates[0][Idx] ## Get the coordinates of the shortest line
        PixelPerUnit = min(combined) ## take the min value of length as scale

        # Append values to lists
        line_Coors.append(Correct_Coor)
        list_of_lengths.append(PixelPerUnit)
        list_of_images.append(line_image)
        list_of_lines.append(lines)
  return list_of_lengths, line_Coors, list_of_images, list_of_lines ## Works right now with 5% Error reimplement giving coordinates

def group_lines(lines):
    # Combines lines and outputs the new coordiantes
    # Lines are grouped by y and then added to each other
    # with a new mean y
    # Input: Line coordinates [[[...],[...],...]]
    # Output: Line coordinates of combined lines [[[...],[...],...]]
    # Length of the combined lines
    import numpy as np

    groups = []

    # Convert lines to a list if it is a NumPy array
    if isinstance(lines, np.ndarray):
        lines = lines.tolist()
        lines_flat = [line[0] for line in lines]

    # Sort lines based on y-coordinate
    lines_flat.sort(key=lambda line: line[1])

    current_group = [lines_flat[0]]

    ### group them according if their difference is smaller than 5 pixels
    for line in lines_flat[1:]:

        if line[1] - current_group[-1][3] <= 3:
            current_group.append(line)

        else:
            groups.append(current_group)
            current_group = [line]

    groups.append(current_group)
    ### We also want to build the lines and output them too

    fused_lines = [] ## This is gonna be the list containing the lists of lines
    temp_lines = [] # These are the lines per image
    extracted_length = [] # This gonna be the list containing the lists of lenghts

    for group in groups:

        xmin = min(line[0] for line in group)
        xmax = max(line[2] for line in group)
        ymean = int(sum(line[1] for line in group)/len(group))
        extracted_length.append(abs(xmax - xmin)) # Calculate length
        temp_lines.append([xmin, ymean, xmax, ymean]) ## Coordinates of our line

    fused_lines.append(temp_lines)

    return extracted_length, fused_lines

def CutAndColour(Line_coordinates, Image_names):
  # Input: Coordinates of the shortest line (calcualted in getLineLength)
  # Output: Two lists, one containing all colour data in format white (0/1)
  # and a list of the scale cut out based on coordinates
  import cv2 as cv2
  import numpy as np
  import matplotlib.pyplot as plt

  list_of_crops = []
  for x in range(len(Image_names)):
      img = cv2.imread(Image_names[x])

      img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

      TempCoor = Line_coordinates[x] # Assign the Coordinates to a value

      if TempCoor == 0:
        list_of_crops.append(img_gray)

      else:
        ## Cut the image we give 5% in x coordinate as buffer to detect the number

        height, width = img_gray.shape
        buffer = int(width*0.05)

        ## Check if the y value still lies within the range of the image
        if int(TempCoor[1] - buffer) > 0 and int(TempCoor[1] + buffer) < height:

          cropped_img = img_gray[TempCoor[1]-buffer:TempCoor[1]+buffer,TempCoor[0]:TempCoor[2]]

        else: ## If the image is not big enough/the scale too low we pad the size of the buffer
              ## In the unlikely case we also add it on the upper part
          padded_img = np.pad(img_gray, ((buffer, buffer), (0, 0)), mode='constant', constant_values= int(np.average(img_gray)))
          cropped_img = padded_img[TempCoor[1]:TempCoor[1] + 2*buffer,TempCoor[0]:TempCoor[2]]

        # Recalculate the position of the scale which is at position buffer as buffer*2 is the new height
        ## Now we want the colour of the line
        ## So we make the image gray and extract the pixel values of of it

        # How about resizing the image. Thos would allow cv2.Canny
        # on small pixel numbers?
        # Now that we do not need exact values for the scale anymore we can process the image
        #Empty = np.zeros(cropped_img)
        img_normalized = cv2.normalize(cropped_img, None, 0, 255, cv2.NORM_MINMAX)
        blurred = cv2.GaussianBlur(img_normalized, (3,3), sigmaX=0, sigmaY=0) ## Blur important
        # Scale and number should normally be the same colour so Scale_colour = Value_colour

        Can = cv2.Canny(blurred,350,400, L2gradient = True)
        #test = np.invert(Can)

        # Find contours and hierarchy
        contours, hierarchy = cv2.findContours(Can, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        # Create an empty canvas with the same shape as Can
        WhiteCanvas = np.ones_like(Can) * 255

        # Find the outer contours by checking the hierarchy
        outer_contours = []
        for i in range(len(contours)):
            if hierarchy[0][i][3] != -1:  # Contour has a parent (no enclosed regions)
                outer_contours.append(contours[i])

        # Fill the outer contours on the canvas
        Filled = cv2.drawContours(WhiteCanvas, outer_contours, contourIdx=-1, color=(0), thickness=cv2.FILLED)
        ### As spaces in letters are filled in we now want the recreate these spaces
        #contours_fill, hierarchy = cv2.findContours(Can, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        plt.clf()
        plt.imshow(Can, cmap = "gray")
        plt.show()

        list_of_crops.append(Filled)

  return(list_of_crops)

def NormalizeDPI(images, image_names):
  # Resize Cutouts of the scale and enforce an uniform DPI scale ##
  # This helps tesseract ocr-to detect scales more confidently  ###
  # according to documentation
  # Input: Folder_path
  # Output: Saved Scale Images 512x512, 600 DPI at the folder path called
  # Converted Scales as Image_name_ConvScale.jpg

  import os
  from PIL import Image
  import re
  import matplotlib.pyplot as plt

  list_of_standardized_images = []
  directory = "Converted_Scales"
  save_path =  os.path.dirname(image_names[0])
  convert_path = os.path.join(save_path,directory)

  if os.path.exists(convert_path) == False:
    os.mkdir(convert_path)
  i = 1

  for image in range(len(images)):

    result = str(image_names[image]).split('/')[-1]

    try:
        new_file = convert_path + "/" + result[:-4] + "_ConvScale.jpg"

        i = i+1
        im = Image.fromarray(images[image])
        im.thumbnail((512,512))
        im.save(new_file,"JPEG",dpi=(600,600))
        list_of_standardized_images.append(new_file)
    except:
        continue
  return(list_of_standardized_images)

#### No we need to get the Number above the line
# def getUnit(cropped_Image) ### For py Tesseract we need sudo install
### white only needs mask
### We need to check if the scale is white
### Therefore we check if we have more 0 pixels in the mask than 255

def get_Scale(path_to_preprocessed_images, psm_mode=7):
  import pytesseract as pytesseract
  import matplotlib.pyplot as plt
  import cv2 as cv2

  ScaleUnit = []
  pytesseract.pytesseract.tesseract_cmd = r'/usr/bin/tesseract'
  for x in range(len(path_to_preprocessed_images)):

    img = cv2.imread(path_to_preprocessed_images[x])
    #try:
      #gaussian_3 = cv2.GaussianBlur(crop_img, (0, 0), 2.0)
      #unsharp_image = cv2.addWeighted(crop_img, 2.0, gaussian_3, -1.0, 0)
    #except:
      #gaussian_3 = crop_img

    ## tesseract detect

    tesseract_config = r'--oem 3 --psm ' + str(psm_mode) +" -c tessedit_char_whitelist=0123456789."

    ## as we dont no which kind of scale was put in we use all 3 (inverse, contrast for white and MasCorrNormBare) and then compare.
    ## We could find out the colour of the scale when we use the lines, but is it necessary?

    number = pytesseract.image_to_string(img, config= tesseract_config).strip()
    ScaleUnit.append(number)
  return(ScaleUnit)

########### We also should detect mm or µm this can be achieved with pytesseract. but we also can guess as
########### high values should always be µm and low ones mm ## psm 7 is good by far not perfect... Run multiple modes?

def makeDfwithfactors(list_of_names, One_scale,ScaleUnitClean=[], list_of_lengths=[], ConvFactor=0.002139):
  ### This function has two modes. 1) If the user declares taht we only have one
  ### scale we take the most common values of length and unit and 2) if more
  ### than one exist we keep the list as they are.
  ### Then we enter the singular or mutliple values into the df
  import pandas as pd
  LengthOpt = [int(item) for item in list_of_lengths] ## Make linelengths int
  if One_scale == 0:
    Scale_df = pd.DataFrame(list_of_names, columns =['Name'])
    Scale_df["distance_per_pixel"] = ConvFactor
    print(f"Using manual factor of {ConvFactor} px/mm")
    return Scale_df

  elif One_scale > 0:

    if One_scale == 1:
      LengthOpt = max(set(list_of_lengths), key = list_of_lengths.count)
      UnitOpt = max(set(ScaleUnitClean),key = ScaleUnitClean.count)

    else:
	    LengthOpt = list_of_lengths
	    UnitOpt = ScaleUnitClean

    Scale_df = pd.DataFrame(list_of_names, columns =['Name'])
    #print(list_of_lengths)
    Scale_df["metric_length"] = UnitOpt
    Scale_df["scale[px]"] = LengthOpt
    Scale_df["distance_per_pixel"] = Scale_df["metric_length"]/Scale_df["scale[px]"]

    return Scale_df

##### Testing space for image prep
##225 or 468
#### Make Lines to Int ### Do we make a config for all Codes?
### 0 = no scale native resolution of 2.8 px/µm with standard zoom (If you know the configuration of
### your own microscope enter it into the config.cfg)
### 1 = one scale for all images (automatically detects scale and Unit for every
### image but takes the most common value for all images -> robust)
### 2 = Multiple scales: tries to find a scale for every image. May be prone to
### error
### The new approach is split differently to increase the accuracy
# 1. Make image gray
# 2. Detect the line, find color and length of it
# 3. Cut out sourrinding area
# 3a. Optionally delete only the line
# 4. Normalize DPI
# 5. Binarise, Invert and Threshold the image based
# 6. Detect with Tesseract
# 7. Save in Df/Aig Cellulose 5000 84.jpg

a,b = Images_list("/home/philipp/JPG/")

c,d,q,z = getLineLength(a)
l= CutAndColour(d,a) ### Here we need another approach if we want to use new scales IDK iterate over 5
k = NormalizeDPI(l,a)
h = get_Scale(k,7)

### Two unlikely values: 1 due to missing scale in image and one due to false length
from collections import Counter
Counter(c)
import cv2
import numpy as np
original_image = cv2.imread(a[-6])
plt.imshow(original_image)
plt.show()
# Load the original RGB image
original_image = original_image.astype(np.float32)
kernel_size = (5,1) # We do not wnt to blur the x axis as we need the length information
blur_gray = cv2.GaussianBlur(original_image,(kernel_size),0)
# Extract the R, G, B channels ## Weird the image does show True values an the mask too
b, g, r = cv2.split(original_image)

#b =np.around(b/5, decimals=0)*5
#g =np.around(g/5, decimals=0)*5
#r =np.around(r/5, decimals=0)*5
# Create a mask for pixels where the absolute differences are within the tolerance

# Create a mask for pixels where the absolute differences are within the tolerance
red_mask = np.logical_or(original_image[:, :, 0] > 150, original_image[:, :, 0] < 100)
green_mask = np.logical_or(original_image[:, :, 1] > 150, original_image[:, :, 1] < 100)
blue_mask = np.logical_or(original_image[:, :, 2] > 150, original_image[:, :, 2] < 100)
mask = np.logical_and.reduce((red_mask, green_mask, blue_mask))

len(np.where(mask == True)[1])
# Create a new grayscale image with the same size as the original image
gray_image = np.zeros_like(r, dtype=np.uint8) + 255

# Set the pixels in the grayscale image where the mask is True to black (0)
gray_image[mask] = 0

# Set the pixels in the grayscale image where the mask is False to white (255)
gray_image[np.logical_not(mask)] = 255

plt.clf()
plt.imshow(gray_image, cmap = "gray")
plt.show()

plt.imsave("Waddup.jpg",arr = gray_image, dpi = 600, cmap = "gray")
a[1]

f
import cv2 as cv2
import matplotlib.pyplot as plt
img_normalized = cv2.normalize(l[2], None, 0, 255, cv2.NORM_MINMAX)
blurred = cv2.GaussianBlur(img_normalized, (3,3), sigmaX=0, sigmaY=0) ## Blur important
# Scale and number should normally be the same colour so Scale_colour = Value_colour
for x in range(50,500,50):
  Can = cv2.Canny(blurred,0,x, L2gradient = True)
  plt.clf()
  plt.imshow(Can, cmap = "gray")
  plt.show()

h[1]

l


#John Fletcher
#Group 98

#Part 1 (30 points)
import csv
import urllib.request,os
def getQuakeData(url,orderby,limit):
    part2_url = ("http://earthquake.usgs.gov/fdsnws/event/1/query.csv?starttime=2016-09-23%2000:00:00&endtime=2016-09-30%2023:59:59&minmagnitude=4.5&eventtype=earthquake&orderby="+orderby+"&"+"limit"+"="+limit)

    web_page = urllib.request.urlopen(part2_url)
    lines = web_page.read().decode(errors="replace")
    web_page.close()
    quake_list = lines.split('\n')
    for line in quake_list:
        isinstance(line,str)
    print(quake_list)
    getTopQuakes(quake_list)

#Part 3 (25 points)
import csv
def getTopQuakes(data):

    print("ID"+(" "*11)+"Mag"+(" "*4)+"Location"+(" "*38)+"Time")
    print("-"*90)
    reader = csv.DictReader(data)

    for entry in reader:

        mag_id = entry['id']
        mag = entry['mag']
        location = entry['place']
        if len(mag) == 1:
            mag = (entry['mag']+".0")
        time = entry['time']
        output = "{0}"+(" "*3)+"{1}"+(" "*4)+"{2}"+" "*(90-(len(mag_id)+len(mag)+len(location)+len(time)+3+4))+"{3}"
        print(output.format(mag_id,mag,location,time))

#main

user_url = ("http://earthquake.usgs.gov/fdsnws/event/1/query.csv?starttime=2016-09-23%2000:00:00&endtime=2016-09-30%2023:59:59&minmagnitude=4.5&eventtype=earthquake&orderby=time")
user_orderby = input("Please enter whether you would like your data to be ordered by 'time' or 'magnitude': ")
user_limit = input("Please enter the amount of earthquake records you would like returned to your system: ")

result = getQuakeData(user_url,user_orderby,user_limit)


#!/usr/bin/env python3

from flask import Flask

app = Flask(__name__)

# Route for the home page
@app.route('/')
def index():
    return '
Python Operations with Flask Routing and Views
'

# Route for printing a string
@app.route('/print/<string:parameter>')
def print_string(parameter):
    print(parameter)
    return parameter

# use Route for counting numbers
@app.route('/count/<int:parameter>')
def count(parameter):
    numbers = '\n'.join(str(num) for num in range(parameter))
    return numbers

# This Route is used for performing math operations
@app.route('/math/<int:num1><operation><int:num2>')
def math(num1, operation, num2):
    result = None
    if operation == '+':
        result = num1 + num2
    elif operation == '-':
        result = num1 - num2
    elif operation == '*':
        result = num1 * num2
    elif operation == 'div':
        result = num1 / num2
    elif operation == '%':
        result = num1 % num2
    return str(result)

if __name__ == '__main__':
    app.run(port=5555, debug=True)


# Author = Valerii Vorobiov
# Date = 08 December, 2021
from functools import partial

def part_one(_input):
    four_digits = [line.split(' | ')[1].split(' ') for line in _input.split('\n')]
    length = {
        1: 2,
        7: 3,
        4: 4,
        8: 7
    }
    return sum(1 for digits in four_digits for d in digits if len(d) in length.values())

def part_two(_input):
    four_digits = [line.split(' | ')[1].split(' ') for line in _input.split('\n')]
    ten_digits = [line.split(' | ')[0].split(' ') for line in _input.split('\n')]
    sums = []
    for i, digits_set in enumerate(ten_digits):
        mapping = get_mapping(digits_set)
        digits = apply_mapping(four_digits[i], mapping)
        sums.append(list_to_number(digits))
    return sum(sums)

def apply_mapping(digits, mapping):
    return [sorted([mapping[d] for d in digit]) for digit in digits]

def list_to_number(lists):
    numbers = [[1, 2, 3, 5, 6, 7],
               [3, 6],
               [1, 3, 4, 5, 7],
               [1, 3, 4, 6, 7],
               [2, 3, 4, 6],
               [1, 2, 4, 6, 7],
               [1, 2, 4, 5, 6, 7],
               [1, 3, 6],
               [1, 2, 3, 4, 5, 6, 7],
               [1, 2, 3, 4, 6, 7]]
    return int(''.join(str(numbers.index(l)) for l in lists))

def len_based(digits, lenth):
    return list(set(i) for i in digits if len(i) == lenth)

one = partial(len_based, lenth=2)
seven = partial(len_based, lenth=3)
four = partial(len_based, lenth=4)

def get_mapping(ten_digits):
    d = dict()
    d[1] = seven(ten_digits)[0] - one(ten_digits)[0]
    d[7] = (len_based(ten_digits, 5)[0].intersection(*len_based(ten_digits, 5)[1:]) - d[1]) - four(ten_digits)[0]
    d[4] = (len_based(ten_digits, 5)[0].intersection(*len_based(ten_digits, 5)[1:]) - d[1]) - d[7]
    d[2] = four(ten_digits)[0] - d[4] - one(ten_digits)[0]
    five = [i for i in len_based(ten_digits, 5) if len(i) > len(i - d[2])][0]
    d[6] = five - d[1] - d[7] - d[2] - d[4]
    d[3] = one(ten_digits)[0] - d[6]
    d[5] = set('abcdefg') - set(''.join(i) for i in d.values())
    return {''.join(v): k for k, v in d.items()}

def read_input():
    with open((__file__.rstrip("main.py") + "input.txt"), 'r') as input_file:
        return input_file.read()

_input = read_input()

print("Part One : " + str(part_one(_input)))

print("Part Two : " + str(part_two(_input)))


homura = int(input())

for i in range(homura):
    madoka = [int(i) for i in input().split()]
    d = madoka[1]-madoka[0]
    s = madoka[2]-madoka[3]
    if s<=0:
        print(-1)
    else:
        print(d/s)


# Try_except

num1 = input('Enter a number: ')

#Intenta convertir num1 a tipo int
try:

	num2 = int(num1)

#Si no puedo o no es válido, entonces se le asigna -1
#-1 es un número obviamente, pero solo es para confirmar
except:

	num2 = -1

# Para los mensajes de confirmación
if num2 > 0:

	print('Nice Work')

else:

	print('Not a number')


# def sort_s(s):
#     s_list = []
#     if len(s) <= 1:
#         return([s])
#     for i in range(len(s)):
#         for j in sort_s(s[0:i] + s[i+1:]):
#             s_list.append(s[i]+j)
#     return s_list

# strings = [x for x in input().split()]
# s_arg = strings[-2]
# s_new = strings[1:-2]
# k = int(strings[-1]) - 1

# all_strs = sort_s(s_arg)
# s_list = [s for s in all_strs if s in s_new]

# print(set(s_list))
# if s_arg in s_list:

#     print(len(s_list)-1)
#     s_list.remove(s_arg)
#     print(s_list[k])
#     print(s_list)
# else:
#     print(len(s_list))
#     print(s_list[k])
#     print(s_list)
while True:
    try:
        st=input().split()
        n=int(st[0])
        s_for_sort=st[1:n+1]
        s=st[-2]
        k=int(st[-1])
        a=[]
        for i in s_for_sort:
            if len(i)==len(s) and i!=s and sorted(i)==sorted(s):
                a.append(i)
        print(len(a))
        if a and k<=len(a):
            print(sorted(a)[k-1])
    except:
        break

数据结构及算法/字符串的全排列.py


def endX(a):
    if len(a) == 0:
        return a
    elif a[0] == "x":
        return endX(a[1:]) + "x"
    else:
        return a[0] + endX(a[1:])
test_inputs = ["xxre","xxhixx","xhixhix","hiy","x","","bxx"]
expected = ["rexx","hixxxx","hihixxx","hiy","x","","bxx"]
all_correct = True
for i in range(len(expected)):
    if endX(test_inputs[i]) != expected[i]:
        print(test_inputs[i],endX(test_inputs[i]))
        all_correct = False
print("All Correct: ", all_correct)


from collections import deque

dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]

# 장마에 잠기는 영역
def rain(n):
    for i in range(N):
        for j in range(N):
            if board[i][j] <= n:
                save[i][j] = False

#안전구역 계산
def save_area(a, b):
    q = deque()
    q.append((a, b))
    while q:
        x, y = q.pop()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0 <= nx <= N - 1 and 0 <= ny <= N - 1:
                if save[nx][ny]:
                    save[nx][ny] = False
                    q.append((nx, ny))

N = int(input())
total_max = 0
board = []
#최대 구역
max_cnt = 0
# 영역 설정하면서 min값 ,max값 찾기
for _ in range(N):
    arr = list(map(int, input().split()))
    temp_max = max(arr)
    if total_max < temp_max:
        total_max = temp_max
    board.append(arr)

for i in range(total_max):
    temp_cnt = 0
    save = [[True] * N for _ in range(N)]
    # 장마로 인해 최소높이부터 물에 잠기기
    rain(i)
    #안전 구역 계산(잠긴 곳은 패스)
    for j in range(N):
        for k in range(N):
            if save[j][k]:
                save[j][k] = False
                save_area(j, k)
                temp_cnt += 1
    if max_cnt < temp_cnt:
        max_cnt = temp_cnt
if max_cnt == 0:
    max_cnt += 1
print(max_cnt)


from django.contrib.auth import views
from django.urls import path
from .views import AdminApproveSellerView, UsersListAPIView, LoginView, RegisterUserAPIView, CurrentProfileAPIView, \
    ProfileAPIView, UserToBecomeSellerView

urlpatterns = [
    path('admin/notice/', AdminApproveSellerView.as_view(), name='admin-notice'),
    path('admin/accounts/list/', UsersListAPIView.as_view(), name='users-list'),

    path('login_token/', LoginView.as_view(), name='get-token'),
    path('register/', RegisterUserAPIView.as_view(), name='register'),
    # path('register/confirm/', ConfirmEmailAPIView.as_view(), name='register'),

    path('accounts/profile/', CurrentProfileAPIView.as_view(), name='current-profile'),
    path('accounts/profile/<int:id>/', ProfileAPIView.as_view(), name='profile-id'),

    path('seller/become/', UserToBecomeSellerView.as_view(), name='seller-become'),

]