#!/usr/bin/python
#-*- coding: utf-8 -*-
#Autor: Luis Angel Ramirez Mendoza

from flask import Flask, render_template, request, Response, redirect, send_file, jsonify
from google.oauth2 import service_account
from googleapiclient.discovery import build

SERVICE_ACCOUNT_FILE = 'Tu Archivo.json'
credentials = service_account.Credentials.from_service_account_file(filename=SERVICE_ACCOUNT_FILE)

service_sheets = build('sheets', 'v4', credentials=credentials)
#print(service_sheets)

GOOGLE_SHEETS_ID = 'TU ID'

worksheet_name = 'Nombre Worksheet!'

global cont
cont = 4

app = Flask(__name__)

@app.route('/')
def route():
	return render_template("index.html")

@app.route('/envia', methods=['GET', 'POST'])
def envia():
	global cont
	if request.method == 'POST':
		Fecha = request.form['fecha']
		Valor = request.form['valor']
		Descripcion = request.form['descripcion']
		Categoria = request.form['categoria']
		cont = cont + 1
		cell_range_insert = ('B' + str(cont) + ':' + 'E' + str(cont))

		values = ((Fecha , Valor, Descripcion, Categoria),)
		value_range_body = {
			'majorDimension': 'ROWS',
			'values': values
		}

		service_sheets.spreadsheets().values().update(
			spreadsheetId=GOOGLE_SHEETS_ID,
			valueInputOption='USER_ENTERED',
			range=worksheet_name + cell_range_insert,
			body = value_range_body).execute()

		return render_template("index.html")

if __name__ == '__main__':
	app.run(host='localhost')


from pathlib import Path
from os import urandom
from os import mkdir
from os.path import exists

from machine_learning.run import train_model
from machine_learning.featurize import featurize
from backends.data_cleanup import fetch_si_ml_dataset
from neo4j_backends.predictions import extract_predictions

if __name__ == "__main__":

    # Model dir to save models
    model_dir = Path("model")
    trials = 30

    if exists(model_dir):
        raise OSError("model directory already exists")
    mkdir(model_dir)

    # grab raw data, remove columns that are known after sample is made
    si_drop_columns = ['Porosity', 'Porosity (%)', 'Pore Volume (cm3/g)', 'Average Pore Diameter (nm)',
                       'Bulk Density (g/cm3)', 'Young Modulus (MPa)', 'Crystalline Phase',
                       'Average Pore Size (nm)', 'Thermal Conductivity (W/mK)', 'Gelation Time (mins)']
    raw_data = fetch_si_ml_dataset(additional_drop_columns=si_drop_columns)

    y_column = 'Surface Area (m2/g)'
    material_col = "Final Material"
    seed = None
    test_split = 0.1

    # Generating training data
    raw_data = raw_data.dropna(subset=[y_column])
    raw_data = raw_data.loc[raw_data['Final Gel Type'] == "Aerogel"]
    raw_data = raw_data.drop(columns=['Final Gel Type'])

    raw_data = raw_data.sample(frac=1, random_state=seed)
    raw_data = raw_data.reset_index(drop=True)
    end_split = int(len(raw_data) * test_split)

    # Train the control model
    mkdir(model_dir / "control")
    train_model(raw_data, y_column, material_col, aerogel_type="si", num_of_trials=10,
                start_test_split=0, end_test_split=end_split, validation_percent=0.1,
                seed=seed, working_dir=model_dir / "control", save_model=True, zip_dir=False)

    raw_data = raw_data.sample(frac=1, random_state=seed)
    raw_data = raw_data.reset_index(drop=True)
    end_split = int(len(raw_data) * test_split)

    # Fetch aerogels that are in no_outliers dataset
    no_out_dir = Path("output/no_outliers")
    no_out_predictions = extract_predictions(output_dir=no_out_dir, aerogel_type="si")
    no_out_gels = no_out_predictions["Final Material"].tolist()
    no_out_df = raw_data.loc[raw_data["Final Material"].isin(no_out_gels)]

    # Train no outliers model
    mkdir(model_dir / "no_outliers")
    train_model(no_out_df, y_column, material_col, aerogel_type="si", num_of_trials=10,
                start_test_split=0, end_test_split=end_split, validation_percent=0.1,
                seed=seed, working_dir=model_dir / "no_outliers", save_model=True, zip_dir=False)

    raw_data = raw_data.sample(frac=1, random_state=seed)
    raw_data = raw_data.reset_index(drop=True)
    end_split = int(len(raw_data) * test_split)

    # Fetch aerogels that are in no_outliers dataset
    drop_dir = Path("output/drop")
    drop_predictions = extract_predictions(output_dir=drop_dir, aerogel_type="si")
    drop_gels = drop_predictions["Final Material"].tolist()
    drop_df = raw_data.loc[raw_data["Final Material"].isin(drop_gels)]

    # Train drop model
    mkdir(model_dir / "drop")
    train_model(drop_df, y_column, material_col, aerogel_type="si", num_of_trials=10,
                start_test_split=0, end_test_split=end_split, validation_percent=0.1,
                seed=seed, working_dir=model_dir / "drop", save_model=True, zip_dir=False)


from rest_framework import serializers
from authMsApp.models.user import User
from authMsApp.models.role import Role

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = [
            "id",
            "date_of_birth",
            "document",
            "document_type",
            "email",
            "enabled",
            "entity",
            "full_name",
            "password",
            "phoneNumber",
            "position",
            "role",
        ]


import os
import random
import re
import warnings
from collections import namedtuple
from struct import Struct
from typing import List, Union

from yaml import SafeDumper, SafeLoader, YAMLObject, safe_dump, safe_load

from maro.data_lib.common import dtype_pack_map
from maro.utils.exception.data_lib_exception import MetaTimestampNotExist

class EntityAttr(YAMLObject):
    """Entity attribute in yaml."""

    yaml_tag = "!MaroAttribute"
    yaml_loader = SafeLoader
    yaml_dumper = SafeDumper

    def __init__(self, name, dtype: str, slot: int, raw_name: str, adjust_ratio: tuple, tzone=None):
        self.name = name
        self.dtype = dtype
        self.slot = slot
        self.raw_name = raw_name
        self.adjust_ratio = adjust_ratio
        self.tzone = None

class Event(YAMLObject):
    """Event from yaml."""

    yaml_tag = "!MaroEvent"
    yaml_loader = SafeLoader
    yaml_dumper = SafeDumper

    def __init__(self, display_name: str, type_name: str, value: object):
        self.display_name = display_name
        self.type_name = type_name
        self.value = value

class BinaryMeta:
    """Meta for binary file."""

    def __init__(self):
        self._item_nt: namedtuple = None
        self._item_struct: Struct = None

        self._tzone = None
        # which attribute used as events

        self._event_attr_name = None
        # if value cannot matched to any event definition, then treat it as default
        self._default_event_name = None
        self._raw_cols = []
        # fields need adjust
        self._adjust_attrs = {}
        # EntityAttr
        self._attrs: List[EntityAttr] = []
        self._events: List[Event] = []

    @property
    def events(self) -> List[Event]:
        """List[Event]: Events definition."""
        return self._events

    @property
    def default_event_name(self) -> str:
        """str: Default event name, if no value matched."""
        return self._default_event_name

    @property
    def event_attr_name(self) -> str:
        """str: Event attribute name."""
        return self._event_attr_name

    @property
    def time_zone(self):
        """Time zone of this meta, used to correct timestamp."""
        return self._tzone

    @property
    def item_size(self) -> int:
        """int: Item binary size (in bytes)."""
        return self._item_struct.size

    @property
    def columns(self) -> dict:
        """dict: Columns to extract."""
        return {a.name: a.raw_name for a in self._attrs}

    def items(self) -> dict:
        """dict: Attribute items."""
        return {a.name: a.dtype for a in self._attrs}

    def from_file(self, file: str):
        """Read meta from yaml file."""
        assert os.path.exists(file)

        with open(file, "rt") as fp:
            conf = safe_load(fp)

            self._validate(conf)

            self._build_item_struct()

    def from_bytes(self, meta_bytes: Union[bytes, bytearray, memoryview]):
        """Construct meta from bytes.

        Args:
            meta_bytes (Union[bytes, bytearray, memoryview]): Bytes content of meta.
        """
        assert meta_bytes is not None

        self._events.clear()
        self._attrs.clear()
        self._raw_cols.clear()

        conf = safe_load(meta_bytes[:].decode())

        # NOTE: this methods used to load meta bytes from converted binary,
        # which is our processed format, so we do not need validate here
        self._events.extend(conf.get("events", []))

        self._attrs.extend(conf.get("attributes", []))

        self._event_attr_name = conf.get("event_attr_name", None)
        self._default_event_name = conf.get("default_event_name", None)

        self._raw_cols = [(a.raw_name, a.dtype) for a in self._attrs]

        self._adjust_attrs = {
            i: a.adjust_ratio
            for i, a, in enumerate(
                self._attrs,
            )
            if a.adjust_ratio is not None
        }

        self._build_item_struct()

    def from_dict(self, meta_dict: dict):
        """Construct meta from dictionary.

        Args:
            meta_dict (dict): Meta dictionary.
        """
        self._validate(meta_dict)

        self._build_item_struct()

    def to_bytes(self):
        """Convert meta into bytes.

        Returns:
            bytes: Bytes content of current meta.
        """
        return safe_dump(
            {
                "events": self._events,
                "attributes": self._attrs,
                "default_event_name": self._default_event_name,
                "event_attr_name": self._event_attr_name,
            },
        ).encode()

    def get_item_values(self, row: dict) -> Union[list, tuple]:
        """Retrieve value for item.

        Args:
            row (dict): A row that from a csv file.

        Returns:
            Union[list, tuple]: Get value for configured attributes from dict.
        """
        # NOTE: keep the order
        return (row[col] for col in self._raw_cols)

    def item_to_bytes(self, item_values: Union[tuple, list], out_bytes: Union[memoryview, bytearray]) -> int:
        """Convert item into bytes.

        Args:
            item_values (Union[tuple, list]): Value of attributes used to construct item.
            out_bytes (Union[memoryview, bytearray]): Item bytes content.

        Returns:
            int: Result item size.
        """
        self._item_struct.pack_into(out_bytes, 0, *item_values)

        return self.item_size

    def item_from_bytes(self, item_bytes: Union[bytes, bytearray, memoryview], adjust_value: bool = False):
        """Convert bytes into item (namedtuple).

        Args:
            item_bytes (Union[bytes, bytearray, memoryview]): Item byte content.
            adjust_value (bool): If need to adjust value for attributes that enabled this feature.

        Returns:
            namedtuple: Result item tuple.
        """
        item_tuple = self._item_struct.unpack_from(item_bytes, 0)

        if adjust_value:
            # convert it into list to that we can change the value
            item_tuple = list(item_tuple)

            for index, ratio in self._adjust_attrs.items():
                # make it percentage
                item_tuple[index] += random.randrange(int(ratio[0]), int(ratio[1])) * 0.01 * item_tuple[index]

        return self._item_nt._make(item_tuple)

    def _build_item_struct(self):
        """Build item struct use field name in meta."""
        self._item_nt = namedtuple("Item", [a.name for a in self._attrs])

        fmt: str = "<" + "".join([dtype_pack_map[a.dtype] for a in self._attrs])

        self._item_struct = Struct(fmt)

    def _validate(self, conf: dict):
        # attributes
        attributes_def = conf.get("entity", {})

        # special events
        self._event_attr_name = attributes_def.get("_event", None)

        has_timestamp_attr = False

        for attr_name, attr_settings in attributes_def.items():
            if type(attr_settings) != dict:
                continue

            dtype = attr_settings.get("dtype", "i")
            col_name = attr_settings.get("column", None)
            tzone = attr_settings.get("tzone", None)
            slots = attr_settings.get("slot", 1)
            adjust_ratio = attr_settings.get("adjust_ratio")

            if dtype in dtype_pack_map and re.match(r"^[a-z A-Z]+", attr_name):
                # keep the index, as we need to change the value later
                if adjust_ratio is not None:
                    self._adjust_attrs[len(self._attrs)] = adjust_ratio

                # TODO: raise error instead
                attr = EntityAttr(attr_name, dtype, slots, col_name, adjust_ratio, tzone)

                if attr_name == "timestamp":
                    has_timestamp_attr = True
                    dtype = "i8"
                    # keep the timestamp as first field
                    self._attrs.insert(0, attr)
                    self._raw_cols.insert(0, attr)
                else:
                    self._attrs.append(attr)
                    self._raw_cols.append((col_name, dtype))
            else:
                warnings.warn(f"invalid attribute {attr_name}, ignore it")

        if not has_timestamp_attr:
            raise MetaTimestampNotExist()

        # events
        events_def = conf.get("events", {})

        self._default_event_name = events_def.get("_default", None)

        for evt_type_name, event_settings in events_def.items():
            if type(event_settings) != dict:
                continue

            display_name = event_settings.get("display_name", evt_type_name)
            value_in_csv = event_settings.get("value_in_csv", None)

            self._events.append(
                Event(display_name, evt_type_name, value_in_csv),
            )

__all__ = ["BinaryMeta"]


from django import forms
from .models import Settings, File
from django.forms.widgets import NumberInput

class FileForm(forms.ModelForm):
    class Meta:
        model = File
        fields = ('file', )

class SettingEditForm(forms.ModelForm):
	class Meta:
		model = Settings
		fields = ['image_width', 'image_height', 'image_rotation', 'timeout']
		widgets = {
			'image_width'	: NumberInput(attrs={'min': 160, 'max': 640}),
			'image_height'	: NumberInput(attrs={'min': 120, 'max': 480}),
			'timeout'		: NumberInput(attrs={'min': 500, 'max': 4000}),
			}


import logging; logger = logging.getLogger("morse." + __name__)
import morse.core.actuator
from morse.core.mathutils import Vector
from morse.helpers.components import add_property
from math import copysign

class Drag(morse.core.actuator.Actuator):
    """
    This actuator allows to simulate the drag force, or fluid
    resistance. It is not controlable, as it depends only of robot
    parameters, and environment.

    The drag force is generally written as:

    Fd = 0.5 * rho * vel_sq * A * Cd

    where
        - rho is the density of the fluid (env dependant)
        - A is the cross sectional area (robot dependent)
        - Cd  is the drag coefficient,  related to the object shape and
          the Reynolds Number

    All these values are aggregated into the property DragCoeff, to ease
    the configuration of the component.
    """

    _name = "Drag"
    _short_desc = "Actuator allowing to simulate the drag force"

    add_property('_drag_coeff_x', 0.09, 'DragCoeffX', 'float'
                'An aggregated value to scale the drag force on X robot axis')
    add_property('_drag_coeff_y', 0.09, 'DragCoeffY', 'float'
                'An aggregated value to scale the drag force on Y robot axis')
    add_property('_drag_coeff_z', 0.5, 'DragCoeffZ', 'float'
                'An aggregated value to scale the drag force on Z robot axis')

    def __init__(self, obj, parent=None):
        logger.info('%s initialization' % obj.name)
        # Call the constructor of the parent class
        morse.core.actuator.Actuator.__init__(self, obj, parent)

        # Make new reference to the robot velocities (mathutils.Vector)
        self.robot_vel = self.robot_parent.bge_object.localLinearVelocity

        self._drag_coeff = [self._drag_coeff_x,
                            self._drag_coeff_y,
                            self._drag_coeff_z]
        logger.info("Component initialized, runs at %.2f Hz ", self.frequency)

    def default_action(self):
        robot = self.robot_parent
        drag = Vector((0.0, 0.0, 0.0))
        for i in range(3):
            v = self.robot_vel[i]
            drag[i] = - copysign(1.0, v) * v  * v * self._drag_coeff[i]

        logger.debug("Drag force %s" % drag)
        robot.bge_object.applyForce(drag, True)


import json
import base64
import boto3

BUCKET_NAME = 'deposit-screenshots'

def lambda_handler(event, context):
    if 'id' not in event:
        return 'id is required!'
    s3 = boto3.client('s3')
    presigned_url = s3.generate_presigned_url(
        ClientMethod='get_object',
        Params={
            'Bucket': BUCKET_NAME,
            'Key': event['id']
        }
    )
    return presigned_url


from dataclasses import dataclass, field
from numpy import uint8, int8, uint16, int16, uint32, int32

MEMORY_SIZE = 2 ** 20

def init_mem():
    """Return a default value for our memory."""

    return [uint8(0)] * (MEMORY_SIZE)

@dataclass
class Memory:
    """Serves as simultaor memory with associated read and write functions."""

    _mem: list[uint8] = field(default_factory=init_mem)

    def __len__(self):
        return len(self._mem)

    def copy_to_mem(self, data: list[uint8]):
        """Copies elements from provided list into begining of memory.

        Args:
            data (list[uint8]): The data to copy over.

        """
        i = 0
        for byte in data:
            self._mem[i] = byte
            i += 1

    def store_byte(self, addr: uint32, data: uint8) -> None:
        """Stores a single byte in memory.

        Args:
            addr (uint32): The memory address to write to.
            data (uint8): The data to write.
        """

        self._mem[addr] = data
        return None

    def load_byte(self, addr: uint32, signed: bool = True) -> int8:
        """Load and return a single byte from memory.

        Args:
            addr (uint32): The memory address to load from.
            signed (bool): Determines if the return data should be signed.

        Return:
            The data stored at the address.
        """

        if signed:
            return int8(self._mem[addr])
        else:
            return uint8(self._mem[addr])

    def store_halfword(self, addr: uint32, data: uint16) -> None:
        """Stores a 16-bit halfword in memory.

        Args:
            addr (uint32): The memory address to write to.
            data (uint16): The data to write.
        """

        d0 = uint8(data & 0xFF)
        d1 = uint8(data >> 8 & 0xFF)

        self._mem[addr] = d0
        self._mem[addr + 1] = d1

        return None

    def load_halfword(self, addr: uint32, signed: bool = True) -> int16:
        """Load and return a 16-bit halfword from memory

        Args:
            addr (uint32): The memory address to load from.
            signed (bool): Determines if the return data should be signed.

        Return:
            The data stored at the address.
        """

        d0 = self._mem[addr]
        d1 = self._mem[addr + 1]
        if signed:
            return int16(d1 << 8 | d0)
        else:
            return uint16(d1 << 8 | d0)

    def store_word(self, addr: uint32, data: uint32) -> None:
        """Stores a 32-bit word in memory.

        Args:
            addr (uint32): The memory address to write to.
            data (uint32): The data to write.
        """

        d0 = uint8(data & 0xFF)
        d1 = uint8(data >> 8 & 0xFF)
        d2 = uint8(data >> 16 & 0xFF)
        d3 = uint8(data >> 24 & 0xFF)

        self._mem[addr] = d0
        self._mem[addr + 1] = d1
        self._mem[addr + 2] = d2
        self._mem[addr + 3] = d3

        return None

    def load_word(self, addr: uint32) -> int32:
        """Load and return a 32-bit word from memory

        Args:
            addr (uint32): The memory address to load from.

        Return:
            The data stored at the address.
        """

        d0 = self._mem[addr]
        d1 = self._mem[addr + 1]
        d2 = self._mem[addr + 2]
        d3 = self._mem[addr + 3]

        return int32((((d3 << 8 | d2) << 8 | d1) << 8 | d0))


from collections.abc import Sized
from typing import Any, Dict, Iterable, Optional

class Progress:
    """Class to track progress during the loop. Includes state_dict/load_state_dict for convenience for checkpointing."""

    def __init__(
        self,
        num_epochs_completed: int = 0,
        num_steps_completed: int = 0,
        num_steps_completed_in_epoch: int = 0,
    ) -> None:
        self._num_epochs_completed: int = num_epochs_completed
        self._num_steps_completed: int = num_steps_completed
        self._num_steps_completed_in_epoch: int = num_steps_completed_in_epoch

    @property
    def num_epochs_completed(self) -> int:
        """Number of epochs completed thus far in loop."""
        return self._num_epochs_completed

    @property
    def num_steps_completed(self) -> int:
        """Number of steps completed thus far in loop."""
        return self._num_steps_completed

    @property
    def num_steps_completed_in_epoch(self) -> int:
        """Number of steps completed thus far in epoch."""
        return self._num_steps_completed_in_epoch

    def increment_step(self) -> None:
        """Increment the step counts completed and completed within the epoch."""
        self._num_steps_completed += 1
        self._num_steps_completed_in_epoch += 1

    def increment_epoch(self) -> None:
        """Increment the epochs completed and resets the steps completed within the epoch."""
        self._num_epochs_completed += 1
        self._num_steps_completed_in_epoch = 0

    def state_dict(self) -> Dict[str, Any]:
        """Returns a state_dict of a Progress instance in accordance with Stateful protocol."""
        return {
            "num_epochs_completed": self._num_epochs_completed,
            "num_steps_completed": self._num_steps_completed,
            "num_steps_completed_in_epoch": self._num_steps_completed_in_epoch,
        }

    def load_state_dict(self, state_dict: Dict[str, Any]) -> None:
        """Restores a Progress instance from a state_dict in accordance with Stateful protocol."""
        self._num_epochs_completed = state_dict["num_epochs_completed"]
        self._num_steps_completed = state_dict["num_steps_completed"]
        self._num_steps_completed_in_epoch = state_dict["num_steps_completed_in_epoch"]

def estimated_steps_in_epoch(
    dataloader: Iterable[object],
    *,
    num_steps_completed: int,
    max_steps: Optional[int],
    max_steps_per_epoch: Optional[int],
) -> float:
    """Estimate the number of remaining steps for the current epoch."""

    total = float("inf")
    if isinstance(dataloader, Sized):
        try:
            total = len(dataloader)
        except NotImplementedError:
            pass

    if max_steps_per_epoch and max_steps:
        total = min(total, max_steps_per_epoch, max_steps - num_steps_completed)
    elif max_steps:
        total = min(total, max_steps - num_steps_completed)
    elif max_steps_per_epoch:
        total = min(total, max_steps_per_epoch)
    return total

def estimated_steps_in_loop(
    dataloader: Iterable[object],
    *,
    max_steps: Optional[int],
    max_steps_per_epoch: Optional[int],
    epochs: Optional[int],
) -> Optional[int]:
    """
    Estimate the total number of steps for the current loop.

    A return value of None indicates that the number of steps couldn't be estimated.
    """

    if not max_steps and not epochs:
        return None

    if not epochs:
        return max_steps

    total_steps = None
    steps_per_epoch = estimated_steps_in_epoch(
        dataloader,
        num_steps_completed=0,
        max_steps=max_steps,
        max_steps_per_epoch=max_steps_per_epoch,
    )
    if steps_per_epoch != float("inf"):
        total_steps = int(steps_per_epoch) * epochs

    if total_steps and max_steps:
        return min(total_steps, max_steps)

    return total_steps or max_steps

def estimated_steps_in_fit(
    *,
    train_dataloader: Iterable[object],
    eval_dataloader: Iterable[object],
    epochs: Optional[int],
    max_steps: Optional[int],
    max_train_steps_per_epoch: Optional[int],
    max_eval_steps_per_epoch: Optional[int],
    eval_every_n_steps: Optional[int],
    eval_every_n_epochs: Optional[int],
) -> Optional[int]:
    """
    Estimate the total number of steps for fit run.

    If the number of training/eval steps couldn't be calculated, None is returned.
    """
    training_steps = estimated_steps_in_loop(
        train_dataloader,
        max_steps=max_steps,
        max_steps_per_epoch=max_train_steps_per_epoch,
        epochs=epochs,
    )
    if not training_steps:
        return None

    if not eval_every_n_steps and not eval_every_n_epochs:
        return training_steps

    number_of_eval_steps_per_eval_epoch = estimated_steps_in_loop(
        eval_dataloader,
        max_steps=None,
        max_steps_per_epoch=max_eval_steps_per_epoch,
        epochs=1,
    )
    if not number_of_eval_steps_per_eval_epoch:
        return None

    total_eval_epochs = 0
    if eval_every_n_epochs and epochs:
        total_eval_epochs += epochs // eval_every_n_epochs

    if eval_every_n_steps:
        total_eval_epochs += training_steps // eval_every_n_steps

    return training_steps + total_eval_epochs * number_of_eval_steps_per_eval_epoch


# -*- coding: utf-8 -*-
import chevron
import logging
import os
import random

log = logging.getLogger(__name__)

def load_file(file_path, name="item"):
    assert file_path, "File path cannot be empty"
    log.info("Start loading %s from: %s", name, file_path)
    with open(file_path, "r") as f:
        text = f.read()
        log.info("Finish loading %s from: %s", name, file_path)
        return text

def files_from_dir(dir_path, name="items"):
    assert dir_path, "Dir path cannot be empty"
    if not dir_path.endswith("/"):
        dir_path = dir_path + "/"
    files = os.listdir(dir_path)
    log.info("Existed %s in %s: %s", name, dir_path, files)
    return list(map(lambda file: dir_path + file, files))

def choose_random(lst, name="item"):
    assert lst, "List cannot be empty"
    item = random.choice(lst)
    log.info("Chosen random %s: %s", name, item)
    return item

def shuffle(lst, name="items"):
    assert lst, "List cannot be empty"
    log.info("Start shuffle %s: %s", name, lst)
    random.shuffle(lst)
    log.info("Shuffle %s result: %s", name, lst)
    return lst

def render_template(template, data):
    log.info("Before render template: %s", template)
    result = chevron.render(template, data)
    log.info("After render template: %s", result)
    return result

def load_and_render(templates_path,
                    products_path,
                    names_path,
                    places_path):
    def load_values(products_p,
                    names_p,
                    places_p):
        values = {}

        products = load_file(products_p, "products")
        assert products, "Products cannot be empty"
        products = products.split("\n")
        values["products"] = shuffle(products, "products")
        values["fname"] = "Ivan"
        values["lname"] = "Ivanov"
        return values

    def load_template(templates_p):
        templates = files_from_dir(templates_p, "templates")
        template_path = choose_random(templates, "template")
        return load_file(template_path, "template")

    data = load_values(products_path, names_path, places_path)
    template = load_template(templates_path)
    return render_template(template, data)

def get_photos(images_path, min_count, max_count):
    assert min_count >= 0, "Min cannot be lesser then 0"
    assert min_count < max_count, "Max cannot be lesser then min"

    images = files_from_dir(images_path, "images")
    result = None
    if images:
        shuffle(images, "images")
        count = random.randint(min_count, min(len(images), max_count))
        result = images[:count]
        log.info("Chosen %d random images: %s", count, result)
    return result

if __name__ == '__main__':
    resources_path = "./resources/"
    message = load_and_render(f"{resources_path}templates",
                              f"{resources_path}products.txt",
                              None, None)


from flask import Flask, jsonify

# Python SQL toolkit and Object Relational Mapper
import sqlalchemy
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine, func

# create engine to hawaii.sqlite
engine = create_engine("sqlite:///Resources/hawaii.sqlite")

# reflect an existing database into a new model
# reflect the tables
Base = automap_base()
Base.prepare(engine, reflect=True)

# Save references to each table
Measurement = Base.classes.measurement
Station = Base.classes.station

# Create our session (link) from Python to the DB
session = Session(engine)

# Find the most recent date in the data set.
most_recent_date = session.query(func.max(Measurement.date))\
    .first()[0]

# Calculate the date one year from the last date in data set.
year_before_recent = '2016-08-23'

# Perform a query to retrieve the data and precipitation scores
year_in_review = session.query(Measurement.date, Measurement.prcp)\
    .filter(Measurement.date.between(year_before_recent, most_recent_date))\
    .all()

# Dictionary of precipitation data
precipitation_dict = {}
count = 0
for record in year_in_review:
    precipitation_dict[year_in_review[count][0]] = year_in_review[count][1]
    count += 1

#Station Data Query
station_data = session.query(Station.station, Station.name).all()

#Dictionary of station data
station_dict = {}
count = 0
for station in station_data:
    station_dict[station_data[count][0]] = station_data[count][1]
    count += 1

#Query for most active station
most_active_station = most_active_stations = session.query(Measurement.station, func.count(Measurement.station))\
    .group_by(Measurement.station)\
    .order_by(func.count(Measurement.station).desc())\
    .all()[0][0]

#Query for dates and temperature observations of the most-active station for the previous year
active_station_year_in_review = session.query(Measurement.date, Measurement.tobs)\
    .filter(Measurement.date.between(year_before_recent, most_recent_date))\
    .filter(Measurement.station == most_active_station)\
    .all()

#Dictionary of temperature for the previous year of the most active station
active_station_temp_dict = {}
count = 0
for temp in active_station_year_in_review:
    active_station_temp_dict[active_station_year_in_review[count][0]] = active_station_year_in_review[count][1]
    count += 1

#Query for a specified temperature range

# Flask Setup
app = Flask(__name__)

# Flask Routes
@app.route("/")
def homepage():
    return (
        f"Welcome to the homepage!
"
        f"Available Routes:
"
        f"/api/v1.0/precipitation
"
        f"/api/v1.0/stations
"
        f"/api/v1.0/tobs
"
        f"
"
        f"You can enter a specific START date, as well as an END date to pull temperature data for that range:
"
        f"/api/v1.0/temp/2017-08-01
"
        f"/api/v1.0/temp/2017-08-01/2017-08-23
"
    )

@app.route("/api/v1.0/precipitation")
def precipitation():
    """Return precipitation data for the last year as json"""

    return jsonify(precipitation_dict)

@app.route("/api/v1.0/stations")
def station():
    """Return station data as json"""

    return jsonify(station_dict)

@app.route("/api/v1.0/tobs")
def temperature():
    """Return temperature data for the last year as json"""

    return jsonify(active_station_temp_dict)

@app.route("/api/v1.0/temp", methods=['get'])
def temp():
    start  = request.args.get('start', None)
    end  = request.args.get('end', None)
    """Return min, max, and average temperature data for the specified date range as json"""

    desc_dict = {}
    #if there is no end date specified
    if end == None:
        min_temp = session.query(Measurement.date, func.min(Measurement.tobs))\
            .filter(Measurement.date >= start)\
            .first()

        max_temp = session.query(Measurement.date, func.max(Measurement.tobs))\
            .filter(Measurement.date >= start)\
            .first()

        avg_temp = session.query(Measurement.date, func.avg(Measurement.tobs))\
            .filter(Measurement.date >= start)\
            .first()

        #building the dict
        desc_dict["Min Temp"] = min_temp[1]
        desc_dict["Max Temp"] = max_temp[1]
        desc_dict["Avg Temp"] = round(avg_temp[1], 1)

        return jsonify(desc_dict)

    #if there is an end date specified
    else:
        min_temp = session.query(Measurement.date, func.min(Measurement.tobs))\
            .filter(Measurement.date.between(start, end))\
            .first()

        max_temp = session.query(Measurement.date, func.max(Measurement.tobs))\
            .filter(Measurement.date.between(start, end))\
            .first()

        avg_temp = session.query(Measurement.date, func.avg(Measurement.tobs))\
            .filter(Measurement.date.between(start, end))\
            .first()

        #building the dict
        desc_dict["Min Temp"] = min_temp[1]
        desc_dict["Max Temp"] = max_temp[1]
        desc_dict["Avg Temp"] = round(avg_temp[1], 1)

        return jsonify(desc_dict)

if __name__ == "__main__":
    app.run(debug=True, use_reloader = False)