#!/usr/bin/env python

from __future__ import print_function
from builtins import zip
from builtins import str
from builtins import range
import os
import argparse
import copy
import numpy as np
import warnings
import matplotlib
# Set matplotlib backend (to create plots where DISPLAY is not set).
matplotlib.use('Agg')
import matplotlib.cm as cm

import lsst.sims.maf.db as db
import lsst.sims.maf.metrics as metrics
import lsst.sims.maf.slicers as slicers
import lsst.sims.maf.stackers as stackers
import lsst.sims.maf.metricBundles as metricBundles
import lsst.sims.maf.plots as plots
import lsst.sims.maf.utils as utils

class SurveyTimeStacker(stackers.BaseStacker):
    def __init__(self, visitTimeCol="visitTime", slewTimeCol="slewTime"):
        self.visitTimeCol = visitTimeCol
        self.slewTimeCol = slewTimeCol
        self.colsReq = [visitTimeCol, slewTimeCol]
        self.colsAdded = ["surveyTime"]
        self.units = ["seconds"]

    def _run(self, simData):
        simData["surveyTime"] = simData[self.visitTimeCol] + simData[self.slewTimeCol]
        return simData

class NormSumMetric(metrics.BaseMetric):
    def __init__(self, col, normValue, metricName="Normalized Sum", **kwargs):
        super(NormSumMetric, self).__init__(col=col, metricName=metricName, **kwargs)
        self.col = col
        self.normValue = normValue
    def run(self, dataSlice, slicePoint=None):
        return np.sum(dataSlice[self.col]) / self.normValue

def makeBundleList(dbFile, runName=None, benchmark='design'):

    seeingCol = 'seeingFwhmEff'
    benchmarkSeeing = 'FWHMeff'
    latCol = "fieldDec"
    lonCol = "fieldRA"
    nside=64

    # List to hold everything we're going to make
    bundleList = []

    # Connect to the databse
    opsimdb = db.OpsimDatabase(dbFile)
    propCol = 'proposalId'

    if runName is None:
        runName = os.path.basename(dbFile).replace('_sqlite.db', '')
        runName = runName.replace('.db', '')

    # Fetch the proposal ID values from the database
    propids, propTags = opsimdb.fetchPropInfo()
    DDpropid = propTags['DD']
    WFDpropid = propTags['WFD']

    # Fetch the telescope location from config
    lat, lon, height = opsimdb.fetchLatLonHeight()

    # Construct a WFD SQL where clause so multiple propIDs can query by WFD:
    wfdWhere = opsdb.createSQLWhere('WFD', propTags)
    print('#FYI: WFD "where" clause: %s' % (wfdWhere))
    ddWhere = opsdb.createSQLWhere('DD', propTags)
    print('#FYI: DD "where" clause: %s' % (ddWhere))

    # Set up benchmark values, scaled to length of opsim run. These are applied to 'all' and 'WFD' plots.
    runLength = opsimdb.fetchRunLength()
    if benchmark == 'requested':
        # Fetch design values for seeing/skybrightness/single visit depth.
        benchmarkVals = utils.scaleBenchmarks(runLength, benchmark='design')
        # Update nvisits with requested visits from config files.
        benchmarkVals['nvisits'] = opsimdb.fetchRequestedNvisits(propId=WFDpropid)
        # Calculate expected coadded depth.
        benchmarkVals['coaddedDepth'] = utils.calcCoaddedDepth(
            benchmarkVals['nvisits'], benchmarkVals['singleVisitDepth'])
    elif (benchmark == 'stretch') or (benchmark == 'design'):
        # Calculate benchmarks for stretch or design.
        benchmarkVals = utils.scaleBenchmarks(runLength, benchmark=benchmark)
        benchmarkVals['coaddedDepth'] = utils.calcCoaddedDepth(
            benchmarkVals['nvisits'], benchmarkVals['singleVisitDepth'])
    else:
        raise ValueError(
            'Could not recognize benchmark value %s, use design, stretch or requested.' % (benchmark))
    # Check that nvisits is not set to zero (for very short run length).
    for f in benchmarkVals['nvisits']:
        if benchmarkVals['nvisits'][f] == 0:
            print('Updating benchmark nvisits value in %s to be nonzero' % (f))
            benchmarkVals['nvisits'][f] = 1

    # Set values for min/max range of nvisits for All/WFD and DD plots. These are somewhat arbitrary.
    nvisitsRange = {}
    nvisitsRange['all'] = {'u': [30, 110], 'g': [50, 160], 'r': [100, 310],
                           'i': [90, 270], 'z': [100, 330], 'y': [50, 300]}

    # Scale these nvisit ranges for the runLength.
    scale = runLength / 10.0
    for prop in nvisitsRange:
        for f in nvisitsRange[prop]:
            for i in [0, 1]:
                nvisitsRange[prop][f][i] = int(np.floor(nvisitsRange[prop][f][i] * scale))

    # Filter list, and map of colors (for plots) to filters.
    filters = ['u', 'g', 'r', 'i', 'z', 'y']
    colors = {'u': 'm', 'g': 'b', 'r': 'g', 'i': 'y', 'z': 'r', 'y': 'k'}
    filtorder = {'u': 1, 'g': 2, 'r': 3, 'i': 4, 'z': 5, 'y': 6}

    slicermetadata = ''

    ###
    # Configure some standard summary statistics dictionaries to apply to appropriate metrics.
    standardStats = [metrics.MeanMetric(),
                     metrics.RmsMetric(), metrics.MedianMetric(), metrics.CountMetric(),
                     metrics.NoutliersNsigmaMetric(metricName='N(+3Sigma)', nSigma=3),
                     metrics.NoutliersNsigmaMetric(metricName='N(-3Sigma)', nSigma=-3.)]

    rangeStats = [metrics.PercentileMetric(metricName='25th%ile', percentile=25),
                  metrics.PercentileMetric(metricName='75th%ile', percentile=75),
                  metrics.MinMetric(), metrics.MaxMetric()]

    allStats = copy.deepcopy(standardStats)
    allStats.extend(rangeStats)

    # Standardize a couple of labels (for ordering purposes in showMaf).
    summaryGroup = 'A: Summary'
    nvisitGroup = 'B: NVisits'
    nvisitPerPropGroup = 'C: NVisits (per prop)'
    surveyTimeGroup = 'D: Survey Time'
    hourangleGroup = 'E: Hour Angle'
    distanceGroup = 'F: Distance to Sun/Moon'
    hourglassGroup = 'G: Hourglass'
    filterGroup = 'H: Filter Changes'
    slewGroup = 'I: Slew'

    # Fetch the total number of visits (to create fraction for number of visits per proposal)
    totalNVisits = opsimdb.fetchNVisits()
    totalSlewN = opsimdb.fetchTotalSlewN()

    # Set up an object to hold all the bundles that will be merged together
    healpixHistPlot = plots.HealpixHistogram()
    mergedHistDict = {}
    healPlots = ['nvisits', 'fullRangeHA', 'meanHourAngle']
    oneDPlots = ['moonDistance', 'hourAngle']

    # set plotters for merged histograms (healpix hist or oned hist)
    for plotName in healPlots:
        mergedHistDict[plotName] = plots.PlotBundle(plotFunc=healpixHistPlot)
    for plotName in oneDPlots:
        mergedHistDict[plotName] = plots.PlotBundle(plotFunc=plots.OneDBinnedData())

    # Metrics calculating values across the sky.
    # Loop over a set of standard analysis metrics, for All Proposals, WFD only, and DD only.

    prop = "WFD"
    for f in filters:
        propCaption = 'for all WFD proposals'
        metadata = '%s band, WFD' % (f) + slicermetadata
        sqlconstraint = 'filter = "%s" and %s' % (f, wfdWhere)
        nvisitsMin = nvisitsRange['all'][f][0]
        nvisitsMax = nvisitsRange['all'][f][1]
        mag_zp = benchmarkVals['coaddedDepth'][f]

        # Make a new slicer for each metric since they can get setup with different fields later
        slicer = slicers.HealpixSlicer(nside=nside, lonCol=lonCol, latCol=latCol)

        # Configure the metrics to run for this sql constraint (all proposals/wfd and filter combo).

        # Count the total number of visits.
        metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
        plotDict = {'xlabel': 'Number of Visits',
                    'xMin':     nvisitsMin, 'xMax':     nvisitsMax,
                    'colorMin': nvisitsMin, 'colorMax': nvisitsMax,
                    'binsize': 5}
        summaryStats = allStats
        displayDict = {'group': nvisitGroup, 'order': filtorder[f], "subgroup": "per filter",
                       'caption': 'Number of visits in filter %s, %s.' % (f, propCaption)}
        histMerge = {'color': colors[f], 'label': '%s' % (f),
                     'xMin': nvisitsMin, 'xMax': nvisitsMax,
                     'binsize': 5, 'legendloc': 'upper right'}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName, metadata=metadata,
                                            summaryMetrics=summaryStats)
        mergedHistDict['nvisits'].addBundle(bundle, plotDict=histMerge)
        bundleList.append(bundle)

        # Calculate the mean of the hour angle.
        metric = metrics.MeanMetric(col='HA')
        plotDict = {'xMin':     -5, 'xMax':     5,
                    'colorMin': -5, 'colorMax': 5,
                    'binsize': 0.05}
        displayDict = {'group': hourangleGroup, 'order': filtorder[f], "subgroup": "mean",
                       'caption': 'Mean of the Hour Angle in filter %s, %s.'
                       % (f, propCaption)}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata,
                                            summaryMetrics=summaryStats)
        histMerge = {'color': colors[f], 'label': '%s' % (f),
                     'xlabel': 'Mean Hour Angle (Hours)',
                     'xMin': -5, 'xMax': 5,
                     'binsize': .05, 'legendloc': 'upper right'}
        mergedHistDict['meanHourAngle'].addBundle(bundle, plotDict=histMerge)
        bundleList.append(bundle)

        # Calculate the Full Range of the hour angle.
        metric = metrics.FullRangeMetric(col='HA')
        plotDict = {'xMin':     0, 'xMax':     12,
                    'colorMin': 0, 'colorMax': 12,
                    'binsize': 0.1}
        displayDict = {'group': hourangleGroup, 'order': filtorder[f], "subgroup": "full range",
                       'caption': 'Full Range of the Hour Angle in filter %s, %s.'
                       % (f, propCaption)}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata,
                                            summaryMetrics=summaryStats)
        histMerge = {'color': colors[f], 'label': '%s' % (f),
                     'xlabel': 'Full Hour Angle Range',
                     'binsize': 0.1,
                     'legendloc': 'upper right'}
        mergedHistDict['fullRangeHA'].addBundle(bundle, plotDict=histMerge)
        bundleList.append(bundle)

    # number of visits in all filters together, WFD only.
    sqlconstraint = wfdWhere
    slicer = slicers.HealpixSlicer(nside=nside, lonCol=lonCol, latCol=latCol)
    metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
    metadata = 'All filters, WFD'
    plotDict = {'xlabel': 'Number of Visits', 'binsize': 5, 'cumulative': False,
                'xMin': 500, 'xMax': 1500,
                "colorMin": 500, "colorMax": 1500}
    summaryStats = allStats
    displayDict = {'group': nvisitPerPropGroup, 'subgroup': 'All Filters', 'order': 0,
                   'caption': 'Number of visits all filters, WFD only'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    # Number of visits in all filters together, DD only.
    sqlconstraint = ddWhere
    slicer = slicers.HealpixSlicer(nside=nside, lonCol=lonCol, latCol=latCol)
    metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
    metadata = 'All filters, DD'
    plotDict = {'xlabel': 'Number of Visits', 'binsize': 5, 'cumulative': False,
                'xMin': 0, 'xMax': 1000,
                "colorMin": 0, "colorMax": 1000}
    summaryStats = allStats
    displayDict = {'group': nvisitPerPropGroup, 'subgroup': 'All Filters', 'order': 1,
                   'caption': 'Number of visits all filters, DD only'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    sqlconstraint = wfdWhere
    slicer = slicers.HealpixSlicer(nside=nside, lonCol=lonCol, latCol=latCol)

    stackerList = [SurveyTimeStacker()]
    metric = NormSumMetric(col="surveyTime", normValue=60, metricName="Survey Time (mins)")
    metadata = "All filters, WFD"
    plotDict = {"xlabel": "Survey Time (minutes)",
                "xMin": 500, "xMax": 800, "binsize": 10,
                "colorMin": 500, "colorMax": 800}
    summaryStats = allStats
    displayDict = {"group": surveyTimeGroup, "subgroup": "WFD",
                   "caption": "Amount of survey time (visit time plus slew time)" + \
                              " spent on each sky pixel"}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName,
                                        metadata=metadata, summaryMetrics=summaryStats,
                                        stackerList=stackerList)
    bundleList.append(bundle)

    # End of all-sky metrics.

    # Hourglass metric.
    hourSlicer = slicers.HourglassSlicer()
    # Calculate Filter Hourglass plots per year (split to make labelling easier).
    yearDates = list(range(0, int(round(365 * runLength)) + 365, 365))
    for i in range(len(yearDates) - 1):
        sqlconstraint = 'night > %i and night <= %i' % (yearDates[i], yearDates[i + 1])
        metadata = 'Year %i-%i' % (i, i + 1)
        metric = metrics.HourglassMetric()
        displayDict = {'group': hourglassGroup, 'subgroup': 'Yearly', 'order': i}
        bundle = metricBundles.MetricBundle(metric, hourSlicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName, metadata=metadata)
        bundleList.append(bundle)

    # Histograms per filter for WFD only (generally used to produce merged histograms).
    summaryStats = standardStats
    prop = "WFD"
    for f in filters:
        # Set some per-proposal information.
        propCaption = ' for all WFD visits.'
        metadata = '%s band, WFD' % (f) + slicermetadata
        sqlconstraint = 'filter = "%s" and %s' % (f, wfdWhere)
        # Set up metrics and slicers for histograms.

        # Histogram the individual visit hour angle values.
        metric = metrics.CountMetric(col='HA', metricName='Hour Angle Histogram')
        histMerge = {'legendloc': 'upper right',
                'color': colors[f], 'label': '%s' % f,
                'xMin': -10., 'xMax': 10, "binsize": 0.1}
        plotDict = {"xMin": -10, "xMax": 10, "binsize": 0.1}
        displayDict = {'group': hourangleGroup, 'order': filtorder[f],
                       'caption': 'Histogram of the hour angle in %s band, %s' % (f, propCaption)}
        slicer = slicers.OneDSlicer(sliceColName='HA', binsize=0.1)
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName, metadata=metadata)
        mergedHistDict['hourAngle'].addBundle(bundle, plotDict=histMerge)
        bundleList.append(bundle)

        # Histogram the individual visit distance to moon values.
        metric = metrics.CountMetric(col='moonDistance', metricName='Distance to Moon Histogram')
        histMerge = {'legendloc': 'upper right',
                     'color': colors[f], 'label': '%s' % f,
                     'xMin': 0., 'xMax': 180., "binsize": 5,
                     'xlabel': 'Distance to Moon (degrees)'}
        plotDict = {"xMin": 0, "xMax": 180, "binsize": 5}
        caption = 'Histogram of the distance between the field and the moon (in radians) '
        caption += 'in %s band, %s' % (f, propCaption)
        displayDict = {'group': distanceGroup, "subgroup": "Moon Distance",
                       'order': filtorder[f], 'caption': caption}
        slicer = slicers.OneDSlicer(sliceColName='moonDistance', binsize=5.)
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            displayDict=displayDict, runName=runName, metadata=metadata)
        mergedHistDict['moonDistance'].addBundle(bundle, plotDict=histMerge)
        bundleList.append(bundle)

    # Slew histograms (time and distance).
    sqlconstraint = wfdWhere
    metadata = "WFD"
    metric = metrics.CountMetric(col='slewTime', metricName='Slew Time Histogram')
    plotDict = {'logScale': True, 'ylabel': 'Count',
                "xMin": -10, "xMax": 160, "binsize": 1}
    displayDict = {'group': slewGroup, 'subgroup': 'Slew Histograms',
                   'caption': 'Histogram of slew times for all visits.',
                   "order": 1}
    slicer = slicers.OneDSlicer(sliceColName='slewTime', binsize=1)
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName,
                                        metadata=metadata)
    bundleList.append(bundle)

    # now a zoomed in time count
    metric = metrics.CountMetric(col="slewTime", metricName="Slew Time Histogram (zoom)")
    plotDict = {"logScale": True, "ylabel": "Count",
                "xMin": 2, "xMax": 18, "binsize": 0.1}
    displayDict = {"group": slewGroup, "subgroup": "Slew Histograms",
                   "caption": "Zoom of histogram of slew times for all visits.",
                   "order": 2}
    slicer = slicers.OneDSlicer(sliceColName="slewTime", binsize=0.1)
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName,
                                        metadata=metadata)
    bundleList.append(bundle)

    # slew distance histogram
    metric = metrics.CountMetric(col='slewDistance', metricName='Slew Distance Histogram')
    plotDict = {'logScale': True, 'ylabel': 'Count',
                "xMin": -10, "xMax": 180, "binsize": 1}
    displayDict = {'group': slewGroup, 'subgroup': 'Slew Histograms',
                   'caption': 'Histogram of slew distances for all visits.',
                   "order": 3}
    slicer = slicers.OneDSlicer(sliceColName='slewDistance', binsize=3.)
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName,
                                        metadata=metadata)
    bundleList.append(bundle)

    # and a zoomed in slew distance histogram
    metric = metrics.CountMetric(col='slewDistance', metricName='Slew Distance Histogram (zoom)')
    plotDict = {'logScale': True, 'ylabel': 'Count',
                "xMin": 0, "xMax": 19, "binsize": 0.1}
    displayDict = {'group': slewGroup, 'subgroup': 'Slew Histograms',
                   'caption': 'Zoomed histogram of slew distances for all visits.',
                   "order": 4}
    slicer = slicers.OneDSlicer(sliceColName='slewDistance', binsize=0.1)
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName,
                                        metadata=metadata)
    bundleList.append(bundle)

    # Plots per night -- the number of visits and the open shutter time fraction.
    # nvisits per night
    slicer = slicers.OneDSlicer(sliceColName='night', binsize=1)
    metadata = 'Per night'
    sqlconstraint = ''
    summaryStats = allStats

    metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
    displayDict = {'group': summaryGroup, 'subgroup': '3: Obs Per Night',
                   'caption': 'Number of visits per night.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    # open shutter time per night
    metric = metrics.OpenShutterFractionMetric()
    caption = 'Open shutter fraction per night. This compares the on-sky image time against '
    caption += 'the on-sky time + slews/filter changes/readout, but does not include downtime '
    caption += 'due to weather.'
    displayDict = {'group': summaryGroup, 'subgroup': '3: Obs Per Night',
                   'caption': caption}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    # filter stats per night
    metric = metrics.NChangesMetric(col='filter', metricName='Filter Changes')
    plotDict = {'ylabel': 'Number of Filter Changes'}
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': 'Number of filter changes per night.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    metric = metrics.MinTimeBetweenStatesMetric(changeCol='filter')
    plotDict = {'yMin': 0, 'yMax': 120}
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': 'Minimum time between filter changes, in minutes.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    cutoff = 10
    metric = metrics.NStateChangesFasterThanMetric(changeCol='filter', cutoff=cutoff)
    plotDict = {}
    caption = 'Number of filter changes, where the time between filter changes is shorter '
    caption += 'than %.1f minutes, per night.' % (cutoff)
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': caption}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    metric = metrics.NStateChangesFasterThanMetric(changeCol='filter', cutoff=20)
    plotDict = {}
    caption = 'Number of filter changes, where the time between filter changes '
    caption += 'is shorter than 20 minutes, per night.'
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': caption}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    metric = metrics.MaxStateChangesWithinMetric(changeCol='filter', timespan=10)
    plotDict = {}
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': 'Max number of filter changes within a window of 10 minutes, per night.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    metric = metrics.MaxStateChangesWithinMetric(changeCol='filter', timespan=20)
    plotDict = {}
    displayDict = {'group': filterGroup, 'subgroup': 'Per Night',
                   'caption': 'Max number of filter changes within a window of 20 minutes, per night.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                        displayDict=displayDict, runName=runName, metadata=metadata,
                                        summaryMetrics=summaryStats)
    bundleList.append(bundle)

    # Unislicer (single number) metrics.
    slicer = slicers.UniSlicer()
    sqlconstraint = wfdWhere
    metadata = 'WFD only'
    order = 0

    metric = metrics.NChangesMetric(col='filter', metricName='Total Filter Changes')
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': 'Total filter changes over survey'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    order += 1
    metric = metrics.MinTimeBetweenStatesMetric(changeCol='filter')
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': 'Minimum time between filter changes, in minutes.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    order += 1
    metric = metrics.NStateChangesFasterThanMetric(changeCol='filter', cutoff=10)
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': 'Number of filter changes faster than 10 minutes over the entire survey.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    order += 1
    metric = metrics.NStateChangesFasterThanMetric(changeCol='filter', cutoff=20)
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': 'Number of filter changes faster than 20 minutes over the entire survey.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    order += 1
    metric = metrics.MaxStateChangesWithinMetric(changeCol='filter', timespan=10)
    caption = 'Max number of filter changes within a window of 10 minutes over the entire survey.'
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': caption}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    order += 1
    metric = metrics.MaxStateChangesWithinMetric(changeCol='filter', timespan=20)
    caption = 'Max number of filter changes within a window of 20 minutes over the entire survey.'
    displayDict = {'group': filterGroup, 'subgroup': 'Whole Survey', 'order': order,
                   'caption': caption}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)
    order += 1

    # Calculate some basic summary info about run, per filter, per proposal and for all proposals.
    propOrder = 0
    prop = "WFD"
    slicer = slicers.UniSlicer()
    for f in filters + ['all']:
        if f != 'all':
            order = filtorder[f] * 100
            sqlconstraint = 'filter = "%s" and' % (f)
        else:
            order = 1000
            sqlconstraint = ''
        subgroup = 'WFD'
        sqlconstraint = sqlconstraint + ' %s' % (wfdWhere)
        metadata = '%s band, WFD' % (f)

        col = 'moonDistance'
        group = distanceGroup

        metric = metrics.MedianMetric(col=col)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.MeanMetric(col=col)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.RmsMetric(col=col)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.NoutliersNsigmaMetric(
            col=col, metricName='N(-3Sigma) %s' % (col), nSigma=-3.)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.NoutliersNsigmaMetric(col=col, metricName='N(+3Sigma) %s' % (col), nSigma=3.)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.CountMetric(col=col, metricName='Count %s' % (col))
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.PercentileMetric(col=col, percentile=25)
        displayDict = {'group': group, 'subgroup': subgroup,
                       'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.PercentileMetric(col=col, percentile=50)
        displayDict = {'group': group, 'subgroup': subgroup,
                       'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1
        metric = metrics.PercentileMetric(col=col, percentile=75)
        displayDict = {'group': group, 'subgroup': subgroup, 'order': order}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1

    # Calculate summary slew statistics.
    slicer = slicers.UniSlicer()
    sqlconstraint = ''
    metadata = 'All Visits'
    # Mean Slewtime
    metric = metrics.MeanMetric(col='slewTime')
    displayDict = {'group': slewGroup, 'subgroup': 'Summary', 'order': 1,
                   'caption': 'Mean slew time in seconds.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)
    # Median Slewtime
    metric = metrics.MedianMetric(col='slewTime')
    displayDict = {'group': slewGroup, 'subgroup': 'Summary', 'order': 2,
                   'caption': 'Median slew time in seconds.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)
    # Mean exposure time
    metric = metrics.MeanMetric(col='visitExposureTime')
    displayDict = {'group': slewGroup, 'subgroup': 'Summary', 'order': 3,
                   'caption': 'Mean visit on-sky time, in seconds.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)
    # Mean visit time
    metric = metrics.MeanMetric(col='visitTime')
    displayDict = {'group': slewGroup, 'subgroup': 'Summary', 'order': 4,
                   'caption':
                   'Mean total visit time (including readout and shutter), in seconds.'}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    # Count total nvisits
    sqlconstraint = ''
    slicer = slicers.UniSlicer()
    metadata = 'All Visits'

    metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
    summaryMetrics = [metrics.IdentityMetric(metricName='Count')]
    displayDict = {'group': summaryGroup, 'subgroup': '0: NVisits', 'order': 0}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    # and total exposure time
    sqlconstraint = ''
    slicer = slicers.UniSlicer()
    metadata = 'All Visits'

    metric = NormSumMetric(col='visitExposureTime', normValue=3600*24,
                           metricName='Exp. Time (days)')
    summaryMetrics = [metrics.IdentityMetric(metricName='days')]
    displayDict = {'group': summaryGroup, 'subgroup': '1: Exp. Time', 'order': 1}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    # Count the total exposure time per proposal, for all proposals
    order = 2
    slicer = slicers.UniSlicer()
    for propid in propids:
        sqlconstraint = '%s = %s' % (propCol, propid)
        metadata = '%s' % (propids[propid])

        metric = metrics.CountMetric(col='observationStartMJD', metricName='NVisits')
        summaryMetrics = [metrics.IdentityMetric(metricName='Count')]
        displayDict = {'group': summaryGroup, 'subgroup': '0: NVisits', 'order': order,
                       'caption': 'Number of visits for %s proposal.'
                       % (propids[propid])}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            summaryMetrics=summaryMetrics,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1

        metric = NormSumMetric(col="visitExposureTime", normValue=3600*24,
                               metricName="Exp. Time (days)")
        summaryMetrics = [metrics.IdentityMetric(metricName='days')]
        displayDict = {'group': summaryGroup, 'subgroup': '1: Exp. Time', 'order': order,
                       'caption': 'Total exposure time for %s proposal (days).'
                       % (propids[propid])}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                            summaryMetrics=summaryMetrics,
                                            displayDict=displayDict, runName=runName,
                                            metadata=metadata)
        bundleList.append(bundle)

        order += 1

    # Count total number of nights
    metadata = "All Visits"
    sqlconstraint = ''
    metric = metrics.CountUniqueMetric(col='night', metricName='Nights with observations')
    summaryMetrics = [metrics.IdentityMetric(metricName='(days)')]
    displayDict = {'group': summaryGroup, 'subgroup': '2: On-sky Time', 'order': 1}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    metric = metrics.FullRangeMetric(col='night', metricName='Total nights in survey')
    summaryMetrics = [metrics.ZeropointMetric(zp=1, metricName='(days)')]
    displayDict = {'group': summaryGroup, 'subgroup': '2: On-sky Time', 'order': 0}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    metric = metrics.TeffMetric(metricName='Total effective time of survey')
    summaryMetrics = [metrics.NormalizeMetric(normVal=24.0 * 60.0 * 60.0, metricName='(days)')]
    displayDict = {'group': summaryGroup, 'subgroup': '2: On-sky Time', 'order': 3}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    metric = metrics.TeffMetric(metricName='Normalized total effective time of survey', normed=True)
    summaryMetrics = [metrics.IdentityMetric(metricName='(fraction)')]
    displayDict = {'group': summaryGroup, 'subgroup': '2: On-sky Time', 'order': 2}
    bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint,
                                        summaryMetrics=summaryMetrics,
                                        displayDict=displayDict, runName=runName, metadata=metadata)
    bundleList.append(bundle)

    # Check the Alt-Az pointing history
    slicer = slicers.HealpixSlicer(nside=nside, latCol='zenithDistance', lonCol='azimuth', useCache=False)
    metric = metrics.CountMetric('observationStartMJD', metricName='NVisits Alt/Az')
    plotDict = {'rot': (0, 90, 0)}
    plotFunc = plots.HealpixSkyMap()
    for f in filters:
        sqlconstraint = 'filter = "%s"' % (f)
        displayDict = {'group': hourangleGroup, 'order': filtorder[f],
                       'caption':
                       'Pointing History on the alt-az sky (zenith center) for filter %s' % f}
        bundle = metricBundles.MetricBundle(metric, slicer, sqlconstraint, plotDict=plotDict,
                                            runName=runName,
                                            plotFuncs=[plotFunc], displayDict=displayDict)
        bundleList.append(bundle)
    displayDict = {'group': hourangleGroup, 'subgroup': 'All Filters',
                   'caption':
                   'Pointing History on the alt-az sky (zenith center), all filters'}
    bundle = metricBundles.MetricBundle(metric, slicer, '', plotDict=plotDict, runName=runName,
                                        plotFuncs=[plotFunc], displayDict=displayDict)
    bundleList.append(bundle)

    # Solar elongation
    sqls = ['filter = "%s"' % f for f in filters]
    orders = [filtorder[f] for f in filters]
    sqls.append('')
    orders.append(0)
    for sql, order in zip(sqls, orders):
        plotFuncs = [plots.HealpixSkyMap(), plots.HealpixHistogram()]
        displayDict = {'group': distanceGroup, 'subgroup': 'Solar Elongation',
                       'caption': 'Median solar elongation in degrees', 'order': order}
        metric = metrics.MedianMetric('solarElong')
        slicer = slicers.HealpixSlicer(nside=nside, lonCol=lonCol, latCol=latCol)
        bundle = metricBundles.MetricBundle(metric, slicer, sql, displayDict=displayDict,
                                            plotFuncs=plotFuncs, runName=runName)
        bundleList.append(bundle)

    return (metricBundles.makeBundlesDictFromList(bundleList), mergedHistDict)

if __name__ == "__main__":

    parser = argparse.ArgumentParser(
        description='Python script to run MAF with the scheduler validation metrics')
    parser.add_argument('dbFile', type=str, default=None, help="full file path to the opsim sqlite file")

    parser.add_argument("--outDir", type=str, default='./Out', help='Output directory for MAF outputs.')

    parser.add_argument('--benchmark', type=str, default='design',
                        help="Can be 'design' or 'requested'")

    parser.add_argument('--plotOnly', dest='plotOnly', action='store_true',
                        default=False, help="Reload the metric values and re-plot them.")

    parser.add_argument('--skipSlew', dest='skipSlew', action='store_true',
                        default=False, help='Skip calculation of slew statistics')
    parser.add_argument("--runName", type=str, help="Name of the run", default=None)

    parser.set_defaults()
    args, extras = parser.parse_known_args()

    resultsDb = db.ResultsDb(outDir=args.outDir)
    opsdb = db.OpsimDatabaseV4(args.dbFile)

    (bundleDict, mergedHistDict) = makeBundleList(args.dbFile, runName=args.runName, benchmark=args.benchmark)

    group = metricBundles.MetricBundleGroup(bundleDict, opsdb, outDir=args.outDir, resultsDb=resultsDb)
    if args.plotOnly:
        group.readAll()
    else:
        group.runAll()
    group.plotAll()

    for key in mergedHistDict:
        if len(mergedHistDict[key].bundleList) > 0:
            mergedHistDict[key].percentileLegend()
            mergedHistDict[key].incrementPlotOrder()
            mergedHistDict[key].plot(outDir=args.outDir, resultsDb=resultsDb, closeFigs=True)
        else:
            warnings.warn('Empty bundleList for %s, skipping merged histogram' % key)

    utils.writeConfigs(opsdb, args.outDir)
    opsdb.close()


"""Wallet base class."""

from abc import ABC, abstractmethod
from typing import List, Sequence, Tuple, Union

from ..ledger.base import BaseLedger
from ..ledger.endpoint_type import EndpointType
from .error import WalletError

from .did_info import DIDInfo, KeyInfo
from .key_type import KeyType
from .did_method import SOV, DIDMethod

class BaseWallet(ABC):
    """Abstract wallet interface."""

    @abstractmethod
    async def create_signing_key(
        self, key_type: KeyType, seed: str = None, metadata: dict = None
    ) -> KeyInfo:
        """Create a new public/private signing keypair.

        Args:
            key_type: Key type to create
            seed: Optional seed allowing deterministic key creation
            metadata: Optional metadata to store with the keypair

        Returns:
            A `KeyInfo` representing the new record

        """

    @abstractmethod
    async def get_signing_key(self, verkey: str) -> KeyInfo:
        """Fetch info for a signing keypair.

        Args:
            verkey: The verification key of the keypair

        Returns:
            A `KeyInfo` representing the keypair

        """

    @abstractmethod
    async def replace_signing_key_metadata(self, verkey: str, metadata: dict):
        """Replace the metadata associated with a signing keypair.

        Args:
            verkey: The verification key of the keypair
            metadata: The new metadata to store

        """

    @abstractmethod
    async def rotate_did_keypair_start(self, did: str, next_seed: str = None) -> str:
        """Begin key rotation for DID that wallet owns: generate new keypair.

        Args:
            did: signing DID
            next_seed: seed for incoming ed25519 key pair (default random)

        Returns:
            The new verification key

        Raises:
            WalletNotFoundError: if wallet does not own DID

        """

    @abstractmethod
    async def rotate_did_keypair_apply(self, did: str) -> None:
        """Apply temporary keypair as main for DID that wallet owns.

        Args:
            did: signing DID

        Raises:
            WalletNotFoundError: if wallet does not own DID
            WalletError: if wallet has not started key rotation

        """

    @abstractmethod
    async def create_local_did(
        self,
        method: DIDMethod,
        key_type: KeyType,
        seed: str = None,
        did: str = None,
        metadata: dict = None,
    ) -> DIDInfo:
        """Create and store a new local DID.

        Args:
            method: The method to use for the DID
            key_type: The key type to use for the DID
            seed: Optional seed to use for DID
            did: The DID to use
            metadata: Metadata to store with DID

        Returns:
            The created `DIDInfo`

        """

    async def create_public_did(
        self,
        method: DIDMethod,
        key_type: KeyType,
        seed: str = None,
        did: str = None,
        metadata: dict = {},
    ) -> DIDInfo:
        """Create and store a new public DID.

        Args:
            seed: Optional seed to use for DID
            did: The DID to use
            metadata: Metadata to store with DID

        Returns:
            The created `DIDInfo`

        """
        metadata = metadata or {}
        metadata.setdefault("posted", True)
        did_info = await self.create_local_did(
            method=method, key_type=key_type, seed=seed, did=did, metadata=metadata
        )
        return await self.set_public_did(did_info)

    @abstractmethod
    async def get_public_did(self) -> DIDInfo:
        """Retrieve the public DID.

        Returns:
            The currently public `DIDInfo`, if any

        """

    @abstractmethod
    async def set_public_did(self, did: Union[str, DIDInfo]) -> DIDInfo:
        """Assign the public DID.

        Returns:
            The updated `DIDInfo`

        """

    @abstractmethod
    async def get_local_dids(self) -> Sequence[DIDInfo]:
        """Get list of defined local DIDs.

        Returns:
            A list of `DIDInfo` instances

        """

    @abstractmethod
    async def get_local_did(self, did: str) -> DIDInfo:
        """Find info for a local DID.

        Args:
            did: The DID for which to get info

        Returns:
            A `DIDInfo` instance for the DID

        """

    @abstractmethod
    async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo:
        """Resolve a local DID from a verkey.

        Args:
            verkey: Verkey for which to get DID info

        Returns:
            A `DIDInfo` instance for the DID

        """

    @abstractmethod
    async def replace_local_did_metadata(self, did: str, metadata: dict):
        """Replace the metadata associated with a local DID.

        Prefer `set_did_endpoint()` to set endpoint in metadata.

        Args:
            did: DID for which to replace metadata
            metadata: The new metadata

        """

    async def get_posted_dids(self) -> Sequence[DIDInfo]:
        """Get list of defined posted DIDs.

        Returns:
            A list of `DIDInfo` instances

        """
        return [
            info for info in await self.get_local_dids() if info.metadata.get("posted")
        ]

    async def set_did_endpoint(
        self,
        did: str,
        endpoint: str,
        _ledger: BaseLedger,
        endpoint_type: EndpointType = None,
        write_ledger: bool = True,
        endorser_did: str = None,
        routing_keys: List[str] = None,
    ):
        """Update the endpoint for a DID in the wallet, send to ledger if posted.

        Args:
            did: DID for which to set endpoint
            endpoint: the endpoint to set, None to clear
            ledger: the ledger to which to send endpoint update if
                DID is public or posted
            endpoint_type: the type of the endpoint/service. Only endpoint_type
                'endpoint' affects local wallet
        """
        did_info = await self.get_local_did(did)

        if did_info.method != SOV:
            raise WalletError("Setting DID endpoint is only allowed for did:sov DIDs")
        metadata = {**did_info.metadata}
        if not endpoint_type:
            endpoint_type = EndpointType.ENDPOINT
        if endpoint_type == EndpointType.ENDPOINT:
            metadata[endpoint_type.indy] = endpoint

        await self.replace_local_did_metadata(did, metadata)

    @abstractmethod
    async def sign_message(
        self, message: Union[List[bytes], bytes], from_verkey: str
    ) -> bytes:
        """Sign message(s) using the private key associated with a given verkey.

        Args:
            message: The message(s) to sign
            from_verkey: Sign using the private key related to this verkey

        Returns:
            The signature

        """

    @abstractmethod
    async def verify_message(
        self,
        message: Union[List[bytes], bytes],
        signature: bytes,
        from_verkey: str,
        key_type: KeyType,
    ) -> bool:
        """Verify a signature against the public key of the signer.

        Args:
            message: The message to verify
            signature: The signature to verify
            from_verkey: Verkey to use in verification
            key_type: The key type to derive the signature verification algorithm from

        Returns:
            True if verified, else False

        """

    @abstractmethod
    async def pack_message(
        self, message: str, to_verkeys: Sequence[str], from_verkey: str = None
    ) -> bytes:
        """Pack a message for one or more recipients.

        Args:
            message: The message to pack
            to_verkeys: The verkeys to pack the message for
            from_verkey: The sender verkey

        Returns:
            The packed message

        """

    @abstractmethod
    async def unpack_message(self, enc_message: bytes) -> Tuple[str, str, str]:
        """Unpack a message.

        Args:
            enc_message: The encrypted message

        Returns:
            A tuple: (message, from_verkey, to_verkey)

        """

    def __repr__(self) -> str:
        """Get a human readable string."""
        return "<{}>".format(self.__class__.__name__)


import pdb

from evac.datafiles.wrfout import WRFOut

class DomainGrid:
    """ Create a common class for verification domains.

    Args:
        inherit: dynamic inheritance depending on the
            domain that is being passed in.
    """
    def __init__(self,inherit=None):
        if isinstance(inherit,WRFOut):
            self.__dict__ = inherit.__dict__.copy()
        elif isinstance(inherit,WRF_native_grid):
            self.__dict__ = inherit.__dict__.copy()
        elif isinstance(inherit,VerifGrid):
            self.__dict__ = inherit.__dict__.copy()
        elif isinstance(inherit,str):
            inherit = WRFOut(inherit)
            self.__dict__ = inherit.__dict__.copy()

            # self.lat_0 = inherit.nc.CEN_LAT
            # self.lon_0 = inherit.nc.CEN_LON
        else:
            raise Exception("Inherited object is {}".format(type(inherit)))
        return


import re

import sublime

import flower
from .core import Runner
from .config import configRoot, allNormalizedIncludes, CONFIG_PATH
from ..pathutils import getParentName

DEFINITION_REGEX = re.compile(
    r"^.*\((?P<path>.*?):(?P<line>\d+)@(?P<bytes>\d+\-\d+)\)\s*\=\s*(?P<msg>[\w\W]+)",
    re.MULTILINE
)
# Old version, changed 26.02.2020
# r"(?P<path>.*?):(?P<line>\d+):(?P<col>\d+):(?P<msg>[\w\W]+)(?=\d+\.\d+s)",
DEFINITIONC_REGEX = re.compile(
    r"(?P<path>.*?):(?P<line>\d+):(?:\d+:){3}\s*(?P<msg>.+)\s*",
    re.MULTILINE
)

# father of all compilers
class Flow:
    cmd = ("flow",)
    config = {}
    configpath = None
    defn_regex = DEFINITION_REGEX

    def __str__(self):
        return "{} ({})".format(
            self.__class__.__name__,
            getParentName(self.configpath, depth=3)
        )

    def __init__(self, config):
        self.config = config
        self.configpath = config.pop(CONFIG_PATH, None)

    # "private" names that aren't listed as actions are "__"-suffixed

    @classmethod
    def isOld__(cls):
        return cls == Flow

    @classmethod
    def name__(cls, action):
        f = '{}.{}'.format(cls.__name__, action)
        return f if cls.isOld__() else '{} ({})'.format(f, Flow.__name__)

    @staticmethod
    def formatIncludes__(includes):
        """Return list of includes formatted for command"""
        return tuple(map("-I {}".format, includes))

    @classmethod
    def findDefinition__(cls):
        """Find definition"""
        return Runner(
            name=cls.name__('finddef'),
            cmd=Flow.cmd,
            options=(
                "--find-definition", "{token}",
            ),
            after='find'
        )

    def linter__(self, path):
        """Return linter command for SublimeLinter"""
        workdir = configRoot(self.configpath, root=True)
        includes = allNormalizedIncludes(path, configpath=self.configpath)
        linterpath = flower.SETTINGS.get("old_linter_path", "") or "{flowpath}/flowtools/bin/{platform}/flow"
        cmd = linterpath + " --sublime --root {workdir} {includes} $file_on_disk"
        return cmd.format(
            flowpath=flower.PATH['flow'],
            platform=sublime.platform().replace('osx', 'mac'),
            includes=' '.join(Flow.formatIncludes__(includes)),
            workdir=workdir,
        ).replace('\\', '/') # sublimelinter requires forward slashes

    # public names without underscore are treated as actions in manager.py

    @classmethod
    def compile(cls):
        """Compile/check specified flow file. Do not run"""
        return Runner(
            name=cls.name__('compile'),
            cmd=Flow.cmd,
            options=(
                "--compile", "{workpath}.bytecode",
                "--debuginfo", "{workpath}.debug",
            )
        )

    @classmethod
    def js(cls):
        """Compile specified file into js. Open in browser when done"""
        return Runner(
            name=cls.name__('js'),
            cmd=Flow.cmd,
            options=(
                "--js {binaryfolder}/{name}.js",
            ),
            after='web'
        )

    @classmethod
    def js_debug(cls):
        """Same as js, but compile debuggable js"""
        runner = cls.js()
        runner.options += (
            "--debug",
        )
        return runner

    @classmethod
    def cpp(cls):
        """Compile & run with byterunner"""
        runner = cls.compile()
        runner.after = 'cpp'
        return runner

    @classmethod
    def debug(cls):
        """Compile & run with byterunner in debug mode for SublimeGDB"""
        runner = cls.compile()
        runner.after = 'gdb'
        return runner

class Flowc(Flow):
    # We do it to be compliant with flowcpp behaviour. In future this may be removed
    cmd = ("flowc1",)
    defn_regex = DEFINITIONC_REGEX

    @staticmethod
    def formatIncludes__(includes):
        return includes and ("I={}".format(','.join(includes)),)

    @classmethod
    def findDefinition__(cls):
        return Runner(
            name=cls.name__('finddef'),
            cmd=cls.cmd,
            options=(
                "find-definition={token}",
            ),
            after='find'
        )

    def linter__(self, path=None):
        return "{exec} lint=1 $file_on_disk".format(exec=' '.join(self.cmd)).replace('\\', '/')

    # public

    @classmethod
    def compile(cls):
        return Runner(
            name=cls.name__('compile'),
            cmd=cls.cmd,
            options=(
                "bytecode={workpath}.bytecode",
                "debug=1",
            )
        )

    @classmethod
    def js(cls):
        return Runner(
            name=cls.name__('js'),
            cmd=cls.cmd,
            options=(
                "js={binaryfolder}/{name}.js",
            ),
            after='web'
        )

    @classmethod
    def js_debug(cls):
        runner = cls.js()
        runner.options += ("debug=1",)
        return runner

    @classmethod
    def cpp(cls):
        return super().cpp()

    @classmethod
    def debug(cls):
        return super().debug()


from discord.ext import commands, tasks
import os
import discord
from datetime import datetime

class Timecards(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

        from dotenv import load_dotenv
        load_dotenv()
        self.url = os.environ.get('TIMECARD_URL')

    async def send_timecard(self):
        for guild in self.bot.guilds:
            if guild.system_channel is not None:
                view = discord.ui.View()
                item = discord.ui.Button(
                    style=discord.ButtonStyle.gray, label="Link", url=f"https://{self.url}.com")
                view.add_item(item=item)

                await guild.system_channel.send("@everyone **Timecard Reminder!**", view=view)

    @tasks.loop(seconds=60)
    async def minute_finder(self):
        if datetime.now().minute == 0:
            self.hour_finder.start()

    @tasks.loop(hours=1)
    async def hour_finder(self):
        self.minute_finder.stop()
        self.minute_finder.cancel()
        if datetime.now().hour == 17 and datetime.now().weekday() == 4:
            await self.send_timecard()

    @commands.Cog.listener()
    async def on_ready(self):
        self.minute_finder.start()

    @commands.command(name="timecard")
    @commands.is_owner()
    async def timecard(self, ctx):
        await self.send_timecard()

async def setup(bot):
    await bot.add_cog(Timecards(bot))


import sys
import torch
import argparse
import time
import ot

import numpy as np
import torch.nn as nn
import torch.nn.functional as F

from tqdm.auto import trange

sys.path.append("../lib")
from hsw import hyper_sliced_wasserstein
from hhsw import horo_hyper_sliced_wasserstein_poincare, horo_hyper_sliced_wasserstein_lorentz
from sw import sliced_wasserstein
from distributions import sampleWrappedNormal
from utils_hyperbolic import *

parser = argparse.ArgumentParser()
parser.add_argument("--ntry", type=int, default=20, help="number of restart")
parser.add_argument("--pbar", action="store_true", help="If yes, plot pbar")
args = parser.parse_args()

device = "cuda" if torch.cuda.is_available() else "cpu"

mu0 = torch.tensor([1,0,0], dtype=torch.float, device=device)
Sigma0 = torch.eye(2, dtype=torch.float, device=device)

ntry = args.ntry

ds = [3, 100]
samples = [int(1e2),int(1e3),int(1e4),int(1e5/2),int(1e5)] #,int(1e6/2)]
projs = [200]

L_hsw = np.zeros((len(ds), len(projs), len(samples), ntry))
L_w = np.zeros((len(ds), len(samples), ntry))
L_s = np.zeros((len(ds), len(samples), ntry))

L_hhswp = np.zeros((len(ds), len(projs), len(samples), ntry))
L_hhswl = np.zeros((len(ds), len(projs), len(samples), ntry))
L_swl = np.zeros((len(ds), len(projs), len(samples), ntry))
L_swp = np.zeros((len(ds), len(projs), len(samples), ntry))

if __name__ == "__main__":
    for i, d in enumerate(ds):
        for k, n_samples in enumerate(samples):
            x0 = sampleWrappedNormal(mu0, Sigma0, n_samples)
            x1 = sampleWrappedNormal(mu0, Sigma0, n_samples)

            if args.pbar:
                bar = trange(ntry)
            else:
                bar = range(ntry)

            for j in bar:
                for l, n_projs in enumerate(projs):
                    try:
                        t0 = time.time()
                        hsw = hyper_sliced_wasserstein(x0, x1, n_projs, device, p=2)
                        L_hsw[i, l, k, j] = time.time()-t0
                    except:
                        L_hsw[i,l,k,j] = np.inf

                    # try:
                    x0_p = lorentz_to_poincare(x0)
                    x1_p = lorentz_to_poincare(x1)
                    t0 = time.time()
                    hhsw = horo_hyper_sliced_wasserstein_poincare(x0_p, x1_p, n_projs, device, p=2)
                    L_hhswp[i,l,k,j] = time.time()-t0
                    # except:
                    #     L_hhswp[i,l,k,j] = np.inf

                    try:
                        t0 = time.time()
                        hsw = horo_hyper_sliced_wasserstein_lorentz(x0, x1, n_projs, device, p=2)
                        L_hhswl[i, l, k, j] = time.time()-t0
                    except:
                        L_hhswl[i,l,k,j] = np.inf

                    try:
                        t0 = time.time()
                        swl = sliced_wasserstein(x0, x1, n_projs, device, p=2)
                        L_swl[i, l, k, j] = time.time()-t0
                    except:
                        L_swl[i,l,k,j] = np.inf

                    try:
                        x0_p = lorentz_to_poincare(x0)
                        x1_p = lorentz_to_poincare(x1)
                        t0 = time.time()
                        swp = sliced_wasserstein(x0_p, x1_p, n_projs, device, p=2)
                        L_swp[i,l,k,j] = time.time()-t0
                    except:
                        L_swp[i,l,k,j] = np.inf

                try:
                    t2 = time.time()

                    a = torch.ones((n_samples,), device=device)/n_samples
                    b = torch.ones((n_samples,), device=device)/n_samples
                    M = torch.arccosh(torch.clamp(-minkowski_ip2(x0, x1), min=1+1e-5))**2
                    w = ot.sinkhorn2(a, b, M, reg=1, numitermax=10000, stopThr=1e-15)

                    L_s[i,k,j] = time.time()-t2
                except:
                    L_s[i,k,j] = np.inf

                try:
                    t1 = time.time()

                    a = torch.ones((n_samples,), device=device)/n_samples
                    b = torch.ones((n_samples,), device=device)/n_samples
                    M = torch.arccosh(torch.clamp(-minkowski_ip2(x0, x1), min=1+1e-5))**2
                    w = ot.emd2(a, b, M)

                    L_w[i,k,j] = time.time()-t1
                except:
                    L_w[i,k,j] = np.inf

    for d in ds:
        for l, n_projs in enumerate(projs):
            np.savetxt("./Comparison_HSW_projs_"+str(n_projs)+"_d"+str(d), L_hsw[0, l])
            np.savetxt("./Comparison_HHSWp_projs_"+str(n_projs)+"_d"+str(d), L_hhswp[0, l])
            np.savetxt("./Comparison_HHSWl_projs_"+str(n_projs)+"_d"+str(d), L_hhswl[0, l])
            np.savetxt("./Comparison_SWp_projs_"+str(n_projs)+"_d"+str(d), L_swp[0, l])
            np.savetxt("./Comparison_SWl_projs_"+str(n_projs)+"_d"+str(d), L_swl[0, l])

        np.savetxt("./Comparison_SW_W_d"+str(d), L_w[0])
        np.savetxt("./Comparison_SW_Sinkhorn_d"+str(d), L_s[0])


#!/usr/bin/python3

x = 15
price = 9.99
discount = 0.2
result = price * (1 - discount)
print(result)

name = "Rolf"
print(name)

name = "Bob"
#greeting = "Hello, " + name
greeting = f'Hello, {name}'
print(greeting)

longer_phrase = "Hello {}, today is {}."
formatted = longer_phrase.format("Rolf", "Monday")
print(formatted)

name = input('What is your name? ')
print('Hi ' + name + ', you smell bad.')

size_input = input('How big is your house (in square feet): ')
square_feet = int(size_input)
square_meters = square_feet / 10.8
print(f"{square_feet} square feet is {square_meters:.2f} square meters.")

user_age = input('What is your age: ')
years = int(user_age)
months = years * 12
print(f"Your age is {years} which is equal to {months} months.")


import pygame
import sys
import random
from game import assets, controls, display, snake_services

# Initialize program
pygame.init()

# describing snake
tile_size = 20
snake_size = 5
snake_color = assets.colors['GREEN']
direction = controls.controllers['LEFT']
position_x = display.display_size / 2
position_y = display.display_size / 2

snake = snake_services.create_snake(
    tile_size, snake_size, direction, position_x, position_y)

# snake state
dead = False

# describing the background
background_color = assets.colors['BLACK']

# describing directions
snake_directions = {
    controls.controllers['LEFT']:  (-tile_size, 0),
    controls.controllers['RIGHT']: (tile_size, 0),
    controls.controllers['UP']:    (0, -tile_size),
    controls.controllers['DOWN']:  (0, tile_size)
}

# defines the limits where the snake will be drawn.
def check_for_borders(pos):
    if pos < display.score_size:
        return pos + display.display_size
    elif pos >= display.score_size + display.display_size:
        return pos - display.display_size
    else:
        return pos

# points controller
points = 0
frame_counter = 0
food_spawn_interval = 10
food_pos = (None, None)

# Beginning Main Game Loop
while True:
    pygame.time.delay(50)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN and event.key in controls.controllers.values() and controls.forbidden_turns[direction] != event.key:
            direction = event.key
            (x, y, old_direction) = snake[0]
            snake[0] = (x, y, direction)  # change the direction of the head.

    # reset the screen
    display.DISPLAY.fill(background_color)

    # Check for colisions between head and body.
    if snake_services.check_collision(snake) or dead:
        dead = True
        display.callText(f'{points} pontos')

    # Check for incrising the snake size - collision with food - spawn food.
    if frame_counter < food_spawn_interval:
        if food_pos != (None, None):
            (x_food_pos, y_food_pos) = food_pos
            snake_services.draw_snake_tile(
                display.DISPLAY, snake_color, x_food_pos, y_food_pos, tile_size)

        frame_counter += 1

    else:
        frame_counter = 0
        if food_pos == (None, None):
            (x_rand, y_rand) = snake_services.spawn_food(
                tile_size, snake, food_pos)
            food_pos = (x_rand, y_rand)
            snake_services.draw_snake_tile(
                display.DISPLAY, snake_color, x_rand, y_rand, tile_size)

    if food_pos != (None, None) and snake_services.isInSnake(snake, food_pos):
        snake_services.add_tile(snake, snake_directions)
        snake_size += 1
        points += frame_counter + 1 #that's a game design decision :)
        food_pos = (None, None)

    # start computing movement

    if not dead:
        display.callText(f'{points}',display.score_size // 2, display.score_size // 2)
        for i in range(snake_size-1, -1, -1):
            (x, y, direction) = snake[i]
            (move_x, move_y) = snake_directions[direction]

            new_pos_x = check_for_borders(x + move_x)
            new_pos_y = check_for_borders(y + move_y)

            # draw on new position.
            snake_services.draw_snake_tile(
                display.DISPLAY, snake_color, new_pos_x, new_pos_y, tile_size)

            if i == 0:  # change all directions, except for the head. update all positions.
                snake[i] = (new_pos_x, new_pos_y, direction)
            else:
                (next_x, next_y, next_direction) = snake[i-1]
                snake[i] = (new_pos_x, new_pos_y, next_direction)
    elif frame_counter % 3 == 0:
        for i in snake:
            (x, y, direction) = i
            snake_services.draw_snake_tile(
                display.DISPLAY, snake_color, x, y, tile_size)

    display.stage()
    pygame.display.update()

    display.FramePerSec


#
# File: pem_test.py
#

import torch
import numpy as np
from ceem.smoother import *
from ceem.learner import *
from ceem.systems import LorenzAttractor
from ceem.dynamics import *
from ceem import utils
from ceem.particleem import *

from ceem import logger

def test_particleem():

    utils.set_rng_seed(1)

    torch.set_default_dtype(torch.float64)

    # setup system
    sigma = torch.tensor([10.])
    rho = torch.tensor([28.])
    beta = torch.tensor([8. / 3.])

    C = torch.randn(2, 3)

    dt = 0.04

    sys = LorenzAttractor(sigma, rho, beta, C, dt, method='midpoint')

    # simulate
    B = 2
    T = 20
    xs = [torch.randn(B, 1, 3)]
    for t in range(T - 1):
        xs.append(sys.step(torch.tensor([0.] * B), xs[-1]))

    xtr = torch.cat(xs, dim=1).detach()
    y = sys.observe(0., xtr).detach()
    # y += torch.rand_like(y) * 0.01

    t = torch.stack([torch.arange(T)] * B).to(torch.get_default_dtype())

    #
    params = list(sys.parameters())
    vparams = parameters_to_vector(params)
    true_vparams = vparams.clone()
    vparams *= 1. + (torch.rand_like(vparams) - 0.5) * 2 * 0.025
    vector_to_parameters(vparams, params)

    Px0 = torch.eye(3)
    Q = 0.1 * torch.eye(3)
    R = 0.1 * torch.eye(2)

    Np = 300

    fapf = faPF(Np, sys, Q, R, Px0)

    def callback(epoch):
        pass

    trainer = SAEMTrainer(fapf, y,
        gamma_sched=lambda x: 0.8,
        xlen_cutoff = 3,
        max_k=10,
        )
    trainer.train(params, callbacks=[callback])

    assert True
    if True:
        print('Passed.')
    else:
        print('Failed.')

if __name__ == '__main__':
    test_particleem()


currentUsers = ['folgee', 'doom', 'quake', 'cartman', 'stanTheMan', 'Ben Dove']

newUsers = ['batman', 'wonder woman', 'doOm', 'STANtheMaN', 'V']

for newUser in newUsers:
    if newUser.lower() in currentUsers:
        print(f"Sorry, {newUser} that user name is already taken, please enter a new name")
    else:
        print(f"Username  '{newUser}' is available, you may continue")


#!/usr/bin/env python3
"""
BIP39 Mnemonic Phrase Generator and Verifier

Secure Coding Principles and Practices (PA193)  https://is.muni.cz/course/fi/autumn2019/PA193?lang=en
Faculty of Informatics (FI)                     https://www.fi.muni.cz/index.html.en
Masaryk University (MU)                         https://www.muni.cz/en

Team Slytherin: @sobuch, @lsolodkova, @mvondracek.

2019
"""
from os import path

from setuptools import setup

__author__ = 'Team Slytherin: @sobuch, @lsolodkova, @mvondracek.'

def readme():
    with open(path.join(path.dirname(path.abspath(__file__)), 'README.md')) as f:
        return f.read()

setup(
    name='pa193mnemonicslytherin',
    version='0.1.0',
    description='BIP39 Mnemonic Phrase Generator and Verifier',
    long_description=readme(),
    long_description_content_type='text/markdown',
    keywords='Bitcoin Improvement Proposal BIP39 Mnemonic Phrase Generator and Verifier Secure Coding Principles and '
             'Practices PA193 Faculty of Informatics FI Masaryk University MU MUNI FIMU FIMUNI ',
    url='https://github.com/mvondracek/PA193_mnemonic_Slytherin',
    author=__author__,

    python_requires='>=3.5, <4',
    packages=['pa193mnemonicslytherin'],
    package_data={
        'pa193mnemonicslytherin': ['*.lst'],
    },
    install_requires=[
        'sphinx',
    ],
    tests_require=[
        'coverage',
    ],
    entry_points={
        'console_scripts': [
            'mnemoniccli = pa193mnemonicslytherin.mnemoniccli:cli_entry_point',
        ]
    },
    include_package_data=True,
    zip_safe=False,

    project_urls={
        'Bug Reports': 'https://github.com/mvondracek/PA193_mnemonic_Slytherin/issues',
        'Source': 'https://github.com/mvondracek/PA193_mnemonic_Slytherin',
        'Secure Coding Principles and Practices (PA193)': 'https://is.muni.cz/course/fi/autumn2019/PA193?lang=en',
        'Faculty of Informatics (FI)': 'https://www.fi.muni.cz/index.html.en',
        'Masaryk University (MU)': 'https://www.muni.cz/en',
    },
)


from fastapi import FastAPI, Request, Form
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from datetime import datetime
import uvicorn
from pathlib import Path
import pandas as pd
import numpy as np
from data_access.location_finder.location_finder import get_details_from_ip_address
from date_parser import parseFullMonth, parseHalfMonth
from data_access.weather_api.weather_api import get_weather_types_for_date, get_weather_types_for_time, get_weather_type_for_each_row, get_weather_data_from_api, get_current_weather_from_european_cities
from data_access.city_data.city_data import get_cities_in_json_format, get_city_latitude_and_longitude_coordinates
from app.src.validation.main_validation import check_if_graph_data_is_valid
from app.src.plotting.plots import plot_weather_graph, plot_europe_temperature_map

app = FastAPI()

app.mount(
    "/static",
    StaticFiles(directory=Path(__file__).parent.parent.absolute() / "GB-World-Weather" / "app" / "src" / "static"),
    name="static",
)

BASE_DIR = Path(__file__).resolve().parent / "app" / "src"

templates = Jinja2Templates(directory=str(Path(BASE_DIR, 'templates')))

@app.get("/")
async def return_home_page(request: Request):

    # details_from_ip_address = get_details_from_ip_address()
    weather_data = get_weather_data_from_api(51.5072, -0.1275, ['temperature_2m','windspeed_10m','relativehumidity_2m','pressure_msl','cloudcover','visibility', 'precipitation_probability', 'weather_code', 'is_day'])

    weather_dataframe = pd.DataFrame(
        {'datetime': weather_data['hourly']['time'],
         'temperature': weather_data['hourly']['temperature_2m'],
         'precipitation_probability': weather_data['hourly']['precipitation_probability'],
         'cloudcover' : weather_data['hourly']['cloudcover'],
         'weather_code' : weather_data['hourly']['weather_code'],
         'is_day' : weather_data['hourly']['is_day']
         },
         index=weather_data['hourly']['time']
    )

    weather_dataframe = weather_dataframe.assign(
        date=weather_dataframe.datetime.apply(lambda x: x.split("T")[0]),
        time=weather_dataframe.datetime.apply(lambda x: x.split("T")[1]),
        weather_type=get_weather_types_for_time(weather_dataframe),
        day=weather_dataframe.datetime.apply(lambda x: x.split("T")[0].split("-")[2])
    )

    weather_types = get_weather_types_for_date(weather_dataframe, True)

    minimum_temperatures = weather_dataframe.groupby('date')['temperature'].min().tolist()
    maximum_temperatures = weather_dataframe.groupby('date')['temperature'].max().tolist()
    seven_day_forecast_dates = weather_dataframe.groupby('date')['temperature'].min().index.tolist()

    current_date = weather_data['current']['time'].split("T")[0]
    all_half_months = [parseHalfMonth(datetime.strftime(datetime.strptime(current_date, "%Y-%m-%d") + pd.Timedelta(f'{day} day'), "%Y-%m-%d")) for day in range(0, 7)]

    seven_day_forecast_weather_details = [weather_dataframe.groupby("day")[metric].apply(list).to_dict() for metric in ['time', 'weather_type', 'temperature', 'precipitation_probability']]

    return templates.TemplateResponse(
        "home.html",
        context={
            "request": request,
            # "current_location": details_from_ip_address['Location'],
            "full_month" : parseFullMonth(current_date),
            "time": weather_data['current']['time'].split("T")[1],
            "temperature": weather_data['current']['temperature_2m'],
            "cloudiness": weather_data['current']['cloudcover'],
            "pressure": weather_data['current']['pressure_msl'],
            "humidity": weather_data['current']['relativehumidity_2m'],
            "visibility": weather_data['current']['visibility'],
            "windspeed" : weather_data['current']['windspeed_10m'],
            "current_weather_type": get_weather_type_for_each_row(weather_data['current']),
            "weather_modal_box_id" : range(1, 8),
            "forecast_info" : zip(minimum_temperatures, maximum_temperatures, all_half_months, weather_types, range(1, 8)),
            "seven_day_forecast_dates" : seven_day_forecast_dates,
            "seven_day_forecast_full_dates" : [parseFullMonth(seven_day_forecast_dates[date_index]) if date_index != 0 else "today" for date_index in range(0, len(seven_day_forecast_dates))],
            "seven_day_forecast_details_time" : [value for value in seven_day_forecast_weather_details[0].values()],
            "seven_day_forecast_details_weather_type" : [value for value in seven_day_forecast_weather_details[1].values()],
            "seven_day_forecast_details_temperature" : [value for value in seven_day_forecast_weather_details[2].values()],
            "seven_day_forecast_details_precipitation" : [value for value in seven_day_forecast_weather_details[3].values()],
            "zip":zip
            }
    )

@app.get("/maps")
async def return_maps_page(request: Request):
    return templates.TemplateResponse("maps.html", context={'request' : request})

@app.get("/graphs")
async def return_graphs_page(request: Request):

    cities, cities_lng, cities_lat = get_cities_in_json_format()

    # details_from_ip_address = get_details_from_ip_address()
    # city = details_from_ip_address['Location'].split(",")[0]

    weather_data = get_weather_data_from_api(51.5072, -0.1275, ['temperature_2m'])
    graph_url = plot_weather_graph(weather_data, 'temperature_2m', 'London')

    return templates.TemplateResponse("graphs.html", context={
        'request' : request,
        'cities' : cities,
        'error_msg' : '',
        'graph_url': graph_url,
        'zip' : zip
        }
    )

@app.post("/graphs")
async def handle_graph_data(
    request: Request,
    startTime: str = Form(None),
    endTime: str = Form(None),
    city: str = Form(...),
    metric_type: str = Form(...),
):

    cities, cities_lng, cities_lat = get_cities_in_json_format()

    error_msg = check_if_graph_data_is_valid(city, startTime, endTime)
    if error_msg != "":
        return templates.TemplateResponse("graphs.html", context={"request": request, "error_msg" : error_msg, "cities" : cities})

    city_latitude, city_longitude = get_city_latitude_and_longitude_coordinates(city)

    if startTime is not None and endTime is None:
        endTime = datetime.strftime(datetime.now(), "%Y-%m-%d")

    if startTime is not None and endTime is not None:
        startTime = datetime.strptime(startTime, "%Y-%m-%d")
        endTime = datetime.strptime(endTime, "%Y-%m-%d")

    weather_data = get_weather_data_from_api(city_latitude, city_longitude, [metric_type])

    graph_url = plot_weather_graph(weather_data, metric_type, city)

    return templates.TemplateResponse("graphs.html", context={
        'request' : request,
        'cities' : cities,
        'error_msg' : '',
        'graph_url' : graph_url,
        'zip' : zip
        }
    )

@app.get("/europe_weather")
async def return_europe_weather_page(request: Request):
    european_weather = get_current_weather_from_european_cities(['temperature_2m', 'weather_code', 'is_day', 'precipitation_probability', 'relative_humidity_2m', 'cloudcover'])
    list_of_current_temperatures = [weather_dict['temperature_2m'] for weather_dict in european_weather.values()]
    list_of_max_temperatures = [weather_dict['max_temperature'] for weather_dict in european_weather.values()]
    list_of_min_temperatures = [weather_dict['min_temperature'] for weather_dict in european_weather.values()]
    list_of_precipitation_probabilities = [weather_dict['precipitation_probability'] for weather_dict in european_weather.values()]
    list_of_humidities = [weather_dict['humidity'] for weather_dict in european_weather.values()]
    list_of_cloudiness = [weather_dict['cloudiness'] for weather_dict in european_weather.values()]

    map_url = plot_europe_temperature_map(european_weather)

    return templates.TemplateResponse("europe_weather.html", context={
        'request' : request,
        'european_weather' : zip(
            european_weather.keys(),
            list_of_current_temperatures,
            list_of_max_temperatures,
            list_of_min_temperatures,
            list_of_precipitation_probabilities,
            ),
        'map_url' : map_url,
        'highest_temperature' : [list(european_weather.keys())[np.array(list_of_current_temperatures).argmax()], max(list_of_current_temperatures)],
        'lowest_temperature' : [list(european_weather.keys())[np.array(list_of_current_temperatures).argmin()], min(list_of_current_temperatures)],
        'highest_precipitation' : [list(european_weather.keys())[np.array(list_of_precipitation_probabilities).argmax()], max(list_of_precipitation_probabilities)],
        'lowest_precipitation' : [list(european_weather.keys())[np.array(list_of_precipitation_probabilities).argmin()], min(list_of_precipitation_probabilities)],
        'highest_cloudiness' : [list(european_weather.keys())[np.array(list_of_cloudiness).argmax()], max(list_of_cloudiness)],
        'lowest_cloudiness' : [list(european_weather.keys())[np.array(list_of_cloudiness).argmin()], min(list_of_cloudiness)],
        'highest_humidity' : [list(european_weather.keys())[np.array(list_of_humidities).argmax()], max(list_of_humidities)],
        'lowest_humidity' : [list(european_weather.keys())[np.array(list_of_humidities).argmin()], min(list_of_humidities)],
        }
    )

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)


"""Check consistency of fit results"""
import os
import copy
# import itertools
import numpy as np
import mpi4py
from mpi4py import MPI
from gvar import gvar
from latfit.config import GEVP, VERBOSE, RESOLVABLE_STATES
from latfit.config import ALTERNATIVE_PARALLELIZATION
from latfit.config import MATRIX_SUBTRACTION, NOLOOP, DIMSELECT
from latfit.include import VALUE_STR, PARAM_OF_INTEREST
from latfit.analysis.errorcodes import FitRangeInconsistency
from latfit.analysis.filename_windows import filename_plus_config_info
import latfit.config

MPIRANK = MPI.COMM_WORLD.rank
MPISIZE = MPI.COMM_WORLD.Get_size()
mpi4py.rc.recv_mprobe = False

DOWRITE = ALTERNATIVE_PARALLELIZATION and not MPIRANK or not ALTERNATIVE_PARALLELIZATION

try:
    PROFILE = profile  # throws an exception when PROFILE isn't defined
except NameError:
    def profile(arg2):
        """Line profiler default."""
        return arg2
    PROFILE = profile

def mod180(deg):
    """Flip negative phase shift to pos"""
    deg = np.real(deg)
    if hasattr(deg, '__iter__'):
        ret = []
        for _, item in enumerate(deg):
            if item >= 0:
                ret.append(item)
            else:
                ret.append(180+item)
        ret = np.asarray(ret)
    else:
        if deg >= 0:
            ret = deg
        else:
            ret = 180+deg
    return ret

# https://stackoverflow.com/questions/1158076/implement-touch-using-python
@PROFILE
def touch(fname, mode=0o666, dir_fd=None, **kwargs):
    """unix touch"""
    flags = os.O_CREAT | os.O_APPEND
    with os.fdopen(os.open(fname, flags=flags,
                           mode=mode, dir_fd=dir_fd)) as fn1:
        os.utime(fn1.fileno() if os.utime in os.supports_fd else fname,
                 dir_fd=None if os.supports_fd else dir_fd, **kwargs)

def create_dummy_skip(meta):
    """If we find an inconsistent fit,
    create a dummy file so that we skip that in any repeat run"""
    fname = filename_plus_config_info(meta, 'badfit')+'.p'
    if DOWRITE:
        print("creating skip file:", fname)
        touch(fname)

def fit_range_consistency_check(meta, min_arr, name, mod_180=False):
    """Check consistency of energies and phase shifts so far"""
    lparam = []
    for i in min_arr:
        lparam.append(getattr(i[0], name))
    consis = consistent_list_params(lparam, mod_180=mod_180,
                                    collapse_check='energy' in name)
    err_handle(meta, consis, lparam, name)

def err_handle(meta, consis, lparam, name):
    """Handle the case where the fit ranges give inconsistent results"""
    try:
        assert consis
    except AssertionError:
        if DOWRITE:
            print("fit ranges are inconsistent with respect to:", name)
        meta.pr_fit_window()
        if DOWRITE:
            print("t-t0:", latfit.config.T0)
            if MATRIX_SUBTRACTION:
                print("dt matsub:", latfit.config.DELTA_T_MATRIX_SUBTRACTION)
            for i in sort_by_val(lparam):
                print(gvar(i.val, i.err))
        create_dummy_skip(meta)
        raise FitRangeInconsistency

def sort_by_val(lparam):
    """Sort a list of Param objects by val"""
    assert isinstance(lparam, list), str(lparam)
    if hasattr(lparam[0].val, '__iter__'):
        ret = sorted(lparam, key=lambda param: param.val[0])
    else:
        assert not GEVP, str(lparam)
        ret = sorted(lparam, key=lambda param: param.val)
    return ret

def consistent_list_params(lparam, mod_180=False, collapse_check=False):
    """Check the consistency across a list of Param objects"""
    ret = True
    for i in lparam:
        if not ret:
            break
        for j in lparam:
            ret, _ = consistent_params(i, j, mod_180=mod_180, verb=True)
            if not ret:
                break
            if collapse_check:
                ret, idx, jdx = state_collapse_check(i, j, mod_180=mod_180)
                if not ret:
                    if DOWRITE:
                        print("States", idx, "and", jdx,
                              "have collapsed into each other.")
                        print(gvar(i.val, i.err))
                        print(gvar(j.val, j.err))
                    break
    return ret

def state_collapse_check(iitem, jitem, mod_180=False):
    """Check all permutations of j's item against i's item for
    duplicate states"""
    ret = True
    idx, jdx = None, None
    ress = RESOLVABLE_STATES
    if hasattr(iitem.val, '__iter__'):
        llen = len(iitem.val)
        for i in range(llen):
            for j in range(llen):
                if i >= j or i >= ress or j >= ress:
                    continue
                kitem = copy.deepcopy(jitem)
                litem = copy.deepcopy(iitem)
                kitem.swapidx(i, 0)
                kitem.swapidx(j, 1)
                litem.swapidx(i, 0)
                litem.swapidx(j, 1)
                litem.swapidx(0, 1)
                _, ret = consistent_params(kitem, litem, mod_180=mod_180)
                if not ret:
                    idx, jdx = i, j
                    break
    #for perm in list(itertools.permutations(range(len(iitem.val)))):
    #print("DEBUG:")
    #print('kitem', gvar(kitem.val, kitem.err))
    #print('iitem', gvar(i.val, i.err))
    #print("END DEBUG:")
    return ret, idx, jdx

def consistent_params(item1, item2, mod_180=False, verb=VERBOSE):
    """Check the consistency of two Param objects
    if discrepant by > 1.5 sigma, return False (inconsistent)
    """
    diff = np.abs(item1.val-item2.val)
    if mod_180:
        item1a = mod180(item1.val)
        item2a = mod180(item2.val)
        diff2 = np.abs(item1a-item2a)
        diff3 = np.abs(item1.val-item2a)
        diff4 = np.abs(item1a-item2.val)
    else:
        diff2 = None
    if diff2 is not None:
        if hasattr(diff, '__iter__'):
            diff = [min(i, j, k, l) for i, j, k, l in zip(
                diff, diff2, diff3, diff4)]
        else:
            diff = min(diff, diff2, diff3, diff4)
    if GEVP:
        diff = np.asarray(diff)
        err = []
        for i, _ in enumerate(item1.err):
            err.append(max(item1.err[i], item2.err[i]))
        err = np.array(err)
    else:
        err = max(item1.err, item2.err)
    err = np.asarray(err)
    diff = np.asarray(diff)
    tlist = list(np.abs(diff/err))
    test = np.max(tlist)
    test_inv = np.min(tlist[:2])
    try:
        idx = tlist.index(test)
    except ValueError:
        print(tlist)
        raise
    ret = not test > 1.5 # True => all consistent
    ret2 = test_inv > 1.5 # True => first two are both inconsistent
    str1 = str(gvar(item1.val[idx], item1.err[idx]))
    str2 = str(gvar(item2.val[idx], item2.err[idx]))
    cond = str1 == str2 and str1 == '0(0)'
    if not ret and not cond and verb and DOWRITE:
        print("problematic diff:", str1,
              str2)
    return ret, ret2

def check_include(result_min):
    """Check that we've obtained the result we're after"""
    ret = not NOLOOP or not VALUE_STR
    if not ret:
        if PARAM_OF_INTEREST == 'phase shift':
            param = result_min.phase_shift
        elif PARAM_OF_INTEREST == 'energy':
            param = result_min.energy
        dim = DIMSELECT
        chk = gvar(param.val[dim], param.err[dim])
        chk = str(chk)
        if VERBOSE:
            print("chk, VALUE_STR:", chk, VALUE_STR)
        if chk == VALUE_STR:
            ret = True
    return ret


import  pandas              as pd
import  numpy               as np
import  bentoml
from    sklearn.preprocessing       import OneHotEncoder
from    sklearn.base                import BaseEstimator, TransformerMixin
from    sklearn.pipeline            import Pipeline
from    sklearn.preprocessing       import StandardScaler
from    sklearn.compose             import ColumnTransformer
from    xgboost                     import XGBClassifier

#reading data from downloaded files
print("Reading in data\n")
df_train    = pd.read_csv('data/train_values.csv',index_col='building_id')
y_train_df  = pd.read_csv('data/train_labels.csv',index_col='building_id')

#converting training labels into an array
y_train     = y_train_df.values.ravel()
y_train     = list(map(lambda x: x - 1, y_train)) #xgboost needs labels to start from zero

#defining different set of features
cat         = ['foundation_type', 'roof_type', 'ground_floor_type', 'other_floor_type']
num         = ['age', 'area_percentage', 'height_percentage', 'count_floors_pre_eq']
num_flag    = ['geo_level_1_id', 'geo_level_2_id', 'geo_level_3_id', 'has_superstructure_mud_mortar_stone', 'has_superstructure_cement_mortar_brick', 'has_superstructure_rc_non_engineered', 'has_superstructure_rc_engineered']

#custom transformer to engineer new feature
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    def __init__(self, drop_age = True):
        self.drop_age = drop_age
    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        age_ix, area_ix, height_ix, floor_ix = 0, 1, 2, 3 #indexes of the features

        for index in (age_ix, area_ix, height_ix, floor_ix):
            X.iloc[:,index] = np.log1p(X.iloc[:,index]) #log transform

        X_arr               =   X.values
        height_per_area     =   X_arr[:, height_ix] / X_arr[:, area_ix]
        hpa_aged            =   (X_arr[:, height_ix] / X_arr[:, area_ix]) * X_arr[:, age_ix]
        floors_per_height   =   X_arr[:, floor_ix] / X_arr[:, height_ix]

        if self.drop_age:
            X_del = np.delete(X_arr,[floor_ix,age_ix],1)
            return np.c_[X_del, height_per_area, hpa_aged, floors_per_height]
        else:
            X_del = np.delete(X_arr,floor_ix,1)
            return np.c_[X_del, height_per_area, hpa_aged, floors_per_height]

#defining the pipeline for numerical features
num_pipeline = Pipeline([
                        ('attribs_adder', CombinedAttributesAdder()),
                        ('std_scaler', StandardScaler()),
                        ])

#defining final tranformation pipeline
transform_pipeline = ColumnTransformer([('num_flag', 'passthrough', num_flag),
                                        ("num", num_pipeline, num),
                                        ("cat", OneHotEncoder(), cat),
                                        ],
                                        remainder='drop')

#preparing data
print("Preparing data\n")
X_train = transform_pipeline.fit_transform(df_train)

#initializing XGBclassifier with the best params obtained from hyperparameter tuning
clf     = XGBClassifier(max_depth           = 17,
                        gamma               = 0.12553660022636093,
                        reg_alpha           = 15.0,
                        reg_lambda          = 0.2938474618567798,
                        colsample_bytree    = 0.9736583765805357,
                        min_child_weight    = 2.0,
                        n_estimators        = 180,
                        seed                = 0,
                        objective           = 'multi:softprob',
                        random_state        = 0)

#fit model to training data
print("Taining the model\n")
clf.fit(X_train,y_train)

#saving model
bentoml.xgboost.save_model("ritcher_predictor_model",clf,
                            custom_objects={
                                "transformer":transform_pipeline
                            },
                            signatures={
                                "predict":{
                                    "batchable": True,
                                    "batch_dim": 0,
                                }
                            })


import matplotlib.pyplot as plt
import pandas as pd

temperatura = [26]
taxa = [0]
for i in range (1,5):
    temperatura.append(26+2*i)
    taxa.append(0)

dados = {'Temperatura (C)': temperatura,
          'Taxa de canto': taxa }

df = pd.DataFrame(dados)

df.to_csv("D:/Gustavo/Faculdade/Disciplinas/2022.2/BECN/ProjetoFinal/ProjetoFinalArduino/DadosFinais/TaxaDeCanto.csv", index=False)

print(df)