import json
import logging
import re
from json import dumps

from six import string_types

from galaxy import model
from galaxy.exceptions import ObjectInvalid
from galaxy.model import LibraryDatasetDatasetAssociation
from galaxy.tools.parameters import update_param
from galaxy.tools.parameters.basic import DataCollectionToolParameter, DataToolParameter, RuntimeValue
from galaxy.tools.parameters.wrapped import WrappedParameters
from galaxy.util import ExecutionTimer
from galaxy.util.none_like import NoneDataset
from galaxy.util.odict import odict
from galaxy.util.template import fill_template
from galaxy.web import url_for

log = logging.getLogger( __name__ )

class ToolExecutionCache( object ):
    """ An object mean to cache calculation caused by repeatedly evaluting
    the same tool by the same user with slightly different parameters.
    """
    def __init__(self, trans):
        self.trans = trans
        self.current_user_roles = trans.get_current_user_roles()

class ToolAction( object ):
    """
    The actions to be taken when a tool is run (after parameters have
    been converted and validated).
    """
    def execute( self, tool, trans, incoming={}, set_output_hid=True ):
        raise TypeError("Abstract method")

class DefaultToolAction( object ):
    """Default tool action is to run an external command"""

    def collect_input_datasets( self, tool, param_values, trans, current_user_roles=None ):
        """
        Collect any dataset inputs from incoming. Returns a mapping from
        parameter name to Dataset instance for each tool parameter that is
        of the DataToolParameter type.
        """
        if current_user_roles is None:
            current_user_roles = trans.get_current_user_roles()
        input_datasets = odict()

        def visitor( input, value, prefix, parent=None, **kwargs ):

            def process_dataset( data, formats=None ):
                if not data or isinstance( data, RuntimeValue ):
                    return None
                if formats is None:
                    formats = input.formats
                if not data.datatype.matches_any( formats ):
                    # Need to refresh in case this conversion just took place, i.e. input above in tool performed the same conversion
                    trans.sa_session.refresh( data )
                    target_ext, converted_dataset = data.find_conversion_destination( formats )
                    if target_ext:
                        if converted_dataset:
                            data = converted_dataset
                        else:
                            data = data.get_converted_dataset( trans, target_ext, target_context=parent )

                if not trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ):
                    raise Exception( "User does not have permission to use a dataset (%s) provided for input." % data.id )
                return data
            if isinstance( input, DataToolParameter ):
                if isinstance( value, list ):
                    # If there are multiple inputs with the same name, they
                    # are stored as name1, name2, ...
                    for i, v in enumerate( value ):
                        processed_dataset = process_dataset( v )
                        if i == 0:
                            # Allow copying metadata to output, first item will be source.
                            input_datasets[ prefix + input.name ] = processed_dataset
                        input_datasets[ prefix + input.name + str( i + 1 ) ] = processed_dataset
                        conversions = []
                        for conversion_name, conversion_extensions, conversion_datatypes in input.conversions:
                            new_data = process_dataset( input_datasets[ prefix + input.name + str( i + 1 ) ], conversion_datatypes )
                            if not new_data or new_data.datatype.matches_any( conversion_datatypes ):
                                input_datasets[ prefix + conversion_name + str( i + 1 ) ] = new_data
                                conversions.append( ( conversion_name, new_data ) )
                            else:
                                raise Exception('A path for explicit datatype conversion has not been found: %s --/--> %s' % ( input_datasets[ prefix + input.name + str( i + 1 ) ].extension, conversion_extensions ) )
                        if parent:
                            parent[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ]
                            for conversion_name, conversion_data in conversions:
                                # allow explicit conversion to be stored in job_parameter table
                                parent[ conversion_name ][ i ] = conversion_data.id  # a more robust way to determine JSONable value is desired
                        else:
                            param_values[ input.name ][ i ] = input_datasets[ prefix + input.name + str( i + 1 ) ]
                            for conversion_name, conversion_data in conversions:
                                # allow explicit conversion to be stored in job_parameter table
                                param_values[ conversion_name ][i] = conversion_data.id  # a more robust way to determine JSONable value is desired
                else:
                    input_datasets[ prefix + input.name ] = process_dataset( value )
                    conversions = []
                    for conversion_name, conversion_extensions, conversion_datatypes in input.conversions:
                        new_data = process_dataset( input_datasets[ prefix + input.name ], conversion_datatypes )
                        if not new_data or new_data.datatype.matches_any( conversion_datatypes ):
                            input_datasets[ prefix + conversion_name ] = new_data
                            conversions.append( ( conversion_name, new_data ) )
                        else:
                            raise Exception( 'A path for explicit datatype conversion has not been found: %s --/--> %s' % ( input_datasets[ prefix + input.name ].extension, conversion_extensions ) )
                    target_dict = parent
                    if not target_dict:
                        target_dict = param_values
                    target_dict[ input.name ] = input_datasets[ prefix + input.name ]
                    for conversion_name, conversion_data in conversions:
                        # allow explicit conversion to be stored in job_parameter table
                        target_dict[ conversion_name ] = conversion_data.id  # a more robust way to determine JSONable value is desired
            elif isinstance( input, DataCollectionToolParameter ):
                if not value:
                    return

                dataset_instances = []
                if hasattr( value, 'child_collection' ):
                    # if we are mapping a collection over a tool, we only require the child_collection
                    dataset_instances = value.child_collection.dataset_instances
                else:
                    # else the tool takes a collection as input so we need everything
                    dataset_instances = value.collection.dataset_instances

                for i, v in enumerate( dataset_instances ):
                    data = v
                    if not trans.app.security_agent.can_access_dataset( current_user_roles, data.dataset ):
                        raise Exception( "User does not have permission to use a dataset (%s) provided for input." % data.id )
                    # Skipping implicit conversion stuff for now, revisit at
                    # some point and figure out if implicitly converting a
                    # dataset collection makes senese.
                    input_datasets[ prefix + input.name + str( i + 1 ) ] = data

        tool.visit_inputs( param_values, visitor )
        return input_datasets

    def collect_input_dataset_collections( self, tool, param_values ):
        def append_to_key( the_dict, key, value ):
            if key not in the_dict:
                the_dict[ key ] = []
            the_dict[ key ].append( value )

        input_dataset_collections = dict()

        def visitor( input, value, prefix, parent=None, **kwargs ):
            if isinstance( input, DataToolParameter ):
                values = value
                if not isinstance( values, list ):
                    values = [ value ]
                for i, value in enumerate(values):
                    if isinstance( value, model.HistoryDatasetCollectionAssociation ):
                        append_to_key( input_dataset_collections, prefix + input.name, ( value, True ) )
                        target_dict = parent
                        if not target_dict:
                            target_dict = param_values
                        # This is just a DataToolParameter, so replace this
                        # collection with individual datasets. Database will still
                        # record collection which should be enought for workflow
                        # extraction and tool rerun.
                        dataset_instances = value.collection.dataset_instances
                        if i == 0:
                            target_dict[ input.name ] = []
                        target_dict[ input.name ].extend( dataset_instances )
            elif isinstance( input, DataCollectionToolParameter ):
                append_to_key( input_dataset_collections, prefix + input.name, ( value, False ) )

        tool.visit_inputs( param_values, visitor )
        return input_dataset_collections

    def _check_access( self, tool, trans ):
        assert tool.allow_user_access( trans.user ), "User (%s) is not allowed to access this tool." % ( trans.user )

    def _collect_inputs( self, tool, trans, incoming, history, current_user_roles ):
        """ Collect history as well as input datasets and collections. """
        app = trans.app
        # Set history.
        if not history:
            history = tool.get_default_history_by_trans( trans, create=True )
        if history not in trans.sa_session:
            history = trans.sa_session.query( app.model.History ).get( history.id )

        # Track input dataset collections - but replace with simply lists so collect
        # input datasets can process these normally.
        inp_dataset_collections = self.collect_input_dataset_collections( tool, incoming )
        # Collect any input datasets from the incoming parameters
        inp_data = self.collect_input_datasets( tool, incoming, trans, current_user_roles=current_user_roles )

        return history, inp_data, inp_dataset_collections

    def execute(self, tool, trans, incoming={}, return_job=False, set_output_hid=True, history=None, job_params=None, rerun_remap_job_id=None, mapping_over_collection=False, execution_cache=None ):
        """
        Executes a tool, creating job and tool outputs, associating them, and
        submitting the job to the job queue. If history is not specified, use
        trans.history as destination for tool's output datasets.
        """
        self._check_access( tool, trans )
        app = trans.app
        if execution_cache is None:
            execution_cache = ToolExecutionCache(trans)
        current_user_roles = execution_cache.current_user_roles
        history, inp_data, inp_dataset_collections = self._collect_inputs(tool, trans, incoming, history, current_user_roles)

        # Build name for output datasets based on tool name and input names
        on_text = self._get_on_text( inp_data )

        # format='input" previously would give you a random extension from
        # the input extensions, now it should just give "input" as the output
        # format.
        input_ext = 'data' if tool.profile < 16.04 else "input"
        input_dbkey = incoming.get( "dbkey", "?" )
        for name, data in reversed(inp_data.items()):
            if not data:
                data = NoneDataset( datatypes_registry=app.datatypes_registry )
                continue

            # Convert LDDA to an HDA.
            if isinstance(data, LibraryDatasetDatasetAssociation):
                data = data.to_history_dataset_association( None )
                inp_data[name] = data

            if tool.profile < 16.04:
                input_ext = data.ext

            if data.dbkey not in [None, '?']:
                input_dbkey = data.dbkey

            identifier = getattr( data, "element_identifier", None )
            if identifier is not None:
                incoming[ "%s|__identifier__" % name ] = identifier

        # Collect chromInfo dataset and add as parameters to incoming
        ( chrom_info, db_dataset ) = app.genome_builds.get_chrom_info( input_dbkey, trans=trans, custom_build_hack_get_len_from_fasta_conversion=tool.id != 'CONVERTER_fasta_to_len' )
        if db_dataset:
            inp_data.update( { "chromInfo": db_dataset } )
        incoming[ "chromInfo" ] = chrom_info

        # Determine output dataset permission/roles list
        existing_datasets = [ inp for inp in inp_data.values() if inp ]
        if existing_datasets:
            output_permissions = app.security_agent.guess_derived_permissions_for_datasets( existing_datasets )
        else:
            # No valid inputs, we will use history defaults
            output_permissions = app.security_agent.history_get_default_permissions( history )

        # Add the dbkey to the incoming parameters
        incoming[ "dbkey" ] = input_dbkey
        # wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed
        wrapped_params = self._wrapped_params( trans, tool, incoming )

        out_data = odict()
        input_collections = dict( (k, v[0][0]) for k, v in inp_dataset_collections.items() )
        output_collections = OutputCollections(
            trans,
            history,
            tool=tool,
            tool_action=self,
            input_collections=input_collections,
            mapping_over_collection=mapping_over_collection,
            on_text=on_text,
            incoming=incoming,
            params=wrapped_params.params,
            job_params=job_params,
        )

        # Keep track of parent / child relationships, we'll create all the
        # datasets first, then create the associations
        parent_to_child_pairs = []
        child_dataset_names = set()
        object_store_populator = ObjectStorePopulator( app )

        def handle_output( name, output, hidden=None ):
            if output.parent:
                parent_to_child_pairs.append( ( output.parent, name ) )
                child_dataset_names.add( name )
            # What is the following hack for? Need to document under what
            # conditions can the following occur? (james@bx.psu.edu)
            # HACK: the output data has already been created
            #      this happens i.e. as a result of the async controller
            if name in incoming:
                dataid = incoming[name]
                data = trans.sa_session.query( app.model.HistoryDatasetAssociation ).get( dataid )
                assert data is not None
                out_data[name] = data
            else:
                ext = determine_output_format(
                    output,
                    wrapped_params.params,
                    inp_data,
                    inp_dataset_collections,
                    input_ext
                )
                data = app.model.HistoryDatasetAssociation( extension=ext, create_dataset=True, flush=False )
                if hidden is None:
                    hidden = output.hidden
                if hidden:
                    data.visible = False
                trans.sa_session.add( data )
                trans.app.security_agent.set_all_dataset_permissions( data.dataset, output_permissions, new=True )

            # Must flush before setting object store id currently.
            # TODO: optimize this.
            trans.sa_session.flush()
            object_store_populator.set_object_store_id( data )

            # This may not be neccesary with the new parent/child associations
            data.designation = name
            # Copy metadata from one of the inputs if requested.

            # metadata source can be either a string referencing an input
            # or an actual object to copy.
            metadata_source = output.metadata_source
            if metadata_source:
                if isinstance( metadata_source, string_types ):
                    metadata_source = inp_data.get( metadata_source )

            if metadata_source is not None:
                data.init_meta( copy_from=metadata_source )
            else:
                data.init_meta()
            # Take dbkey from LAST input
            data.dbkey = str(input_dbkey)
            # Set state
            data.blurb = "queued"
            # Set output label
            data.name = self.get_output_name( output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params )
            # Store output
            out_data[ name ] = data
            if output.actions:
                # Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
                output_action_params = dict( out_data )
                output_action_params.update( incoming )
                output.actions.apply_action( data, output_action_params )
            # Also set the default values of actions of type metadata
            self.set_metadata_defaults( output, data, tool, on_text, trans, incoming, history, wrapped_params.params, job_params )
            # Flush all datasets at once.
            return data

        for name, output in tool.outputs.items():
            if not filter_output(output, incoming):
                if output.collection:
                    collections_manager = app.dataset_collections_service
                    element_identifiers = []
                    known_outputs = output.known_outputs( input_collections, collections_manager.type_registry )
                    # Just to echo TODO elsewhere - this should be restructured to allow
                    # nested collections.
                    for output_part_def in known_outputs:
                        # Add elements to top-level collection, unless nested...
                        current_element_identifiers = element_identifiers
                        current_collection_type = output.structure.collection_type

                        for parent_id in (output_part_def.parent_ids or []):
                            # TODO: replace following line with formal abstractions for doing this.
                            current_collection_type = ":".join(current_collection_type.split(":")[1:])
                            name_to_index = dict((value["name"], index) for (index, value) in enumerate(current_element_identifiers))
                            if parent_id not in name_to_index:
                                if parent_id not in current_element_identifiers:
                                    index = len(current_element_identifiers)
                                    current_element_identifiers.append(dict(
                                        name=parent_id,
                                        collection_type=current_collection_type,
                                        src="new_collection",
                                        element_identifiers=[],
                                    ))
                                else:
                                    index = name_to_index[parent_id]
                            current_element_identifiers = current_element_identifiers[ index ][ "element_identifiers" ]

                        effective_output_name = output_part_def.effective_output_name
                        element = handle_output( effective_output_name, output_part_def.output_def, hidden=True )
                        # TODO: this shouldn't exist in the top-level of the history at all
                        # but for now we are still working around that by hiding the contents
                        # there.
                        # Following hack causes dataset to no be added to history...
                        child_dataset_names.add( effective_output_name )

                        history.add_dataset( element, set_hid=set_output_hid, quota=False )
                        trans.sa_session.add( element )
                        trans.sa_session.flush()

                        current_element_identifiers.append({
                            "__object__": element,
                            "name": output_part_def.element_identifier,
                        })
                        log.info(element_identifiers)

                    if output.dynamic_structure:
                        assert not element_identifiers  # known_outputs must have been empty
                        element_kwds = dict(elements=collections_manager.ELEMENTS_UNINITIALIZED)
                    else:
                        element_kwds = dict(element_identifiers=element_identifiers)

                    output_collections.create_collection(
                        output=output,
                        name=name,
                        **element_kwds
                    )
                else:
                    handle_output_timer = ExecutionTimer()
                    handle_output( name, output )
                    log.info("Handled output named %s for tool %s %s" % (name, tool.id, handle_output_timer))

        add_datasets_timer = ExecutionTimer()
        # Add all the top-level (non-child) datasets to the history unless otherwise specified
        datasets_to_persist = []
        for name in out_data.keys():
            if name not in child_dataset_names and name not in incoming:  # don't add children; or already existing datasets, i.e. async created
                data = out_data[ name ]
                datasets_to_persist.append( data )
        # Set HID and add to history.
        # This is brand new and certainly empty so don't worry about quota.
        # TOOL OPTIMIZATION NOTE - from above loop to the job create below 99%+
        # of execution time happens within in history.add_datasets.
        history.add_datasets( trans.sa_session, datasets_to_persist, set_hid=set_output_hid, quota=False, flush=False )

        # Add all the children to their parents
        for parent_name, child_name in parent_to_child_pairs:
            parent_dataset = out_data[ parent_name ]
            child_dataset = out_data[ child_name ]
            parent_dataset.children.append( child_dataset )

        log.info("Added output datasets to history %s" % add_datasets_timer)
        job_setup_timer = ExecutionTimer()
        # Create the job object
        job, galaxy_session = self._new_job_for_session( trans, tool, history )
        self._record_inputs( trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles )
        self._record_outputs( job, out_data, output_collections )
        job.object_store_id = object_store_populator.object_store_id
        if job_params:
            job.params = dumps( job_params )
        job.set_handler(tool.get_job_handler(job_params))
        trans.sa_session.add( job )
        # Now that we have a job id, we can remap any outputs if this is a rerun and the user chose to continue dependent jobs
        # This functionality requires tracking jobs in the database.
        if app.config.track_jobs_in_database and rerun_remap_job_id is not None:
            try:
                old_job = trans.sa_session.query( app.model.Job ).get(rerun_remap_job_id)
                assert old_job is not None, '(%s/%s): Old job id is invalid' % (rerun_remap_job_id, job.id)
                assert old_job.tool_id == job.tool_id, '(%s/%s): Old tool id (%s) does not match rerun tool id (%s)' % (old_job.id, job.id, old_job.tool_id, job.tool_id)
                if trans.user is not None:
                    assert old_job.user_id == trans.user.id, '(%s/%s): Old user id (%s) does not match rerun user id (%s)' % (old_job.id, job.id, old_job.user_id, trans.user.id)
                elif trans.user is None and type( galaxy_session ) == trans.model.GalaxySession:
                    assert old_job.session_id == galaxy_session.id, '(%s/%s): Old session id (%s) does not match rerun session id (%s)' % (old_job.id, job.id, old_job.session_id, galaxy_session.id)
                else:
                    raise Exception('(%s/%s): Remapping via the API is not (yet) supported' % (old_job.id, job.id))
                # Duplicate PJAs before remap.
                for pjaa in old_job.post_job_actions:
                    job.add_post_job_action(pjaa.post_job_action)
                for jtod in old_job.output_datasets:
                    for (job_to_remap, jtid) in [(jtid.job, jtid) for jtid in jtod.dataset.dependent_jobs]:
                        if (trans.user is not None and job_to_remap.user_id == trans.user.id) or (trans.user is None and job_to_remap.session_id == galaxy_session.id):
                            if job_to_remap.state == job_to_remap.states.PAUSED:
                                job_to_remap.state = job_to_remap.states.NEW
                            for hda in [ dep_jtod.dataset for dep_jtod in job_to_remap.output_datasets ]:
                                if hda.state == hda.states.PAUSED:
                                    hda.state = hda.states.NEW
                                    hda.info = None
                            input_values = dict( [ ( p.name, json.loads( p.value ) ) for p in job_to_remap.parameters ] )
                            update_param( jtid.name, input_values, str( out_data[ jtod.name ].id ) )
                            for p in job_to_remap.parameters:
                                p.value = json.dumps( input_values[ p.name ] )
                            jtid.dataset = out_data[jtod.name]
                            jtid.dataset.hid = jtod.dataset.hid
                            log.info('Job %s input HDA %s remapped to new HDA %s' % (job_to_remap.id, jtod.dataset.id, jtid.dataset.id))
                            trans.sa_session.add(job_to_remap)
                            trans.sa_session.add(jtid)
                    jtod.dataset.visible = False
                    trans.sa_session.add(jtod)
            except Exception:
                log.exception('Cannot remap rerun dependencies.')

        log.info("Setup for job %s complete, ready to flush %s" % (job.log_str(), job_setup_timer))

        job_flush_timer = ExecutionTimer()
        trans.sa_session.flush()
        log.info("Flushed transaction for job %s %s" % (job.log_str(), job_flush_timer))
        # Some tools are not really executable, but jobs are still created for them ( for record keeping ).
        # Examples include tools that redirect to other applications ( epigraph ).  These special tools must
        # include something that can be retrieved from the params ( e.g., REDIRECT_URL ) to keep the job
        # from being queued.
        if 'REDIRECT_URL' in incoming:
            # Get the dataset - there should only be 1
            for name in inp_data.keys():
                dataset = inp_data[ name ]
            redirect_url = tool.parse_redirect_url( dataset, incoming )
            # GALAXY_URL should be include in the tool params to enable the external application
            # to send back to the current Galaxy instance
            GALAXY_URL = incoming.get( 'GALAXY_URL', None )
            assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config."
            redirect_url += "&GALAXY_URL=%s" % GALAXY_URL
            # Job should not be queued, so set state to ok
            job.set_state( app.model.Job.states.OK )
            job.info = "Redirected to: %s" % redirect_url
            trans.sa_session.add( job )
            trans.sa_session.flush()
            trans.response.send_redirect( url_for( controller='tool_runner', action='redirect', redirect_url=redirect_url ) )
        else:
            # Put the job in the queue if tracking in memory
            app.job_queue.put( job.id, job.tool_id )
            trans.log_event( "Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id )
            return job, out_data

    def _wrapped_params( self, trans, tool, incoming ):
        wrapped_params = WrappedParameters( trans, tool, incoming )
        return wrapped_params

    def _get_on_text( self, inp_data ):
        input_names = []
        for name, data in reversed( inp_data.items() ):
            if getattr( data, "hid", None ):
                input_names.append( 'data %s' % data.hid )

        return on_text_for_names( input_names )

    def _new_job_for_session( self, trans, tool, history ):
        job = trans.app.model.Job()
        galaxy_session = None

        if hasattr( trans, "get_galaxy_session" ):
            galaxy_session = trans.get_galaxy_session()
            # If we're submitting from the API, there won't be a session.
            if type( galaxy_session ) == trans.model.GalaxySession:
                job.session_id = galaxy_session.id
        if trans.user is not None:
            job.user_id = trans.user.id
        job.history_id = history.id
        job.tool_id = tool.id
        try:
            # For backward compatibility, some tools may not have versions yet.
            job.tool_version = tool.version
        except:
            job.tool_version = "1.0.0"
        return job, galaxy_session

    def _record_inputs( self, trans, tool, job, incoming, inp_data, inp_dataset_collections, current_user_roles ):
        # FIXME: Don't need all of incoming here, just the defined parameters
        #        from the tool. We need to deal with tools that pass all post
        #        parameters to the command as a special case.
        for name, dataset_collection_info_pairs in inp_dataset_collections.items():
            first_reduction = True
            for ( dataset_collection, reduced ) in dataset_collection_info_pairs:
                # TODO: update incoming for list...
                if reduced and first_reduction:
                    first_reduction = False
                    incoming[ name ] = []
                if reduced:
                    incoming[ name ].append( { 'id': dataset_collection.id, 'src': 'hdca' } )
                # Should verify security? We check security of individual
                # datasets below?
                # TODO: verify can have multiple with same name, don't want to loose tracability
                job.add_input_dataset_collection( name, dataset_collection )
        for name, value in tool.params_to_strings( incoming, trans.app ).items():
            job.add_parameter( name, value )
        self._check_input_data_access( trans, job, inp_data, current_user_roles )

    def _record_outputs( self, job, out_data, output_collections ):
        out_collections = output_collections.out_collections
        out_collection_instances = output_collections.out_collection_instances
        for name, dataset in out_data.items():
            job.add_output_dataset( name, dataset )
        for name, dataset_collection in out_collections.items():
            job.add_implicit_output_dataset_collection( name, dataset_collection )
        for name, dataset_collection_instance in out_collection_instances.items():
            job.add_output_dataset_collection( name, dataset_collection_instance )

    def _check_input_data_access( self, trans, job, inp_data, current_user_roles ):
        access_timer = ExecutionTimer()
        for name, dataset in inp_data.items():
            if dataset:
                if not trans.app.security_agent.can_access_dataset( current_user_roles, dataset.dataset ):
                    raise Exception("User does not have permission to use a dataset (%s) provided for input." % dataset.id)
                if dataset in trans.sa_session:
                    job.add_input_dataset( name, dataset=dataset )
                else:
                    job.add_input_dataset( name, dataset_id=dataset.id )
            else:
                job.add_input_dataset( name, None )
        job_str = job.log_str()
        log.info("Verified access to datasets for %s %s" % (job_str, access_timer))

    def get_output_name( self, output, dataset, tool, on_text, trans, incoming, history, params, job_params ):
        if output.label:
            params['tool'] = tool
            params['on_string'] = on_text
            return fill_template( output.label, context=params )
        else:
            return self._get_default_data_name( dataset, tool, on_text=on_text, trans=trans, incoming=incoming, history=history, params=params, job_params=job_params )

    def set_metadata_defaults( self, output, dataset, tool, on_text, trans, incoming, history, params, job_params ):
        """
        This allows to map names of input files to metadata default values. Example:

        <data format="tabular" name="output" label="Tabular output, aggregates data from individual_inputs" >
            <actions>
                <action name="column_names" type="metadata" default="${','.join([input.name for input in $individual_inputs ])}" />
            </actions>
        </data>
        """
        if output.actions:
            for action in output.actions.actions:
                if action.tag == "metadata" and action.default:
                    metadata_new_value = fill_template( action.default, context=params ).split(",")
                    dataset.metadata.__setattr__(str(action.name), metadata_new_value)

    def _get_default_data_name( self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd ):
        name = tool.name
        if on_text:
            name += ( " on " + on_text )
        return name

class ObjectStorePopulator( object ):
    """ Small helper for interacting with the object store and making sure all
    datasets from a job end up with the same object_store_id.
    """

    def __init__( self, app ):
        self.object_store = app.object_store
        self.object_store_id = None

    def set_object_store_id( self, data ):
        # Create an empty file immediately.  The first dataset will be
        # created in the "default" store, all others will be created in
        # the same store as the first.
        data.dataset.object_store_id = self.object_store_id
        try:
            self.object_store.create( data.dataset )
        except ObjectInvalid:
            raise Exception('Unable to create output dataset: object store is full')
        self.object_store_id = data.dataset.object_store_id  # these will be the same thing after the first output

class OutputCollections(object):
    """ Keeps track of collections (DC or HDCA) created by actions.

    Actions do fairly different things depending on whether we are creating
    just part of an collection or a whole output collection (mapping_over_collection
    parameter).
    """

    def __init__(self, trans, history, tool, tool_action, input_collections, mapping_over_collection, on_text, incoming, params, job_params):
        self.trans = trans
        self.history = history
        self.tool = tool
        self.tool_action = tool_action
        self.input_collections = input_collections
        self.mapping_over_collection = mapping_over_collection
        self.on_text = on_text
        self.incoming = incoming
        self.params = params
        self.job_params = job_params
        self.out_collections = {}
        self.out_collection_instances = {}

    def create_collection(self, output, name, **element_kwds):
        input_collections = self.input_collections
        collections_manager = self.trans.app.dataset_collections_service
        collection_type = output.structure.collection_type
        if collection_type is None:
            collection_type_source = output.structure.collection_type_source
            if collection_type_source is None:
                # TODO: Not a new problem, but this should be determined
                # sooner.
                raise Exception("Could not determine collection type to create.")
            if collection_type_source not in input_collections:
                raise Exception("Could not find collection type source with name [%s]." % collection_type_source)

            collection_type = input_collections[collection_type_source].collection.collection_type

        if self.mapping_over_collection:
            dc = collections_manager.create_dataset_collection(
                self.trans,
                collection_type=collection_type,
                **element_kwds
            )
            self.out_collections[ name ] = dc
        else:
            hdca_name = self.tool_action.get_output_name(
                output,
                None,
                self.tool,
                self.on_text,
                self.trans,
                self.incoming,
                self.history,
                self.params,
                self.job_params,
            )
            hdca = collections_manager.create(
                self.trans,
                self.history,
                name=hdca_name,
                collection_type=collection_type,
                trusted_identifiers=True,
                **element_kwds
            )
            # name here is name of the output element - not name
            # of the hdca.
            self.out_collection_instances[ name ] = hdca

def on_text_for_names( input_names ):
    # input_names may contain duplicates... this is because the first value in
    # multiple input dataset parameters will appear twice once as param_name
    # and once as param_name1.
    unique_names = []
    for name in input_names:
        if name not in unique_names:
            unique_names.append( name )
    input_names = unique_names

    # Build name for output datasets based on tool name and input names
    if len( input_names ) == 1:
        on_text = input_names[0]
    elif len( input_names ) == 2:
        on_text = '%s and %s' % tuple(input_names[0:2])
    elif len( input_names ) == 3:
        on_text = '%s, %s, and %s' % tuple(input_names[0:3])
    elif len( input_names ) > 3:
        on_text = '%s, %s, and others' % tuple(input_names[0:2])
    else:
        on_text = ""
    return on_text

def filter_output(output, incoming):
    for filter in output.filters:
        try:
            if not eval( filter.text.strip(), globals(), incoming ):
                return True  # do not create this dataset
        except Exception as e:
            log.debug( 'Dataset output filter failed: %s' % e )
    return False

def determine_output_format(output, parameter_context, input_datasets, input_dataset_collections, random_input_ext):
    """ Determines the output format for a dataset based on an abstract
    description of the output (galaxy.tools.parser.ToolOutput), the parameter
    wrappers, a map of the input datasets (name => HDA), and the last input
    extensions in the tool form.

    TODO: Don't deal with XML here - move this logic into ToolOutput.
    TODO: Make the input extension used deterministic instead of random.
    """
    # the type should match the input
    ext = output.format
    if ext == "input":
        ext = random_input_ext
    format_source = output.format_source
    if format_source is not None and format_source in input_datasets:
        try:
            input_dataset = input_datasets[output.format_source]
            input_extension = input_dataset.ext
            ext = input_extension
        except Exception:
            pass
    elif format_source is not None:
        if re.match(r"^[^\[\]]*\[[^\[\]]*\]$", format_source):
            collection_name, element_index = format_source[0:-1].split("[")
            # Treat as json to interpret "forward" vs 0 with type
            # Make it feel more like Python, single quote better in XML also.
            element_index = element_index.replace("'", '"')
            element_index = json.loads(element_index)

            if collection_name in input_dataset_collections:
                try:
                    input_collection = input_dataset_collections[collection_name][0][0]
                    input_dataset = input_collection.collection[element_index].element_object
                    input_extension = input_dataset.ext
                    ext = input_extension
                except Exception as e:
                    log.debug("Exception while trying to determine format_source: %s", e)
                    pass

    # process change_format tags
    if output.change_format is not None:
        new_format_set = False
        for change_elem in output.change_format:
            for when_elem in change_elem.findall( 'when' ):
                check = when_elem.get( 'input', None )
                if check is not None:
                    try:
                        if '$' not in check:
                            # allow a simple name or more complex specifications
                            check = '${%s}' % check
                        if str( fill_template( check, context=parameter_context ) ) == when_elem.get( 'value', None ):
                            ext = when_elem.get( 'format', ext )
                    except:  # bad tag input value; possibly referencing a param within a different conditional when block or other nonexistent grouping construct
                        continue
                else:
                    check = when_elem.get( 'input_dataset', None )
                    if check is not None:
                        check = input_datasets.get( check, None )
                        # At this point check is a HistoryDatasetAssociation object.
                        check_format = when_elem.get( 'format', ext )
                        check_value = when_elem.get( 'value', None )
                        check_attribute = when_elem.get( 'attribute', None )
                        if check is not None and check_value is not None and check_attribute is not None:
                            # See if the attribute to be checked belongs to the HistoryDatasetAssociation object.
                            if hasattr( check, check_attribute ):
                                if str( getattr( check, check_attribute ) ) == str( check_value ):
                                    ext = check_format
                                    new_format_set = True
                                    break
                            # See if the attribute to be checked belongs to the metadata associated with the
                            # HistoryDatasetAssociation object.
                            if check.metadata is not None:
                                metadata_value = check.metadata.get( check_attribute, None )
                                if metadata_value is not None:
                                    if str( metadata_value ) == str( check_value ):
                                        ext = check_format
                                        new_format_set = True
                                        break
            if new_format_set:
                break
    return ext


import pygame

pygame.init()

screen_info=pygame.display.Info()
print(screen_info)
size=(width,hieght)=(screen_info.current_w,screen_info.current_h)
clock=pygame.time.clock
screen=pygame.display.set_mode(size)
color= (0,255,0)
def main():
  clock.tick(60)
  screen.fill
  pygame.display.flip()
if __name__=='__main__':
  main()


import argparse
import winreg
from datetime import datetime, timedelta
import struct
import io

"""
This code is designed to query an active Windows Registry for key values and artifacts that
investigators may find useful. This code can be used to gather a rudimentary baseline of a
registry or to hunt for abnormal keys and values within certain registries on a LIVE system.

Usage: LiveRegistryEnumeration.py [--hive $VAR1] [--wireless] [--usb] [--shimcache]

"""
#Setup the command line arguments and help messages
parser = argparse.ArgumentParser(description="Live Registry Analysis:\nThis program provides some options for quick registry analysis to include Run Key Analysis, Wireless Network listings, USB information,and SHIM CACHE information.")
parser.add_argument('--hive', type=str, help='Display Run Keys. Options are: \'HKLM\', \'HKCU\', or \'Both\'')
parser.add_argument('-w', '--wireless', help='List Wireless Networks(Admin)', action="store_true")
parser.add_argument('-u', '--usb', help='List USB Devices', action="store_true")
parser.add_argument('--shimcache', type=int, help='(Experimental) Show logged data starting at given number of days ago.')
args = parser.parse_args()

def checkRunKeys(hive, name):
	#Check the Run and RunOnce keys in the given Hive and print results to screen
	#Results will be in the form 'Program Name: Program Path of Execution'
	keys = ['SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce']
	for k in keys:
		checkKey = winreg.CreateKey(hive, k)
		print ("Attempting enumeration of {0}\\{1}".format(name, k))
		print ("--- Program Name: Command Line")
		numValues = winreg.QueryInfoKey(checkKey)[1]
		if numValues > 0:
		  for index in range(numValues):
			  nameKey = winreg.EnumValue(checkKey, index)
			  print ("--- {0}: {1}".format(nameKey[0], nameKey[1]))
		  print ("Key Complete\n")
		else:
			print ("No Values in this Key\n")

def wirelessNetworks():
	#Check for all recorded connections to wireless networks and print results to the screen
	#Results will be in the form 'DNS Name: SSID'
	print ("Attempting enumeration of HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged")
	print ("--- DNS Suffix: SSID")
	checkKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged')
	numValues = winreg.QueryInfoKey(checkKey)[0]
	if numValues > 0:
		for index in range(numValues):
			nameKey = winreg.EnumKey(checkKey, index)
			trueKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged\\' + nameKey)
			DnsSuffix = winreg.EnumValue(trueKey, 3)
			FirstNetwork = winreg.EnumValue(trueKey, 4)
			print ("--- {0}: {1}".format(DnsSuffix[1], FirstNetwork[1]))
		print ("No additional Wireless Network Connections Found\n")
	else:
		print ("No Values in this Key\n")

def usbDevices():
	#Check for all recorded USB Devices that have connected to the system and print results to the screen
	#Results will be in the form 'Last Key Modification Date(UTC) - Friendly Name: ContainerID'
	print ("Attempting enumeration of USB Storage Devices")
	print ("--- Date First Connected(UTC) - Friendly Name: ContainerID")
	checkKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Enum\\USBSTOR')
	numValues = winreg.QueryInfoKey(checkKey)[0]
	if numValues > 0:
		for index in range(numValues):
			nextKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Enum\\USBSTOR\\' + winreg.EnumKey(checkKey, index))
			trueKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Enum\\USBSTOR\\' + winreg.EnumKey(checkKey, index) + '\\' + winreg.EnumKey(nextKey, 0))
			#For some reason, some keys index values differently. We must iterate through all values to guarantee accuracy as a result.
			for value in range(winreg.QueryInfoKey(trueKey)[1]):
				curValue = winreg.EnumValue(trueKey, value)[0]
				nanoseconds = winreg.QueryInfoKey(trueKey)[2]
				if curValue == 'ContainerID':
					ContainerID = winreg.EnumValue(trueKey, value)
				elif curValue == 'FriendlyName':
					FriendlyName = winreg.EnumValue(trueKey, value)
			print ("--- {2} - {0}: {1}".format(FriendlyName[1], ContainerID[1], translateTime(nanoseconds)))
		print ("No additional devices found in USBSTOR\n")
	else:
		print ("No values in this key.")

def readShimCache(daysOfData):
	#Check for all programs recorded in the App Compatibility Cache (shim cache).
	#Results are in the form: Modification date : Path
	#What about execution flag?
	print ("(Experimental) Attempting enumeration of the SHIM Cache (Experimental)")
	print ("--- UTC Modification Time : Path")
	try:
	  checkKey = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache')
	except:
	  print ("Shim Cache not in expected Key. Other Keys and OS Version Support under development.\n")
	entrylist = []
	shim = winreg.EnumValue(checkKey, 0)[1]
	cache_data = shim[0x34:]
	data = io.BytesIO(cache_data)
	while data.tell() < len(cache_data):
		header = data.read(12)
		magic, crc32_hash, entry_len = struct.unpack('<4sLL', header)
		entry_data = io.BytesIO(data.read(entry_len))
		path_len = struct.unpack('<H', entry_data.read(2))[0]
		if path_len == 0:
			path = 'None'
		else:
			path = entry_data.read(path_len).decode('utf-16le', 'replace').encode('utf-8')
		low_datetime, high_datetime = struct.unpack('<LL', entry_data.read(8))
		adjustedTimes = (high_datetime << 32) | low_datetime
		#Windows uses January 1, 1601 as start time. Get number of microseconds from then to given number of days ago.
		timeRange = (timedelta(days=(152539-daysOfData)).total_seconds()) * 1000000
		if adjustedTimes / 10 > timeRange:
			row = [adjustedTimes, path.decode()]
			entrylist.append(row)
		else:
			continue
  #Need to sort dates appropriately as time-order is not maintained in the cache
	return sorted(entrylist)

def translateTime(nanoseconds):
	#Windows timestamps are number of 100ns since January 1st, 1601. This function translates time to make it more readable.
	return format(datetime(1601,1,1) + timedelta(microseconds=nanoseconds/10), '%d %B %Y %H:%M:%S')

def main():
	if args.hive is None and not args.wireless and not args.usb and args.shimcache is None:
		print ("No options given! Add -h to the command to display options!\n")
	else:
		if args.hive is not None:
			if args.hive.upper() in ("HKLM", "HKCU", "BOTH"):
				print ("--------------RUN KEYS--------------")
				if args.hive.upper() == "HKLM":
					checkRunKeys(winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE")
				elif args.hive.upper() == "HKCU":
					checkRunKeys(winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER")
				elif args.hive.lower() == "both":
					checkRunKeys(winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE")
					checkRunKeys(winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER")
			else:
				print ("Unrecongnized Choice for HIVE: \"{}\" is not valid.\n".format(args.hive))
		if args.wireless:
			print ("--------------WIRELESS NETWORK CONNECTIONS--------------")
			try:
				wirelessNetworks()
			except PermissionError:
				print ("You must be admin to check this key.\n")
		if args.usb:
			print ("--------------USB DEVICES--------------")
			usbDevices()
		if args.shimcache is not None:
			print ("--------------SHIM CACHE DATA--------------")
			results = readShimCache(args.shimcache)
			for entry in results:
				if entry[0] == 0:
					entry[0] = "No Timestamp - Unknown"
				else:
					entry[0] = translateTime(entry[0])
				print ("--- {0} : {1}".format(entry[0], entry[1]))
			print ("End of SHIM Cache Information\n")
	print ("Execution Complete")

if __name__ == "__main__":
  main()


"""
This program is used to test the three part 3 functions.
Complete the three functions.
The expected output is shown in the comment at the bottom of this file.

*** MAKE SURE YOU COPY YOUR PART 1 AND PART 2 FUNCTIONS INTO THIS PROGRAM ***
Author: Adriana Ferraro
"""

import random

#----------------------------------------------------------
# Main function - Testing Part 3 of assignment 2
#----------------------------------------------------------
def main():
	print("get_list_of_dice_rolls()")
	print("Note:  the dice values are random so your output will be different.")
	print()
	dice_rolls1 = get_list_of_dice_rolls(3)
	dice_rolls2 = get_list_of_dice_rolls(1)
	dice_rolls3 = get_list_of_dice_rolls(5)
	dice_rolls4 = get_list_of_dice_rolls(4)
	print("Should be a list of 3 random dice values:", dice_rolls1)
	print("Should be a list of 1 random dice value:", dice_rolls2)
	print("Should be a list of 5 random dice values:", dice_rolls3)
	print("Should be a list of 4 random dice values:", dice_rolls4)
	print("---------------------------")
	print("---------------------------")
	print("get_score()")
	score1 = get_score([4, 6, 6, 6, 6])
	score2 = get_score([4, 5, 6, 5, 6])
	score3 = get_score([6, 6, 2, 2, 2])
	score4 = get_score([5, 5, 6, 5, 5])
	score5 = get_score([6, 6, 6, 6, 6])
	score6 = get_score([6, 5, 1, 3, 2])
	print(score1, score2, score3, score4, score5, score6)
	print("---------------------------")
	print("---------------------------")
	print("Note:  the dice values are random so your output will be different.")
	print("Check that the scores are correct for the dice which are rolled.")
	print()
	print("process_player_turn()")
	player_score1 = process_player_turn("Adriana")
	print("Player score: ", player_score1)
	print()
	player_score2 = process_player_turn("Joe")
	print("Player score: ", player_score2)
	print("---------------------------")
	print("---------------------------")
#----------------------------------------------------------
# Functions which process a player's turn.
# Complete the following 3 functions below.
#----------------------------------------------------------
def get_list_of_dice_rolls(number_of_rolls):
    return [random.randrange(1, 7) for i in range(number_of_rolls)]

def get_score(list_of_dice):
	matches = get_max_number_of_matches(list_of_dice)
	score = 0
	if matches == 1 or matches == 2:
		score = 0
	elif matches == 3:
		score = 3
	elif matches == 4:
		score = 4
	elif matches == 5:
		score = 5
	return score

def process_player_turn(player_name):
	player_turn = display_turn_info(player_name)
	five_random_dice_rolls = get_list_of_dice_rolls(5)
	dice_list = display_dice(five_random_dice_rolls)
	max_matches = get_max_number_of_matches(five_random_dice_rolls)
	if max_matches == 2:
		dice_value = get_dice_number_with_two_matches(five_random_dice_rolls)
		elements_list = [dice_value, dice_value]
		two_matches = display_exactly_two_matches_message(dice_value)
		three_random_dice_rolls = get_list_of_dice_rolls(3)
		elements_list += (three_random_dice_rolls)
		new_dice_list = display_dice(elements_list)
		final_score = get_score(elements_list)
		return final_score
	else:
		return get_score(five_random_dice_rolls)

#----------------------------------------------------------
# Functions from part 2
# which obtain the maximum number of matches and
# the dice number if there are exactly two matches
#----------------------------------------------------------
def get_how_many_match(value_to_match, list_of_dice):
	values = 0
	for dice in list_of_dice:
		if dice == value_to_match:
			values += 1
	return values

def get_dice_number_with_two_matches(list_of_dice):
	for dice in list_of_dice:
		matched = get_how_many_match(dice, list_of_dice)
		if matched == 2:
			return dice

def get_max_number_of_matches(list_of_dice):
	number_of_times_1 = 0
	number_of_times_2 = 0
	number_of_times_3 = 0
	number_of_times_4 = 0
	number_of_times_5 = 0
	number_of_times_6 = 0
	for dice in list_of_dice:
		if dice == 1:
			number_of_times_1 += 1
		elif dice == 2:
			number_of_times_2 += 1
		elif dice == 3:
			number_of_times_3 += 1
		elif dice == 4:
			number_of_times_4 += 1
		elif dice == 5:
			number_of_times_5 += 1
		elif dice == 6:
			number_of_times_6 += 1
	return max(number_of_times_1, number_of_times_2, number_of_times_3, number_of_times_4, number_of_times_5, number_of_times_6)

#----------------------------------------------------------
# Functions from part 1
#----------------------------------------------------------
def display_welcome():
	print()
	print("Welcome to DICEY, written by skha787")
	print()

def display_turn_info(player_name):
	print("  " + player_name + "'s turn")

def display_dice(list_of_dice):
	print("    Dice:  ", end = "")
	for numbers in list_of_dice:
		print(numbers, end = " ")
	print()

def display_exactly_two_matches_message(dice_number):
	print("    Two ", dice_number, "'s. Roll the remaining 3 dice.", sep = "")

def display_round_results(round_number, player1_name, player1_score, player2_name, player2_score):
	print("Round ", round_number, " results: ", player1_name, " has ", player1_score, " points, and ", player2_name, " has ", player2_score, " points", sep = "")
	print()

def display_final_results(player1_name, player1_score, player2_name, player2_score):
	print()
	if player1_score > player2_score:
		message = "Congratulations to " + player1_name
		print("*" * len(message))
		print(message)
		print("*" * len(message))
	elif player2_score > player1_score:
		message = "Congratulations to " + player2_name
		print("*" * len(message))
		print(message)
		print("*" * len(message))
	elif player1_score == player2_score:
		message = "The result is a tie"
		print("*" * len(message))
		print(message)
		print("*" * len(message))
	print()

main()

"""
EXPECTED OUTPUT FOR PART 3
get_list_of_dice_rolls()
Note:  the dice values are random so your output will be different.

Should be a list of 3 random dice values: [6, 4, 2]
Should be a list of 1 random dice value: [6]
Should be a list of 5 random dice values: [1, 4, 3, 5, 3]
Should be a list of 4 random dice values: [2, 1, 3, 3]
---------------------------
---------------------------
get_score()
4 0 3 4 5 0
---------------------------
---------------------------
Note:  the dice values are random so your output will be different.
Check that your scores are correct for the dice which are rolled.

process_player_turn()
  Adriana's turn
    Dice:  6 2 3 4 5
Player score:  0

  Joe's turn
    Dice:  5 2 6 1 2
    Two 2's. Roll the remaining 3 dice.
    Dice:  2 2 6 4 6
Player score:  0
---------------------------
---------------------------
"""


from django.conf.urls import url

from . import views

app_name = 'sealion'

urlpatterns = [
    url(r'^$', views.index, name='index'),

    url(r'^(?P<group_id>[0-9]+)/$' , views.detail, name='detail'),

    url(r'^search/$', views.search, name='search'),

    url(r'^group_add/$' , views.group_add, name='group_add'),
    url(r'^(?P<group_id>[0-9]+)/group_del/$' , views.group_del, name='group_del'),

    url(r'^(?P<group_id>[0-9]+)/todo_add/$' , views.todo_add, name='todo_add'),
    url(r'^(?P<group_id>[0-9]+)/(?P<todo_id>[0-9]+)/todo_del/$' , views.todo_del, name='todo_del'),
    url(r'^(?P<todo_id>[0-9]+)/done/$' , views.done, name='done'),

    url(r'^for_ajax/$', views.for_ajax, name='for_ajax'),
]


from django.conf.urls import url

from . import views
#Esquema de url-vista
#Nombre de la app
app_name = 'asociacion'
urlpatterns = [
    url(r'^perfil/', views.IndexView, name='index'),
    url(r'^about/', view=views.AboutView.as_view(), name='about'),
    url(r'^contact/', view=views.ContactView.as_view(), name='contact'),
    url(r'^resumen/', views.get_impagos,name='resumen'),
    url(r'^tesoreria/', views.get_cuentas, name='tesoreria'),
    url(r'^buscar/', views.search, name='buscar'),
    url(r'^documentos',view=views.DocsView.as_view(),name='documentos'),
    url(r'^test', views.get_charts,name='test'),
    url(r'^informes(?P<tipo>\w+)/$', views.socios_report, name="informes")
]


#35

# Time:  O(logn)
# Space: O(1)

# Given a sorted array and a target value, return the index
# if the target is found. If not, return the index where
# it would be if it were inserted in order.
#
# You may assume no duplicates in the array.
#
# Example 1:
# Input: [1,3,5,6], 5
# Output: 2
#
# Example 2:
# Input: [1,3,5,6], 2
# Output: 1
#
# Example 3:
# Input: [1,3,5,6], 7
# Output: 4
#
# Example 1:
# Input: [1,3,5,6], 0
# Output: 0

class binarySearchSol():
    def insertPosition(self,nums,target):
        left,right=0,len(nums)-1
        while left<=right:
            mid=(left+right)//2
            if nums[mid]>=target:
                right=mid-1
            else:
                left=mid+1
        return left


#!/usr/bin/env python
# -*- cording: utf-8 -*-

from flask import Flask, render_template, send_from_directory
import logging

app = Flask(__name__, static_folder='../frontend/dist/static', template_folder='../frontend/dist')
app.config.from_pyfile('../config.py')
app.logger.setLevel(logging.DEBUG)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
    return render_template('index.html')

@app.route('/media/<path:path>')
def send_media(path):
    print(path)
    return send_from_directory('../media', path)

from backend.apis import character, photo, voice

app.register_blueprint(character.app, url_prefix='/api/character')
app.register_blueprint(photo.app, url_prefix='/api/photo')
app.register_blueprint(voice.app, url_prefix='/api/voice')


import turtle

n = turtle.Turtle()

n.speed(10)
for i in range(180):
    n.forward(100)
    n.right(30)
    n.forward(20)
    n.left(60)
    n.forward(50)
    n.right(30)
    n.penup()
    n.setposition(0, 0)
    n.pendown()
    n.right(2)

turtle.done()


import copy
import numpy as np
import pandas as pd
import torch
from torch import nn as nn

import torch.nn.functional as F
from torch_geometric.nn import MessagePassing
from torch_geometric.nn.inits import glorot
from torch_geometric.utils import softmax
from torch_sparse.tensor import SparseTensor
import pytorch_lightning as pl

class LATTE(nn.Module):
    def __init__(self, t_order: int, embedding_dim: int, in_channels_dict: dict, num_nodes_dict: dict, metapaths: list,
                 activation: str = "relu", attn_heads=1, attn_activation="sharpening", attn_dropout=0.5,
                 use_proximity=True, neg_sampling_ratio=2.0):
        super(LATTE, self).__init__()
        self.metapaths = metapaths
        self.node_types = list(num_nodes_dict.keys())
        self.embedding_dim = embedding_dim * t_order
        self.use_proximity = use_proximity
        self.t_order = t_order
        self.neg_sampling_ratio = neg_sampling_ratio

        layers = []
        t_order_metapaths = copy.deepcopy(metapaths)
        for t in range(t_order):
            layers.append(
                LATTEConv(embedding_dim=embedding_dim, in_channels_dict=in_channels_dict, num_nodes_dict=num_nodes_dict,
                          metapaths=t_order_metapaths, activation=activation, attn_heads=attn_heads,
                          attn_activation=attn_activation, attn_dropout=attn_dropout, use_proximity=use_proximity,
                          neg_sampling_ratio=neg_sampling_ratio,
                          first=True if t == 0 else False,
                          embeddings=layers[0].embeddings if t > 0 else None))
            t_order_metapaths = LATTE.join_metapaths(t_order_metapaths, metapaths)
        self.layers = nn.ModuleList(layers)

    @staticmethod
    def join_metapaths(metapath_A, metapath_B):
        metapaths = []
        for relation_a in metapath_A:
            for relation_b in metapath_B:
                if relation_a[-1] == relation_b[0]:
                    new_relation = relation_a + relation_b[1:]
                    metapaths.append(new_relation)
        return metapaths

    @staticmethod
    def get_edge_index_values(edge_index_tup: [tuple, torch.Tensor]):
        if isinstance(edge_index_tup, tuple):
            edge_index = edge_index_tup[0]
            edge_values = edge_index[1]

            if edge_values.dtype != torch.float:
                edge_values = edge_values.to(torch.float)
        elif isinstance(edge_index_tup, torch.Tensor) and edge_index_tup.size(1) > 0:
            edge_index = edge_index_tup
            edge_values = torch.ones_like(edge_index_tup[0], dtype=torch.float)
        else:
            return None, None

        return edge_index, edge_values

    @staticmethod
    def join_edge_indexes(edge_index_dict_A, edge_index_dict_B, global_node_idx):
        output_dict = {}
        for metapath_a, edge_index_a in edge_index_dict_A.items():
            if is_negative(metapath_a): continue
            edge_index_a, values_a = LATTE.get_edge_index_values(edge_index_a)
            if edge_index_a is None: continue

            for metapath_b, edge_index_b in edge_index_dict_B.items():
                if metapath_a[-1] != metapath_b[0] or is_negative(metapath_b): continue

                new_metapath = metapath_a + metapath_b[1:]
                edge_index_b, values_b = LATTE.get_edge_index_values(edge_index_b)
                if edge_index_b is None: continue
                try:
                    new_edge_index = adamic_adar(indexA=edge_index_a, valueA=values_a, indexB=edge_index_b,
                                                 valueB=values_b,
                                                 m=global_node_idx[metapath_a[0]].size(0),
                                                 k=global_node_idx[metapath_a[-1]].size(0),
                                                 n=global_node_idx[metapath_b[-1]].size(0),
                                                 coalesced=True, sampling=True)
                    if new_edge_index[0].size(1) == 0: continue
                    output_dict[new_metapath] = new_edge_index

                except Exception as e:
                    print(f"{str(e)} \n {metapath_a}: {edge_index_a.size(1)}, {metapath_b}: {edge_index_b.size(1)}")
                    continue

        return output_dict

    def forward(self, X: dict, edge_index_dict: dict, global_node_idx: dict, save_betas=False):
        """
        This
        :param X: Dict of <node_type>:<tensor size (batch_size, in_channels)>. If nodes are not attributed, then pass an empty dict.
        :param global_node_idx: Dict of <node_type>:<int tensor size (batch_size,)>
        :param edge_index_dict: Dict of <metapath>:<tensor size (2, num_edge_index)>
        :param save_betas: whether to save _beta values for batch
        :return embedding_output, proximity_loss, edge_pred_dict:
        """
        # device = global_node_idx[list(global_node_idx.keys())[0]].device
        proximity_loss = torch.tensor(0.0, device=self.layers[0].device) if self.use_proximity else None

        h_layers = {node_type: [] for node_type in global_node_idx}
        for t in range(self.t_order):
            if t == 0:
                h_dict, t_loss, edge_pred_dict = self.layers[t].forward(x_l=X, x_r=X,
                                                                        edge_index_dict=edge_index_dict,
                                                                        global_node_idx=global_node_idx,
                                                                        save_betas=save_betas)
                next_edge_index_dict = edge_index_dict
            else:
                next_edge_index_dict = LATTE.join_edge_indexes(next_edge_index_dict, edge_index_dict, global_node_idx)
                h_dict, t_loss, _ = self.layers[t].forward(x_l=h_dict, x_r=X,
                                                           edge_index_dict=next_edge_index_dict,
                                                           global_node_idx=global_node_idx,
                                                           save_betas=save_betas)

            for node_type in global_node_idx:
                h_layers[node_type].append(h_dict[node_type])

            if self.use_proximity:
                proximity_loss += t_loss

        concat_out = {node_type: torch.cat(h_list, dim=1) for node_type, h_list in h_layers.items() \
                      if len(h_list) > 0}

        return concat_out, proximity_loss, edge_pred_dict

    def get_attn_activation_weights(self, t):
        return dict(zip(self.layers[t].metapaths, self.layers[t].alpha_activation.detach().numpy().tolist()))

    def get_relation_weights(self, t):
        return self.layers[t].get_relation_weights()

class LATTEConv(MessagePassing, pl.LightningModule):
    def __init__(self, embedding_dim: int, in_channels_dict: {str: int}, num_nodes_dict: {str: int}, metapaths: list,
                 activation: str = "relu", attn_heads=4, attn_activation="sharpening", attn_dropout=0.2,
                 use_proximity=False, neg_sampling_ratio=1.0, first=True, embeddings=None) -> None:
        super(LATTEConv, self).__init__(aggr="add", flow="target_to_source", node_dim=0)
        self.first = first
        self.node_types = list(num_nodes_dict.keys())
        self.metapaths = list(metapaths)
        self.num_nodes_dict = num_nodes_dict
        self.embedding_dim = embedding_dim
        self.use_proximity = use_proximity
        self.neg_sampling_ratio = neg_sampling_ratio
        self.attn_heads = attn_heads
        self.attn_dropout = attn_dropout

        self.activation = activation.lower()
        if self.activation not in ["sigmoid", "tanh", "relu"]:
            print(f"Embedding activation arg `{self.activation}` did not match, so uses linear activation.")

        self.conv = torch.nn.ModuleDict(
            {node_type: torch.nn.Conv1d(
                in_channels=in_channels_dict[
                    node_type] if first and node_type in in_channels_dict else embedding_dim,
                out_channels=self.num_head_relations(node_type),
                kernel_size=1) \
                for node_type in self.node_types})  # W_phi.shape (D x F)

        if first:
            self.linear_l = nn.ModuleDict(
                {node_type: nn.Linear(in_channels, embedding_dim, bias=True) \
                 for node_type, in_channels in in_channels_dict.items()})  # W.shape (F x D_m)
            self.linear_r = nn.ModuleDict(
                {node_type: nn.Linear(in_channels, embedding_dim, bias=True) \
                 for node_type, in_channels in in_channels_dict.items()})  # W.shape (F x D_m)
        else:
            self.linear_l = nn.ModuleDict(
                {node_type: nn.Linear(embedding_dim, embedding_dim, bias=True) \
                 for node_type in self.node_types})  # W.shape (F x F)
            self.linear_r = nn.ModuleDict(
                {node_type: nn.Linear(in_channels, embedding_dim, bias=True) \
                 for node_type, in_channels in in_channels_dict.items()})  # W.shape (F x D_m}

        self.out_channels = self.embedding_dim // attn_heads
        self.attn_l = nn.ModuleList(
            [nn.Linear(embedding_dim, 1, bias=True) for metapath in self.metapaths])
        self.attn_r = nn.ModuleList(
            [nn.Linear(embedding_dim, 1, bias=True) for metapath in self.metapaths])
        # self.attn_q = nn.ModuleList(
        #     [nn.Sequential(nn.Tanh(), nn.Linear(2 * self.out_channels, 1, bias=False)) for metapath in self.metapaths])

        if attn_activation == "sharpening":
            self.alpha_activation = nn.Parameter(torch.Tensor(len(self.metapaths)).fill_(1.0))
        elif attn_activation == "PReLU":
            self.alpha_activation = nn.PReLU(init=0.2)
        elif attn_activation == "LeakyReLU":
            self.alpha_activation = nn.LeakyReLU(negative_slope=0.2)
        else:
            print(f"WARNING: alpha_activation `{attn_activation}` did not match, so used linear activation")
            self.alpha_activation = None

        # If some node type are not attributed, instantiate embeddings for them
        non_attr_node_types = (num_nodes_dict.keys() - in_channels_dict.keys())
        if first and len(non_attr_node_types) > 0:
            if embedding_dim > 256 or sum([v for k, v in self.num_nodes_dict.items()]) > 1000000:
                print("INFO: Embedding.device = 'cpu'")
                self.embeddings = {node_type: nn.Embedding(num_embeddings=self.num_nodes_dict[node_type],
                                                           embedding_dim=embedding_dim,
                                                           sparse=True).cpu() for node_type in non_attr_node_types}
            else:
                self.embeddings = nn.ModuleDict(
                    {node_type: nn.Embedding(num_embeddings=self.num_nodes_dict[node_type],
                                             embedding_dim=embedding_dim,
                                             sparse=False) for node_type in non_attr_node_types})
        elif embeddings is not None:
            self.embeddings = embeddings
        else:
            self.embeddings = None

        self.reset_parameters()

    def reset_parameters(self):
        for i, metapath in enumerate(self.metapaths):
            glorot(self.attn_l[i].weight)
            glorot(self.attn_r[i].weight)

        # glorot(self.attn_q[-1].weight)

        for node_type in self.linear_l:
            glorot(self.linear_l[node_type].weight)
        for node_type in self.linear_r:
            glorot(self.linear_r[node_type].weight)
        for node_type in self.conv:
            glorot(self.conv[node_type].weight)

        if self.embeddings is not None and len(self.embeddings.keys()) > 0:
            for node_type in self.embeddings:
                self.embeddings[node_type].reset_parameters()

    def forward(self, x_l, edge_index_dict, global_node_idx, x_r=None, save_betas=False):
        """

        :param x_l: a dict of node attributes indexed node_type
        :param global_node_idx: A dict of index values indexed by node_type in this mini-batch sampling
        :param edge_index_dict: Sparse adjacency matrices for each metapath relation. A dict of edge_index indexed by metapath
        :param x_r: Context embedding of the previous order, required for t >= 2. Default: None (if first order). A dict of (node_type: tensor)
        :return: output_emb, loss
        """
        # H_t = W_t * x
        l_dict = self.get_h_dict(x_l, global_node_idx, left_right="left")
        r_dict = self.get_h_dict(x_r, global_node_idx, left_right="right")

        # Predict relations attention coefficients
        beta = self.get_beta_weights(x_dict=x_l, h_dict=l_dict, h_prev=l_dict, global_node_idx=global_node_idx)
        # Save beta weights from testing samples
        if not self.training: self.save_relation_weights(beta, global_node_idx)

        # Compute node-level attention coefficients
        alpha_l, alpha_r = self.get_alphas(edge_index_dict, l_dict, r_dict)

        # For each metapath in a node_type, use GAT message passing to aggregate h_j neighbors
        out = {}
        for node_type in global_node_idx:
            out[node_type] = self.agg_relation_neighbors(node_type=node_type, alpha_l=alpha_l, alpha_r=alpha_r,
                                                         l_dict=l_dict, r_dict=r_dict, edge_index_dict=edge_index_dict,
                                                         global_node_idx=global_node_idx)
            out[node_type][:, -1] = l_dict[node_type]
            # Soft-select the relation-specific embeddings by a weighted average with beta[node_type]
            out[node_type] = torch.bmm(out[node_type].permute(0, 2, 1), beta[node_type]).squeeze(-1)

            # Apply \sigma activation to all embeddings
            out[node_type] = self.embedding_activation(out[node_type])

        proximity_loss, edge_pred_dict = None, None
        if self.use_proximity:
            proximity_loss, edge_pred_dict = self.proximity_loss(edge_index_dict,
                                                                 alpha_l=alpha_l, alpha_r=alpha_r,
                                                                 global_node_idx=global_node_idx)
        return out, proximity_loss, edge_pred_dict

    def agg_relation_neighbors(self, node_type, alpha_l, alpha_r, l_dict, r_dict, edge_index_dict, global_node_idx):
        # Initialize embeddings, size: (num_nodes, num_relations, embedding_dim)
        emb_relations = torch.zeros(
            size=(global_node_idx[node_type].size(0),
                  self.num_head_relations(node_type),
                  self.embedding_dim)).type_as(self.conv[node_type].weight)

        for i, metapath in enumerate(self.get_head_relations(node_type)):
            if metapath not in edge_index_dict or edge_index_dict[metapath] == None: continue
            head, tail = metapath[0], metapath[-1]
            num_node_head, num_node_tail = len(global_node_idx[head]), len(global_node_idx[tail])

            edge_index, values = LATTE.get_edge_index_values(edge_index_dict[metapath])
            if edge_index is None: continue
            # Propapate flows from target nodes to source nodes
            out = self.propagate(
                edge_index=edge_index,
                x=(r_dict[tail], l_dict[head]),
                alpha=(alpha_r[metapath], alpha_l[metapath]),
                size=(num_node_tail, num_node_head),
                metapath_idx=self.metapaths.index(metapath))
            emb_relations[:, i] = out

        return emb_relations

    def message(self, x_j, alpha_j, alpha_i, index, ptr, size_i, metapath_idx):
        alpha = alpha_j if alpha_i is None else alpha_j + alpha_i
        # alpha = self.attn_q[metapath_idx].forward(torch.cat([alpha_i, alpha_j], dim=1))
        alpha = self.attn_activation(alpha, metapath_idx)
        alpha = softmax(alpha, index=index, ptr=ptr, num_nodes=size_i)
        alpha = F.dropout(alpha, p=self.attn_dropout, training=self.training)
        return x_j * alpha

    def get_h_dict(self, input, global_node_idx, left_right="left"):
        h_dict = {}
        for node_type in global_node_idx:
            if node_type in input:
                if left_right == "left":
                    h_dict[node_type] = self.linear_l[node_type].forward(input[node_type])
                elif left_right == "right":
                    h_dict[node_type] = self.linear_r[node_type].forward(input[node_type])
            else:
                h_dict[node_type] = self.embeddings[node_type].weight[global_node_idx[node_type]] \
                    .to(self.conv[node_type].weight.device)
        return h_dict

    def get_alphas(self, edge_index_dict, l_dict, r_dict):
        alpha_l, alpha_r = {}, {}

        for i, metapath in enumerate(self.metapaths):
            if metapath not in edge_index_dict or edge_index_dict[metapath] is None:
                continue
            head_type, tail_type = metapath[0], metapath[-1]
            alpha_l[metapath] = self.attn_l[i].forward(l_dict[head_type])
            alpha_r[metapath] = self.attn_r[i].forward(r_dict[tail_type])
        return alpha_l, alpha_r

    def get_beta_weights(self, x_dict, h_dict, h_prev, global_node_idx):
        beta = {}
        for node_type in global_node_idx:
            # beta[node_type] = self.conv[node_type].forward(h_dict[node_type].unsqueeze(-1))
            if self.first:
                if node_type in x_dict:
                    beta[node_type] = self.conv[node_type].forward(x_dict[node_type].unsqueeze(-1))
                else:
                    # node_type is not attributed, use h_dict contains self.embeddings in first layer
                    beta[node_type] = self.conv[node_type].forward(h_dict[node_type].unsqueeze(-1))
            else:
                beta[node_type] = self.conv[node_type].forward(h_prev[node_type].unsqueeze(-1))

            beta[node_type] = torch.softmax(beta[node_type], dim=1)
        return beta

    def predict_scores(self, edge_index, alpha_l, alpha_r, metapath, logits=False):
        assert metapath in self.metapaths, f"If metapath `{metapath}` is tag_negative()'ed, then pass it with untag_negative()"

        # e_pred = self.attn_q[self.metapaths.index(metapath)].forward(
        #     torch.cat([alpha_l[metapath][edge_index[0]], alpha_r[metapath][edge_index[1]]], dim=1)).squeeze(-1)

        e_pred = self.attn_activation(alpha_l[metapath][edge_index[0]] + alpha_r[metapath][edge_index[1]],
                                      metapath_id=self.metapaths.index(metapath)).squeeze(-1)
        if logits:
            return e_pred
        else:
            return F.sigmoid(e_pred)

    def proximity_loss(self, edge_index_dict, alpha_l, alpha_r, global_node_idx):
        """
        For each relation/metapath type given in `edge_index_dict`, this function both predict link scores and computes
        the NCE loss for both positive and negative (sampled) links. For each relation type in `edge_index_dict`, if the
        negative metapath is not included, then the function automatically samples for random negative edges. And, if it
        is included, then computes the NCE loss over the given negative edges. This function returns the scores of the
        predicted positive and negative edges.

        :param edge_index_dict (dict): Dict of <relation/metapath>: <Tensor(2, num_edges)>
        :param alpha_l (dict): Dict of <node_type>:<alpha_l tensor>
        :param alpha_r (dict): Dict of <node_type>:<alpha_r tensor>
        :param global_node_idx (dict): Dict of <node_type>:<Tensor(node_idx,)>
        :return loss, edge_pred_dict: NCE loss. edge_pred_dict will contain both positive relations of shape (num_edges,) and negative relations of shape (num_edges*num_neg_edges, )
        """
        loss = torch.tensor(0.0, dtype=torch.float, device=self.conv[self.node_types[0]].weight.device)
        edge_pred_dict = {}
        for metapath, edge_index in edge_index_dict.items():
            # KL Divergence over observed positive edges or negative edges (if included)
            if isinstance(edge_index, tuple):  # Weighted edges
                edge_index, values = edge_index
            else:
                values = 1.0
            if edge_index is None: continue

            if not is_negative(metapath):
                e_pred_logits = self.predict_scores(edge_index, alpha_l, alpha_r, metapath, logits=True)
                loss += -torch.mean(values * F.logsigmoid(e_pred_logits), dim=-1)
            elif is_negative(metapath):
                e_pred_logits = self.predict_scores(edge_index, alpha_l, alpha_r, untag_negative(metapath), logits=True)
                loss += -torch.mean(F.logsigmoid(-e_pred_logits), dim=-1)

            edge_pred_dict[metapath] = F.sigmoid(e_pred_logits.detach())

            # Only need to sample for negative edges if negative metapath is not included
            if not is_negative(metapath) and tag_negative(metapath) not in edge_index_dict:
                neg_edge_index = negative_sample(edge_index,
                                                 M=global_node_idx[metapath[0]].size(0),
                                                 N=global_node_idx[metapath[-1]].size(0),
                                                 n_sample_per_edge=self.neg_sampling_ratio)
                if neg_edge_index is None or neg_edge_index.size(1) <= 1: continue

                e_neg_logits = self.predict_scores(neg_edge_index, alpha_l, alpha_r, metapath, logits=True)
                loss += -torch.mean(F.logsigmoid(-e_neg_logits), dim=-1)
                edge_pred_dict[tag_negative(metapath)] = F.sigmoid(e_neg_logits.detach())

        loss = torch.true_divide(loss, max(len(edge_index_dict) * 2, 1))
        return loss, edge_pred_dict

    def embedding_activation(self, embeddings):
        if self.activation == "sigmoid":
            return F.sigmoid(embeddings)
        elif self.activation == "tanh":
            return F.tanh(embeddings)
        elif self.activation == "relu":
            return F.relu(embeddings)
        else:
            return embeddings

    def attn_activation(self, alpha, metapath_id):
        if isinstance(self.alpha_activation, torch.Tensor):
            return self.alpha_activation[metapath_id] * alpha
        elif isinstance(self.alpha_activation, nn.Module):
            return self.alpha_activation.forward(alpha)
        else:
            return alpha

    def get_head_relations(self, head_node_type, to_str=False) -> list:
        relations = [".".join(metapath) if to_str and isinstance(metapath, tuple) else metapath for metapath in
                     self.metapaths if
                     metapath[0] == head_node_type]
        return relations

    def num_head_relations(self, node_type) -> int:
        """
        Return the number of metapaths with head node type equals to :param node_type: and plus one for none-selection.
        :param node_type (str):
        :return:
        """
        relations = self.get_head_relations(node_type)
        return len(relations) + 1

    def save_relation_weights(self, beta, global_node_idx):
        # Only save relation weights if beta has weights for all node_types in the global_node_idx batch
        if len(beta) < len(self.node_types): return

        self._betas = {}
        self._beta_avg = {}
        self._beta_std = {}
        for node_type in beta:
            with torch.no_grad():
                self._betas[node_type] = pd.DataFrame(beta[node_type].squeeze(-1).cpu().numpy(),
                                                      columns=self.get_head_relations(node_type, True) + [node_type, ],
                                                      index=global_node_idx[node_type].cpu().numpy())

                _beta_avg = np.around(beta[node_type].mean(dim=0).squeeze(-1).cpu().numpy(), decimals=3)
                _beta_std = np.around(beta[node_type].std(dim=0).squeeze(-1).cpu().numpy(), decimals=2)
                self._beta_avg[node_type] = {metapath: _beta_avg[i] for i, metapath in
                                             enumerate(self.get_head_relations(node_type, True) + [node_type])}
                self._beta_std[node_type] = {metapath: _beta_std[i] for i, metapath in
                                             enumerate(self.get_head_relations(node_type, True) + [node_type])}

    def save_attn_weights(self, node_type, attn_weights, node_idx):
        if not hasattr(self, "_betas"):
            self._betas = {}
        if not hasattr(self, "_beta_avg"):
            self._beta_avg = {}
        if not hasattr(self, "_beta_std"):
            self._beta_std = {}

        betas = attn_weights.sum(1)
        with torch.no_grad():
            self._betas[node_type] = pd.DataFrame(betas.cpu().numpy(),
                                                  columns=self.get_head_relations(node_type, True) + [node_type, ],
                                                  index=node_idx.cpu().numpy())

            _beta_avg = np.around(betas.mean(dim=0).cpu().numpy(), decimals=3)
            _beta_std = np.around(betas.std(dim=0).cpu().numpy(), decimals=2)
            self._beta_avg[node_type] = {metapath: _beta_avg[i] for i, metapath in
                                         enumerate(self.get_head_relations(node_type, True) + [node_type])}
            self._beta_std[node_type] = {metapath: _beta_std[i] for i, metapath in
                                         enumerate(self.get_head_relations(node_type, True) + [node_type])}

    def get_relation_weights(self):
        """
        Get the mean and std of relation attention weights for all nodes
        :return:
        """
        return {(metapath if "." in metapath or len(metapath) > 1 else node_type): (avg, std) \
                for node_type in self._beta_avg for (metapath, avg), (relation_b, std) in
                zip(self._beta_avg[node_type].items(), self._beta_std[node_type].items())}

def tag_negative(metapath):
    if isinstance(metapath, tuple):
        return metapath + ("neg",)
    elif isinstance(metapath, str):
        return metapath + "_neg"
    else:
        return "neg"

def untag_negative(metapath):
    if isinstance(metapath, tuple) and metapath[-1] == "neg":
        return metapath[:-1]
    elif isinstance(metapath, str):
        return metapath.strip("_neg")
    else:
        return metapath

def is_negative(metapath):
    if isinstance(metapath, tuple) and metapath[-1] == "neg":
        return True
    elif isinstance(metapath, str) and "_neg" in metapath:
        return True
    else:
        return False

def adamic_adar(indexA, valueA, indexB, valueB, m, k, n, coalesced=False, sampling=True):
    A = SparseTensor(row=indexA[0], col=indexA[1], value=valueA,
                     sparse_sizes=(m, k), is_sorted=not coalesced)
    B = SparseTensor(row=indexB[0], col=indexB[1], value=valueB,
                     sparse_sizes=(k, n), is_sorted=not coalesced)

    deg_A = A.storage.colcount()
    deg_B = B.storage.rowcount()
    deg_normalized = 1.0 / (deg_A + deg_B).to(torch.float)
    deg_normalized[deg_normalized == float('inf')] = 0.0

    D = SparseTensor(row=torch.arange(deg_normalized.size(0), device=valueA.device),
                     col=torch.arange(deg_normalized.size(0), device=valueA.device),
                     value=deg_normalized.type_as(valueA),
                     sparse_sizes=(deg_normalized.size(0), deg_normalized.size(0)))

    out = A @ D @ B
    row, col, values = out.coo()

    num_samples = min(int(valueA.numel()), int(valueB.numel()), values.numel())
    if sampling and values.numel() > num_samples:
        idx = torch.multinomial(values, num_samples=num_samples,
                                replacement=False)
        row, col, values = row[idx], col[idx], values[idx]

    return torch.stack([row, col], dim=0), values

def negative_sample(edge_index, M: int, N: int, n_sample_per_edge: int):
    num_neg_samples = edge_index.size(1) * n_sample_per_edge
    num_neg_samples = int(min(num_neg_samples, M * N - edge_index.size(1)))
    rng = range(M * N)
    idx = (edge_index[0] * N + edge_index[1]).to('cpu')  # idx = N * i + j

    perm = torch.tensor(random.sample(rng, num_neg_samples))
    mask = torch.from_numpy(np.isin(perm, idx)).to(torch.bool)
    rest = mask.nonzero().view(-1)
    while rest.numel() > 0:  # pragma: no cover
        tmp = torch.tensor(random.sample(rng, rest.size(0)))
        mask = torch.from_numpy(np.isin(tmp, idx)).to(torch.bool)
        perm[rest] = tmp
        rest = rest[mask.nonzero().view(-1)]

    row = perm // N
    col = perm % N
    neg_edge_index = torch.stack([row, col], dim=0).long()

    return neg_edge_index.to(edge_index.device)


from Knapsack import firstGreedyChoice
import random
class Bank:
    def __init__(self, players_list: list, nPicks: list, mask = [], trSize = 1):
        self.players_list = players_list
        self.nPicks = nPicks
        self.mask = mask
        self.trSize = trSize
        self.bankCacife = float("inf")
        self.bankQuotations = []

    def createFirstMask(self, size=3):
        maskSize = len(firstGreedyChoice()[2])
        mask = []
        for _ in range(size):
            r = random.randint(0,maskSize-1)
            if r not in mask:
                mask.append(r)
            else:
                while r in mask:
                    r = random.randint(0,maskSize-1)
                mask.append(r)
        self.mask = mask
        return self.mask

    def createBankQuotations(self, players_list = []):
        sums = []
        for m in range(2**len(self.mask)):
            total = 0
            for p in players_list:
                total += p.ratings[m]
            sums.append(total)
        print("\nSUMS:",sums)

        bankQuotations = list(map(lambda x: (1/x), sums))
        print("\nBANKQUOT:",bankQuotations)
        self.bankQuotations = bankQuotations
        return self.bankQuotations

    def best(self):
        pass


# words = "navgurukul is great"
# counter = 0
# while counter < len(words):
#     print (words[counter])
#     # print("------")
#     counter+=1
# print("-----")

sentence = "NavGurukul is an alternative to higher education reducing the barriers of current formal education system"
i=0
a=[]
while i<len(sentence):
    print(sentence[i])
    # a.append(sentence)
    x=sentence.split()
    i=i+1
print(x)
# i=i+1


"""openapi"""
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

from stac_api.config import ApiExtensions, ApiSettings

def config_openapi(app: FastAPI, settings: ApiSettings):
    """config openapi"""

    def custom_openapi():
        """config openapi"""
        if app.openapi_schema:
            return app.openapi_schema

        openapi_schema = get_openapi(
            title="Sherlock STAC API", version="0.1", routes=app.routes
        )

        if settings.api_extension_is_enabled(ApiExtensions.fields):
            openapi_schema["paths"]["/search"]["get"]["responses"]["200"]["content"][
                "application/json"
            ]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}
            openapi_schema["paths"]["/search"]["post"]["responses"]["200"]["content"][
                "application/json"
            ]["schema"] = {"$ref": "#/components/schemas/ItemCollection"}

        app.openapi_schema = openapi_schema
        return app.openapi_schema

    app.openapi = custom_openapi


"""Support for the Vallox ventilation unit fan."""
from __future__ import annotations

from collections.abc import Mapping
import logging
from typing import Any, NamedTuple

from vallox_websocket_api import Vallox
from vallox_websocket_api.exceptions import ValloxApiException

from homeassistant.components.fan import (
    FanEntity,
    FanEntityFeature,
    NotValidPresetModeError,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from . import ValloxDataUpdateCoordinator, ValloxEntity
from .const import (
    DOMAIN,
    METRIC_KEY_MODE,
    METRIC_KEY_PROFILE_FAN_SPEED_AWAY,
    METRIC_KEY_PROFILE_FAN_SPEED_BOOST,
    METRIC_KEY_PROFILE_FAN_SPEED_HOME,
    MODE_OFF,
    MODE_ON,
    STR_TO_VALLOX_PROFILE_SETTABLE,
    VALLOX_PROFILE_TO_STR_SETTABLE,
)

_LOGGER = logging.getLogger(__name__)

class ExtraStateAttributeDetails(NamedTuple):
    """Extra state attribute details."""

    description: str
    metric_key: str

EXTRA_STATE_ATTRIBUTES = (
    ExtraStateAttributeDetails(
        description="fan_speed_home", metric_key=METRIC_KEY_PROFILE_FAN_SPEED_HOME
    ),
    ExtraStateAttributeDetails(
        description="fan_speed_away", metric_key=METRIC_KEY_PROFILE_FAN_SPEED_AWAY
    ),
    ExtraStateAttributeDetails(
        description="fan_speed_boost", metric_key=METRIC_KEY_PROFILE_FAN_SPEED_BOOST
    ),
)

def _convert_fan_speed_value(value: StateType) -> int | None:
    if isinstance(value, (int, float)):
        return int(value)

    return None

async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up the fan device."""
    data = hass.data[DOMAIN][entry.entry_id]

    client = data["client"]
    client.set_settable_address(METRIC_KEY_MODE, int)

    device = ValloxFanEntity(
        data["name"],
        client,
        data["coordinator"],
    )

    async_add_entities([device])

class ValloxFanEntity(ValloxEntity, FanEntity):
    """Representation of the fan."""

    _attr_supported_features = FanEntityFeature.PRESET_MODE
    _attr_has_entity_name = True

    def __init__(
        self,
        name: str,
        client: Vallox,
        coordinator: ValloxDataUpdateCoordinator,
    ) -> None:
        """Initialize the fan."""
        super().__init__(name, coordinator)

        self._client = client

        self._attr_unique_id = str(self._device_uuid)

    @property
    def preset_modes(self) -> list[str]:
        """Return a list of available preset modes."""
        # Use the Vallox profile names for the preset names.
        return list(STR_TO_VALLOX_PROFILE_SETTABLE.keys())

    @property
    def is_on(self) -> bool:
        """Return if device is on."""
        return self.coordinator.data.get_metric(METRIC_KEY_MODE) == MODE_ON

    @property
    def preset_mode(self) -> str | None:
        """Return the current preset mode."""
        vallox_profile = self.coordinator.data.profile
        return VALLOX_PROFILE_TO_STR_SETTABLE.get(vallox_profile)

    @property
    def extra_state_attributes(self) -> Mapping[str, int | None]:
        """Return device specific state attributes."""
        data = self.coordinator.data

        return {
            attr.description: _convert_fan_speed_value(data.get_metric(attr.metric_key))
            for attr in EXTRA_STATE_ATTRIBUTES
        }

    async def _async_set_preset_mode_internal(self, preset_mode: str) -> bool:
        """
        Set new preset mode.

        Returns true if the mode has been changed, false otherwise.
        """
        try:
            self._valid_preset_mode_or_raise(preset_mode)

        except NotValidPresetModeError as err:
            _LOGGER.error(err)
            return False

        if preset_mode == self.preset_mode:
            return False

        try:
            await self._client.set_profile(STR_TO_VALLOX_PROFILE_SETTABLE[preset_mode])

        except (OSError, ValloxApiException) as err:
            _LOGGER.error("Error setting preset: %s", err)
            return False

        return True

    async def async_set_preset_mode(self, preset_mode: str) -> None:
        """Set new preset mode."""
        update_needed = await self._async_set_preset_mode_internal(preset_mode)

        if update_needed:
            # This state change affects other entities like sensors. Force an immediate update that
            # can be observed by all parties involved.
            await self.coordinator.async_request_refresh()

    async def async_turn_on(
        self,
        percentage: int | None = None,
        preset_mode: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Turn the device on."""
        _LOGGER.debug("Turn on")

        update_needed = False

        if preset_mode:
            update_needed = await self._async_set_preset_mode_internal(preset_mode)

        if not self.is_on:
            try:
                await self._client.set_values({METRIC_KEY_MODE: MODE_ON})

            except OSError as err:
                _LOGGER.error("Error turning on: %s", err)

            else:
                update_needed = True

        if update_needed:
            # This state change affects other entities like sensors. Force an immediate update that
            # can be observed by all parties involved.
            await self.coordinator.async_request_refresh()

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the device off."""
        if not self.is_on:
            return

        try:
            await self._client.set_values({METRIC_KEY_MODE: MODE_OFF})

        except OSError as err:
            _LOGGER.error("Error turning off: %s", err)
            return

        # Same as for turn_on method.
        await self.coordinator.async_request_refresh()