def get_timestamps(series):
    return [
        1000
        * (
            calendar.timegm(
                datetime.strptime(d.created_at.split(".")[0], "%Y-%m-%dT%H:%M:%S").replace(second=0).timetuple()
            )
        )
        for d in series
    ] def get_daily_pv_chart_data(pv):
    timestamps_pv = get_timestamps(pv)
    return [list(x) for x in zip(timestamps_pv, [(int(d.current_power or 0)) for d in pv])] @cache.memoize(timeout=300)
def get_last_n_days_import(n):
    return (
        Electricity.query.with_entities(
            func.strftime("%Y-%m-%dT00:00:00", Electricity.created_at).label("created_at"),
            (func.max(Electricity.meter_180) - func.min(Electricity.meter_180)).label("electricity_import"),
        )
        .filter(Electricity.created_at > (datetime.now() - timedelta(days=n)))
        .group_by(func.strftime("%Y-%m-%d", Electricity.created_at))
        .all()
    ) def get_sec(s):
    l = list(map(int, s.split(":")))
    return sum(n * sec for n, sec in zip(l[::-1], (1, 60, 3600))) def get_todays_date():
    return datetime.now().date() @cache.memoize(timeout=300)
def get_daily_energy_series(current_date):
    """
    :param current_date: date of the energy series
    :return: energy series
    """
    tomorrow = current_date + timedelta(days=1)
    return (
        PVData.query.with_entities(
            PVData.created_at,
            PVData.current_power,
            PVData.daily_energy,
            PVData.dc_1_u,
            PVData.dc_2_u,
            PVData.ac_1_u,
            PVData.ac_2_u,
            PVData.ac_3_u,
        )
        .filter(PVData.created_at > current_date.strftime("%Y-%m-%d"))
        .filter(PVData.created_at < tomorrow.strftime("%Y-%m-%d"))
        .filter(PVData.current_power > 0)
        .all()
    ) def get_7_day_max_energy_series(current_date):
    """
    :param current_date: pivot date of the energy series
    :return: theoretical maximum energy series ± 3 days around current date
    """
    return (
        PVData.query.with_entities(
            func.strftime("%H:%M:00", PVData.created_at).label("pvdata_created_at"),
            func.max(PVData.current_power).label("pv_max"),
        )
        .filter(PVData.created_at >= (current_date - timedelta(days=3)).strftime("%Y-%m-%d"))
        .filter(PVData.created_at <= (current_date + timedelta(days=3)).strftime("%Y-%m-%d"))
        .filter(PVData.current_power > 0)
        .group_by(func.strftime("%H:%M:00", PVData.created_at))
        .all()
    ) @cache.cached(timeout=3600, key_prefix="yearly_series")
def get_yearly_series():
    """Returns a list of yearly generated energy for past years
    :return: list of yearly generated energy for past years
    """
    return (
        PVData.query.with_entities(
            func.strftime("%Y", PVData.created_at).label("year"),
            (func.max(PVData.total_energy) - func.min(PVData.total_energy)).label("yearly_output"),
        )
        .group_by(func.strftime("%Y", PVData.created_at))
        .all()
    ) @cache.memoize(timeout=300)
def get_last_n_days(n):
    """Returns a list of daily yields
    :param n: number of last days
    :return: list of daily yields
    """
    return (
        PVData.query.with_entities(
            func.strftime("%Y-%m-%dT00:00:00", PVData.created_at).label("created_at"),
            func.max(PVData.daily_energy).label("daily_energy"),
        )
        .filter(PVData.created_at > (datetime.now() - timedelta(days=n)))
        .group_by(func.strftime("%Y-%m-%d", PVData.created_at))
        .all()
    ) def get_current_year_prediction():
    """Computes the prediction of kWh for the remaining days of the current year

    :return: the number of kWh for the remaining year
    """
    query = """SELECT 
                    SUM(energy) AS prediction 
                FROM (
                    SELECT 
                        max(total_energy) - min(total_energy) AS energy 
                    FROM 
                        pvdata 
                    WHERE 
                        strftime('%Y', created_at) = strftime('%Y', 'now') 
                    UNION 
                    SELECT 
                        AVG(max_rest_year - min_rest_year) AS energy 
                    FROM 
                        (
                            SELECT 
                                min(total_energy) min_rest_year, 
                                max(total_energy) max_rest_year 
                            FROM 
                                pvdata 
                            WHERE 
                                strftime('%j', created_at) > strftime('%j', 'now') 
                                AND strftime('%Y', created_at) < strftime('%Y', 'now') 
                            GROUP BY 
                                strftime('%Y', created_at)
                        ) q
                );"""
    result = db.engine.execute(query)
    return result
import calendar
from datetime import datetime, timedelta
from flask import Blueprint, render_template
from solarpi.charts.helper import get_timestamps, get_daily_pv_chart_data
from solarpi.electricity.helper import get_last_n_days_import
from solarpi.pvdata.helper import (
    get_sec,
    get_todays_date,
    get_daily_energy_series,
    get_7_day_max_energy_series,
    get_yearly_series,
    get_last_n_days,
    get_current_year_prediction,
)
# -*- coding: utf-8 -*-



blueprint = Blueprint("charts", __name__, url_prefix="/charts", static_folder="../static")


@blueprint.route("/daily")
@blueprint.route("/daily/<date>")
def daily(date=None):
    """Renders a page with charts for a given date
    :param date: a date in the format %Y-%m-%d
    :return: page with charts for a given date
    """
    error = None
    current_date = get_todays_date()
    try:
        if date:
            current_date = datetime.strptime(date, "%Y-%m-%d")
    except ValueError:
        error = "invalid date, displaying today's data instead"
        current_date = get_todays_date()
    yesterday = current_date - timedelta(days=1)
    tomorrow = current_date + timedelta(days=1)

    # get photovoltaic data for the day
    pv = get_daily_energy_series(current_date)

    daily_chart_data = get_daily_pv_chart_data(pv)

    timestamps_pv = get_timestamps(pv)
    # voltages
    input_voltage_1_chart_data = [list(x) for x in zip(timestamps_pv, [(int(d.dc_1_u or 0)) for d in pv])]
    input_voltage_2_chart_data = [list(x) for x in zip(timestamps_pv, [(int(d.dc_2_u or 0)) for d in pv])]
    output_voltage_1_chart_data = [list(x) for x in zip(timestamps_pv, [(int(d.ac_1_u or 0)) for d in pv])]
    output_voltage_2_chart_data = [list(x) for x in zip(timestamps_pv, [(int(d.ac_2_u or 0)) for d in pv])]
    output_voltage_3_chart_data = [list(x) for x in zip(timestamps_pv, [(int(d.ac_3_u or 0)) for d in pv])]

    # get maxium photovoltaic data for ± 3 days
    pv_max = get_7_day_max_energy_series(current_date)
    current_date_midnight = calendar.timegm(current_date.timetuple())
    timestamps_pv_max = [1000 * (get_sec(d.pvdata_created_at) + current_date_midnight) for d in pv_max]
    daily_chart_max_data = [list(x) for x in zip(timestamps_pv_max, [(int(d.pv_max or 0)) for d in pv_max])]

    # additional data
    daily_energy = 0.0
    if pv:
        daily_energy = pv[-1].daily_energy

    return render_template(
        "charts/daily.html",
        data=daily_chart_data,
        data2=daily_chart_max_data,
        yesterday=yesterday,
        today=current_date,
        tomorrow=tomorrow,
        daily_energy=daily_energy,
        all_data=pv,
        input_voltage_1_chart_data=input_voltage_1_chart_data,
        input_voltage_2_chart_data=input_voltage_2_chart_data,
        output_voltage_1_chart_data=output_voltage_1_chart_data,
        output_voltage_2_chart_data=output_voltage_2_chart_data,
        output_voltage_3_chart_data=output_voltage_3_chart_data,
        error=error,
    )


@blueprint.route("/weekly")
def weekly():
    """Renders a page with charts for the last seven days
    :return: a page with charts for the last seven days
    """
    return get_last_days_chart(number_of_days=7)


@blueprint.route("/monthly")
def monthly():
    """Renders a page with charts for the last 30 days
    :return: a page with charts for the last 30 days
    """
    return get_last_days_chart(number_of_days=30)


def get_last_days_chart(number_of_days=7):
    pv = get_last_n_days(number_of_days)
    timestamps = get_timestamps(pv)
    series = [(float(d.daily_energy or 0)) for d in pv]
    total_energy = sum(series)
    pv_chart_data = [list(x) for x in zip(timestamps, series)]


    electricity_import = [(float(d.electricity_import or 0)) for d in list(get_last_n_days_import(number_of_days))]