Instruction:
Can you generate the documentation for the following Python 3 function
def map_package_to_dataset(package, portal_url):
    dataset = dict()
    resources = package["resources"]
    groups = package["groups"]
    tags = package["tags"]

    dataset_mapping = {
        'title': 'title',
        'notes': 'description',
        'metadata_created': 'issued',
        'metadata_modified': 'modified',
        'license_title': 'license',
        'id': 'identifier',
        'url': 'landingPage'
    }

    for package_key, dataset_key in iteritems(dataset_mapping):
        try:
            dataset[dataset_key] = package[package_key]
        except BaseException:
            logger.exception("""
            La clave '%s' no está en el endpoint 'package_show' para el
            package '%s'. No se puede completar dataset['%s'].""",
                             package_key, package['name'], dataset_key)

    publisher_mapping = {
        'author': 'name',
        'author_email': 'mbox'
    }

    if any([k in package for k in publisher_mapping.keys()]):
        dataset["publisher"] = dict()
        for package_key, publisher_key in iteritems(publisher_mapping):
            try:
                dataset['publisher'][publisher_key] = package[package_key]
            except BaseException:
                logger.exception("""
                La clave '%s' no está en el endpoint 'package_show' para el
                package '%s'. No se puede completar
                dataset['publisher']['%s'].""",
                                 package_key, package['name'], publisher_key)

    contact_point_mapping = {
        'maintainer': 'fn',
        'maintainer_email': 'hasEmail'
    }

    if any([k in package for k in contact_point_mapping.keys()]):
        dataset["contactPoint"] = dict()
        for package_key, contact_key in iteritems(contact_point_mapping):
            try:
                dataset['contactPoint'][contact_key] = package[package_key]
            except BaseException:
                logger.exception("""
                La clave '%s' no está en el endpoint 'package_show' para el
                package '%s'. No se puede completar
                dataset['contactPoint']['%s'].""",
                                 package_key, package['name'], contact_key)

    # Si existen campos extras en la información del package, busco las claves
    # "Frecuencia de actualización" y "Temática global" para completar los
    # campos "accrualPeriodicity" y "superTheme" del dataset, respectivamente.
    if "extras" in package:
        add_accrualPeriodicity(dataset, package)
        add_superTheme(dataset, package)
        add_temporal(dataset, package)

    dataset["distribution"] = map_resources_to_distributions(resources,
                                                             portal_url)
    dataset["theme"] = [grp['name'] for grp in groups]
    dataset['keyword'] = [tag['name'] for tag in tags]

    return dataset

Response: Mapea un diccionario con metadatos de CKAN a un diccionario con un package de CKAN a un diccionario con metadatos de CKAN a un diccionario con un dataset de CKAN.


Instruction:
Here you have a function in Python 3, explain what it does
def map_group_to_theme(group):
    theme = dict()

    theme_mapping = {
        'name': 'id',
        'title': 'label',
        'description': 'description'
    }

    for group_key, theme_key in iteritems(theme_mapping):
        try:
            theme[theme_key] = group[group_key]
        except BaseException:
            logger.exception("""
            La clave '%s' no está en la metadata del 'group' '%s'. No
            se puede completar theme['%s'].""",
                             group_key, theme['name'], theme_key)

    return theme

Response: Mapea un diccionario con metadatos de CKAN a un diccionario con metadatos de CKAN a un diccionario con metadatos de CKAN a un diccionario con un CKAN a un diccionario con un CKAN a un diccionario con un CKAN a un diccionario con un CKAN.


Instruction:
How would you explain what the following Python 3 function does
def write_tables(tables, path, column_styles=None, cell_styles=None,
                 tables_fields=None, tables_names=None):
    """ Exporta un reporte con varias tablas en CSV o XLSX.

    Si la extensión es ".csv" se crean varias tablas agregando el nombre de la
    tabla al final del "path". Si la extensión es ".xlsx" todas las tablas se
    escriben en el mismo excel.

    Args:
        table (dict of (list of dicts)): Conjunto de tablas a ser exportadas
            donde {
                "table_name": [{
                    "field_name1": "field_value1",
                    "field_name2": "field_value2",
                    "field_name3": "field_value3"
                }]
            }
        path (str): Path al archivo CSV o XLSX de exportación.
    """
    assert isinstance(path, string_types), "`path` debe ser un string"
    assert isinstance(tables, dict), "`table` es dict de listas de dicts"

    # Deduzco el formato de archivo de `path` y redirijo según corresponda.
    suffix = path.split(".")[-1]
    if suffix == "csv":
        for table_name, table in tables:
            root_path = "".join(path.split(".")[:-1])
            table_path = "{}_{}.csv".format(root_path, table_name)
            _write_csv_table(table, table_path)

    elif suffix == "xlsx":
        return _write_xlsx_table(tables, path, column_styles, cell_styles,
                                 tables_fields=tables_fields,
                                 tables_names=tables_names)

    else:
        raise ValueError("""
{} no es un sufijo reconocido. Pruebe con .csv o.xlsx""".format(suffix))

Response: Exporta un reporte de un tablelas en CSV o XLSX.


Instruction:
Can you tell what is the following Python 3 function doing
def write_json(obj, path):
    obj_str = text_type(json.dumps(obj, indent=4, separators=(",", ": "),
                                   ensure_ascii=False))

    helpers.ensure_dir_exists(os.path.dirname(path))

    with io.open(path, "w", encoding='utf-8') as target:
        target.write(obj_str)

Response: Escribo un objeto a un archivo JSON con codificación UTF - 8.


Instruction:
Can you generate the documentation for the following Python 3 function
def download(url, tries=DEFAULT_TRIES, retry_delay=RETRY_DELAY,
             try_timeout=None, proxies=None, verify=True):
    """
    Descarga un archivo a través del protocolo HTTP, en uno o más intentos.

    Args:
        url (str): URL (schema HTTP) del archivo a descargar.
        tries (int): Intentos a realizar (default: 1).
        retry_delay (int o float): Tiempo a esperar, en segundos, entre cada
            intento.
        try_timeout (int o float): Tiempo máximo a esperar por intento.
        proxies (dict): Proxies a utilizar. El diccionario debe contener los
            valores 'http' y 'https', cada uno asociados a la URL del proxy
            correspondiente.

    Returns:
        bytes: Contenido del archivo
    """
    for i in range(tries):
        try:
            return requests.get(url, timeout=try_timeout, proxies=proxies,
                                verify=verify).content
        except Exception as e:
            download_exception = e

            if i < tries - 1:
                time.sleep(retry_delay)

    raise download_exception

Response: Download a file from a URL.


Instruction:
Implement a function in Python 3 to
download a file from url to file_path.

Response:
def download_to_file(url, file_path, **kwargs):
    """
    Descarga un archivo a través del protocolo HTTP, en uno o más intentos, y
    escribe el contenido descargado el el path especificado.

    Args:
        url (str): URL (schema HTTP) del archivo a descargar.
        file_path (str): Path del archivo a escribir. Si un archivo ya existe
            en el path especificado, se sobrescribirá con nuevos contenidos.
        kwargs: Parámetros para download().
    """
    content = download(url, **kwargs)
    with open(file_path, "wb") as f:
        f.write(content)


Instruction:
Given the following Python 3 function, write the documentation
def replace_combiner(value, mutator, *args, **kwargs):
    args = list(args) + [value]
    return mutator(*args, **kwargs)

Response: Replaces the output of the source or mutator with the output of the subsequent mutator.


Instruction:
Here you have a function in Python 3, explain what it does
def set_combiner(value, mutator, *args, **kwargs):
    value.add(mutator(*args, **kwargs))
    return value

Response: Adds a mutator to the output of the source.


Instruction:
Here you have a function in Python 3, explain what it does
def list_combiner(value, mutator, *args, **kwargs):
    value.append(mutator(*args, **kwargs))
    return value

Response: Takes a list and a mutator function and appends it to the output of the source.


Instruction:
Implement a function in Python 3 to
register a callable as the producer of a named value.

Response:
def register_value_producer(self, value_name: str, source: Callable[..., pd.DataFrame]=None,
                                preferred_combiner: Callable=replace_combiner,
                                preferred_post_processor: Callable[..., pd.DataFrame]=None) -> Pipeline:
        """Marks a ``Callable`` as the producer of a named value.

        Parameters
        ----------
        value_name :
            The name of the new dynamic value pipeline.
        source :
            A callable source for the dynamic value pipeline.
        preferred_combiner :
            A strategy for combining the source and the results of any calls to mutators in the pipeline.
            ``vivarium`` provides the strategies ``replace_combiner`` (the default), ``list_combiner``,
            and ``set_combiner`` which are importable from ``vivarium.framework.values``.  Client code
            may define additional strategies as necessary.
        preferred_post_processor :
            A strategy for processing the final output of the pipeline. ``vivarium`` provides the strategies
            ``rescale_post_processor`` and ``joint_value_post_processor`` which are importable from
            ``vivarium.framework.values``.  Client code may define additional strategies as necessary.

        Returns
        -------
        Callable
            A callable reference to the named dynamic value pipeline.
        """
        return self._value_manager.register_value_producer(value_name, source,
                                                           preferred_combiner,
                                                           preferred_post_processor)


Instruction:
Implement a function in Python 3 to
mark a callable as the producer of a named rate.

Response:
def register_rate_producer(self, rate_name: str, source: Callable[..., pd.DataFrame]=None) -> Pipeline:
        """Marks a ``Callable`` as the producer of a named rate.

        This is a convenience wrapper around ``register_value_producer`` that makes sure
        rate data is appropriately scaled to the size of the simulation time step.
        It is equivalent to ``register_value_producer(value_name, source,
        preferred_combiner=replace_combiner, preferred_post_processor=rescale_post_processor)``

        Parameters
        ----------
        rate_name :
            The name of the new dynamic rate pipeline.
        source :
            A callable source for the dynamic rate pipeline.

        Returns
        -------
        Callable
            A callable reference to the named dynamic rate pipeline.
        """
        return self._value_manager.register_rate_producer(rate_name, source)


Instruction:
Can you implement a function in Python 3 that
registers a callable as a modifier of a named value in the dynamic value pipeline.

Response:
def register_value_modifier(self, value_name: str, modifier: Callable, priority: int=5):
        """Marks a ``Callable`` as the modifier of a named value.

        Parameters
        ----------
        value_name :
            The name of the dynamic value pipeline to be modified.
        modifier :
            A function that modifies the source of the dynamic value pipeline when called.
            If the pipeline has a ``replace_combiner``, the modifier should accept the same
            arguments as the pipeline source with an additional last positional argument
            for the results of the previous stage in the pipeline. For the ``list_combiner`` and
            ``set_combiner`` strategies, the pipeline modifiers should have the same signature
            as the pipeline source.
        priority : {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
            An indication of the order in which pipeline modifiers should be called. Modifiers with
            smaller priority values will be called earlier in the pipeline.  Modifiers with
            the same priority have no guaranteed ordering, and so should be commutative.
        """
        self._value_manager.register_value_modifier(value_name, modifier, priority)


Instruction:
How would you explain what the following Python 3 function does
def run(model_specification, results_directory, verbose, log, with_debugger):
    log_level = logging.DEBUG if verbose else logging.ERROR
    logging.basicConfig(filename=log, level=log_level)

    try:
        run_simulation(model_specification, results_directory)
    except (BdbQuit, KeyboardInterrupt):
        raise
    except Exception as e:
        if with_debugger:
            import pdb
            import traceback
            traceback.print_exc()
            pdb.post_mortem()
        else:
            logging.exception("Uncaught exception {}".format(e))
            raise

Response: Runs a simulation from the command line.


Instruction:
Can you implement a function in Python 3 that
runs a simulation based on the MODEL_SPECIFICATION and profile the run.

Response:
def profile(model_specification, results_directory, process):
    """Run a simulation based on the provided MODEL_SPECIFICATION and profile
    the run.
    """
    model_specification = Path(model_specification)
    results_directory = Path(results_directory)

    out_stats_file = results_directory / f'{model_specification.name}'.replace('yaml', 'stats')
    command = f'run_simulation("{model_specification}", "{results_directory}")'
    cProfile.runctx(command, globals=globals(), locals=locals(), filename=out_stats_file)

    if process:
        out_txt_file = results_directory / (out_stats_file.name + '.txt')
        with open(out_txt_file, 'w') as f:
            p = pstats.Stats(str(out_stats_file), stream=f)
            p.sort_stats('cumulative')
            p.print_stats()


Instruction:
Create a Python 3 function for
creating a new event which is a copy of this one but with a new index.

Response:
def split(self, new_index):
        """Create a new event which is a copy of this one but with a new index.
        """
        new_event = Event(new_index, self.user_data)
        new_event.time = self.time
        new_event.step_size = self.step_size
        return new_event


Instruction:
Given the following Python 3 function, write the documentation
def emit(self, event):
        if hasattr(event, 'time'):
            event.step_size = self.manager.step_size()
            event.time = self.manager.clock() + self.manager.step_size()

        for priority_bucket in self.listeners:
            for listener in sorted(priority_bucket, key=lambda x: x.__name__):
                listener(event)
        return event

Response: Notifies all listeners that an event has occurred.


Instruction:
Can you create a Python 3 function that
performs this components simulation setup.

Response:
def setup(self, builder):
        """Performs this components simulation setup.

        Parameters
        ----------
        builder : vivarium.framework.engine.Builder
            Object giving access to core framework functionality.
        """
        self.clock = builder.time.clock()
        self.step_size = builder.time.step_size()


Instruction:
How would you implement a function in Python 3 that
registers a new listener to the named event.

Response:
def register_listener(self, name, listener, priority=5):
        """Registers a new listener to the named event.

        Parameters
        ----------
        name : str
            The name of the event.
        listener : Callable
            The consumer of the named event.
        priority : int
            Number in range(10) used to assign the ordering in which listeners process the event.
        """
        self._event_types[name].listeners[priority].append(listener)


Instruction:
Create a Python 3 function for
getting and returns an emitter for a named event.

Response:
def get_emitter(self, name: str) -> Callable[[Event], Event]:
        """Gets and emitter for a named event.

        Parameters
        ----------
        name :
            The name of the event he requested emitter will emit.
            Users may provide their own named events by requesting an emitter with this function,
            but should do so with caution as it makes time much more difficult to think about.

        Returns
        -------
            An emitter for the named event. The emitter should be called by the requesting component
            at the appropriate point in the simulation lifecycle.
        """
        return self._event_manager.get_emitter(name)


Instruction:
How would you explain what the following Python 3 function does
def register_listener(self, name: str, listener: Callable[[Event], None], priority: int=5) -> None:
        self._event_manager.register_listener(name, listener, priority)

Response: Registers a callable as a listener to a events with the given name.


Instruction:
How would you explain what the following Python 3 function does
def setup(self, builder):
        super().setup(builder)
        self.clock = builder.time.clock()

        self.excess_mortality_rate = builder.value.register_rate_producer(
            f'{self.state_id}.excess_mortality_rate',
            source=self.risk_deleted_excess_mortality_rate
        )
        self.excess_mortality_rate_paf = builder.value.register_value_producer(
            f'{self.state_id}.excess_mortality_rate.population_attributable_fraction',
            source=lambda index: [pd.Series(0, index=index)],
            preferred_combiner=list_combiner,
            preferred_post_processor=joint_value_post_processor
        )

        builder.value.register_value_modifier('mortality_rate', self.add_in_excess_mortality)
        self.population_view = builder.population.get_view(
            [self._model], query=f"alive == 'alive' and {self._model} == '{self.state_id}'")

Response: Performs this component s simulation setup.


Instruction:
Explain what the following Python 3 code does
def get(self, index: pd.Index, query: str='', omit_missing_columns: bool=False) -> pd.DataFrame:

        pop = self.manager.get_population(True).loc[index]

        if self._query:
            pop = pop.query(self._query)
        if query:
            pop = pop.query(query)

        if not self._columns:
            return pop
        else:
            if omit_missing_columns:
                columns = list(set(self._columns).intersection(pop.columns))
            else:
                columns = self._columns
            try:
                return pop[columns].copy()
            except KeyError:
                non_existent_columns = set(columns) - set(pop.columns)
                raise PopulationError(f'Requested column(s) {non_existent_columns} not in population table.')

Response: For the rows in index get the columns from the simulation s population which this view is configured.


Instruction:
Write a Python 3 function for
updating the simulation s state to match the given pop.

Response:
def update(self, pop: Union[pd.DataFrame, pd.Series]):
        """Update the simulation's state to match ``pop``

        Parameters
        ----------
        pop :
              The data which should be copied into the simulation's state. If ``pop`` is a DataFrame only those columns
              included in the view's columns will be used. If ``pop`` is a Series it must have a name that matches
              one of the view's columns unless the view only has one column in which case the Series will be assumed to
              refer to that regardless of its name.
        """

        if not pop.empty:
            if isinstance(pop, pd.Series):
                if pop.name in self._columns:
                    affected_columns = [pop.name]
                elif len(self._columns) == 1:
                    affected_columns = self._columns
                else:
                    raise PopulationError('Cannot update with a Series unless the series name equals a column '
                                          'name or there is only a single column in the view')
            else:
                affected_columns = set(pop.columns)

            affected_columns = set(affected_columns).intersection(self._columns)
            state_table = self.manager.get_population(True)
            if not self.manager.growing:
                affected_columns = set(affected_columns).intersection(state_table.columns)

            for c in affected_columns:
                if c in state_table:
                    v = state_table[c].values
                    if isinstance(pop, pd.Series):
                        v2 = pop.values
                    else:
                        v2 = pop[c].values
                    v[pop.index] = v2

                    if v.dtype != v2.dtype:
                        # This happens when the population is being grown because extending
                        # the index forces columns that don't have a natural null type
                        # to become 'object'
                        if not self.manager.growing:
                            raise PopulationError('Component corrupting population table. '
                                                  'Old column type: {} New column type: {}'.format(v.dtype, v2.dtype))
                        v = v.astype(v2.dtype)
                else:
                    if isinstance(pop, pd.Series):
                        v = pop.values
                    else:
                        v = pop[c].values
                self.manager._population[c] = v


Instruction:
Can you write a function in Python 3 where it
returns a configured PopulationView for the given columns.

Response:
def get_view(self, columns: Sequence[str], query: str=None) -> PopulationView:
        """Return a configured PopulationView

        Notes
        -----
        Client code should only need this (and only through the version exposed as
        ``population_view`` on the builder during setup) if it uses dynamically
        generated column names that aren't known at definition time. Otherwise
        components should use ``uses_columns``.
        """
        if 'tracked' not in columns:
            query_with_track = query + 'and tracked == True' if query else 'tracked == True'
            return PopulationView(self, columns, query_with_track)
        return PopulationView(self, columns, query)


Instruction:
How would you implement a function in Python 3 that
gets a time - varying view of the state table.

Response:
def get_view(self, columns: Sequence[str], query: str = None) -> PopulationView:
        """Get a time-varying view of the population state table.

        The requested population view can be used to view the current state or to update the state
        with new values.

        Parameters
        ----------
        columns :
            A subset of the state table columns that will be available in the returned view.
        query :
            A filter on the population state.  This filters out particular rows (simulants) based
            on their current state.  The query should be provided in a way that is understood by
            the ``pandas.DataFrame.query`` method and may reference state table columns not
            requested in the ``columns`` argument.

        Returns
        -------
        PopulationView
            A filtered view of the requested columns of the population state table.
        """
        return self._population_manager.get_view(columns, query)


Instruction:
How would you explain what the following Python 3 function does
def get_simulant_creator(self) -> Callable[[int, Union[Mapping[str, Any], None]], pd.Index]:
        return self._population_manager.get_simulant_creator()

Response: Returns a function that creates new simulants and returns a reference to the function that creates new simulants.


Instruction:
Can you write a function in Python 3 where it
marks a callable as a source of initial state information about new simulants.

Response:
def initializes_simulants(self, initializer: Callable[[SimulantData], None],
                              creates_columns: Sequence[str]=(),
                              requires_columns: Sequence[str]=()):
        """Marks a callable as a source of initial state information for new simulants.

        Parameters
        ----------
        initializer :
            A callable that adds or updates initial state information about new simulants.
        creates_columns :
            A list of the state table columns that the given initializer provides the initial state information for.
        requires_columns :
            A list of the state table columns that already need to be present and populated
            in the state table before the provided initializer is called.
        """
        self._population_manager.register_simulant_initializer(initializer, creates_columns, requires_columns)


Instruction:
Explain what the following Python 3 code does
def ensure_dir_exists(directory):
    if directory and not os.path.exists(directory):
        os.makedirs(directory)

Response: Se asegura de que un directorio exista.


Instruction:
Create a Python 3 function for
testing if a list of dictionaries comprueba que una lista esté compuesta únicamente por diccionarios.

Response:
def is_list_of_matching_dicts(list_of_dicts, expected_keys=None):
    """Comprueba que una lista esté compuesta únicamente por diccionarios,
    que comparten exactamente las mismas claves.

    Args:
        list_of_dicts (list): Lista de diccionarios a comparar.
        expected_keys (set): Conjunto de las claves que cada diccionario debe
            tener. Si no se incluye, se asume que son las claves del primer
            diccionario de la lista.

    Returns:
        bool: True si todos los diccionarios comparten sus claves.
    """

    if isinstance(list_of_dicts, list) and len(list_of_dicts) == 0:
        return False

    is_not_list_msg = """
{} no es una lista. Debe ingresar una lista""".format(list_of_dicts)
    assert isinstance(list_of_dicts, list), is_not_list_msg

    not_all_dicts_msg = """
No todos los elementos de {} son diccionarios. Ingrese una lista compuesta
solo por diccionarios.""".format(list_of_dicts)
    assert all([isinstance(d, dict) for d in list_of_dicts]), not_all_dicts_msg

    # Si no se pasan expected_keys, se las toma del primer diccionario
    expected_keys = expected_keys or set(list_of_dicts[0].keys())

    elements = [set(d.keys()) == expected_keys for d in list_of_dicts]

    return all(elements)


Instruction:
Make a summary of the following Python 3 code
def parse_repeating_time_interval_to_days(date_str):

    intervals = {
        'Y': 365,
        'M': 30,
        'W': 7,
        'D': 1,
        'H': 0,
        'S': 0
    }

    if date_str.find('R/P') != 0:  # Periodicity mal formada
        return 0

    date_str = date_str.strip('R/P')
    days = 0
    index = 0
    for interval in intervals:
        value_end = date_str.find(interval)
        if value_end < 0:
            continue
        try:
            days += int(float(date_str[index:value_end]) * intervals[interval])
        # Valor de accrualPeriodicity inválido, se toma como 0
        except ValueError:
            continue
        index = value_end

    # Si el número de días es menor lo redondeamos a 1
    return max(days, 1)

Response:
Parse a string con un intervalo de tiempo con repetición especificado
    Devuelve 0 en caso de que el intervalo sea inválido.


Instruction:
Can you tell what is the following Python 3 function doing
def parse_repeating_time_interval_to_str(date_str):

    with open(os.path.join(ABSOLUTE_SCHEMA_DIR,
                           "accrualPeriodicity.json"), "r") as f:
        freqs_map = {freq["id"]: freq["description"]
                     for freq in json.load(f)}

    return freqs_map[date_str]

Response: Parse a date string to a list of intervals.


Instruction:
Explain what the following Python 3 code does
def find_ws_name(wb, name):
    if isinstance(wb, string_types):
        # FIXME: importar o borrar segun corresponda
        wb = load_workbook(wb, read_only=True, data_only=True)

    for sheetname in wb.sheetnames:
        if sheetname.lower() == name.lower():
            return sheetname

    raise Exception("No existe la hoja {}".format(name))

Response: Busca una hoja en un workbook sin importar mayúsculas / minúsculas.


Instruction:
Make a summary of the following Python 3 code
def generate_distribution_ids(catalog):

    for dataset in catalog.get("dataset", []):
        for distribution_index, distribution in enumerate(
                dataset.get("distribution", [])):
            if "identifier" not in distribution:
                distribution["identifier"] = "{}_{}".format(
                    dataset["identifier"], distribution_index)

Response: Genera identificadores para los distribuciones que no los tienen.


Instruction:
How would you explain what the following Python 3 function does
def _get_theme_label(catalog, theme):
    try:
        label = catalog.get_theme(identifier=theme)['label']
    except BaseException:
        try:
            label = catalog.get_theme(label=theme)['label']
        except BaseException:
            raise ce.ThemeNonExistentError(theme)

    label = re.sub(r'[^\wá-úÁ-ÚñÑ .-]+', '',
                   label, flags=re.UNICODE)
    return label

Response: Intenta conseguir el theme por id o por label.


Instruction:
Implement a Python 3 function for
making a backup of the given list of catalogs.

Response:
def make_catalogs_backup(catalogs, local_catalogs_dir="",
                         include_metadata=True, include_data=True,
                         include_metadata_xlsx=False, use_short_path=False):
    """Realiza una copia local de los datos y metadatos de un catálogo.

    Args:
        catalogs (list or dict): Lista de catálogos (elementos que pueden
            ser interpretados por DataJson como catálogos) o diccionario
            donde las keys se interpretan como los catalog_identifier:
                {
                "modernizacion":
                "http://infra.datos.gob.ar/catalog/modernizacion/data.json"
                }
            Cuando es una lista, los ids se toman de catalog_identifer, y
            se ignoran los catálogos que no tengan catalog_identifier.
            Cuando se pasa un diccionario, los keys reemplazan a los
            catalog_identifier (estos no se leeen).
        catalog_id (str): Si se especifica, se usa este identificador para el
            backup. Si no se espedifica, se usa catalog["identifier"].
        local_catalogs_dir (str): Directorio local en el cual se va a crear
            la carpeta "catalog/..." con todos los catálogos.
        include_metadata (bool): Si es verdadero, se generan los archivos
            data.json y catalog.xlsx.
        include_data (bool): Si es verdadero, se descargan todas las
            distribuciones de todos los catálogos.

    Return:
        None
    """

    if isinstance(catalogs, list):
        for catalog in catalogs:
            try:
                make_catalog_backup(
                    catalog,
                    local_catalogs_dir=local_catalogs_dir,
                    include_metadata=include_metadata,
                    include_metadata_xlsx=include_metadata_xlsx,
                    include_data=include_data,
                    use_short_path=use_short_path)
            except Exception:
                logger.exception("ERROR en {}".format(catalog))

    elif isinstance(catalogs, dict):
        for catalog_id, catalog in catalogs.iteritems():
            try:
                make_catalog_backup(
                    catalog, catalog_id,
                    local_catalogs_dir=local_catalogs_dir,
                    include_metadata=include_metadata,
                    include_metadata_xlsx=include_metadata_xlsx,
                    include_data=include_data,
                    use_short_path=use_short_path)
            except Exception:
                logger.exception(
                    "ERROR en {} ({})".format(catalog, catalog_id))


Instruction:
Create a Python 3 function to
create a backup of the catalog.

Response:
def make_catalog_backup(catalog, catalog_id=None, local_catalogs_dir="",
                        include_metadata=True, include_data=True,
                        include_datasets=None,
                        include_distribution_formats=['CSV', 'XLS'],
                        include_metadata_xlsx=True, use_short_path=False):
    """Realiza una copia local de los datos y metadatos de un catálogo.

    Args:
        catalog (dict or str): Representación externa/interna de un catálogo.
            Una representación _externa_ es un path local o una URL remota a un
            archivo con la metadata de un catálogo, en formato JSON o XLSX. La
            representación _interna_ de un catálogo es un diccionario.
        catalog_id (str): Si se especifica, se usa este identificador para el
            backup. Si no se especifica, se usa catalog["identifier"].
        local_catalogs_dir (str): Directorio local en el cual se va a crear
            la carpeta "catalog/..." con todos los catálogos.
        include_metadata (bool): Si es verdadero, se generan los archivos
            data.json y catalog.xlsx.
        include_data (bool): Si es verdadero, se descargan todas las
            distribuciones de todos los catálogos.
        include_datasets (list): Si se especifica, se descargan únicamente los
            datasets indicados. Si no, se descargan todos.
        include_distribution_formats (list): Si se especifica, se descargan
            únicamente las distribuciones de los formatos indicados. Si no, se
            descargan todas.
        use_short_path (bool): No implementado. Si es verdadero, se utiliza una
            jerarquía de directorios simplificada. Caso contrario, se replica
            la existente en infra.
    Return:
        None
    """

    catalog = pydatajson.DataJson(catalog)
    catalog_identifier = catalog_id if catalog_id else catalog["identifier"]

    if include_metadata:
        logger.info(
            "Descargando catálogo {}".format(
                catalog_identifier.ljust(30)))

        # catálogo en json
        catalog_path = get_catalog_path(catalog_identifier, local_catalogs_dir)
        ensure_dir_exists(os.path.dirname(catalog_path))
        catalog.to_json(catalog_path)

        if include_metadata_xlsx:
            # catálogo en xlsx
            catalog_path = get_catalog_path(
                catalog_identifier, local_catalogs_dir, fmt="xlsx")
            ensure_dir_exists(os.path.dirname(catalog_path))
            catalog.to_xlsx(catalog_path)

    if include_data:
        distributions = catalog.distributions
        distributions_num = len(distributions)

        for index, distribution in enumerate(distributions):
            logger.info("Descargando distribución {} de {} ({})".format(
                index + 1, distributions_num, catalog_identifier))

            dataset_id = distribution["dataset_identifier"]

            if include_datasets and (dataset_id not in include_datasets):
                pass
            else:
                distribution_id = distribution["identifier"]
                distribution_download_url = distribution["downloadURL"]

                # si no se especifica un file name, se toma de la URL
                distribution_file_name = distribution.get(
                    "fileName",
                    distribution_download_url[
                        distribution_download_url.rfind("/") + 1:]
                )

                # si no espicifica un formato, toma de distribution_file_name
                # asume que formato está al menos en distribution_file_name
                distribution_format = distribution.get(
                    "format",
                    distribution_file_name[
                        distribution_file_name.rfind(".") + 1:]
                )
                if (include_distribution_formats and
                        (distribution_format
                            not in include_distribution_formats)):
                    pass
                else:

                    # genera el path local donde descargar el archivo
                    file_path = get_distribution_path(
                        catalog_identifier, dataset_id, distribution_id,
                        distribution_file_name, local_catalogs_dir,
                        use_short_path=use_short_path)
                    ensure_dir_exists(os.path.dirname(file_path))

                    # decarga el archivo
                    download_to_file(distribution_download_url, file_path)


Instruction:
Given the following Python 3 function, write the documentation
def get_distribution_path(catalog_id, dataset_id, distribution_id,
                          distribution_file_name, catalogs_dir=CATALOGS_DIR,
                          use_short_path=False):
    """Genera el path estándar de un catálogo en un filesystem."""
    if use_short_path:
        distribution_dir = get_distribution_dir(
            catalog_id, dataset_id, distribution_id, catalogs_dir,
            use_short_path=True)
        distribution_file_path = os.path.join(
            distribution_dir, distribution_file_name)
    else:
        distribution_dir = get_distribution_dir(
            catalog_id, dataset_id, distribution_id, catalogs_dir,
            use_short_path=False)
        distribution_file_path = os.path.join(
            distribution_dir, "download", distribution_file_name)

    return os.path.abspath(distribution_file_path)

Response: Genera el path estándar un catálogo en un filesystem.


Instruction:
Make a summary of the following Python 3 code
def get_catalog_path(catalog_id, catalogs_dir=CATALOGS_DIR, fmt="json"):

    base_path = os.path.join(catalogs_dir, "catalog", catalog_id)
    if fmt == "json":
        return os.path.join(base_path, "data.json")
    elif fmt == "xlsx":
        return os.path.join(base_path, "catalog.xlsx")
    else:
        raise NotImplementedError("El formato {} no está implementado.".format(
            fmt))

Response: Genera el path estándar un catálogo en un filesystem.


Instruction:
Can you generate the documentation for the following Python 3 function
def main(catalogs, include_data=True, use_short_path=True):
    include_data = bool(int(include_data))
    make_catalogs_backup(catalogs.split(
        ","), include_data=include_data, use_short_path=use_short_path)

Response: A function that makes a backup of the given list of catalogs.


Instruction:
How would you implement a function in Python 3 that
generates a README file for a given catalog.

Response:
def generate_readme(catalog, export_path=None):
    """Genera una descripción textual en formato Markdown sobre los
    metadatos generales de un catálogo (título, editor, fecha de
    publicación, et cetera), junto con:
        - estado de los metadatos a nivel catálogo,
        - estado global de los metadatos,
        - cantidad de datasets federados y no federados,
        - detalles de los datasets no federados
        - cantidad de datasets y distribuciones incluidas

    Es utilizada por la rutina diaria de `libreria-catalogos` para generar
    un README con información básica sobre los catálogos mantenidos.

    Args:
        catalog (str o dict): Path a un catálogo en cualquier formato,
            JSON, XLSX, o diccionario de python.
        export_path (str): Path donde exportar el texto generado (en
            formato Markdown). Si se especifica, el método no devolverá
            nada.

    Returns:
        str: Texto de la descripción generada.
    """
    # Si se paso una ruta, guardarla
    if isinstance(catalog, string_types):
        catalog_path_or_url = catalog
    else:
        catalog_path_or_url = None

    catalog = read_catalog(catalog)
    validation = validate_catalog(catalog)
    # Solo necesito indicadores para un catalogo
    indicators = generate_catalogs_indicators(
        catalog, CENTRAL_CATALOG)[0][0]

    with io.open(os.path.join(TEMPLATES_PATH, 'catalog_readme.txt'), 'r',
                 encoding='utf-8') as template_file:
        readme_template = template_file.read()

        not_federated_datasets_list = "\n".join([
            "- [{}]({})".format(dataset[0], dataset[1])
            for dataset in indicators["datasets_no_federados"]
        ])
        federated_removed_datasets_list = "\n".join([
            "- [{}]({})".format(dataset[0], dataset[1])
            for dataset in indicators["datasets_federados_eliminados"]
        ])
        federated_datasets_list = "\n".join([
            "- [{}]({})".format(dataset[0], dataset[1])
            for dataset in indicators["datasets_federados"]
        ])
        non_federated_pct = 1.0 - indicators["datasets_federados_pct"] if \
            indicators["datasets_federados_pct"] is not None else \
            indicators["datasets_federados_pct"]
        content = {
            "title": catalog.get("title"),
            "publisher_name": traverse_dict(
                catalog, ["publisher", "name"]),
            "publisher_mbox": traverse_dict(
                catalog, ["publisher", "mbox"]),
            "catalog_path_or_url": catalog_path_or_url,
            "description": catalog.get("description"),
            "global_status": validation["status"],
            "catalog_status": validation["error"]["catalog"]["status"],
            "no_of_datasets": len(catalog["dataset"]),
            "no_of_distributions": sum([len(dataset["distribution"]) for
                                        dataset in catalog["dataset"]]),
            "federated_datasets": indicators["datasets_federados_cant"],
            "not_federated_datasets": indicators["datasets_no_federados_cant"],
            "not_federated_datasets_pct": non_federated_pct,
            "not_federated_datasets_list": not_federated_datasets_list,
            "federated_removed_datasets_list": federated_removed_datasets_list,
            "federated_datasets_list": federated_datasets_list,
        }

        catalog_readme = readme_template.format(**content)

    if export_path:
        with io.open(export_path, 'w+', encoding='utf-8') as target:
            target.write(catalog_readme)
    else:
        return catalog_readme


Instruction:
How would you explain what the following Python 3 function does
def setup(self, builder: Builder):
        self.config = builder.configuration

        self.with_common_random_numbers = bool(self.config.randomness.key_columns)
        self.register = builder.randomness.register_simulants
        if (self.with_common_random_numbers
                and not ['entrance_time', 'age'] == self.config.randomness.key_columns):
            raise ValueError("If running with CRN, you must specify ['entrance_time', 'age'] as"
                             "the randomness key columns.")

        self.age_randomness = builder.randomness.get_stream('age_initialization',
                                                            for_initialization=self.with_common_random_numbers)
        self.sex_randomness = builder.randomness.get_stream('sex_initialization')

        columns_created = ['age', 'sex', 'alive', 'entrance_time']
        builder.population.initializes_simulants(self.on_initialize_simulants,
                                                 creates_columns=columns_created)

        self.population_view = builder.population.get_view(columns_created)

        builder.event.register_listener('time_step', self.age_simulants)

Response: Performs the simulation setup.


Instruction:
Implement a function in Python 3 to
call by the simulation when new simulants are added. This component is responsible for creating and filling the new simulants in the population state table.

Response:
def on_initialize_simulants(self, pop_data: SimulantData):
        """Called by the simulation whenever new simulants are added.

        This component is responsible for creating and filling four columns
        in the population state table:

        'age' :
            The age of the simulant in fractional years.
        'sex' :
            The sex of the simulant. One of {'Male', 'Female'}
        'alive' :
            Whether or not the simulant is alive. One of {'alive', 'dead'}
        'entrance_time' :
            The time that the simulant entered the simulation. The 'birthday'
            for simulants that enter as newborns. A `pandas.Timestamp`.

        Parameters
        ----------
        pop_data :
            A record containing the index of the new simulants, the
            start of the time step the simulants are added on, the width
            of the time step, and the age boundaries for the simulants to
            generate.

        """

        age_start = self.config.population.age_start
        age_end = self.config.population.age_end
        if age_start == age_end:
            age_window = pop_data.creation_window / pd.Timedelta(days=365)
        else:
            age_window = age_end - age_start

        age_draw = self.age_randomness.get_draw(pop_data.index)
        age = age_start + age_draw * age_window

        if self.with_common_random_numbers:
            population = pd.DataFrame({'entrance_time': pop_data.creation_time,
                                       'age': age.values}, index=pop_data.index)
            self.register(population)
            population['sex'] = self.sex_randomness.choice(pop_data.index, ['Male', 'Female'])
            population['alive'] = 'alive'
        else:
            population = pd.DataFrame(
                {'age': age.values,
                 'sex': self.sex_randomness.choice(pop_data.index, ['Male', 'Female']),
                 'alive': pd.Series('alive', index=pop_data.index),
                 'entrance_time': pop_data.creation_time},
                index=pop_data.index)

        self.population_view.update(population)


Instruction:
How would you code a function in Python 3 to
update the age of the simulant in the simulation view every time step.

Response:
def age_simulants(self, event: Event):
        """Updates simulant age on every time step.

        Parameters
        ----------
        event :
            An event object emitted by the simulation containing an index
            representing the simulants affected by the event and timing
            information.
        """
        population = self.population_view.get(event.index, query="alive == 'alive'")
        population['age'] += event.step_size / pd.Timedelta(days=365)
        self.population_view.update(population)


Instruction:
Here you have a function in Python 3, explain what it does
def _generate_indicators(catalog, validator=None, only_numeric=False):

    result = {}

    # Obtengo summary para los indicadores del estado de los metadatos
    result.update(_generate_status_indicators(catalog, validator=validator))

    # Genero los indicadores relacionados con fechas, y los agrego
    result.update(
        _generate_date_indicators(catalog, only_numeric=only_numeric))

    # Agrego la cuenta de los formatos de las distribuciones
    if not only_numeric:
        if 'dataset' in catalog:
            format_count = count_fields(get_distributions(catalog), 'format')
            type_count = count_fields(get_distributions(catalog), 'type')
            license_count = count_fields(get_datasets(catalog), 'license')
        else:
            format_count = type_count = license_count = {}

        result.update({
            'distribuciones_formatos_cant': format_count,
            'distribuciones_tipos_cant': type_count,
            'datasets_licencias_cant': license_count,
        })

    # Agrego porcentaje de campos recomendados/optativos usados
    fields_count = _count_required_and_optional_fields(catalog)
    recomendados_pct = float(fields_count['recomendado']) / \
        fields_count['total_recomendado']
    optativos_pct = float(fields_count['optativo']) / \
        fields_count['total_optativo']
    result.update({
        'campos_recomendados_pct': round(recomendados_pct, 4),
        'campos_optativos_pct': round(optativos_pct, 4)
    })
    return fields_count, result

Response: Genera los indicadores de un catálogo individual.


Instruction:
Can you tell what is the following Python 3 function doing
def _federation_indicators(catalog, central_catalog,
                           identifier_search=False):
    """Cuenta la cantidad de datasets incluídos tanto en la lista
    'catalogs' como en el catálogo central, y genera indicadores a partir
    de esa información.

    Args:
        catalog (dict): catálogo ya parseado
        central_catalog (str o dict): ruta a catálogo central, o un dict
            con el catálogo ya parseado
    """
    result = {
        'datasets_federados_cant': None,
        'datasets_federados_pct': None,
        'datasets_no_federados_cant': None,
        'datasets_federados_eliminados_cant': None,
        'distribuciones_federadas_cant': None,
        'datasets_federados_eliminados': [],
        'datasets_no_federados': [],
        'datasets_federados': [],

    }
    try:
        central_catalog = readers.read_catalog(central_catalog)
    except Exception as e:
        msg = u'Error leyendo el catálogo central: {}'.format(str(e))
        logger.error(msg)
        return result

    generator = FederationIndicatorsGenerator(central_catalog, catalog,
                                              id_based=identifier_search)
    result.update({
            'datasets_federados_cant':
            generator.datasets_federados_cant(),
            'datasets_no_federados_cant':
            generator.datasets_no_federados_cant(),
            'datasets_federados_eliminados_cant':
            generator.datasets_federados_eliminados_cant(),
            'datasets_federados_eliminados':
            generator.datasets_federados_eliminados(),
            'datasets_no_federados':
            generator.datasets_no_federados(),
            'datasets_federados':
            generator.datasets_federados(),
            'datasets_federados_pct':
            generator.datasets_federados_pct(),
            'distribuciones_federadas_cant':
            generator.distribuciones_federadas_cant()
        })
    return result

Response: Generate a list of all the federation indicators for a given catalog and central catalog.


Instruction:
Can you generate the documentation for the following Python 3 function
def _network_indicator_percentages(fields, network_indicators):

    # Los porcentuales no se pueden sumar, tienen que ser recalculados
    percentages = {
        'datasets_meta_ok_pct':
            (network_indicators.get('datasets_meta_ok_cant'),
             network_indicators.get('datasets_meta_error_cant')),
        'datasets_actualizados_pct':
            (network_indicators.get('datasets_actualizados_cant'),
             network_indicators.get('datasets_desactualizados_cant')),
        'datasets_federados_pct':
            (network_indicators.get('datasets_federados_cant'),
             network_indicators.get('datasets_no_federados_cant')),
        'datasets_con_datos_pct':
            (network_indicators.get('datasets_con_datos_cant'),
             network_indicators.get('datasets_sin_datos_cant')),
    }

    for indicator in percentages:
        pct = 0.00
        partial = percentages[indicator][0] or 0
        total = partial + (percentages[indicator][1] or 0)
        # Evita division por 0
        if total:
            pct = float(partial) / total
        network_indicators[indicator] = round(pct, 4)

    # % de campos recomendados y optativos utilizados en el catálogo entero
    if fields:  # 'fields' puede estar vacío si ningún campo es válido
        rec_pct = float(fields['recomendado']) / \
            fields['total_recomendado']

        opt_pct = float(fields['optativo']) / fields['total_optativo']

        network_indicators.update({
            'campos_recomendados_pct': round(rec_pct, 4),
            'campos_optativos_pct': round(opt_pct, 4)
        })

Response: Encapsula el cálculo de indicadores de porcentaje de los cantidad de nodos entera.


Instruction:
How would you implement a function in Python 3 that
generates status indicators for a set of items in the base catálogo.

Response:
def _generate_status_indicators(catalog, validator=None):
    """Genera indicadores básicos sobre el estado de un catálogo

    Args:
        catalog (dict): diccionario de un data.json parseado

    Returns:
        dict: indicadores básicos sobre el catálogo, tal como la cantidad
        de datasets, distribuciones y número de errores
    """
    result = {
        'datasets_cant': None,
        'distribuciones_cant': None,
        'datasets_meta_ok_cant': None,
        'datasets_meta_error_cant': None,
        'datasets_meta_ok_pct': None,
        'datasets_con_datos_cant': None,
        'datasets_sin_datos_cant': None,
        'datasets_con_datos_pct': None
    }
    try:
        summary = generate_datasets_summary(catalog, validator=validator)
    except Exception as e:
        msg = u'Error generando resumen del catálogo {}: {}'.format(
            catalog['title'], str(e))
        logger.error(msg)
        return result

    cant_ok = 0
    cant_error = 0
    cant_data = 0
    cant_without_data = 0
    cant_distribuciones = 0
    datasets_total = len(summary)
    for dataset in summary:
        cant_distribuciones += dataset['cant_distribuciones']

        # chequea si el dataset tiene datos
        if dataset['tiene_datos'] == "SI":
            cant_data += 1
        else:  # == "ERROR"
            cant_without_data += 1

        # chequea estado de los metadatos
        if dataset['estado_metadatos'] == "OK":
            cant_ok += 1
        else:  # == "ERROR"
            cant_error += 1

    datasets_ok_pct = 0
    datasets_with_data_pct = 0
    if datasets_total:
        datasets_ok_pct = round(float(cant_ok) / datasets_total, 4)
        datasets_with_data_pct = round(float(cant_data) / datasets_total, 4)

    result.update({
        'datasets_cant': datasets_total,
        'distribuciones_cant': cant_distribuciones,
        'datasets_meta_ok_cant': cant_ok,
        'datasets_meta_error_cant': cant_error,
        'datasets_meta_ok_pct': datasets_ok_pct,
        'datasets_con_datos_cant': cant_data,
        'datasets_sin_datos_cant': cant_without_data,
        'datasets_con_datos_pct': datasets_with_data_pct

    })
    return result


Instruction:
Can you generate the documentation for the following Python 3 function
def _generate_date_indicators(catalog, tolerance=0.2, only_numeric=False):
    result = {
        'datasets_desactualizados_cant': None,
        'datasets_actualizados_cant': None,
        'datasets_actualizados_pct': None,
        'catalogo_ultima_actualizacion_dias': None
    }
    if not only_numeric:
        result.update({
            'datasets_frecuencia_cant': {}
        })

    try:
        dias_ultima_actualizacion =\
            _days_from_last_update(catalog, "modified")
        if not dias_ultima_actualizacion:
            dias_ultima_actualizacion =\
                _days_from_last_update(catalog, "issued")

        result['catalogo_ultima_actualizacion_dias'] = \
            dias_ultima_actualizacion

    except Exception as e:
        msg = u'Error generando indicadores de fecha del catálogo {}: {}'\
            .format(catalog['title'], str(e))
        logger.error(msg)
        return result

    actualizados = 0
    desactualizados = 0
    periodicity_amount = {}

    for dataset in catalog.get('dataset', []):
        # Parseo la fecha de publicación, y la frecuencia de actualización
        periodicity = dataset.get('accrualPeriodicity')
        if not periodicity:
            continue
        # Si la periodicity es eventual, se considera como actualizado
        if periodicity == 'eventual':
            actualizados += 1
            prev_periodicity = periodicity_amount.get(periodicity, 0)
            periodicity_amount[periodicity] = prev_periodicity + 1
            continue

        # dataset sin fecha de última actualización es desactualizado
        if "modified" not in dataset:
            desactualizados += 1
        else:
            # Calculo el período de días que puede pasar sin actualizarse
            # Se parsea el período especificado por accrualPeriodicity,
            # cumple con el estándar ISO 8601 para tiempos con repetición
            try:
                date = helpers.parse_date_string(dataset['modified'])
                days_diff = float((datetime.now() - date).days)
                interval = helpers.parse_repeating_time_interval(
                    periodicity) * \
                    (1 + tolerance)
            except Exception as e:
                msg = u'Error generando indicadores'\
                      u'de fecha del dataset {} en {}: {}'
                msg.format(dataset['identifier'], catalog['title'], str(e))
                logger.error(msg)
                # Asumo desactualizado
                desactualizados += 1
                continue

            if days_diff < interval:
                actualizados += 1
            else:
                desactualizados += 1

        prev_periodicity = periodicity_amount.get(periodicity, 0)
        periodicity_amount[periodicity] = prev_periodicity + 1

    datasets_total = len(catalog.get('dataset', []))
    actualizados_pct = 0
    if datasets_total:
        actualizados_pct = float(actualizados) / datasets_total
    result.update({
        'datasets_desactualizados_cant': desactualizados,
        'datasets_actualizados_cant': actualizados,
        'datasets_actualizados_pct': round(actualizados_pct, 4)
    })
    if not only_numeric:
        result.update({
            'datasets_frecuencia_cant': periodicity_amount
        })
    return result

Response: Genera indicadores relacionados a las fechas de publicación de un catálogo pasado por parámetro.


Instruction:
How would you code a function in Python 3 to
return the number of days from last update.

Response:
def _days_from_last_update(catalog, date_field="modified"):
    """Calcula días desde la última actualización del catálogo.

    Args:
        catalog (dict): Un catálogo.
        date_field (str): Campo de metadatos a utilizar para considerar
            los días desde la última actualización del catálogo.

    Returns:
        int or None: Cantidad de días desde la última actualización del
            catálogo o None, si no pudo ser calculada.
    """

    # el "date_field" se busca primero a nivel catálogo, luego a nivel
    # de cada dataset, y nos quedamos con el que sea más reciente
    date_modified = catalog.get(date_field, None)
    dias_ultima_actualizacion = None
    # "date_field" a nivel de catálogo puede no ser obligatorio,
    # si no está pasamos
    if isinstance(date_modified, string_types):
        date = helpers.parse_date_string(date_modified)
        dias_ultima_actualizacion = (
            datetime.now() - date).days if date else None

    for dataset in catalog.get('dataset', []):
        date = helpers.parse_date_string(dataset.get(date_field, ""))
        days_diff = float((datetime.now() - date).days) if date else None

        # Actualizo el indicador de días de actualización si corresponde
        if not dias_ultima_actualizacion or \
                (days_diff and days_diff < dias_ultima_actualizacion):
            dias_ultima_actualizacion = days_diff

    if dias_ultima_actualizacion:
        return int(dias_ultima_actualizacion)
    else:
        return None


Instruction:
How would you explain what the following Python 3 function does
def _count_fields_recursive(dataset, fields):

    key_count = {
        'recomendado': 0,
        'optativo': 0,
        'requerido': 0,
        'total_optativo': 0,
        'total_recomendado': 0,
        'total_requerido': 0
    }

    for k, v in fields.items():
        # Si la clave es un diccionario se implementa recursivamente el
        # mismo algoritmo
        if isinstance(v, dict):
            # dataset[k] puede ser o un dict o una lista, ej 'dataset' es
            # list, 'publisher' no. Si no es lista, lo metemos en una.
            # Si no es ninguno de los dos, dataset[k] es inválido
            # y se pasa un diccionario vacío para poder comparar
            elements = dataset.get(k)
            if not isinstance(elements, (list, dict)):
                elements = [{}]

            if isinstance(elements, dict):
                elements = [dataset[k].copy()]
            for element in elements:
                # Llamada recursiva y suma del resultado al nuestro
                result = _count_fields_recursive(element, v)
                for key in result:
                    key_count[key] += result[key]
        # Es un elemento normal (no iterable), se verifica si está en
        # dataset o no. Se suma 1 siempre al total de su tipo
        else:
            # total_requerido, total_recomendado, o total_optativo
            key_count['total_' + v] += 1

            if k in dataset:
                key_count[v] += 1

    return key_count

Response: A recursive function that counts the fields in the given dict.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def random(key: str, index: Index, index_map: IndexMap=None) -> pd.Series:
    if len(index) > 0:
        random_state = np.random.RandomState(seed=get_hash(key))

        # Generate a random number for every simulant.
        #
        # NOTE: We generate a full set of random numbers for the population
        # even when we may only need a few.  This ensures consistency in outcomes
        # across simulations.
        # See Also:
        # 1. https://en.wikipedia.org/wiki/Variance_reduction
        # 2. Untangling Uncertainty with Common Random Numbers: A Simulation Study; A.Flaxman, et. al., Summersim 2017
        sample_size = index_map.map_size if index_map is not None else index.max() + 1
        try:
            draw_index = index_map[index]
        except (IndexError, TypeError):
            draw_index = index
        raw_draws = random_state.random_sample(sample_size)
        return pd.Series(raw_draws[draw_index], index=index)

    return pd.Series(index=index)

Response: Generates a series of uniformly distributed random numbers.


Instruction:
Can you tell what is the following Python 3 function doing
def get_hash(key: str) -> int:
    # 4294967295 == 2**32 - 1 which is the maximum allowable seed for a `numpy.random.RandomState`.
    return int(hashlib.sha1(key.encode('utf8')).hexdigest(), 16) % 4294967295

Response: Gets a hash of the provided key.


Instruction:
Write a Python 3 script for
generating a random number array from a set of choices.

Response:
def choice(key: str, index: Index, choices: Array, p: Array=None, index_map: IndexMap=None) -> pd.Series:
    """Decides between a weighted or unweighted set of choices.

    Given a a set of choices with or without corresponding weights,
    returns an indexed set of decisions from those choices. This is
    simply a vectorized way to make decisions with some book-keeping.

    Parameters
    ----------
    key :
        A string used to create a seed for the random number generation.
    index : `pandas.Index`
        An index whose length is the number of random draws made
        and which indexes the returned `pandas.Series`.
    choices :
        A set of options to choose from.
    p :
        The relative weights of the choices.  Can be either a 1-d array of
        the same length as `choices` or a 2-d array with `len(index)` rows
        and `len(choices)` columns.  In the 1-d case, the same set of weights
        are used to decide among the choices for every item in the `index`.
        In the 2-d case, each row in `p` contains a separate set of weights
        for every item in the `index`.
    index_map :
        A mapping between the provided index (which may contain ints, floats,
        datetimes or any arbitrary combination of them) and an integer index
        into the random number array.

    Returns
    -------
    pd.Series
        An indexed set of decisions from among the available `choices`.

    Raises
    ------
    RandomnessError
        If any row in `p` contains `RESIDUAL_CHOICE` and the remaining
        weights in the row are not normalized or any row of `p` contains
        more than one reference to `RESIDUAL_CHOICE`.
    """
    # Convert p to normalized probabilities broadcasted over index.
    p = _set_residual_probability(_normalize_shape(p, index)) if p is not None else np.ones((len(index), len(choices)))
    p = p/p.sum(axis=1, keepdims=True)

    draw = random(key, index, index_map)

    p_bins = np.cumsum(p, axis=1)
    # Use the random draw to make a choice for every row in index.
    choice_index = (draw.values[np.newaxis].T > p_bins).sum(axis=1)

    return pd.Series(np.array(choices)[choice_index], index=index)


Instruction:
How would you explain what the following Python 3 function does
def _set_residual_probability(p: np.ndarray) -> np.ndarray:
    residual_mask = p == RESIDUAL_CHOICE
    if residual_mask.any():  # I.E. if we have any placeholders.
        if np.any(np.sum(residual_mask, axis=1) - 1):
            raise RandomnessError(
                'More than one residual choice supplied for a single set of weights. Weights: {}.'.format(p))

        p[residual_mask] = 0
        residual_p = 1 - np.sum(p, axis=1)  # Probabilities sum to 1.

        if np.any(residual_p < 0):  # We got un-normalized probability weights.
            raise RandomnessError(
                'Residual choice supplied with weights that summed to more than 1. Weights: {}.'.format(p))

        p[residual_mask] = residual_p
    return p

Response: Turns any use of RESIDUAL_CHOICE into a residual probability.


Instruction:
Can you generate the documentation for the following Python 3 function
def filter_for_probability(key: str, population: Union[pd.DataFrame, pd.Series, Index],
                           probability: Array, index_map: IndexMap=None) -> Union[pd.DataFrame, pd.Series, Index]:
    """Decide an event outcome for each individual in a population from probabilities.

    Given a population or its index and an array of associated probabilities
    for some event to happen, we create and return the sub-population for whom
    the event occurred.

    Parameters
    ----------
    key :
        A string used to create a seed for the random number generation.
    population :
        A view on the simulants for which we are determining the
        outcome of an event.
    probability :
        A 1d list of probabilities of the event under consideration
        occurring which corresponds (i.e. `len(population) == len(probability)`)
        to the population array passed in.
    index_map :
        A mapping between the provided index (which may contain ints, floats,
        datetimes or any arbitrary combination of them) and an integer index
        into the random number array.

    Returns
    -------
    pd.core.generic.PandasObject
        The sub-population of the simulants for whom the event occurred.
        The return type will be the same as type(population)
    """
    if population.empty:
        return population

    index = population if isinstance(population, pd.Index) else population.index
    draw = random(key, index, index_map)
    mask = np.array(draw < probability)
    return population[mask]

Response: Given a population or index and a probability array return a sub - population of simulants for whom the event occurred.


Instruction:
Explain what the following Python 3 code does
def update(self, new_keys: Index):
        if not self._map.index.intersection(new_keys).empty:
            raise KeyError("Non-unique keys in index.")

        mapping_update = self.hash_(new_keys)
        if self._map.empty:
            self._map = mapping_update.drop_duplicates()
        else:
            self._map = self._map.append(mapping_update).drop_duplicates()

        collisions = mapping_update.index.difference(self._map.index)
        salt = 1
        while not collisions.empty:
            mapping_update = self.hash_(collisions, salt)
            self._map = self._map.append(mapping_update).drop_duplicates()
            collisions = mapping_update.index.difference(self._map.index)
            salt += 1

Response: Adds the new keys to the mapping.


Instruction:
Can you generate the documentation for the following Python 3 function
def hash_(self, keys: Index, salt: int = 0) -> pd.Series:
        key_frame = keys.to_frame()
        new_map = pd.Series(0, index=keys)
        salt = self.convert_to_ten_digit_int(pd.Series(salt, index=keys))

        for i, column_name in enumerate(key_frame.columns):
            column = self.convert_to_ten_digit_int(key_frame[column_name])

            primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 27]
            out = pd.Series(1, index=column.index)
            for idx, p in enumerate(primes):
                # numpy will almost always overflow here, but it is equivalent to modding
                # out by 2**64.  Since it's much much larger than our map size
                # the amount of additional periodicity this introduces is pretty trivial.
                out *= np.power(p, self.digit(column, idx))
            new_map += out + salt

        return new_map % self.map_size

Response: Hashes the given index into an integer index in the range [ 0 self. stride ).


Instruction:
Implement a function in Python 3 to
convert a column of datetimes integers or floats into a column of 10 digit integers.

Response:
def convert_to_ten_digit_int(self, column: pd.Series) -> pd.Series:
        """Converts a column of datetimes, integers, or floats into a column of 10 digit integers.

        Parameters
        ----------
        column :
            A series of datetimes, integers, or floats.

        Returns
        -------
        pd.Series
            A series of ten digit integers based on the input data.

        Raises
        ------
        RandomnessError :
            If the column contains data that is neither a datetime-like nor numeric.
        """
        if isinstance(column.iloc[0], datetime.datetime):
            column = self.clip_to_seconds(column.astype(int))
        elif np.issubdtype(column.iloc[0], np.integer):
            if not len(column >= 0) == len(column):
                raise RandomnessError("Values in integer columns must be greater than or equal to zero.")
            column = self.spread(column)
        elif np.issubdtype(column.iloc[0], np.floating):
            column = self.shift(column)
        else:
            raise RandomnessError(f"Unhashable column type {type(column.iloc[0])}. "
                                  "IndexMap accepts datetime like columns and numeric columns.")
        return column


Instruction:
Given the following Python 3 function, write the documentation
def digit(m: Union[int, pd.Series], n: int) -> Union[int, pd.Series]:
        return (m // (10 ** n)) % 10

Response: Returns the nth digit of each number in m.


Instruction:
How would you explain what the following Python 3 function does
def clip_to_seconds(m: Union[int, pd.Series]) -> Union[int, pd.Series]:
        return m // pd.Timedelta(1, unit='s').value

Response: Clips UTC datetime in nanoseconds to seconds.


Instruction:
Write a Python 3 function for
spreading out integer values to give smaller values more weight.

Response:
def spread(self, m: Union[int, pd.Series]) -> Union[int, pd.Series]:
        """Spreads out integer values to give smaller values more weight."""
        return (m * 111_111) % self.TEN_DIGIT_MODULUS


Instruction:
How would you explain what the following Python 3 function does
def shift(self, m: Union[float, pd.Series]) -> Union[int, pd.Series]:
        out = m % 1 * self.TEN_DIGIT_MODULUS // 1
        if isinstance(out, pd.Series):
            return out.astype(int)
        return int(out)

Response: Shifts floats so that the first 10 decimal digits are significant.


Instruction:
Can you implement a function in Python 3 that
creates a copy of this RandomnessStream that combines this RandomnessStream with the additional key.

Response:
def copy_with_additional_key(self, key: Any) -> 'RandomnessStream':
        """Creates a copy of this stream that combines this streams key with a new one.

        Parameters
        ----------
        key :
            The additional key to describe the new stream with.

        Returns
        -------
        RandomnessStream
            A new RandomnessStream with a combined key.
        """
        if self._for_initialization:
            raise RandomnessError('Initialization streams cannot be copied.')
        elif self._manager:
            return self._manager.get_randomness_stream('_'.join([self.key, key]))
        else:
            return RandomnessStream(self.key, self.clock, self.seed, self.index_map)


Instruction:
Explain what the following Python 3 code does
def _key(self, additional_key: Any=None) -> str:
        return '_'.join([self.key, str(self.clock()), str(additional_key), str(self.seed)])

Response: Construct a hashable key from this object s state.


Instruction:
Write a Python 3 script to
get an indexed sequence of floats pulled from a uniform distribution over [ 0 1 ).

Response:
def get_draw(self, index: Index, additional_key: Any=None) -> pd.Series:
        """Get an indexed sequence of floats pulled from a uniform distribution over [0.0, 1.0)

        Parameters
        ----------
        index :
            An index whose length is the number of random draws made
            and which indexes the returned `pandas.Series`.
        additional_key :
            Any additional information used to seed random number generation.

        Returns
        -------
        pd.Series
            A series of random numbers indexed by the provided `pandas.Index`.
        """
        if self._for_initialization:
            draw = random(self._key(additional_key), pd.Index(range(len(index))), self.index_map)
            draw.index = index
        else:
            draw = random(self._key(additional_key), index, self.index_map)

        return draw


Instruction:
Create a Python 3 function for
deciding an event outcome for each individual in a population and rates for each individual in a population.

Response:
def filter_for_rate(self, population: Union[pd.DataFrame, pd.Series, Index],
                        rate: Array, additional_key: Any=None) -> Index:
        """Decide an event outcome for each individual in a population from rates.

        Given a population or its index and an array of associated rates for
        some event to happen, we create and return the sub-population for whom
        the event occurred.

        Parameters
        ----------
        population :
            A view on the simulants for which we are determining the
            outcome of an event.
        rate :
            A 1d list of rates of the event under consideration occurring which
            corresponds (i.e. `len(population) == len(probability))` to the
            population view passed in. The rates must be scaled to the simulation
            time-step size either manually or as a post-processing step in a
            rate pipeline.
        additional_key :
            Any additional information used to create the seed.

        Returns
        -------
        Index
            The index of the simulants for whom the event occurred.

        See Also
        --------
        framework.values:
            Value/rate pipeline management module.
        """
        return self.filter_for_probability(population, rate_to_probability(rate), additional_key)


Instruction:
Make a summary of the following Python 3 code
def filter_for_probability(self, population: Union[pd.DataFrame, pd.Series, Index],
                               probability: Array, additional_key: Any=None) -> Index:
        """Decide an event outcome for each individual in a population from probabilities.

        Given a population or its index and an array of associated probabilities
        for some event to happen, we create and return the sub-population for whom
        the event occurred.

        Parameters
        ----------
        population :
            A view on the simulants for which we are determining the
            outcome of an event.
        probability :
            A 1d list of probabilities of the event under consideration
            occurring which corresponds (i.e. `len(population) == len(probability)`
            to the population view passed in.
        additional_key :
            Any additional information used to create the seed.

        Returns
        -------
        Index
            The sub-population of the simulants for whom the event occurred.
            The return type will be the same as type(population)
        """
        return filter_for_probability(self._key(additional_key), population, probability, self.index_map)

Response: Decide an event outcome for each individual in a population and a probability array.


Instruction:
Write a Python 3 function that can
return a set of decisions from a set of choices.

Response:
def choice(self, index: Index, choices: Array, p: Array=None, additional_key: Any=None) -> pd.Series:
        """Decides between a weighted or unweighted set of choices.

        Given a a set of choices with or without corresponding weights,
        returns an indexed set of decisions from those choices. This is
        simply a vectorized way to make decisions with some book-keeping.

        Parameters
        ----------
        index :
            An index whose length is the number of random draws made
            and which indexes the returned `pandas.Series`.
        choices :
            A set of options to choose from.
        p :
            The relative weights of the choices.  Can be either a 1-d array of
            the same length as `choices` or a 2-d array with `len(index)` rows
            and `len(choices)` columns.  In the 1-d case, the same set of weights
            are used to decide among the choices for every item in the `index`.
            In the 2-d case, each row in `p` contains a separate set of weights
            for every item in the `index`.
        additional_key :
            Any additional information used to seed random number generation.

        Returns
        -------
        pd.Series
            An indexed set of decisions from among the available `choices`.

        Raises
        ------
        RandomnessError
            If any row in `p` contains `RESIDUAL_CHOICE` and the remaining
            weights in the row are not normalized or any row of `p contains
            more than one reference to `RESIDUAL_CHOICE`.
        """
        return choice(self._key(additional_key), index, choices, p, self.index_map)


Instruction:
Write a Python 3 function that can
return a new randomness stream for the given decision point.

Response:
def get_randomness_stream(self, decision_point: str, for_initialization: bool=False) -> RandomnessStream:
        """Provides a new source of random numbers for the given decision point.

        Parameters
        ----------
        decision_point :
            A unique identifier for a stream of random numbers.  Typically represents
            a decision that needs to be made each time step like 'moves_left' or
            'gets_disease'.
        for_initialization :
            A flag indicating whether this stream is used to generate key initialization information
            that will be used to identify simulants in the Common Random Number framework. These streams
            cannot be copied and should only be used to generate the state table columns specified
            in ``builder.configuration.randomness.key_columns``.

        Raises
        ------
        RandomnessError :
            If another location in the simulation has already created a randomness stream
            with the same identifier.
        """
        if decision_point in self._decision_points:
            raise RandomnessError(f"Two separate places are attempting to create "
                                  f"the same randomness stream for {decision_point}")
        stream = RandomnessStream(key=decision_point, clock=self._clock, seed=self._seed,
                                  index_map=self._key_mapping, manager=self, for_initialization=for_initialization)
        self._decision_points[decision_point] = stream
        return stream


Instruction:
Can you create a Python 3 function that
adds new simulants to the randomness mapping.

Response:
def register_simulants(self, simulants: pd.DataFrame):
        """Adds new simulants to the randomness mapping.

        Parameters
        ----------
        simulants :
            A table with state data representing the new simulants.  Each simulant should
            pass through this function exactly once.

        Raises
        ------
        RandomnessError :
            If the provided table does not contain all key columns specified in the configuration.
        """
        if not all(k in simulants.columns for k in self._key_columns):
            raise RandomnessError("The simulants dataframe does not have all specified key_columns.")
        self._key_mapping.update(simulants.set_index(self._key_columns).index)


Instruction:
Can you create a Python 3 function that
provides a new stream of random numbers for the given decision point.

Response:
def get_stream(self, decision_point: str, for_initialization: bool = False) -> RandomnessStream:
        """Provides a new source of random numbers for the given decision point.

        ``vivarium`` provides a framework for Common Random Numbers which allows for variance reduction
        when modeling counter-factual scenarios. Users interested in causal analysis and comparisons
        between simulation scenarios should be careful to use randomness streams provided by the framework
        wherever randomness is employed.

        Parameters
        ----------
        decision_point :
            A unique identifier for a stream of random numbers.  Typically represents
            a decision that needs to be made each time step like 'moves_left' or
            'gets_disease'.
        for_initialization :
            A flag indicating whether this stream is used to generate key initialization information
            that will be used to identify simulants in the Common Random Number framework. These streams
            cannot be copied and should only be used to generate the state table columns specified
            in ``builder.configuration.randomness.key_columns``.

        Returns
        -------
        RandomnessStream
            An entry point into the Common Random Number generation framework. The stream provides
            vectorized access to random numbers and a few other utilities.
        """
        return self._randomness_manager.get_randomness_stream(decision_point, for_initialization)


Instruction:
Explain what the following Python 3 code does
def student_visible(self):
        group_ids = set()
        for group in Group.objects.all():
            if group.properties.student_visible:
                group_ids.add(group.id)

        return Group.objects.filter(id__in=group_ids)

Response: Return a list of groups that are student - visible.


Instruction:
Can you write a function in Python 3 where it
returns a Unicode object.

Response:
def to_python(self, value):
        """Returns a Unicode object."""
        if value in self.empty_values:
            return ""
        value = force_text(value).strip()
        return value


Instruction:
How would you code a function in Python 3 to
resolve the time in seconds of a configuration value.

Response:
def _resolve_time(value):
  '''
  Resolve the time in seconds of a configuration value.
  '''
  if value is None or isinstance(value,(int,long)):
    return value

  if NUMBER_TIME.match(value):
    return long(value)

  simple = SIMPLE_TIME.match(value)
  if SIMPLE_TIME.match(value):
    multiplier = long( simple.groups()[0] )
    constant = SIMPLE_TIMES[ simple.groups()[1] ]
    return multiplier * constant

  if value in GREGORIAN_TIMES:
    return value

  raise ValueError('Unsupported time format %s'%value)


Instruction:
Can you tell what is the following Python 3 function doing
def step_size(self, t0=None, t1=None):
    '''
    Return the time in seconds of a step. If a begin and end timestamp,
    return the time in seconds between them after adjusting for what buckets
    they alias to. If t1 and t0 resolve to the same bucket,
    '''
    if t0!=None and t1!=None:
      tb0 = self.to_bucket( t0 )
      tb1 = self.to_bucket( t1, steps=1 ) # NOTE: "end" of second bucket
      if tb0==tb1:
        return self._step
      return self.from_bucket( tb1 ) - self.from_bucket( tb0 )
    return self._step

Response: Return the size of a step.


Instruction:
Can you tell what is the following Python 3 function doing
def buckets(self, start, end):
    '''
    Calculate the buckets within a starting and ending timestamp.
    '''
    start_bucket = self.to_bucket(start)
    end_bucket = self.to_bucket(end)
    return range(start_bucket, end_bucket+1)

Response: Calculate the buckets within a starting and ending timestamp.


Instruction:
Implement a function in Python 3 to
return the ttl given the number of steps and optional relative time.

Response:
def ttl(self, steps, relative_time=None):
    '''
    Return the ttl given the number of steps, None if steps is not defined
    or we're otherwise unable to calculate one. If relative_time is defined,
    then return a ttl that is the number of seconds from now that the
    record should be expired.
    '''
    if steps:
      if relative_time:
        rtime = self.to_bucket(relative_time)
        ntime = self.to_bucket(time.time())
        # The relative time is beyond our TTL cutoff
        if (ntime - rtime) > steps:
          return 0
        # The relative time is in "recent" past or future
        else:
          return (steps + rtime - ntime) * self._step
      return steps * self._step

    return None


Instruction:
How would you explain what the following Python 3 function does
def step_size(self, t0, t1=None):
    '''
    Return the time in seconds for each step. Requires that we know a time
    relative to which we should calculate to account for variable length
    intervals (e.g. February)
    '''
    tb0 = self.to_bucket( t0 )
    if t1:
      tb1 = self.to_bucket( t1, steps=1 ) # NOTE: "end" of second bucket
    else:
      tb1 = self.to_bucket( t0, steps=1 )

    # Calculate the difference in days, then multiply by simple scalar
    days = (self.from_bucket(tb1, native=True) - self.from_bucket(tb0, native=True)).days
    return days * SIMPLE_TIMES['d']

Response: Return the time in seconds for each step.


Instruction:
Can you create a Python 3 function that
calculates the bucket from a timestamp.

Response:
def to_bucket(self, timestamp, steps=0):
    '''
    Calculate the bucket from a timestamp.
    '''
    dt = datetime.utcfromtimestamp( timestamp )

    if steps!=0:
      if self._step == 'daily':
        dt = dt + timedelta(days=steps)
      elif self._step == 'weekly':
        dt = dt + timedelta(weeks=steps)
      elif self._step == 'monthly':
        dt = dt + MonthDelta(steps)
      elif self._step == 'yearly':
        year = int(dt.strftime( self.FORMATS[self._step] ))
        year += steps
        dt = datetime(year=year, month=1, day=1)

    return int(dt.strftime( self.FORMATS[self._step] ))


Instruction:
Write a Python 3 function for
calculating the timestamp given a bucket.

Response:
def from_bucket(self, bucket, native=False):
    '''
    Calculate the timestamp given a bucket.
    '''
    # NOTE: this is due to a bug somewhere in strptime that does not process
    # the week number of '%Y%U' correctly. That bug could be very specific to
    # the combination of python and ubuntu that I was testing.
    bucket = str(bucket)
    if self._step == 'weekly':
      year, week = bucket[:4], bucket[4:]
      normal = datetime(year=int(year), month=1, day=1) + timedelta(weeks=int(week))
    else:
      normal = datetime.strptime(bucket, self.FORMATS[self._step])
    if native:
      return normal
    return long(time.mktime( normal.timetuple() ))


Instruction:
Can you generate the documentation for the following Python 3 function
def buckets(self, start, end):
    '''
    Calculate the buckets within a starting and ending timestamp.
    '''
    rval = [ self.to_bucket(start) ]
    step = 1

    # In theory there's already been a check that end>start
    # TODO: Not a fan of an unbound while loop here
    while True:
      bucket = self.to_bucket(start, step)
      bucket_time = self.from_bucket( bucket )
      if bucket_time >= end:
        if bucket_time==end:
          rval.append( bucket )
        break
      rval.append( bucket )
      step += 1

    return rval

Response: Calculate the buckets within a starting and ending timestamp.


Instruction:
Write a Python 3 script for
normalizing a timestamp according to the interval configuration.

Response:
def normalize(self, timestamp, steps=0):
    '''
    Normalize a timestamp according to the interval configuration. Optionally
    can be used to calculate the timestamp N steps away.
    '''
    # So far, the only commonality with RelativeTime
    return self.from_bucket( self.to_bucket(timestamp, steps) )


Instruction:
Can you create a Python 3 function that
returns the ttl given the number of steps and optional relative time.

Response:
def ttl(self, steps, relative_time=None):
    '''
    Return the ttl given the number of steps, None if steps is not defined
    or we're otherwise unable to calculate one. If relative_time is defined,
    then return a ttl that is the number of seconds from now that the
    record should be expired.
    '''
    if steps:
      # Approximate the ttl based on number of seconds, since it's
      # "close enough"
      if relative_time:
        rtime = self.to_bucket(relative_time)
        ntime = self.to_bucket(time.time())

        # Convert to number of days
        day_diff = (self.from_bucket(ntime, native=True) - self.from_bucket(rtime, native=True)).days
        # Convert steps to number of days as well
        step_diff = (steps*SIMPLE_TIMES[self._step[0]]) / SIMPLE_TIMES['d']

        # The relative time is beyond our TTL cutoff
        if day_diff > step_diff:
          return 0
        # The relative time is in "recent" past or future
        else:
          return (step_diff - day_diff) * SIMPLE_TIMES['d']
      return steps * SIMPLE_TIMES[ self._step[0] ]

    return None


Instruction:
How would you code a function in Python 3 to
perform a bulk insert of the given values into the base tree.

Response:
def bulk_insert(self, inserts, intervals=0, **kwargs):
    '''
    Perform a bulk insert. The format of the inserts must be:

    {
      timestamp : {
        name: [ values ],
        ...
      },
      ...
    }

    If the timestamp should be auto-generated, then "timestamp" should be None.

    Backends can implement this in any number of ways, the default being to
    perform a single insert for each value, with no specialized locking,
    transactions or batching.
    '''
    if None in inserts:
      inserts[ time.time() ] = inserts.pop(None)
    if self._write_func:
      for timestamp,names in inserts.iteritems():
        for name,values in names.iteritems():
          names[name] = [ self._write_func(v) for v in values ]
    self._batch_insert(inserts, intervals, **kwargs)


Instruction:
Implement a function in Python 3 to
insert a value into the timeseries with the given name.

Response:
def insert(self, name, value, timestamp=None, intervals=0, **kwargs):
    '''
    Insert a value for the timeseries "name". For each interval in the
    configuration, will insert the value into a bucket for the interval
    "timestamp". If time is not supplied, will default to time.time(), else it
    should be a floating point value.

    If "intervals" is less than 0, inserts the value into timestamps
    "abs(intervals)" preceeding "timestamp" (i.e. "-1" inserts one extra value).
    If "intervals" is greater than 0, inserts the value into that many more
    intervals after "timestamp". The default behavior is to insert for a single
    timestamp.

    This supports the public methods of the same name in the subclasses. The
    value is expected to already be converted.
    '''
    if not timestamp:
      timestamp = time.time()

    if isinstance(value, (list,tuple,set)):
      if self._write_func:
        value = [ self._write_func(v) for v in value ]
      return self._batch_insert({timestamp:{name:value}}, intervals, **kwargs)

    if self._write_func:
      value = self._write_func(value)

    # TODO: document acceptable names
    # TODO: document what types values are supported
    # TODO: document behavior when time is outside the bounds of TTLed config
    # TODO: document how the data is stored.
    # TODO: better abstraction for "intervals" processing rather than in each implementation

    self._insert( name, value, timestamp, intervals, **kwargs )


Instruction:
How would you implement a function in Python 3 that
supports for batch insert. Default implementation is non-optimized and is a simple loop over values.

Response:
def _batch_insert(self, inserts, intervals, **kwargs):
    '''
    Support for batch insert. Default implementation is non-optimized and
    is a simple loop over values.
    '''
    for timestamp,names in inserts.iteritems():
      for name,values in names.iteritems():
        for value in values:
          self._insert( name, value, timestamp, intervals, **kwargs )


Instruction:
Here you have a function in Python 3, explain what it does
def _normalize_timestamps(self, timestamp, intervals, config):
    '''
    Helper for the subclasses to generate a list of timestamps.
    '''
    rval = [timestamp]
    if intervals<0:
      while intervals<0:
        rval.append( config['i_calc'].normalize(timestamp, intervals) )
        intervals += 1
    elif intervals>0:
      while intervals>0:
        rval.append( config['i_calc'].normalize(timestamp, intervals) )
        intervals -= 1
    return rval

Response: Helper for the subclasses to generate a list of timestamps.


Instruction:
Explain what the following Python 3 code does
def iterate(self, name, interval, **kwargs):
    '''
    Returns a generator that iterates over all the intervals and returns
    data for various timestamps, in the form:

      ( unix_timestamp, data )

    This will check for all timestamp buckets that might exist between
    the first and last timestamp in a series. Each timestamp bucket will
    be fetched separately to keep this memory efficient, at the cost of
    extra trips to the data store.

    Keyword arguments are the same as get().
    '''
    config = self._intervals.get(interval)
    if not config:
      raise UnknownInterval(interval)
    properties = self.properties(name)[interval]

    i_buckets = config['i_calc'].buckets(properties['first'], properties['last'])
    for i_bucket in i_buckets:
      data = self.get(name, interval,
        timestamp=config['i_calc'].from_bucket(i_bucket), **kwargs)
      for timestamp,row in data.items():
        yield (timestamp,row)

Response:
Iterate over all the intervals and returns
    data for various timestamps